-
Notifications
You must be signed in to change notification settings - Fork 527
/
CompileNativeAssembly.cs
145 lines (121 loc) · 4.12 KB
/
CompileNativeAssembly.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Xamarin.Android.Tools;
using Microsoft.Android.Build.Tasks;
namespace Xamarin.Android.Tasks
{
public class CompileNativeAssembly : AsyncTask
{
public override string TaskPrefix => "CNA";
sealed class Config
{
public string AssemblerPath;
public string AssemblerOptions;
public string InputSource;
}
[Required]
public ITaskItem[] Sources { get; set; }
[Required]
public bool DebugBuild { get; set; }
[Required]
public new string WorkingDirectory { get; set; }
[Required]
public string AndroidBinUtilsDirectory { get; set; }
public override System.Threading.Tasks.Task RunTaskAsync ()
{
return this.WhenAll (GetAssemblerConfigs (), RunAssembler);
}
void RunAssembler (Config config)
{
var stdout_completed = new ManualResetEvent (false);
var stderr_completed = new ManualResetEvent (false);
var psi = new ProcessStartInfo () {
FileName = config.AssemblerPath,
Arguments = config.AssemblerOptions,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = WorkingDirectory,
};
string assemblerName = Path.GetFileName (config.AssemblerPath);
LogDebugMessage ($"[LLVM llc] {psi.FileName} {psi.Arguments}");
var stdoutLines = new List<string> ();
var stderrLines = new List<string> ();
using (var proc = new Process ()) {
proc.OutputDataReceived += (s, e) => {
if (e.Data != null) {
OnOutputData (assemblerName, s, e);
stdoutLines.Add (e.Data);
} else
stdout_completed.Set ();
};
proc.ErrorDataReceived += (s, e) => {
if (e.Data != null) {
OnErrorData (assemblerName, s, e);
stderrLines.Add (e.Data);
} else
stderr_completed.Set ();
};
proc.StartInfo = psi;
proc.Start ();
proc.BeginOutputReadLine ();
proc.BeginErrorReadLine ();
CancellationToken.Register (() => { try { proc.Kill (); } catch (Exception) { } });
proc.WaitForExit ();
if (psi.RedirectStandardError)
stderr_completed.WaitOne (TimeSpan.FromSeconds (30));
if (psi.RedirectStandardOutput)
stdout_completed.WaitOne (TimeSpan.FromSeconds (30));
if (proc.ExitCode != 0) {
var sb = MonoAndroidHelper.MergeStdoutAndStderrMessages (stdoutLines, stderrLines);
LogCodedError ("XA3006", Properties.Resources.XA3006, Path.GetFileName (config.InputSource), sb.ToString ());
Cancel ();
}
}
}
IEnumerable<Config> GetAssemblerConfigs ()
{
const string assemblerOptions =
"-O2 " +
"--debugger-tune=lldb " + // NDK uses lldb now
"--debugify-level=location+variables " +
"--fatal-warnings " +
"--filetype=obj " +
"--relocation-model=pic";
string llcPath = Path.Combine (AndroidBinUtilsDirectory, "llc");
foreach (ITaskItem item in Sources) {
// We don't need the directory since our WorkingDirectory is where all the sources are
string sourceFile = Path.GetFileName (item.ItemSpec);
string outputFile = QuoteFileName (sourceFile.Replace (".ll", ".o"));
string executableDir = Path.GetDirectoryName (llcPath);
string executableName = MonoAndroidHelper.GetExecutablePath (executableDir, Path.GetFileName (llcPath));
yield return new Config {
InputSource = item.ItemSpec,
AssemblerPath = Path.Combine (executableDir, executableName),
AssemblerOptions = $"{assemblerOptions} -o={outputFile} {QuoteFileName (sourceFile)}",
};
}
}
void OnOutputData (string assemblerName, object sender, DataReceivedEventArgs e)
{
LogDebugMessage ($"[{assemblerName} stdout] {e.Data}");
}
void OnErrorData (string assemblerName, object sender, DataReceivedEventArgs e)
{
LogMessage ($"[{assemblerName} stderr] {e.Data}", MessageImportance.High);
}
static string QuoteFileName (string fileName)
{
var builder = new CommandLineBuilder ();
builder.AppendFileNameIfNotNull (fileName);
return builder.ToString ();
}
}
}