diff --git a/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs b/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs
index c886cc4367..950d57a2e0 100644
--- a/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs
+++ b/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs
@@ -18,13 +18,13 @@ public class SharedRoslynCompilerWorkerHostTests
public void ConfigureWorkerDotnetRuntimeEnvironment_WhenCalled_ShouldDisableMultilevelLookup()
{
ProcessStartInfo startInfo = new();
- startInfo.EnvironmentVariables[SharedRoslynCompilerWorkerHost.DotnetMultilevelLookupEnvironmentVariableName] = "1";
+ startInfo.EnvironmentVariables[SharedRoslynCompilerWorkerAssemblyBuilder.DotnetMultilevelLookupEnvironmentVariableName] = "1";
- SharedRoslynCompilerWorkerHost.ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
+ SharedRoslynCompilerWorkerAssemblyBuilder.ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
Assert.That(
- startInfo.EnvironmentVariables[SharedRoslynCompilerWorkerHost.DotnetMultilevelLookupEnvironmentVariableName],
- Is.EqualTo(SharedRoslynCompilerWorkerHost.DotnetMultilevelLookupDisabledValue));
+ startInfo.EnvironmentVariables[SharedRoslynCompilerWorkerAssemblyBuilder.DotnetMultilevelLookupEnvironmentVariableName],
+ Is.EqualTo(SharedRoslynCompilerWorkerAssemblyBuilder.DotnetMultilevelLookupDisabledValue));
}
[Test]
diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs
new file mode 100644
index 0000000000..468d84030a
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using UnityEditor.Compilation;
+using Debug = UnityEngine.Debug;
+
+using io.github.hatayama.UnityCliLoop.ToolContracts;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// Builds the shared Roslyn compiler worker and configures its .NET process environment.
+ ///
+ internal static class SharedRoslynCompilerWorkerAssemblyBuilder
+ {
+ private const int WorkerAssemblyBuildTimeoutMilliseconds = 30000;
+ internal const string DotnetMultilevelLookupEnvironmentVariableName = "DOTNET_MULTILEVEL_LOOKUP";
+ internal const string DotnetMultilevelLookupDisabledValue = "0";
+
+ ///
+ /// Carries the result data produced by Worker Assembly Build behavior.
+ ///
+ internal sealed class WorkerAssemblyBuildResult
+ {
+ public bool StartedSuccessfully { get; }
+
+ public CompilerMessage[] Messages { get; }
+
+ public string FailureReason { get; }
+
+ public object FailureContext { get; }
+
+ private WorkerAssemblyBuildResult(
+ bool startedSuccessfully,
+ CompilerMessage[] messages,
+ string failureReason,
+ object failureContext)
+ {
+ StartedSuccessfully = startedSuccessfully;
+ Messages = messages;
+ FailureReason = failureReason;
+ FailureContext = failureContext;
+ }
+
+ public static WorkerAssemblyBuildResult Started(CompilerMessage[] messages)
+ {
+ return new WorkerAssemblyBuildResult(true, messages, null, null);
+ }
+
+ public static WorkerAssemblyBuildResult StartFailure(string failureReason, object failureContext)
+ {
+ return new WorkerAssemblyBuildResult(false, null, failureReason, failureContext);
+ }
+ }
+
+ internal static void ConfigureWorkerDotnetRuntimeEnvironment(ProcessStartInfo startInfo)
+ {
+ Debug.Assert(startInfo != null, "startInfo must not be null");
+
+ // Why: global probing can select a system .NET 6 runtime while Unity 6000.4 worker
+ // references come from the bundled .NET 8 runtime, which breaks assembly binding.
+ startInfo.EnvironmentVariables[DotnetMultilevelLookupEnvironmentVariableName] =
+ DotnetMultilevelLookupDisabledValue;
+ }
+
+ internal static WorkerAssemblyBuildResult CompileWorkerAssembly(
+ ExternalCompilerPaths externalCompilerPaths,
+ string workerSourcePath,
+ string workerAssemblyPath,
+ string workerCompileResponseFilePath)
+ {
+ WriteWorkerCompilerResponseFile(
+ workerCompileResponseFilePath,
+ workerSourcePath,
+ workerAssemblyPath,
+ BuildWorkerReferenceSet(externalCompilerPaths));
+
+ ProcessStartInfo startInfo = new() {
+ FileName = externalCompilerPaths.DotnetHostPath,
+ Arguments = $"{QuoteCommandLineArgument(externalCompilerPaths.CompilerDllPath)} @{QuoteCommandLineArgument(workerCompileResponseFilePath)}",
+ WorkingDirectory = Path.GetDirectoryName(workerSourcePath),
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true
+ };
+ ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
+
+ using Process process = ProcessStartHelper.TryStart(startInfo);
+ if (process == null)
+ {
+ return WorkerAssemblyBuildResult.StartFailure(
+ "worker_compiler_start_failed",
+ new
+ {
+ dotnet_host_path = externalCompilerPaths.DotnetHostPath,
+ compiler_dll_path = externalCompilerPaths.CompilerDllPath
+ });
+ }
+
+ Task stdoutTask = process.StandardOutput.ReadToEndAsync();
+ Task stderrTask = process.StandardError.ReadToEndAsync();
+ if (!process.WaitForExit(WorkerAssemblyBuildTimeoutMilliseconds))
+ {
+ if (!process.HasExited)
+ {
+ process.Kill();
+ process.WaitForExit(500);
+ }
+
+ Task.WaitAll(stdoutTask, stderrTask);
+ return WorkerAssemblyBuildResult.StartFailure(
+ "worker_compiler_timeout",
+ new
+ {
+ timeout_ms = WorkerAssemblyBuildTimeoutMilliseconds,
+ dotnet_host_path = externalCompilerPaths.DotnetHostPath,
+ compiler_dll_path = externalCompilerPaths.CompilerDllPath
+ });
+ }
+
+ Task.WaitAll(stdoutTask, stderrTask);
+ CompilerMessage[] compilerMessages = ExternalCompilerMessageParser.Parse(
+ stdoutTask.GetAwaiter().GetResult(),
+ stderrTask.GetAwaiter().GetResult(),
+ process.ExitCode);
+ return WorkerAssemblyBuildResult.Started(compilerMessages);
+ }
+
+ private static List BuildWorkerReferenceSet(ExternalCompilerPaths externalCompilerPaths)
+ {
+ string sharedRuntimeDirectoryPath = externalCompilerPaths.NetCoreRuntimeSharedDirectoryPath;
+ List references = new() {
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Private.CoreLib.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Console.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Collections.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.IO.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.Tasks.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Text.Encoding.Extensions.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.Extensions.dll"),
+ Path.Combine(sharedRuntimeDirectoryPath, "netstandard.dll"),
+ externalCompilerPaths.CodeAnalysisDllPath,
+ externalCompilerPaths.CodeAnalysisCSharpDllPath
+ };
+
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Collections.Immutable.dll"));
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Reflection.Metadata.dll"));
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.CompilerServices.Unsafe.dll"));
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Memory.dll"));
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Buffers.dll"));
+ AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.Tasks.Extensions.dll"));
+
+ return references;
+ }
+
+ private static void WriteWorkerCompilerResponseFile(
+ string responseFilePath,
+ string sourcePath,
+ string dllPath,
+ IReadOnlyCollection references)
+ {
+ List lines = new() {
+ "-nologo",
+ "-nostdlib+",
+ "-target:exe",
+ "-optimize+",
+ "-debug-",
+ QuoteResponseFileArgument("-out:", dllPath)
+ };
+
+ foreach (string reference in references)
+ {
+ lines.Add(QuoteResponseFileArgument("-r:", reference));
+ }
+
+ lines.Add(QuoteResponseFilePath(sourcePath));
+ File.WriteAllLines(responseFilePath, lines);
+ }
+
+ internal static void DeleteWorkerAssemblyIfPresent(string assemblyPath)
+ {
+ if (File.Exists(assemblyPath))
+ {
+ File.Delete(assemblyPath);
+ }
+ }
+
+ private static string QuoteResponseFileArgument(string prefix, string value)
+ {
+ return $"{prefix}{QuoteResponseFilePath(value)}";
+ }
+
+ private static string QuoteResponseFilePath(string path)
+ {
+ return $"\"{path}\"";
+ }
+
+ internal static string QuoteCommandLineArgument(string value)
+ {
+ return $"\"{value}\"";
+ }
+
+ private static void AddIfExists(
+ List destination,
+ string referencePath)
+ {
+ if (string.IsNullOrEmpty(referencePath) || !File.Exists(referencePath))
+ {
+ return;
+ }
+
+ destination.Add(referencePath);
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs.meta b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs.meta
new file mode 100644
index 0000000000..a51b9bf37a
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerAssemblyBuilder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f87f89a82616c479e829263620053937
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerHost.cs b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerHost.cs
index a995e0fde1..db6be6a175 100644
--- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerHost.cs
+++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/DynamicCompilation/SharedRoslynCompilerWorkerHost.cs
@@ -3,7 +3,6 @@
using System.Diagnostics;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Compilation;
using Debug = UnityEngine.Debug;
@@ -21,9 +20,6 @@ internal static class SharedRoslynCompilerWorkerHost
private const string RoslynWorkerSourceFileName = "RoslynCompilerWorker.cs";
private const string RoslynWorkerAssemblyFileName = "RoslynCompilerWorker.dll";
private const string RoslynWorkerCompileResponseFileName = "RoslynCompilerWorker.rsp";
- private const int WorkerAssemblyBuildTimeoutMilliseconds = 30000;
- internal const string DotnetMultilevelLookupEnvironmentVariableName = "DOTNET_MULTILEVEL_LOOKUP";
- internal const string DotnetMultilevelLookupDisabledValue = "0";
private static readonly object SharedCompilerWorkerLock = new();
private static Action s_deleteWorkerDirectory = path => Directory.Delete(path, true);
@@ -100,42 +96,6 @@ public static WorkerStartupResult Failure(string failureReason, object failureCo
}
}
- ///
- /// Carries the result data produced by Worker Assembly Build behavior.
- ///
- private sealed class WorkerAssemblyBuildResult
- {
- public bool StartedSuccessfully { get; }
-
- public CompilerMessage[] Messages { get; }
-
- public string FailureReason { get; }
-
- public object FailureContext { get; }
-
- private WorkerAssemblyBuildResult(
- bool startedSuccessfully,
- CompilerMessage[] messages,
- string failureReason,
- object failureContext)
- {
- StartedSuccessfully = startedSuccessfully;
- Messages = messages;
- FailureReason = failureReason;
- FailureContext = failureContext;
- }
-
- public static WorkerAssemblyBuildResult Started(CompilerMessage[] messages)
- {
- return new WorkerAssemblyBuildResult(true, messages, null, null);
- }
-
- public static WorkerAssemblyBuildResult StartFailure(string failureReason, object failureContext)
- {
- return new WorkerAssemblyBuildResult(false, null, failureReason, failureContext);
- }
- }
-
///
/// Provides Worker Paths behavior for Unity CLI Loop.
///
@@ -283,7 +243,7 @@ private static WorkerStartupResult EnsureWorkerAssemblyBuilt(
return WorkerStartupResult.Ready();
}
- WorkerAssemblyBuildResult buildResult = CompileWorkerAssembly(
+ SharedRoslynCompilerWorkerAssemblyBuilder.WorkerAssemblyBuildResult buildResult = CompileWorkerAssembly(
externalCompilerPaths,
workerPaths.SourcePath,
workerPaths.AssemblyPath,
@@ -300,7 +260,7 @@ private static WorkerStartupResult EnsureWorkerAssemblyBuilt(
return WorkerStartupResult.Ready();
}
- DeleteWorkerAssemblyIfPresent(workerPaths.AssemblyPath);
+ SharedRoslynCompilerWorkerAssemblyBuilder.DeleteWorkerAssemblyIfPresent(workerPaths.AssemblyPath);
return WorkerStartupResult.Failure(
"worker_build_failed",
new
@@ -462,9 +422,9 @@ private static ProcessStartInfo CreateWorkerStartInfo(
ProcessStartInfo startInfo = new() {
FileName = externalCompilerPaths.DotnetHostPath,
Arguments = "exec"
- + " --runtimeconfig " + QuoteCommandLineArgument(externalCompilerPaths.CompilerRuntimeConfigPath)
- + " --depsfile " + QuoteCommandLineArgument(externalCompilerPaths.CompilerDepsFilePath)
- + " " + QuoteCommandLineArgument(workerPaths.AssemblyPath),
+ + " --runtimeconfig " + SharedRoslynCompilerWorkerAssemblyBuilder.QuoteCommandLineArgument(externalCompilerPaths.CompilerRuntimeConfigPath)
+ + " --depsfile " + SharedRoslynCompilerWorkerAssemblyBuilder.QuoteCommandLineArgument(externalCompilerPaths.CompilerDepsFilePath)
+ + " " + SharedRoslynCompilerWorkerAssemblyBuilder.QuoteCommandLineArgument(workerPaths.AssemblyPath),
WorkingDirectory = workerPaths.DirectoryPath,
UseShellExecute = false,
RedirectStandardInput = true,
@@ -473,21 +433,11 @@ private static ProcessStartInfo CreateWorkerStartInfo(
CreateNoWindow = true
};
- ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
+ SharedRoslynCompilerWorkerAssemblyBuilder.ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
return startInfo;
}
- internal static void ConfigureWorkerDotnetRuntimeEnvironment(ProcessStartInfo startInfo)
- {
- Debug.Assert(startInfo != null, "startInfo must not be null");
-
- // Why: global probing can select a system .NET 6 runtime while Unity 6000.4 worker
- // references come from the bundled .NET 8 runtime, which breaks assembly binding.
- startInfo.EnvironmentVariables[DotnetMultilevelLookupEnvironmentVariableName] =
- DotnetMultilevelLookupDisabledValue;
- }
-
- private static WorkerAssemblyBuildResult CompileWorkerAssembly(
+ private static SharedRoslynCompilerWorkerAssemblyBuilder.WorkerAssemblyBuildResult CompileWorkerAssembly(
ExternalCompilerPaths externalCompilerPaths,
string workerSourcePath,
string workerAssemblyPath,
@@ -495,7 +445,7 @@ private static WorkerAssemblyBuildResult CompileWorkerAssembly(
{
if (s_compileWorkerAssemblyForTests != null)
{
- return WorkerAssemblyBuildResult.Started(
+ return SharedRoslynCompilerWorkerAssemblyBuilder.WorkerAssemblyBuildResult.Started(
s_compileWorkerAssemblyForTests(
externalCompilerPaths,
workerSourcePath,
@@ -503,114 +453,11 @@ private static WorkerAssemblyBuildResult CompileWorkerAssembly(
workerCompileResponseFilePath));
}
- WriteWorkerCompilerResponseFile(
- workerCompileResponseFilePath,
+ return SharedRoslynCompilerWorkerAssemblyBuilder.CompileWorkerAssembly(
+ externalCompilerPaths,
workerSourcePath,
workerAssemblyPath,
- BuildWorkerReferenceSet(externalCompilerPaths));
-
- ProcessStartInfo startInfo = new() {
- FileName = externalCompilerPaths.DotnetHostPath,
- Arguments = $"{QuoteCommandLineArgument(externalCompilerPaths.CompilerDllPath)} @{QuoteCommandLineArgument(workerCompileResponseFilePath)}",
- WorkingDirectory = Path.GetDirectoryName(workerSourcePath),
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
- };
- ConfigureWorkerDotnetRuntimeEnvironment(startInfo);
-
- using Process process = ProcessStartHelper.TryStart(startInfo);
- if (process == null)
- {
- return WorkerAssemblyBuildResult.StartFailure(
- "worker_compiler_start_failed",
- new
- {
- dotnet_host_path = externalCompilerPaths.DotnetHostPath,
- compiler_dll_path = externalCompilerPaths.CompilerDllPath
- });
- }
-
- Task stdoutTask = process.StandardOutput.ReadToEndAsync();
- Task stderrTask = process.StandardError.ReadToEndAsync();
- if (!process.WaitForExit(WorkerAssemblyBuildTimeoutMilliseconds))
- {
- if (!process.HasExited)
- {
- process.Kill();
- process.WaitForExit(500);
- }
-
- Task.WaitAll(stdoutTask, stderrTask);
- return WorkerAssemblyBuildResult.StartFailure(
- "worker_compiler_timeout",
- new
- {
- timeout_ms = WorkerAssemblyBuildTimeoutMilliseconds,
- dotnet_host_path = externalCompilerPaths.DotnetHostPath,
- compiler_dll_path = externalCompilerPaths.CompilerDllPath
- });
- }
-
- Task.WaitAll(stdoutTask, stderrTask);
- CompilerMessage[] compilerMessages = ExternalCompilerMessageParser.Parse(
- stdoutTask.GetAwaiter().GetResult(),
- stderrTask.GetAwaiter().GetResult(),
- process.ExitCode);
- return WorkerAssemblyBuildResult.Started(compilerMessages);
- }
-
- private static List BuildWorkerReferenceSet(ExternalCompilerPaths externalCompilerPaths)
- {
- string sharedRuntimeDirectoryPath = externalCompilerPaths.NetCoreRuntimeSharedDirectoryPath;
- List references = new() {
- Path.Combine(sharedRuntimeDirectoryPath, "System.Private.CoreLib.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Console.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Collections.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.IO.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.Tasks.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Text.Encoding.Extensions.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.Extensions.dll"),
- Path.Combine(sharedRuntimeDirectoryPath, "netstandard.dll"),
- externalCompilerPaths.CodeAnalysisDllPath,
- externalCompilerPaths.CodeAnalysisCSharpDllPath
- };
-
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Collections.Immutable.dll"));
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Reflection.Metadata.dll"));
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Runtime.CompilerServices.Unsafe.dll"));
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Memory.dll"));
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Buffers.dll"));
- AddIfExists(references, Path.Combine(sharedRuntimeDirectoryPath, "System.Threading.Tasks.Extensions.dll"));
-
- return references;
- }
-
- private static void WriteWorkerCompilerResponseFile(
- string responseFilePath,
- string sourcePath,
- string dllPath,
- IReadOnlyCollection references)
- {
- List lines = new() {
- "-nologo",
- "-nostdlib+",
- "-target:exe",
- "-optimize+",
- "-debug-",
- QuoteResponseFileArgument("-out:", dllPath)
- };
-
- foreach (string reference in references)
- {
- lines.Add(QuoteResponseFileArgument("-r:", reference));
- }
-
- lines.Add(QuoteResponseFilePath(sourcePath));
- File.WriteAllLines(responseFilePath, lines);
+ workerCompileResponseFilePath);
}
private static bool HasLiveWorkerProcess()
@@ -644,14 +491,6 @@ private static string FindFirstErrorMessage(IReadOnlyCollection
return string.Empty;
}
- private static void DeleteWorkerAssemblyIfPresent(string assemblyPath)
- {
- if (File.Exists(assemblyPath))
- {
- File.Delete(assemblyPath);
- }
- }
-
private static void ShutdownForReload()
{
Shutdown();
@@ -834,32 +673,5 @@ private static object AppendAttempt(object failureContext, int attempt)
};
}
- private static string QuoteResponseFileArgument(string prefix, string value)
- {
- return $"{prefix}{QuoteResponseFilePath(value)}";
- }
-
- private static string QuoteResponseFilePath(string path)
- {
- return $"\"{path}\"";
- }
-
- private static string QuoteCommandLineArgument(string value)
- {
- return $"\"{value}\"";
- }
-
- private static void AddIfExists(
- List destination,
- string referencePath)
- {
- if (string.IsNullOrEmpty(referencePath) || !File.Exists(referencePath))
- {
- return;
- }
-
- destination.Add(referencePath);
- }
-
}
}