Unify code for executing processes. (#8848)
* Create a simple Xamarin.Utils.Execution class that can handle all our process execution needs: * Captures or streams stdout/stderr (in UTF8). * Supports async * Supports a timeout * Does not depend on any other source file we have, only uses BCL API. * Have the execution helper classes from mtouch/mmp (Xamarin.BundlerDriver.RunCommand) and the tests (Xamarin.Tests.ExecutionHelper) use this new class. * Some simplifications were made: * All API that took a string array for the environment now takes a Dictionary<string, string>. * The Driver.RunCommand methods were split out to a separate file. This file also contains a Verbosity field, which is conditioned on not being in mtouch nor mmp, which makes including this file from other projects simpler (such as bgen - in particular bgen was modified to use this Verbosity field instead of its own).
This commit is contained in:
Родитель
ac7056ccb9
Коммит
c3bcfac582
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>net461</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
|
@ -51,6 +52,9 @@
|
|||
<Compile Include="..\..\..\tests\common\ExecutionHelper.cs">
|
||||
<Link>ExecutionHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\common\Execution.cs">
|
||||
<Link>Execution.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tests\mtouch\Cache.cs">
|
||||
<Link>Cache.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -328,6 +328,9 @@
|
|||
<Compile Include="..\Resources.Designer.cs">
|
||||
<DependentUpon>..\Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Driver.execution.cs">
|
||||
<Link>Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Resources.resx">
|
||||
|
|
|
@ -217,7 +217,6 @@ public class BindingTouch {
|
|||
string tmpdir = null;
|
||||
string ns = null;
|
||||
bool delete_temp = true, debug = false;
|
||||
bool verbose = false;
|
||||
bool unsafef = true;
|
||||
bool external = false;
|
||||
bool public_mode = true;
|
||||
|
@ -256,8 +255,8 @@ public class BindingTouch {
|
|||
{ "d=", "Defines a symbol", v => defines.Add (v) },
|
||||
{ "api=", "Adds a API definition source file", v => api_sources.Add (v) },
|
||||
{ "s=", "Adds a source file required to build the API", v => core_sources.Add (v) },
|
||||
{ "q", "Quiet", v => verbose = false },
|
||||
{ "v", "Sets verbose mode", v => verbose = true },
|
||||
{ "q", "Quiet", v => Driver.Verbosity++ },
|
||||
{ "v", "Sets verbose mode", v => Driver.Verbosity-- },
|
||||
{ "x=", "Adds the specified file to the build, used after the core files are compiled", v => extra_sources.Add (v) },
|
||||
{ "e", "Generates smaller classes that can not be subclassed (previously called 'external mode')", v => external = true },
|
||||
{ "p", "Sets private mode", v => public_mode = false },
|
||||
|
@ -450,7 +449,7 @@ public class BindingTouch {
|
|||
if (!string.IsNullOrEmpty (Path.GetDirectoryName (baselibdll)))
|
||||
cargs.Add ("-lib:" + Path.GetDirectoryName (baselibdll));
|
||||
|
||||
if (Driver.RunCommand (compiler, cargs, null, out var compile_output, true, verbose ? 1 : 0) != 0)
|
||||
if (Driver.RunCommand (compiler, cargs, null, out var compile_output, true, Driver.Verbosity) != 0)
|
||||
throw ErrorHelper.CreateError (2, compile_output.ToString ().Replace ("\n", "\n\t"));
|
||||
|
||||
|
||||
|
@ -493,7 +492,7 @@ public class BindingTouch {
|
|||
try {
|
||||
api = universe.LoadFile (tmpass);
|
||||
} catch (Exception e) {
|
||||
if (verbose)
|
||||
if (Driver.Verbosity > 0)
|
||||
Console.WriteLine (e);
|
||||
|
||||
Console.Error.WriteLine ("Error loading API definition from {0}", tmpass);
|
||||
|
@ -504,7 +503,7 @@ public class BindingTouch {
|
|||
try {
|
||||
baselib = universe.LoadFile (baselibdll);
|
||||
} catch (Exception e){
|
||||
if (verbose)
|
||||
if (Driver.Verbosity > 0)
|
||||
Console.WriteLine (e);
|
||||
|
||||
Console.Error.WriteLine ("Error loading base library {0}", baselibdll);
|
||||
|
@ -604,7 +603,7 @@ public class BindingTouch {
|
|||
if (!string.IsNullOrEmpty (Path.GetDirectoryName (baselibdll)))
|
||||
cargs.Add ("-lib:" + Path.GetDirectoryName (baselibdll));
|
||||
|
||||
if (Driver.RunCommand (compiler, cargs, null, out var generated_compile_output, true, verbose ? 1 : 0) != 0)
|
||||
if (Driver.RunCommand (compiler, cargs, null, out var generated_compile_output, true, Driver.Verbosity) != 0)
|
||||
throw ErrorHelper.CreateError (1000, generated_compile_output.ToString ().Replace ("\n", "\n\t"));
|
||||
} finally {
|
||||
if (delete_temp)
|
||||
|
|
|
@ -389,6 +389,9 @@
|
|||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<Compile Include="..\tools\common\Driver.execution.cs">
|
||||
<Link>Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources.resx">
|
||||
|
|
|
@ -31,5 +31,8 @@
|
|||
<Compile Include="..\..\tools\common\StringUtils.cs">
|
||||
<Link>StringUtils.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Execution.cs">
|
||||
<Link>Execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -2,11 +2,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Utils;
|
||||
|
@ -438,29 +435,6 @@ namespace Xamarin.Tests
|
|||
}
|
||||
|
||||
static class ExecutionHelper {
|
||||
static int Execute (string fileName, IList<string> arguments, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, StringUtils.FormatArguments (arguments));
|
||||
return Execute (psi, (line) => {
|
||||
lock (stdout)
|
||||
stdout.AppendLine (line);
|
||||
}, (line) => {
|
||||
lock (stderr)
|
||||
stderr.AppendLine (line);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
static int Execute (ProcessStartInfo psi, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (psi, (line) => {
|
||||
lock (stdout)
|
||||
stdout.AppendLine (line);
|
||||
}, (line) => {
|
||||
lock (stderr)
|
||||
stderr.AppendLine (line);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments)
|
||||
{
|
||||
return Execute (fileName, arguments, null, null, null, null);
|
||||
|
@ -471,16 +445,9 @@ namespace Xamarin.Tests
|
|||
return Execute (fileName, arguments, null, null, null, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (fileName, arguments, null, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, string working_directory = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, StringUtils.FormatArguments (arguments));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
return Execute (psi, stdout_callback, stderr_callback, timeout);
|
||||
return Execute (fileName, arguments, timed_out: out var _, workingDirectory: working_directory, stdout_callback: stdout_callback, stderr_callback: stderr_callback, timeout: timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, out StringBuilder output)
|
||||
|
@ -491,190 +458,58 @@ namespace Xamarin.Tests
|
|||
public static int Execute (string fileName, IList<string> arguments, out StringBuilder output, string working_directory, TimeSpan? timeout = null)
|
||||
{
|
||||
output = new StringBuilder ();
|
||||
var psi = new ProcessStartInfo (fileName, StringUtils.FormatArguments (arguments));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
var capturedOutput = output;
|
||||
var callback = new Action<string> ((v) => {
|
||||
lock (psi)
|
||||
capturedOutput.AppendLine (v);
|
||||
});
|
||||
return Execute (psi, callback, callback, timeout);
|
||||
return Execute (fileName, arguments, out var _, workingDirectory: working_directory, stdout: output, stderr: output, timeout: timeout);
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, out bool timed_out, Dictionary<string, string> environment_variables = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
public static int Execute (string fileName, IList<string> arguments, out bool timed_out, string workingDirectory = null, Dictionary<string, string> environment_variables = null, StringBuilder stdout = null, StringBuilder stderr = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, StringUtils.FormatArguments (arguments));
|
||||
if (environment_variables != null) {
|
||||
foreach (var ev in environment_variables)
|
||||
psi.EnvironmentVariables [ev.Key] = ev.Value;
|
||||
}
|
||||
return Execute (psi, out timed_out, stdout_callback, stderr_callback, timeout);
|
||||
var rv = Execution.RunWithStringBuildersAsync (fileName, arguments, workingDirectory: workingDirectory, environment: environment_variables, standardOutput: stdout, standardError: stderr, timeout: timeout).Result;
|
||||
timed_out = rv.TimedOut;
|
||||
if (rv.TimedOut)
|
||||
Console.WriteLine ($"Command '{fileName} {StringUtils.FormatArguments (arguments)}' didn't finish in {timeout.Value.TotalMilliseconds} minutes, and was killed.", timeout.Value.TotalMinutes);
|
||||
return rv.ExitCode;
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, out bool timed_out, string working_directory = null, Dictionary<string, string> environment_variables = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
public static int Execute (string fileName, IList<string> arguments, out bool timed_out, string workingDirectory = null, Dictionary<string, string> environment_variables = null, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo (fileName, StringUtils.FormatArguments (arguments));
|
||||
psi.WorkingDirectory = working_directory;
|
||||
if (environment_variables != null) {
|
||||
foreach (var ev in environment_variables)
|
||||
psi.EnvironmentVariables [ev.Key] = ev.Value;
|
||||
}
|
||||
return Execute (psi, out timed_out, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (ProcessStartInfo psi, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (psi, out var _, stdout_callback, stderr_callback, timeout);
|
||||
}
|
||||
|
||||
public static int Execute (ProcessStartInfo psi, out bool timed_out, Action<string> stdout_callback = null, Action<string> stderr_callback = null, TimeSpan? timeout = null)
|
||||
{
|
||||
var watch = new Stopwatch ();
|
||||
watch.Start ();
|
||||
|
||||
if (stdout_callback == null)
|
||||
stdout_callback = Console.WriteLine;
|
||||
if (stderr_callback == null)
|
||||
stderr_callback = Console.Error.WriteLine;
|
||||
|
||||
try {
|
||||
psi.UseShellExecute = false;
|
||||
psi.RedirectStandardError = true;
|
||||
psi.RedirectStandardOutput = true;
|
||||
if (!string.IsNullOrEmpty (psi.WorkingDirectory))
|
||||
Console.Write ($"cd {StringUtils.Quote (psi.WorkingDirectory)} && ");
|
||||
Console.WriteLine ("{0} {1}", psi.FileName, psi.Arguments);
|
||||
using (var p = new Process ()) {
|
||||
p.StartInfo = psi;
|
||||
// mtouch/mmp writes UTF8 data outside of the ASCII range, so we need to make sure
|
||||
// we read it in the same format. This also means we can't use the events to get
|
||||
// stdout/stderr, because mono's Process class parses those using Encoding.Default.
|
||||
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
|
||||
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||
p.Start ();
|
||||
|
||||
var outReader = new Thread (() =>
|
||||
{
|
||||
string l;
|
||||
while ((l = p.StandardOutput.ReadLine ()) != null) {
|
||||
stdout_callback (l);
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
};
|
||||
outReader.Start ();
|
||||
|
||||
var errReader = new Thread (() =>
|
||||
{
|
||||
string l;
|
||||
while ((l = p.StandardError.ReadLine ()) != null) {
|
||||
stderr_callback (l);
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
};
|
||||
errReader.Start ();
|
||||
|
||||
if (timeout == null)
|
||||
timeout = TimeSpan.FromMinutes (5);
|
||||
if (!p.WaitForExit ((int) timeout.Value.TotalMilliseconds)) {
|
||||
timed_out = true;
|
||||
Console.WriteLine ("Command didn't finish in {0} minutes:", timeout.Value.TotalMinutes);
|
||||
Console.WriteLine ("{0} {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
|
||||
Console.WriteLine ("Will now kill the process");
|
||||
kill (p.Id, 9);
|
||||
if (!p.WaitForExit (1000 /* killing should be fairly quick */)) {
|
||||
Console.WriteLine ("Kill failed to kill in 1 second !?");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
timed_out = false;
|
||||
}
|
||||
|
||||
outReader.Join (TimeSpan.FromSeconds (1));
|
||||
errReader.Join (TimeSpan.FromSeconds (1));
|
||||
|
||||
return p.ExitCode;
|
||||
}
|
||||
} finally {
|
||||
Console.WriteLine ("{0} Executed in {1}: {2} {3}", DateTime.Now, watch.Elapsed.ToString (), psi.FileName, psi.Arguments);
|
||||
}
|
||||
var rv = Execution.RunWithCallbacksAsync (fileName, arguments, workingDirectory: workingDirectory, environment: environment_variables, standardOutput: stdout_callback, standardError: stderr_callback, timeout: timeout).Result;
|
||||
timed_out = rv.TimedOut;
|
||||
if (rv.TimedOut)
|
||||
Console.WriteLine ($"Command '{fileName} {StringUtils.FormatArguments (arguments)}' didn't finish in {timeout.Value.TotalMilliseconds} minutes, and was killed.", timeout.Value.TotalMinutes);
|
||||
return rv.ExitCode;
|
||||
}
|
||||
|
||||
public static int Execute (string fileName, IList<string> arguments, out string output, TimeSpan? timeout = null)
|
||||
{
|
||||
var sb = new StringBuilder ();
|
||||
var psi = new ProcessStartInfo ();
|
||||
psi.FileName = fileName;
|
||||
psi.Arguments = StringUtils.FormatArguments (arguments);
|
||||
var rv = Execute (psi, sb, sb, timeout);
|
||||
var rv = Execute (fileName, arguments, timed_out: out var _, stdout: sb, stderr: sb, timeout: timeout);
|
||||
output = sb.ToString ();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The arguments are automatically quoted.
|
||||
public static int Execute (string fileName, IList<string> arguments, Dictionary<string, string> environmentVariables, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null, string workingDirectory = null)
|
||||
{
|
||||
return Execute (fileName, StringUtils.FormatArguments (arguments), environmentVariables, stdout, stderr, timeout, workingDirectory);
|
||||
return Execute (fileName, arguments, timed_out: out var _, workingDirectory: workingDirectory, environment_variables: environmentVariables, stdout: stdout, stderr: stderr, timeout: timeout);
|
||||
}
|
||||
|
||||
static int Execute (string fileName, string arguments, Dictionary<string, string> environmentVariables, StringBuilder stdout, StringBuilder stderr, TimeSpan? timeout = null, string workingDirectory = null)
|
||||
{
|
||||
if (stdout == null)
|
||||
stdout = new StringBuilder ();
|
||||
if (stderr == null)
|
||||
stderr = new StringBuilder ();
|
||||
|
||||
var psi = new ProcessStartInfo ();
|
||||
psi.FileName = fileName;
|
||||
psi.Arguments = arguments;
|
||||
if (!string.IsNullOrEmpty (workingDirectory))
|
||||
psi.WorkingDirectory = workingDirectory;
|
||||
if (environmentVariables != null) {
|
||||
var envs = psi.EnvironmentVariables;
|
||||
foreach (var kvp in environmentVariables) {
|
||||
if (kvp.Value == null) {
|
||||
envs.Remove (kvp.Key);
|
||||
} else {
|
||||
envs [kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Execute (psi, stdout, stderr, timeout);
|
||||
}
|
||||
|
||||
[DllImport ("libc")]
|
||||
private static extern void kill (int pid, int sig);
|
||||
public static string Execute (string fileName, IList<string> arguments, bool throwOnError = true, Dictionary<string, string> environmentVariables = null, bool hide_output = false, TimeSpan? timeout = null)
|
||||
{
|
||||
return Execute (fileName, StringUtils.FormatArguments (arguments), throwOnError, environmentVariables, hide_output, timeout);
|
||||
}
|
||||
|
||||
static string Execute (string fileName, string arguments, bool throwOnError = true, Dictionary<string, string> environmentVariables = null,
|
||||
bool hide_output = false, TimeSpan? timeout = null
|
||||
)
|
||||
{
|
||||
StringBuilder output = new StringBuilder ();
|
||||
int exitCode = Execute (fileName, arguments, environmentVariables, output, output, timeout);
|
||||
var throw_exc = throwOnError && exitCode != 0;
|
||||
var rv = Execution.RunAsync (fileName, arguments, mergeOutput: true, environment: environmentVariables, timeout: timeout).Result;
|
||||
var output = rv.StandardOutput.ToString ();
|
||||
var throw_exc = throwOnError && rv.ExitCode != 0;
|
||||
if (!hide_output || throw_exc) {
|
||||
Console.WriteLine ("{0} {1}", fileName, arguments);
|
||||
Console.WriteLine ($"{fileName} {StringUtils.FormatArguments (arguments)} (exit code: {rv.ExitCode})");
|
||||
Console.WriteLine (output);
|
||||
Console.WriteLine ("Exit code: {0}", exitCode);
|
||||
Console.WriteLine ("Exit code: {0}", rv.ExitCode);
|
||||
}
|
||||
if (throw_exc)
|
||||
throw new TestExecutionException ($"Execution failed (exit code: {exitCode}) for '{fileName} {arguments}'");
|
||||
return output.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
class TestExecutionException : Exception {
|
||||
public TestExecutionException (string output)
|
||||
: base (output)
|
||||
{
|
||||
if (throwOnError && rv.ExitCode != 0)
|
||||
throw new Exception ();
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,10 +209,11 @@ namespace Xamarin.MMP.Tests
|
|||
return RunAndAssert (exe, args, "Command: " + exe);
|
||||
}
|
||||
|
||||
public static string RunAndAssert (string exe, IList<string> args, string stepName, bool shouldFail = false, Func<string> getAdditionalFailInfo = null, string[] environment = null)
|
||||
public static string RunAndAssert (string exe, IList<string> args, string stepName, bool shouldFail = false, Func<string> getAdditionalFailInfo = null, Dictionary<string, string> environment = null)
|
||||
{
|
||||
StringBuilder output = new StringBuilder ();
|
||||
Environment.SetEnvironmentVariable ("MONO_PATH", null);
|
||||
environment ??= new Dictionary<string, string> ();
|
||||
environment ["MONO_PATH"] = null;
|
||||
int compileResult = Xamarin.Bundler.Driver.RunCommand (exe, args, environment, output, suppressPrintOnErrors: shouldFail);
|
||||
if (!shouldFail && compileResult != 0 && Xamarin.Bundler.Driver.Verbosity < 1) {
|
||||
Console.WriteLine ($"Execution failed; exit code: {compileResult}");
|
||||
|
@ -247,29 +248,32 @@ namespace Xamarin.MMP.Tests
|
|||
|
||||
// TODO - This is not enough for MSBuild to really work. We need stuff to have it not use system targets!
|
||||
// These are required to have xbuild use are local build instead of system install
|
||||
Environment.SetEnvironmentVariable ("TargetFrameworkFallbackSearchPaths", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks");
|
||||
Environment.SetEnvironmentVariable ("MSBuildExtensionsPathFallbackPathsOverride", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild");
|
||||
Environment.SetEnvironmentVariable ("XAMMAC_FRAMEWORK_PATH", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
Environment.SetEnvironmentVariable ("XamarinMacFrameworkRoot", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
var env = new Dictionary<string, string> {
|
||||
{ "TargetFrameworkFallbackSearchPaths", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks" },
|
||||
{ "MSBuildExtensionsPathFallbackPathsOverride", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild" },
|
||||
{ "XAMMAC_FRAMEWORK_PATH", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current" },
|
||||
{ "XamarinMacFrameworkRoot", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current" },
|
||||
};
|
||||
|
||||
// This is to force build to use our mmp and not system mmp
|
||||
var buildArgs = new List<string> ();
|
||||
buildArgs.Add ("build");
|
||||
buildArgs.Add (csprojTarget);
|
||||
|
||||
return RunAndAssert ("/Applications/Visual Studio.app/Contents/MacOS/vstool", buildArgs, "Compile", shouldFail: true);
|
||||
return RunAndAssert ("/Applications/Visual Studio.app/Contents/MacOS/vstool", buildArgs, "Compile", shouldFail: true, environment: env);
|
||||
}
|
||||
|
||||
public static string BuildProject (string csprojTarget, bool shouldFail = false, bool release = false, string[] environment = null, IList<string> extraArgs = null)
|
||||
public static string BuildProject (string csprojTarget, bool shouldFail = false, bool release = false, Dictionary<string, string> environment = null, IList<string> extraArgs = null)
|
||||
{
|
||||
string rootDirectory = FindRootDirectory ();
|
||||
|
||||
// TODO - This is not enough for MSBuild to really work. We need stuff to have it not use system targets!
|
||||
// These are required to have xbuild use are local build instead of system install
|
||||
Environment.SetEnvironmentVariable ("TargetFrameworkFallbackSearchPaths", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks");
|
||||
Environment.SetEnvironmentVariable ("MSBuildExtensionsPathFallbackPathsOverride", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild");
|
||||
Environment.SetEnvironmentVariable ("XAMMAC_FRAMEWORK_PATH", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
Environment.SetEnvironmentVariable ("XamarinMacFrameworkRoot", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
environment ??= new Dictionary<string, string> ();
|
||||
environment ["TargetFrameworkFallbackSearchPaths"] = rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks";
|
||||
environment ["MSBuildExtensionsPathFallbackPathsOverride"] = rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild";
|
||||
environment ["XAMMAC_FRAMEWORK_PATH"] = rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current";
|
||||
environment ["XamarinMacFrameworkRoot"] = rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current";
|
||||
|
||||
// This is to force build to use our mmp and not system mmp
|
||||
var buildArgs = new List<string> ();
|
||||
|
@ -433,7 +437,7 @@ namespace Xamarin.MMP.Tests
|
|||
return GenerateEXEProject (config);
|
||||
}
|
||||
|
||||
public static string GenerateAndBuildUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, string[] environment = null)
|
||||
public static string GenerateAndBuildUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, Dictionary<string, string> environment = null)
|
||||
{
|
||||
string csprojTarget = GenerateUnifiedExecutableProject (config);
|
||||
return BuildProject (csprojTarget, shouldFail: shouldFail, release: config.Release, environment: environment);
|
||||
|
@ -444,7 +448,7 @@ namespace Xamarin.MMP.Tests
|
|||
return RunEXEAndVerifyGUID (config.TmpDir, config.guid, config.ExecutablePath);
|
||||
}
|
||||
|
||||
public static OutputText TestUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, string[] environment = null)
|
||||
public static OutputText TestUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, Dictionary<string, string> environment = null)
|
||||
{
|
||||
AddGUIDTestCode (config);
|
||||
|
||||
|
@ -583,12 +587,15 @@ namespace TestCase
|
|||
{
|
||||
string rootDirectory = FindRootDirectory ();
|
||||
|
||||
Environment.SetEnvironmentVariable ("TargetFrameworkFallbackSearchPaths", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks");
|
||||
Environment.SetEnvironmentVariable ("MSBuildExtensionsPathFallbackPathsOverride", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild");
|
||||
Environment.SetEnvironmentVariable ("XAMMAC_FRAMEWORK_PATH", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
Environment.SetEnvironmentVariable ("XamarinMacFrameworkRoot", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current");
|
||||
var env = new Dictionary<string, string> {
|
||||
{ "TargetFrameworkFallbackSearchPaths", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild-frameworks" },
|
||||
{ "MSBuildExtensionsPathFallbackPathsOverride", rootDirectory + "/Library/Frameworks/Mono.framework/External/xbuild" },
|
||||
{ "XAMMAC_FRAMEWORK_PATH", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current" },
|
||||
{ "XamarinMacFrameworkRoot", rootDirectory + "/Library/Frameworks/Xamarin.Mac.framework/Versions/Current" },
|
||||
};
|
||||
|
||||
var rv = ExecutionHelper.Execute (Configuration.XIBuildPath, new [] { $"--", "/t:Restore", project}, out var output);
|
||||
var output = new StringBuilder ();
|
||||
var rv = ExecutionHelper.Execute (Configuration.XIBuildPath, new [] { $"--", "/t:Restore", project}, stdout: output, stderr: output, environmentVariables: env);
|
||||
if (rv != 0) {
|
||||
Console.WriteLine ("nuget restore failed:");
|
||||
Console.WriteLine (output);
|
||||
|
@ -629,88 +636,3 @@ namespace TestCase
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bit of a hack so we can reuse all of the RunCommand logic
|
||||
#if !MMP_TEST
|
||||
namespace Xamarin.Bundler {
|
||||
public static partial class Driver
|
||||
{
|
||||
public static int verbose { get { return 0; } }
|
||||
public static int Verbosity { get { return verbose; }}
|
||||
public static int RunCommand (string path, IList<string> args, string[] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
Exception stdin_exc = null;
|
||||
var info = new ProcessStartInfo (path, StringUtils.FormatArguments (args));
|
||||
info.UseShellExecute = false;
|
||||
info.RedirectStandardInput = false;
|
||||
info.RedirectStandardOutput = true;
|
||||
info.RedirectStandardError = true;
|
||||
System.Threading.ManualResetEvent stdout_completed = new System.Threading.ManualResetEvent (false);
|
||||
System.Threading.ManualResetEvent stderr_completed = new System.Threading.ManualResetEvent (false);
|
||||
|
||||
if (output == null)
|
||||
output = new StringBuilder ();
|
||||
|
||||
if (env != null){
|
||||
if (env.Length % 2 != 0)
|
||||
throw new Exception ("You passed an environment key without a value");
|
||||
|
||||
for (int i = 0; i < env.Length; i += 2) {
|
||||
if (env [i + 1] == null) {
|
||||
info.EnvironmentVariables.Remove (env [i]);
|
||||
} else {
|
||||
info.EnvironmentVariables [env [i]] = env [i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 0)
|
||||
Console.WriteLine ("{0} {1}", path, args);
|
||||
|
||||
using (var p = Process.Start (info)) {
|
||||
|
||||
p.OutputDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.ErrorDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.BeginOutputReadLine ();
|
||||
p.BeginErrorReadLine ();
|
||||
|
||||
p.WaitForExit ();
|
||||
|
||||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
|
||||
if (p.ExitCode != 0) {
|
||||
// note: this repeat the failing command line. However we can't avoid this since we're often
|
||||
// running commands in parallel (so the last one printed might not be the one failing)
|
||||
if (!suppressPrintOnErrors)
|
||||
Console.Error.WriteLine ("Process exited with code {0}, command:\n{1} {2}{3}", p.ExitCode, path, StringUtils.FormatArguments (args), output.Length > 0 ? "\n" + output.ToString () : string.Empty);
|
||||
return p.ExitCode;
|
||||
} else if (verbose > 0 && output.Length > 0 && !suppressPrintOnErrors) {
|
||||
Console.WriteLine (output.ToString ());
|
||||
}
|
||||
|
||||
if (stdin_exc != null)
|
||||
throw stdin_exc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<Compile Include="..\..\..\tools\common\ApplePlatform.cs">
|
||||
<Link>external\ApplePlatform.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\common\Execution.cs">
|
||||
<Link>external\Execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="external\" />
|
||||
|
|
|
@ -59,12 +59,11 @@ namespace Xamarin.MMP.Tests.Unit
|
|||
commandsRun.Clear ();
|
||||
}
|
||||
|
||||
int OnRunCommand (string path, IList<string> args, string [] env, StringBuilder output, bool suppressPrintOnErrors)
|
||||
int OnRunCommand (string path, IList<string> args, Dictionary<string, string> env, StringBuilder output, bool suppressPrintOnErrors)
|
||||
{
|
||||
commandsRun.Add (Tuple.Create <string, IList<string>>(path, args));
|
||||
if (path != AOTCompiler.StripCommand && path != AOTCompiler.DeleteDebugSymbolCommand) {
|
||||
Assert.IsTrue (env[0] == "MONO_PATH", "MONO_PATH should be first env set");
|
||||
Assert.IsTrue (env[1] == TestRootDir, "MONO_PATH should be set to our expected value");
|
||||
Assert.AreEqual (TestRootDir, env ["MONO_PATH"], "MONO_PATH should be set to our expected value");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<RootNamespace>Xamarin.MMP.Tests</RootNamespace>
|
||||
<AssemblyName>mmptest</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Xamarin.MMP.Tests
|
|||
TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) { CSProjConfig = projectConfig };
|
||||
string buildOutput = TI.TestUnifiedExecutable (test).BuildOutput;
|
||||
string [] splitBuildOutput = TI.TestUnifiedExecutable (test).BuildOutput.Split (new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
string clangInvocation = splitBuildOutput.Single (x => x.Contains ("usr/bin/clang"));
|
||||
string clangInvocation = splitBuildOutput.Single (x => x.Contains ("usr/bin/clang") && x.Contains ("mmacosx-version-min"));
|
||||
return clangInvocation.Split (new string[] { " " }, StringSplitOptions.None);
|
||||
}
|
||||
|
||||
|
@ -548,7 +548,7 @@ namespace Xamarin.MMP.Tests
|
|||
TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) {
|
||||
CSProjConfig = "<DebugSymbols>True</DebugSymbols>", // This makes the msbuild tasks pass /debug to mmp
|
||||
};
|
||||
TI.TestUnifiedExecutable (test, shouldFail: false, environment: new [] { "MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (oldXcode)) });
|
||||
TI.TestUnifiedExecutable (test, shouldFail: false, environment: new Dictionary<string, string> { { "MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (oldXcode)) } });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -112,8 +112,10 @@ namespace Xamarin.MMP.Tests
|
|||
MMPTests.RunMMPTest (tmpDir => {
|
||||
TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) { ItemGroup = CreateSingleNativeRef (SimpleStaticPath, "Static") };
|
||||
NativeReferenceTestCore (tmpDir, test, "Unified_WithNativeReferences_InMainProjectWorks - Static", null, true, false, s => {
|
||||
string clangLine = s.Split ('\n').First (x => x.Contains ("usr/bin/clang"));
|
||||
return clangLine.Contains ("SimpleClassStatic.a");
|
||||
var clangLines = s.Split ('\n').Where (x => x.Contains ("usr/bin/clang"));
|
||||
var staticLib = clangLines.Where (x => x.Contains ("SimpleClassStatic.a"));
|
||||
Assert.That (staticLib, Is.Not.Empty, "SimpleClassStatic.a:\n\t{0}", string.Join ("\n\t", clangLines));
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
|
@ -31,10 +32,10 @@ namespace Xamarin.MMP.Tests {
|
|||
|
||||
string netStandardProject = TI.GenerateNetStandardProject (config);
|
||||
|
||||
var environment = new string [] {
|
||||
"MSBUILD_EXE_PATH", null,
|
||||
"MSBuildExtensionsPathFallbackPathsOverride", null,
|
||||
"MSBuildSDKsPath", null,
|
||||
var environment = new Dictionary<string, string> {
|
||||
{ "MSBUILD_EXE_PATH", null },
|
||||
{ "MSBuildExtensionsPathFallbackPathsOverride", null },
|
||||
{ "MSBuildSDKsPath", null },
|
||||
};
|
||||
|
||||
TI.RunAndAssert("/usr/local/share/dotnet/dotnet", new [] { "restore", netStandardProject }, "Restore", environment: environment);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
|
||||
|
@ -20,7 +21,7 @@ namespace Xamarin.MMP.Tests
|
|||
|
||||
MMPTests.RunMMPTest (tmpDir => {
|
||||
TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir);
|
||||
var output = TI.TestUnifiedExecutable (test, environment: new string[] { "MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (oldXcode)) });
|
||||
var output = TI.TestUnifiedExecutable (test, environment: new Dictionary<string, string> { { "MD_APPLE_SDK_ROOT", Path.GetDirectoryName (Path.GetDirectoryName (oldXcode)) } });
|
||||
output.Messages.AssertWarningPattern (135, $"Did not link system framework");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<RootNamespace>msbuildMac</RootNamespace>
|
||||
<AssemblyName>msbuild-mac</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -64,6 +65,12 @@
|
|||
<Compile Include="..\common\Profile.cs">
|
||||
<Link>Profile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Driver.execution.cs">
|
||||
<Link>Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Execution.cs">
|
||||
<Link>Execution.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\mtouch\Cache.cs">
|
||||
<Link>Cache.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -130,7 +130,7 @@ namespace Xamarin.MMP.Tests
|
|||
Assert.IsTrue (File.Exists (Path.Combine (tmpDir, "bin/Debug/UnifiedExample.app/Contents/MonoBundle/SimpleClassDylib.dylib")));
|
||||
|
||||
StringBuilder output = new StringBuilder ();
|
||||
Xamarin.Bundler.Driver.RunCommand ("/usr/bin/otool", new [] { "-L", Path.Combine (tmpDir, "bin/Debug/UnifiedExample.app/Contents/MacOS/UnifiedExample") }, null, output);
|
||||
Xamarin.Bundler.Driver.RunCommand ("/usr/bin/otool", new [] { "-L", Path.Combine (tmpDir, "bin/Debug/UnifiedExample.app/Contents/MacOS/UnifiedExample") }, output);
|
||||
Assert.IsTrue (output.ToString ().Contains ("SimpleClassDylib.dylib"));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -107,6 +107,12 @@
|
|||
</Compile>
|
||||
<Compile Include="Setup.cs" />
|
||||
<Compile Include="Compat.cs" />
|
||||
<Compile Include="..\..\tools\common\Driver.execution.cs">
|
||||
<Link>Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Execution.cs">
|
||||
<Link>Execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -60,6 +60,9 @@
|
|||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Samples.cs" />
|
||||
<Compile Include="..\..\tools\common\Execution.cs">
|
||||
<Link>external\Execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -101,6 +101,12 @@
|
|||
<Compile Include="..\..\tests\test-libraries\TrampolineTest.generated.cs" />
|
||||
<Compile Include="..\..\tests\test-libraries\RegistrarTest.generated.cs" />
|
||||
<Compile Include="AppKit\NSGridViewTest.cs" />
|
||||
<Compile Include="..\..\tools\common\Driver.execution.cs">
|
||||
<Link>Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\Execution.cs">
|
||||
<Link>Execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
|
|
|
@ -490,174 +490,6 @@ namespace Xamarin.Bundler {
|
|||
ErrorHelper.Warning (90, Errors.MX0090, /* The target framework '{0}' is deprecated. Use '{1}' instead. */ fx, TargetFramework);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, params string [] args)
|
||||
{
|
||||
return RunCommand (path, args, null, (Action<string>) null, (Action<string>) null, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args)
|
||||
{
|
||||
return RunCommand (path, args, null, (Action<string>) null, (Action<string>) null, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output)
|
||||
{
|
||||
return RunCommand (path, args, null, output, output, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, null, output, output, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, StringBuilder output)
|
||||
{
|
||||
return RunCommand (path, args, env, output, output, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, StringBuilder output, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, env, output, output, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, StringBuilder output, StringBuilder error)
|
||||
{
|
||||
return RunCommand (path, args, env, output, error, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, StringBuilder error)
|
||||
{
|
||||
return RunCommand (path, args, null, output, error, false);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, StringBuilder error, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, null, output, error, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, StringBuilder output, StringBuilder error, bool suppressPrintOnErrors)
|
||||
{
|
||||
var output_received = output == null ? null : new Action<string> ((v) => { if (v != null) output.AppendLine (v); });
|
||||
var error_received = error == null ? null : new Action<string> ((v) => { if (v != null) error.AppendLine (v); });
|
||||
return RunCommand (path, args, env, output_received, error_received, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, string [] env, Action<string> output_received, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, env, output_received, output_received, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, string [] env, Action<string> output_received, Action<string> error_received)
|
||||
{
|
||||
return RunCommand (path, args, env, output_received, error_received, false);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, string[] env, Action<string> output_received, Action<string> error_received, bool suppressPrintOnErrors)
|
||||
{
|
||||
Exception stdin_exc = null;
|
||||
var info = new ProcessStartInfo (path, StringUtils.FormatArguments (args));
|
||||
info.UseShellExecute = false;
|
||||
info.RedirectStandardInput = false;
|
||||
info.RedirectStandardOutput = true;
|
||||
info.RedirectStandardError = true;
|
||||
System.Threading.ManualResetEvent stdout_completed = new System.Threading.ManualResetEvent (false);
|
||||
System.Threading.ManualResetEvent stderr_completed = new System.Threading.ManualResetEvent (false);
|
||||
|
||||
if (output_received == null ^ error_received == null)
|
||||
throw new ArgumentException ("Either both or neither of 'output_received' and 'error_received' can be specified.");
|
||||
|
||||
var lockobj = new object ();
|
||||
StringBuilder output = null;
|
||||
if (output_received == null) {
|
||||
output = new StringBuilder ();
|
||||
output_received = (line) => {
|
||||
if (line != null)
|
||||
output.AppendLine (line);
|
||||
};
|
||||
error_received = output_received;
|
||||
}
|
||||
|
||||
if (env != null){
|
||||
if (env.Length % 2 != 0)
|
||||
throw new Exception ("You passed an environment key without a value");
|
||||
|
||||
for (int i = 0; i < env.Length; i += 2) {
|
||||
if (env [i + 1] == null) {
|
||||
info.EnvironmentVariables.Remove (env [i]);
|
||||
} else {
|
||||
info.EnvironmentVariables [env [i]] = env [i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log (1, "{0} {1}", info.FileName, info.Arguments);
|
||||
|
||||
using (var p = Process.Start (info)) {
|
||||
|
||||
p.OutputDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (lockobj)
|
||||
output_received (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.ErrorDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (lockobj)
|
||||
error_received (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.BeginOutputReadLine ();
|
||||
p.BeginErrorReadLine ();
|
||||
|
||||
p.WaitForExit ();
|
||||
|
||||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
|
||||
output_received (null);
|
||||
|
||||
if (p.ExitCode != 0) {
|
||||
// note: this repeat the failing command line. However we can't avoid this since we're often
|
||||
// running commands in parallel (so the last one printed might not be the one failing)
|
||||
if (!suppressPrintOnErrors) {
|
||||
// We re-use the stringbuilder so that we avoid duplicating the amount of required memory,
|
||||
// while only calling Console.WriteLine once to make it less probable that other threads
|
||||
// also write to the Console, confusing the output.
|
||||
if (output == null)
|
||||
output = new StringBuilder ();
|
||||
output.Insert (0, $"Process exited with code {p.ExitCode}, command:\n{path} {StringUtils.FormatArguments (args)}\n");
|
||||
Console.Error.WriteLine (output);
|
||||
}
|
||||
return p.ExitCode;
|
||||
} else if (Verbosity > 0 && output != null && output.Length > 0 && !suppressPrintOnErrors) {
|
||||
Console.WriteLine (output.ToString ());
|
||||
}
|
||||
|
||||
if (stdin_exc != null)
|
||||
throw stdin_exc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, string[] args, string [] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
if (output != null)
|
||||
return RunCommandAsync (path, args, env, (v) => { if (v != null) output.AppendLine (v); }, suppressPrintOnErrors);
|
||||
return RunCommandAsync (path, args, env, (Action<string>) null, suppressPrintOnErrors);
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, string[] args, string [] env = null, Action<string> output_received = null, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output_received, suppressPrintOnErrors));
|
||||
}
|
||||
|
||||
#if !MMP_TEST
|
||||
static void FileMove (string source, string target)
|
||||
{
|
||||
|
@ -1262,15 +1094,12 @@ namespace Xamarin.Bundler {
|
|||
|
||||
static bool XcrunFind (ApplePlatform platform, bool is_simulator, string tool, out string path)
|
||||
{
|
||||
var env = new List<string> ();
|
||||
var env = new Dictionary<string, string> ();
|
||||
// Unset XCODE_DEVELOPER_DIR_PATH. See https://github.com/xamarin/xamarin-macios/issues/3931.
|
||||
env.Add ("XCODE_DEVELOPER_DIR_PATH");
|
||||
env.Add (null);
|
||||
env.Add ("XCODE_DEVELOPER_DIR_PATH", null);
|
||||
// Set DEVELOPER_DIR if we have it
|
||||
if (!string.IsNullOrEmpty (DeveloperDirectory)) {
|
||||
env.Add ("DEVELOPER_DIR");
|
||||
env.Add (DeveloperDirectory);
|
||||
}
|
||||
if (!string.IsNullOrEmpty (DeveloperDirectory))
|
||||
env.Add ("DEVELOPER_DIR", DeveloperDirectory);
|
||||
|
||||
path = null;
|
||||
|
||||
|
@ -1304,7 +1133,7 @@ namespace Xamarin.Bundler {
|
|||
// We also want to print out what happened if something went wrong, and in that case we don't want stdout
|
||||
// and stderr captured separately, because related lines could end up printed far from eachother in time,
|
||||
// and that's confusing. So capture stdout and stderr by themselves, and also capture both together.
|
||||
int ret = RunCommand ("xcrun", args, env.ToArray (),
|
||||
int ret = RunCommand ("xcrun", args, env,
|
||||
(v) => {
|
||||
lock (both) {
|
||||
both.AppendLine (v);
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2014 Xamarin Inc. All rights reserved.
|
||||
* Copyright 2019 Microsoft Corp. All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Rolf Bjarne Kvinge <rolf@xamarin.com>
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Utils;
|
||||
|
||||
namespace Xamarin.Bundler {
|
||||
public partial class Driver {
|
||||
public static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, out StringBuilder output, bool suppressPrintOnErrors, int verbose)
|
||||
{
|
||||
output = new StringBuilder ();
|
||||
return RunCommand (path, args, env, output, suppressPrintOnErrors, verbose);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, out StringBuilder output, bool suppressPrintOnErrors)
|
||||
{
|
||||
output = new StringBuilder ();
|
||||
return RunCommand (path, args, env, output, suppressPrintOnErrors, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, StringBuilder output, bool suppressPrintOnErrors, int verbosity)
|
||||
{
|
||||
return RunCommand (path, args, env, output, output, suppressPrintOnErrors, verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, StringBuilder output, bool suppressPrintOnErrors = false)
|
||||
{
|
||||
return RunCommand (path, args, env, output, output, suppressPrintOnErrors, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, params string [] args)
|
||||
{
|
||||
return RunCommand (path, args, null, (Action<string>) null, (Action<string>) null, false, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args)
|
||||
{
|
||||
return RunCommand (path, args, null, (Action<string>) null, (Action<string>) null, false, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output)
|
||||
{
|
||||
return RunCommand (path, args, null, output, output, false, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, null, output, output, suppressPrintOnErrors, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, StringBuilder error)
|
||||
{
|
||||
return RunCommand (path, args, null, output, error, false, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, StringBuilder output, StringBuilder error, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, null, output, error, suppressPrintOnErrors, Verbosity);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, StringBuilder output, StringBuilder error, bool suppressPrintOnErrors, int verbosity)
|
||||
{
|
||||
var output_received = output == null ? null : new Action<string> ((v) => { if (v != null) output.AppendLine (v); });
|
||||
var error_received = error == null ? null : new Action<string> ((v) => { if (v != null) error.AppendLine (v); });
|
||||
return RunCommand (path, args, env, output_received, error_received, suppressPrintOnErrors, verbosity);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, Action<string> output_received, bool suppressPrintOnErrors)
|
||||
{
|
||||
return RunCommand (path, args, env, output_received, output_received, suppressPrintOnErrors, Verbosity);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, Action<string> output_received, Action<string> error_received)
|
||||
{
|
||||
return RunCommand (path, args, env, output_received, error_received, false, Verbosity);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, IList<string> args, Dictionary<string, string> env, Action<string> output_received, Action<string> error_received, bool suppressPrintOnErrors, int verbosity)
|
||||
{
|
||||
var output = new StringBuilder ();
|
||||
var outputCallback = new Action<string> ((string line) => {
|
||||
if (output_received != null)
|
||||
output_received (line);
|
||||
lock (output)
|
||||
output.AppendLine (line);
|
||||
});
|
||||
var errorCallback = new Action<string> ((string line) => {
|
||||
if (error_received != null)
|
||||
error_received (line);
|
||||
lock (output)
|
||||
output.AppendLine (line);
|
||||
});
|
||||
|
||||
if (verbosity > 0)
|
||||
Console.WriteLine ($"{path} {StringUtils.FormatArguments (args)}");
|
||||
|
||||
var p = Execution.RunWithCallbacksAsync (path, args, env, outputCallback, errorCallback).Result;
|
||||
|
||||
if (output_received != null)
|
||||
output_received (null);
|
||||
if (error_received != null)
|
||||
error_received (null);
|
||||
|
||||
if (p.ExitCode != 0) {
|
||||
// note: this repeats the failing command line. However we can't avoid this since we're often
|
||||
// running commands in parallel (so the last one printed might not be the one failing)
|
||||
if (!suppressPrintOnErrors) {
|
||||
// We re-use the stringbuilder so that we avoid duplicating the amount of required memory,
|
||||
// while only calling Console.WriteLine once to make it less probable that other threads
|
||||
// also write to the Console, confusing the output.
|
||||
output.Insert (0, $"Process exited with code {p.ExitCode}, command:\n{path} {StringUtils.FormatArguments (args)}\n");
|
||||
Console.Error.WriteLine (output);
|
||||
}
|
||||
return p.ExitCode;
|
||||
} else if (verbosity > 0 && output?.Length > 0 && !suppressPrintOnErrors) {
|
||||
Console.WriteLine (output.ToString ());
|
||||
}
|
||||
|
||||
return p.ExitCode;
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, IList<string> args, Dictionary<string, string> env = null, Action<string> output_received = null, bool suppressPrintOnErrors = false, int? verbosity = null)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output_received, output_received, suppressPrintOnErrors, verbosity ?? Verbosity));
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, IList<string> args, Dictionary<string, string> env = null, StringBuilder output = null, bool suppressPrintOnErrors = false, int? verbosity = null)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output, output, suppressPrintOnErrors, verbosity ?? Verbosity));
|
||||
}
|
||||
|
||||
#if !MTOUCH && !MMP
|
||||
internal static int Verbosity;
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright 2014 Xamarin Inc. All rights reserved.
|
||||
* Copyright 2019 Microsoft Corp. All rights reserved.
|
||||
* Copyright 2019, 2020 Microsoft Corp. All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Rolf Bjarne Kvinge <rolf@xamarin.com>
|
||||
|
@ -10,94 +10,200 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Utils;
|
||||
namespace Xamarin.Utils {
|
||||
public class Execution {
|
||||
public string FileName;
|
||||
public IList<string> Arguments;
|
||||
public IDictionary<string, string> Environment;
|
||||
public string WorkingDirectory;
|
||||
public TimeSpan? Timeout;
|
||||
public CancellationToken? CancellationToken;
|
||||
|
||||
namespace Xamarin.Bundler {
|
||||
public partial class Driver {
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, out StringBuilder output, bool suppressPrintOnErrors, int verbose)
|
||||
public TextWriter Log;
|
||||
|
||||
public int ExitCode { get; private set; }
|
||||
public bool TimedOut { get; private set; }
|
||||
public TextWriter StandardOutput { get; private set; }
|
||||
public TextWriter StandardError { get; private set; }
|
||||
|
||||
static Thread StartOutputThread (TaskCompletionSource<Execution> tcs, object lockobj, StreamReader reader, TextWriter writer, string thread_name)
|
||||
{
|
||||
output = new StringBuilder ();
|
||||
return RunCommand (path, StringUtils.FormatArguments (args), env, output, suppressPrintOnErrors, verbose);
|
||||
var thread = new Thread (() => {
|
||||
try {
|
||||
string line;
|
||||
while ((line = reader.ReadLine ()) != null) {
|
||||
lock (lockobj)
|
||||
writer.WriteLine (line);
|
||||
}
|
||||
|
||||
public static int RunCommand (string path, IList<string> args, string [] env, StringBuilder output, bool suppressPrintOnErrors, int verbose)
|
||||
{
|
||||
return RunCommand (path, StringUtils.FormatArguments (args), env, output, suppressPrintOnErrors, verbose);
|
||||
}
|
||||
|
||||
static int RunCommand (string path, string args, string[] env, StringBuilder output, bool suppressPrintOnErrors, int verbose)
|
||||
{
|
||||
var info = new ProcessStartInfo (path, args);
|
||||
info.UseShellExecute = false;
|
||||
info.RedirectStandardInput = false;
|
||||
info.RedirectStandardOutput = true;
|
||||
info.RedirectStandardError = true;
|
||||
var stdout_completed = new ManualResetEvent (false);
|
||||
var stderr_completed = new ManualResetEvent (false);
|
||||
|
||||
if (output == null)
|
||||
output = new StringBuilder ();
|
||||
|
||||
if (env != null){
|
||||
if (env.Length % 2 != 0)
|
||||
throw new Exception ("You passed an environment key without a value");
|
||||
|
||||
for (int i = 0; i < env.Length; i+= 2)
|
||||
info.EnvironmentVariables [env[i]] = env[i+1];
|
||||
}
|
||||
|
||||
if (verbose > 0)
|
||||
Console.WriteLine ("{0} {1}", path, args);
|
||||
|
||||
using (var p = Process.Start (info)) {
|
||||
|
||||
p.OutputDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
} catch (Exception e) {
|
||||
tcs.TrySetException (e);
|
||||
} finally {
|
||||
// The Process instance doesn't dispose these streams, which means we need to do it,
|
||||
// otherwise we can run out of file descriptors while waiting for the GC to kick in.
|
||||
// Ref: https://bugzilla.xamarin.com/show_bug.cgi?id=43462
|
||||
reader.Dispose ();
|
||||
}
|
||||
}) {
|
||||
IsBackground = true,
|
||||
Name = thread_name,
|
||||
};
|
||||
|
||||
p.ErrorDataReceived += (s, e) => {
|
||||
if (e.Data != null) {
|
||||
lock (output)
|
||||
output.AppendLine (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
thread.Start ();
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
|
||||
p.BeginOutputReadLine ();
|
||||
p.BeginErrorReadLine ();
|
||||
public Task<Execution> RunAsync ()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<Execution> ();
|
||||
var lockobj = new object ();
|
||||
|
||||
try {
|
||||
var p = new Process ();
|
||||
p.StartInfo.FileName = FileName;
|
||||
p.StartInfo.Arguments = StringUtils.FormatArguments (Arguments);
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.RedirectStandardInput = false;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
if (!string.IsNullOrEmpty (WorkingDirectory))
|
||||
p.StartInfo.WorkingDirectory = WorkingDirectory;
|
||||
|
||||
// mtouch/mmp writes UTF8 data outside of the ASCII range, so we need to make sure
|
||||
// we read it in the same format. This also means we can't use the events to get
|
||||
// stdout/stderr, because mono's Process class parses those using Encoding.Default.
|
||||
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
|
||||
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||
|
||||
if (Environment != null) {
|
||||
foreach (var kvp in Environment) {
|
||||
if (kvp.Value == null) {
|
||||
p.StartInfo.EnvironmentVariables.Remove (kvp.Key);
|
||||
} else {
|
||||
p.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StandardOutput ??= new StringWriter ();
|
||||
StandardError ??= new StringWriter ();
|
||||
|
||||
var thread = new Thread (() => {
|
||||
try {
|
||||
if (Log != null) {
|
||||
if (!string.IsNullOrEmpty (p.StartInfo.WorkingDirectory))
|
||||
Log.Write ($"cd {StringUtils.Quote (p.StartInfo.WorkingDirectory)} && ");
|
||||
Log.WriteLine ("{0} {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
|
||||
}
|
||||
|
||||
p.Start ();
|
||||
var pid = p.Id;
|
||||
|
||||
var stdoutThread = StartOutputThread (tcs, lockobj, p.StandardOutput, StandardOutput, $"StandardOutput reader for {p.StartInfo.FileName} (PID: {pid})");
|
||||
var stderrThread = StartOutputThread (tcs, lockobj, p.StandardError, StandardError, $"StandardError reader for {p.StartInfo.FileName} (PID: {pid})");
|
||||
|
||||
CancellationToken?.Register (() => {
|
||||
// Don't call tcs.TrySetCanceled, that won't return an Execution result to the caller.
|
||||
try {
|
||||
p.Kill ();
|
||||
} catch (Exception ex) {
|
||||
// The process could be disposed already. Just ignore any exceptions here.
|
||||
Log?.WriteLine ($"Failed to cancel and kill PID {pid}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
if (Timeout.HasValue) {
|
||||
if (!p.WaitForExit ((int) Timeout.Value.TotalMilliseconds)) {
|
||||
Log?.WriteLine ($"Command '{p.StartInfo.FileName} {p.StartInfo.Arguments}' didn't finish in {Timeout.Value.TotalMilliseconds} minutes, and will be killed.");
|
||||
TimedOut = true;
|
||||
try {
|
||||
p.Kill ();
|
||||
} catch (Exception ex) {
|
||||
// According to the documentation, there can be exceptions here we can't prepare for, so just ignore them.
|
||||
Log?.WriteLine ($"Failed to kill PID {pid}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always call this WaitForExit overload to be make sure the stdout/stderr buffers have been flushed,
|
||||
// even if we've called the WaitForExit (int) overload
|
||||
p.WaitForExit ();
|
||||
ExitCode = p.ExitCode;
|
||||
|
||||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdoutThread.Join (TimeSpan.FromSeconds (1));
|
||||
stderrThread.Join (TimeSpan.FromSeconds (1));
|
||||
|
||||
if (p.ExitCode != 0) {
|
||||
// note: this repeat the failing command line. However we can't avoid this since we're often
|
||||
// running commands in parallel (so the last one printed might not be the one failing)
|
||||
if (!suppressPrintOnErrors)
|
||||
Console.Error.WriteLine ("Process exited with code {0}, command:\n{1} {2}{3}", p.ExitCode, path, args, output.Length > 0 ? "\n" + output.ToString () : string.Empty);
|
||||
return p.ExitCode;
|
||||
} else if (verbose > 0 && output.Length > 0 && !suppressPrintOnErrors) {
|
||||
Console.WriteLine (output.ToString ());
|
||||
tcs.TrySetResult (this);
|
||||
} catch (Exception e) {
|
||||
tcs.TrySetException (e);
|
||||
} finally {
|
||||
p.Dispose ();
|
||||
}
|
||||
}) {
|
||||
IsBackground = true,
|
||||
Name = $"Thread waiting for {p.StartInfo.FileName} to finish",
|
||||
};
|
||||
thread.Start ();
|
||||
} catch (Exception e) {
|
||||
tcs.TrySetException (e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public static Task<int> RunCommandAsync (string path, string args, string [] env, StringBuilder output, bool suppressPrintOnErrors, int verbose)
|
||||
public static Task<Execution> RunWithCallbacksAsync (string filename, IList<string> arguments, Dictionary<string, string> environment = null, Action<string> standardOutput = null, Action<string> standardError = null, TextWriter log = null, string workingDirectory = null, TimeSpan? timeout = null, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
return Task.Run (() => RunCommand (path, args, env, output, suppressPrintOnErrors, verbose));
|
||||
CallbackWriter outputCallback = null;
|
||||
CallbackWriter errorCallback = null;
|
||||
if (standardOutput != null)
|
||||
outputCallback = new CallbackWriter { Callback = standardOutput };
|
||||
if (standardOutput == standardError)
|
||||
errorCallback = outputCallback;
|
||||
else if (standardError != null)
|
||||
errorCallback = new CallbackWriter { Callback = standardError };
|
||||
return RunAsync (filename, arguments, environment, outputCallback, errorCallback, log, workingDirectory, timeout, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<Execution> RunAsync (string filename, IList<string> arguments, Dictionary<string, string> environment = null, TextWriter standardOutput = null, TextWriter standardError = null, TextWriter log = null, string workingDirectory = null, TimeSpan? timeout = null, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
return new Execution {
|
||||
FileName = filename,
|
||||
Arguments = arguments,
|
||||
Environment = environment,
|
||||
StandardOutput = standardOutput,
|
||||
StandardError = standardError,
|
||||
WorkingDirectory = workingDirectory,
|
||||
CancellationToken = cancellationToken,
|
||||
Timeout = timeout,
|
||||
Log = log,
|
||||
}.RunAsync ();
|
||||
}
|
||||
|
||||
public static Task<Execution> RunAsync (string filename, IList<string> arguments, Dictionary<string, string> environment = null, bool mergeOutput = false, string workingDirectory = null, TextWriter log = null, TimeSpan? timeout = null, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
var standardOutput = new StringWriter ();
|
||||
var standardError = mergeOutput ? standardOutput : new StringWriter ();
|
||||
return RunAsync (filename, arguments, environment, standardOutput, standardError, log, workingDirectory, timeout, cancellationToken);
|
||||
}
|
||||
|
||||
public static Task<Execution> RunWithStringBuildersAsync (string filename, IList<string> arguments, Dictionary<string, string> environment = null, StringBuilder standardOutput = null, StringBuilder standardError = null, TextWriter log = null, string workingDirectory = null, TimeSpan? timeout = null, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
var stdout = standardOutput == null ? null : new StringWriter (standardOutput);
|
||||
var stderr = standardError == null ? null : (standardOutput == standardError ? stdout : new StringWriter (standardError));
|
||||
return RunAsync (filename, arguments, environment, stdout, stderr, log, workingDirectory, timeout, cancellationToken);
|
||||
}
|
||||
|
||||
class CallbackWriter : TextWriter {
|
||||
public Action<string> Callback;
|
||||
public override void WriteLine (string value)
|
||||
{
|
||||
Callback (value);
|
||||
}
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace Xamarin.Bundler {
|
|||
}
|
||||
}
|
||||
|
||||
public delegate int RunCommandDelegate (string path, IList<string> args, string[] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false);
|
||||
public delegate int RunCommandDelegate (string path, IList<string> args, Dictionary<string, string> env = null, StringBuilder output = null, bool suppressPrintOnErrors = false);
|
||||
|
||||
public enum AOTCompilerType {
|
||||
Invalid,
|
||||
|
@ -202,7 +202,7 @@ namespace Xamarin.Bundler {
|
|||
if (!options.IsAOT)
|
||||
throw ErrorHelper.CreateError (0099, Errors.MX0099, $"\"AOTBundle with aot: {options.CompilationType}\" ");
|
||||
|
||||
var monoEnv = new string [] {"MONO_PATH", files.RootDir };
|
||||
var monoEnv = new Dictionary<string, string> { { "MONO_PATH", files.RootDir } };
|
||||
|
||||
List<string> filesToAOT = GetFilesToAOT (files);
|
||||
Parallel.ForEach (filesToAOT, ParallelOptions, file => {
|
||||
|
|
|
@ -35,7 +35,6 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
@ -1084,10 +1083,10 @@ namespace Xamarin.Bundler {
|
|||
|
||||
static string RunPkgConfig (string option, bool force_system_mono = false)
|
||||
{
|
||||
string [] env = null;
|
||||
Dictionary<string, string> env = null;
|
||||
|
||||
if (!IsUnifiedFullSystemFramework && !force_system_mono)
|
||||
env = new [] { "PKG_CONFIG_PATH", Path.Combine (FrameworkLibDirectory, "pkgconfig") };
|
||||
env = new Dictionary<string, string> { { "PKG_CONFIG_PATH", Path.Combine (FrameworkLibDirectory, "pkgconfig") } };
|
||||
|
||||
var sb = new StringBuilder ();
|
||||
int rv;
|
||||
|
|
|
@ -446,6 +446,9 @@
|
|||
<Compile Include="..\common\ErrorHelper.tools.cs">
|
||||
<Link>tools\common\ErrorHelper.tools.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\common\Driver.execution.cs">
|
||||
<Link>tools\common\Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -480,6 +483,7 @@
|
|||
<None Include="..\..\docs\website\mmp-errors.md">
|
||||
<Link>docs\website\mmp-errors.md</Link>
|
||||
</None>
|
||||
<None Include="..\.editorconfig" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\..\packages\XliffTasks.1.0.0-beta.20060.1\build\XliffTasks.targets" Condition="Exists('..\..\packages\XliffTasks.1.0.0-beta.20060.1\build\XliffTasks.targets')" />
|
||||
<Target Name="BuildSdkVersions" Inputs="../common/SdkVersions.cs.in" Outputs="../common/SdkVersions.cs">
|
||||
|
|
|
@ -288,14 +288,16 @@ namespace Xamarin.Bundler {
|
|||
AssemblyName = assembly_path,
|
||||
AddBitcodeMarkerSection = BuildTarget != AssemblyBuildTarget.StaticObject && App.EnableMarkerOnlyBitCode,
|
||||
AssemblyPath = asm,
|
||||
ProcessStartInfo = Driver.CreateStartInfo (App, aotCompiler, aotArgs, Path.GetDirectoryName (assembly_path)),
|
||||
FileName = aotCompiler,
|
||||
Arguments = aotArgs,
|
||||
Environment = new Dictionary<string, string> { { "MONO_PATH", Path.GetDirectoryName (assembly_path) } },
|
||||
AotInfo = aotInfo,
|
||||
};
|
||||
if (App.Platform == ApplePlatform.WatchOS) {
|
||||
// Visual Studio for Mac sets this environment variable, and it confuses the AOT compiler.
|
||||
// So unset it.
|
||||
// See https://github.com/mono/mono/issues/11765
|
||||
task.ProcessStartInfo.EnvironmentVariables.Remove ("MONO_THREADS_SUSPEND");
|
||||
task.Environment ["MONO_THREADS_SUSPEND"] = null;
|
||||
}
|
||||
|
||||
aotInfo.Task = task;
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.MacDev;
|
||||
|
@ -14,20 +12,22 @@ namespace Xamarin.Bundler
|
|||
{
|
||||
public abstract class ProcessTask : BuildTask
|
||||
{
|
||||
public ProcessStartInfo ProcessStartInfo;
|
||||
public string FileName;
|
||||
public IList<string> Arguments;
|
||||
public Dictionary<string, string> Environment = new Dictionary<string, string> ();
|
||||
protected StringBuilder Output;
|
||||
|
||||
protected string Command {
|
||||
get {
|
||||
var result = new StringBuilder ();
|
||||
if (ProcessStartInfo.EnvironmentVariables.ContainsKey ("MONO_PATH")) {
|
||||
if (Environment.TryGetValue ("MONO_PATH", out var mono_path)) {
|
||||
result.Append ("MONO_PATH=");
|
||||
result.Append (StringUtils.Quote (ProcessStartInfo.EnvironmentVariables ["MONO_PATH"]));
|
||||
result.Append (StringUtils.Quote (mono_path));
|
||||
result.Append (' ');
|
||||
}
|
||||
result.Append (ProcessStartInfo.FileName);
|
||||
result.Append (FileName);
|
||||
result.Append (' ');
|
||||
result.Append (ProcessStartInfo.Arguments);
|
||||
result.Append (StringUtils.FormatArguments (Arguments));
|
||||
return result.ToString ();
|
||||
}
|
||||
}
|
||||
|
@ -51,51 +51,13 @@ namespace Xamarin.Bundler
|
|||
if (Driver.Verbosity > 0)
|
||||
Console.WriteLine (Command);
|
||||
|
||||
var info = ProcessStartInfo;
|
||||
var stdout_completed = new ManualResetEvent (false);
|
||||
var stderr_completed = new ManualResetEvent (false);
|
||||
var lockobj = new object ();
|
||||
|
||||
Output = new StringBuilder ();
|
||||
|
||||
using (var p = Process.Start (info)) {
|
||||
p.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null) {
|
||||
lock (lockobj)
|
||||
OutputReceived (e.Data);
|
||||
} else {
|
||||
stdout_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null) {
|
||||
lock (lockobj)
|
||||
OutputReceived (e.Data);
|
||||
} else {
|
||||
stderr_completed.Set ();
|
||||
}
|
||||
};
|
||||
|
||||
p.BeginOutputReadLine ();
|
||||
p.BeginErrorReadLine ();
|
||||
|
||||
p.WaitForExit ();
|
||||
|
||||
stderr_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
stdout_completed.WaitOne (TimeSpan.FromSeconds (1));
|
||||
var rv = Execution.RunWithCallbacksAsync (FileName, Arguments, environment: Environment, standardOutput: OutputReceived, standardError: OutputReceived).Result;
|
||||
|
||||
OutputReceived (null);
|
||||
|
||||
GC.Collect (); // Workaround for: https://bugzilla.xamarin.com/show_bug.cgi?id=43462#c14
|
||||
|
||||
if (Driver.Verbosity >= 2 && Output.Length > 0)
|
||||
Console.Error.WriteLine (Output.ToString ());
|
||||
|
||||
return p.ExitCode;
|
||||
}
|
||||
return rv.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,7 +272,7 @@ namespace Xamarin.Bundler
|
|||
var cmd_length = Target.App.CompilerPath.Length + 1 + CompilerFlags.ToString ().Length;
|
||||
|
||||
try {
|
||||
var code = await Driver.RunCommandAsync (Target.App.CompilerPath, CompilerFlags.ToArray (), null, output, suppressPrintOnErrors: true);
|
||||
var code = await Driver.RunCommandAsync (Target.App.CompilerPath, CompilerFlags.ToArray (), output: output, suppressPrintOnErrors: true);
|
||||
|
||||
Application.ProcessNativeLinkerOutput (Target, output.ToString (), CompilerFlags.AllLibraries, linker_errors, code != 0);
|
||||
|
||||
|
@ -589,7 +551,7 @@ namespace Xamarin.Bundler
|
|||
CheckFor5107 (assembly_name, line, exceptions);
|
||||
});
|
||||
|
||||
var rv = await Driver.RunCommandAsync (App.CompilerPath, CompilerFlags.ToArray (), null, output_received, suppressPrintOnErrors: true);
|
||||
var rv = await Driver.RunCommandAsync (App.CompilerPath, CompilerFlags.ToArray (), output_received: output_received, suppressPrintOnErrors: true);
|
||||
|
||||
WriteLimitedOutput (rv != 0 ? $"Compilation failed with code {rv}, command:\n{App.CompilerPath} {CompilerFlags.ToString ()}" : null, output, exceptions);
|
||||
|
||||
|
|
|
@ -300,20 +300,6 @@ namespace Xamarin.Bundler
|
|||
return args;
|
||||
}
|
||||
|
||||
public static ProcessStartInfo CreateStartInfo (Application app, string file_name, IList<string> arguments, string mono_path, string mono_debug = null)
|
||||
{
|
||||
var info = new ProcessStartInfo (file_name, StringUtils.FormatArguments (arguments));
|
||||
info.UseShellExecute = false;
|
||||
info.RedirectStandardOutput = true;
|
||||
info.RedirectStandardError = true;
|
||||
|
||||
info.EnvironmentVariables ["MONO_PATH"] = mono_path;
|
||||
if (mono_debug != null)
|
||||
info.EnvironmentVariables ["MONO_DEBUG"] = mono_debug;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static string EncodeAotSymbol (string symbol)
|
||||
{
|
||||
var sb = new StringBuilder ();
|
||||
|
|
|
@ -463,6 +463,9 @@
|
|||
<Compile Include="..\common\ErrorHelper.tools.cs">
|
||||
<Link>tools\common\ErrorHelper.tools.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\common\Driver.execution.cs">
|
||||
<Link>tools\common\Driver.execution.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Core" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче