-
Notifications
You must be signed in to change notification settings - Fork 566
Expand file tree
/
Copy pathCompileNativeAssembly.cs
More file actions
158 lines (132 loc) · 4.78 KB
/
CompileNativeAssembly.cs
File metadata and controls
158 lines (132 loc) · 4.78 KB
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
146
147
148
149
150
151
152
153
154
155
156
157
158
#nullable enable
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;
public string? OutputFile;
}
[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)
{
if (config.OutputFile is not null && File.Exists (config.OutputFile)) {
string sourceFile = Path.Combine (WorkingDirectory, Path.GetFileName (config.InputSource));
if (File.Exists (sourceFile) && File.GetLastWriteTimeUtc (config.OutputFile) >= File.GetLastWriteTimeUtc (sourceFile)) {
LogDebugMessage ($"[LLVM llc] Skipping '{Path.GetFileName (config.InputSource)}' because '{Path.GetFileName (config.OutputFile)}' is up to date");
return;
}
}
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));
string outputFilePath = Path.Combine (WorkingDirectory, sourceFile.Replace (".ll", ".o"));
yield return new Config {
InputSource = item.ItemSpec,
AssemblerPath = Path.Combine (executableDir, executableName),
AssemblerOptions = $"{assemblerOptions} -o={outputFile} {QuoteFileName (sourceFile)}",
OutputFile = outputFilePath,
};
}
}
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 ();
}
}
}