281 строка
8.0 KiB
C#
281 строка
8.0 KiB
C#
/*
|
|
* Copyright 2014 Xamarin Inc. All rights reserved.
|
|
*
|
|
* Authors:
|
|
* Rolf Bjarne Kvinge <rolf@xamarin.com>
|
|
*
|
|
*/
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
using Xamarin.Utils;
|
|
using XamCore.ObjCRuntime;
|
|
|
|
namespace Xamarin.Bundler {
|
|
public partial class Driver {
|
|
static void AddSharedOptions (Mono.Options.OptionSet options)
|
|
{
|
|
options.Add ("coop:", "If the GC should run in cooperative mode.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true);
|
|
options.Add ("marshal-objectivec-exceptions:", v => {
|
|
switch (v) {
|
|
case "default":
|
|
App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Default;
|
|
break;
|
|
case "unwindmanaged":
|
|
case "unwindmanagedcode":
|
|
App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.UnwindManagedCode;
|
|
break;
|
|
case "throwmanaged":
|
|
case "throwmanagedexception":
|
|
App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException;
|
|
break;
|
|
case "abort":
|
|
App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Abort;
|
|
break;
|
|
case "disable":
|
|
App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Disable;
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-objective-exceptions", "Invalid value: " + v);
|
|
}
|
|
});
|
|
options.Add ("marshal-managed-exceptions:", v => {
|
|
switch (v) {
|
|
case "default":
|
|
App.MarshalManagedExceptions = MarshalManagedExceptionMode.Default;
|
|
break;
|
|
case "unwindnative":
|
|
case "unwindnativecode":
|
|
App.MarshalManagedExceptions = MarshalManagedExceptionMode.UnwindNativeCode;
|
|
break;
|
|
case "throwobjectivec":
|
|
case "throwobjectivecexception":
|
|
App.MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException;
|
|
break;
|
|
case "abort":
|
|
App.MarshalManagedExceptions = MarshalManagedExceptionMode.Abort;
|
|
break;
|
|
case "disable":
|
|
App.MarshalManagedExceptions = MarshalManagedExceptionMode.Disable;
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-managed-exceptions", "Invalid value: " + v);
|
|
}
|
|
});
|
|
}
|
|
|
|
#if MONOMAC
|
|
#pragma warning disable 0414
|
|
static string userTargetFramework = TargetFramework.Default.ToString ();
|
|
#pragma warning restore 0414
|
|
#endif
|
|
|
|
static TargetFramework? targetFramework;
|
|
public static bool HasTargetFramework {
|
|
get { return targetFramework.HasValue; }
|
|
}
|
|
|
|
public static TargetFramework TargetFramework {
|
|
get { return targetFramework.Value; }
|
|
set { targetFramework = value; }
|
|
}
|
|
|
|
static void SetTargetFramework (string fx)
|
|
{
|
|
#if MONOMAC
|
|
userTargetFramework = fx;
|
|
#endif
|
|
|
|
switch (fx.Trim ().ToLowerInvariant ()) {
|
|
#if MONOMAC
|
|
case "xammac":
|
|
case "mobile":
|
|
case "xamarin.mac":
|
|
targetFramework = TargetFramework.Xamarin_Mac_2_0;
|
|
break;
|
|
#endif
|
|
default:
|
|
TargetFramework parsedFramework;
|
|
if (!Xamarin.Utils.TargetFramework.TryParse (fx, out parsedFramework))
|
|
throw ErrorHelper.CreateError (68, "Invalid value for target framework: {0}.", fx);
|
|
#if MONOMAC
|
|
if (parsedFramework == TargetFramework.Net_3_0 || parsedFramework == TargetFramework.Net_3_5)
|
|
parsedFramework = TargetFramework.Net_2_0;
|
|
#endif
|
|
|
|
targetFramework = parsedFramework;
|
|
|
|
break;
|
|
}
|
|
|
|
#if MTOUCH
|
|
if (Array.IndexOf (TargetFramework.ValidFrameworks, targetFramework.Value) == -1)
|
|
throw ErrorHelper.CreateError (70, "Invalid target framework: {0}. Valid target frameworks are: {1}.", targetFramework.Value, string.Join (" ", TargetFramework.ValidFrameworks.Select ((v) => v.ToString ()).ToArray ()));
|
|
#endif
|
|
}
|
|
|
|
public static int RunCommand (string path, string args, string[] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false)
|
|
{
|
|
Exception stdin_exc = null;
|
|
var info = new ProcessStartInfo (path, 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)
|
|
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, 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;
|
|
}
|
|
|
|
#if !MMP_TEST
|
|
static void FileMove (string source, string target)
|
|
{
|
|
Application.TryDelete (target);
|
|
File.Move (source, target);
|
|
}
|
|
|
|
static void MoveIfDifferent (string path, string tmp)
|
|
{
|
|
// Don't read the entire file into memory, it can be quite big in certain cases.
|
|
|
|
bool move = false;
|
|
|
|
using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) {
|
|
using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) {
|
|
if (fs1.Length != fs2.Length) {
|
|
Log (3, "New file '{0}' has different length, writing new file.", path);
|
|
move = true;
|
|
} else {
|
|
move = !Cache.CompareStreams (fs1, fs2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (move) {
|
|
FileMove (tmp, path);
|
|
} else {
|
|
Log (3, "Target {0} is up-to-date.", path);
|
|
}
|
|
}
|
|
|
|
public static void WriteIfDifferent (string path, string contents)
|
|
{
|
|
var tmp = path + ".tmp";
|
|
|
|
try {
|
|
if (!File.Exists (path)) {
|
|
File.WriteAllText (path, contents);
|
|
Log (3, "File '{0}' does not exist, creating it.", path);
|
|
return;
|
|
}
|
|
|
|
File.WriteAllText (tmp, contents);
|
|
MoveIfDifferent (path, tmp);
|
|
} catch (Exception e) {
|
|
File.WriteAllText (path, contents);
|
|
ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
|
|
} finally {
|
|
Application.TryDelete (tmp);
|
|
}
|
|
}
|
|
|
|
public static void WriteIfDifferent (string path, byte[] contents)
|
|
{
|
|
var tmp = path + ".tmp";
|
|
|
|
try {
|
|
if (!File.Exists (path)) {
|
|
File.WriteAllBytes (path, contents);
|
|
Log (3, "File '{0}' does not exist, creating it.", path);
|
|
return;
|
|
}
|
|
|
|
File.WriteAllBytes (tmp, contents);
|
|
MoveIfDifferent (path, tmp);
|
|
} catch (Exception e) {
|
|
File.WriteAllBytes (path, contents);
|
|
ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
|
|
} finally {
|
|
Application.TryDelete (tmp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
internal static string GetFullPath ()
|
|
{
|
|
return System.Reflection.Assembly.GetExecutingAssembly ().Location;
|
|
}
|
|
|
|
static Version xcode_version;
|
|
public static Version XcodeVersion {
|
|
get {
|
|
return xcode_version;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|