Skip to content

Commit 46f53e3

Browse files
authored
[mono] Extend MonoAOTCompiler Task (#70851)
* [mono] Extend HelloWorld sample to use MonoAotCompiler Task * [mono] Extend MonoAotCompiler Task to invoke dotnet-pgo * Use cache for incremental builds
1 parent 058f83b commit 46f53e3

File tree

4 files changed

+133
-12
lines changed

4 files changed

+133
-12
lines changed

src/mono/mono.proj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<BuildMonoAOTCrossCompiler Condition="'$(TargetsiOS)' == 'true'">true</BuildMonoAOTCrossCompiler>
4141
<BuildMonoAOTCrossCompiler Condition="'$(TargetstvOS)' == 'true'">true</BuildMonoAOTCrossCompiler>
4242
<BuildMonoAOTCrossCompiler Condition="'$(TargetsMacCatalyst)' == 'true'">true</BuildMonoAOTCrossCompiler>
43+
<BuildMonoAOTCrossCompiler Condition="'$(TargetsOSX)' == 'true'">true</BuildMonoAOTCrossCompiler>
4344
<BuildMonoAOTCrossCompiler Condition="'$(TargetsBrowser)' == 'true'">true</BuildMonoAOTCrossCompiler>
4445
<BuildMonoAOTCrossCompiler Condition="'$(TargetsAndroid)' == 'true'">true</BuildMonoAOTCrossCompiler>
4546
<MonoObjCrossDir>$([MSBuild]::NormalizeDirectory('$(MonoObjDir)', 'cross'))</MonoObjCrossDir>

src/mono/sample/HelloWorld/HelloWorld.csproj

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,34 @@
33
<OutputType>Exe</OutputType>
44
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
55
</PropertyGroup>
6+
7+
<UsingTask TaskName="MonoAOTCompiler"
8+
AssemblyFile="$(MonoAOTCompilerTasksAssemblyPath)" />
9+
10+
<Target Name="AOTCompileApp" Condition="'$(RunAOTCompilation)' == 'true'" AfterTargets="CopyFilesToPublishDirectory">
11+
<PropertyGroup>
12+
<_AotOutputType>Library</_AotOutputType>
13+
<_AotLibraryFormat>Dylib</_AotLibraryFormat>
14+
<UseAotDataFile>false</UseAotDataFile>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<AotInputAssemblies Include="$(PublishDir)\*.dll" />
19+
</ItemGroup>
20+
21+
<MonoAOTCompiler
22+
CompilerBinaryPath="@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','$(TargetOS.ToLowerInvariant())-$(TargetArchitecture.ToLowerInvariant())'))"
23+
OutputType="$(_AotOutputType)"
24+
LibraryFormat="$(_AotLibraryFormat)"
25+
Assemblies="@(AotInputAssemblies)"
26+
OutputDir="$(PublishDir)"
27+
IntermediateOutputPath="$(IntermediateOutputPath)"
28+
UseAotDataFile="$(UseAotDataFile)"
29+
CacheFilePath="$(IntermediateOutputPath)aot_compiler_cache.json"
30+
NetTracePath="$(NetTracePath)"
31+
PgoBinaryPath="$(PgoBinaryPath)"
32+
MibcProfilePath="$(MibcProfilePath)">
33+
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
34+
</MonoAOTCompiler>
35+
</Target>
636
</Project>

src/mono/sample/HelloWorld/Makefile

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary
44

55
MONO_CONFIG ?=Debug
66
MONO_ARCH=x64
7+
AOT?=false
8+
9+
#NET_TRACE_PATH=<path-to-trace-of-sample>
10+
#PGO_BINARY_PATH=<path-to-dotnet-pgo-executable>
11+
#MIBC_PROFILE_PATH=<path-to-mibc-for-sample>
712

813
OS := $(shell uname -s)
914
ifeq ($(OS),Darwin)
@@ -12,10 +17,16 @@ else
1217
TARGET_OS=linux
1318
endif
1419

15-
MONO_ENV_OPTIONS ?=--llvm
20+
MONO_ENV_OPTIONS ?=
1621

1722
publish:
18-
$(DOTNET) publish -c $(MONO_CONFIG) -r $(TARGET_OS)-$(MONO_ARCH)
23+
$(DOTNET) publish \
24+
-c $(MONO_CONFIG) \
25+
-r $(TARGET_OS)-$(MONO_ARCH) \
26+
/p:RunAOTCompilation=$(AOT) \
27+
'/p:NetTracePath="$(NET_TRACE_PATH)"' \
28+
'/p:PgoBinaryPath="$(PGO_BINARY_PATH)"' \
29+
'/p:MibcProfilePath="$(MIBC_PROFILE_PATH)"'
1930

2031
run: publish
2132
COMPlus_DebugWriteToStdErr=1 \

src/tasks/AotCompilerTask/MonoAOTCompiler.cs

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
111111
/// </summary>
112112
public bool UseDwarfDebug { get; set; }
113113

114+
/// <summary>
115+
/// Path to Dotnet PGO binary (dotnet-pgo)
116+
/// </summary>
117+
public string? PgoBinaryPath { get; set; }
118+
119+
/// <summary>
120+
/// NetTrace file to use when invoking dotnet-pgo for
121+
/// </summary>
122+
public string? NetTracePath { get; set; }
123+
114124
/// <summary>
115125
/// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
116126
/// </summary>
@@ -119,7 +129,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
119129
/// <summary>
120130
/// Mibc file to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
121131
/// </summary>
122-
public string[]? MibcProfilePath { get; set; }
132+
public string[] MibcProfilePath { get; set; } = Array.Empty<string>();
123133

124134
/// <summary>
125135
/// List of profilers to use.
@@ -271,6 +281,20 @@ private bool ProcessAndValidateArguments()
271281
if (!Directory.Exists(IntermediateOutputPath))
272282
Directory.CreateDirectory(IntermediateOutputPath);
273283

284+
if (!string.IsNullOrEmpty(NetTracePath))
285+
{
286+
if (!File.Exists(NetTracePath))
287+
{
288+
Log.LogError($"NetTracePath={nameof(NetTracePath)} doesn't exist");
289+
return false;
290+
}
291+
if (!File.Exists(PgoBinaryPath))
292+
{
293+
Log.LogError($"NetTracePath was provided, but {nameof(PgoBinaryPath)}='{PgoBinaryPath}' doesn't exist");
294+
return false;
295+
}
296+
}
297+
274298
if (AotProfilePath != null)
275299
{
276300
foreach (var path in AotProfilePath)
@@ -283,15 +307,12 @@ private bool ProcessAndValidateArguments()
283307
}
284308
}
285309

286-
if (MibcProfilePath != null)
310+
foreach (var path in MibcProfilePath)
287311
{
288-
foreach (var path in MibcProfilePath)
312+
if (!File.Exists(path))
289313
{
290-
if (!File.Exists(path))
291-
{
292-
Log.LogError($"MibcProfilePath '{path}' doesn't exist.");
293-
return false;
294-
}
314+
Log.LogError($"MibcProfilePath '{path}' doesn't exist.");
315+
return false;
295316
}
296317
}
297318

@@ -400,6 +421,39 @@ public override bool Execute()
400421
}
401422
}
402423

424+
private bool ProcessNettrace(string netTraceFile)
425+
{
426+
var outputMibcPath = Path.Combine(OutputDir, Path.ChangeExtension(Path.GetFileName(netTraceFile), ".mibc"));
427+
428+
if (_cache!.Enabled)
429+
{
430+
string hash = Utils.ComputeHash(netTraceFile);
431+
if (!_cache!.UpdateAndCheckHasFileChanged($"-mibc-source-file-{Path.GetFileName(netTraceFile)}", hash))
432+
{
433+
Log.LogMessage(MessageImportance.Low, $"Skipping generating {outputMibcPath} from {netTraceFile} because source file hasn't changed");
434+
return true;
435+
}
436+
else
437+
{
438+
Log.LogMessage(MessageImportance.Low, $"Generating {outputMibcPath} from {netTraceFile} because the source file's hash has changed.");
439+
}
440+
}
441+
442+
(int exitCode, string output) = Utils.TryRunProcess(Log,
443+
PgoBinaryPath!,
444+
$"create-mibc --trace {netTraceFile} --output {outputMibcPath}");
445+
446+
if (exitCode != 0)
447+
{
448+
Log.LogError($"dotnet-pgo({PgoBinaryPath}) failed for {netTraceFile}:{output}");
449+
return false;
450+
}
451+
452+
MibcProfilePath = MibcProfilePath.Append(outputMibcPath).ToArray();
453+
Log.LogMessage(MessageImportance.Low, $"Generated {outputMibcPath} from {PgoBinaryPath}");
454+
return true;
455+
}
456+
403457
private bool ExecuteInternal()
404458
{
405459
if (!ProcessAndValidateArguments())
@@ -417,6 +471,9 @@ private bool ExecuteInternal()
417471

418472
_cache = new FileCache(CacheFilePath, Log);
419473

474+
if (!string.IsNullOrEmpty(NetTracePath) && !ProcessNettrace(NetTracePath))
475+
return false;
476+
420477
List<PrecompileArguments> argsList = new();
421478
foreach (var assemblyItem in _assembliesToCompile)
422479
argsList.Add(GetPrecompileArgumentsFor(assemblyItem, monoPaths));
@@ -756,7 +813,7 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
756813
}
757814
}
758815

759-
if (MibcProfilePath?.Length > 0)
816+
if (MibcProfilePath.Length > 0)
760817
{
761818
aotArgs.Add("profile-only");
762819
foreach (var path in MibcProfilePath)
@@ -833,12 +890,13 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
833890
private bool PrecompileLibrary(PrecompileArguments args)
834891
{
835892
string assembly = args.AOTAssembly.GetMetadata("FullPath");
893+
string output;
836894
try
837895
{
838896
string msgPrefix = $"[{Path.GetFileName(assembly)}] ";
839897

840898
// run the AOT compiler
841-
(int exitCode, string output) = Utils.TryRunProcess(Log,
899+
(int exitCode, output) = Utils.TryRunProcess(Log,
842900
CompilerBinaryPath,
843901
$"--response=\"{args.ResponseFilePath}\"",
844902
args.EnvironmentVariables,
@@ -878,6 +936,12 @@ private bool PrecompileLibrary(PrecompileArguments args)
878936
bool copied = false;
879937
foreach (var proxyFile in args.ProxyFiles)
880938
{
939+
if (!File.Exists(proxyFile.TempFile))
940+
{
941+
Log.LogError($"Precompile command succeeded, but can't find the expected temporary output file - {proxyFile.TempFile} for {assembly}.{Environment.NewLine}{output}");
942+
return false;
943+
}
944+
881945
copied |= proxyFile.CopyOutputFileIfChanged();
882946
_fileWrites.Add(proxyFile.TargetFile);
883947
}
@@ -1092,8 +1156,20 @@ public FileCache(string? cacheFilePath, TaskLoggingHelper log)
10921156
_newCache = new(_oldCache.FileHashes);
10931157
}
10941158

1159+
public bool UpdateAndCheckHasFileChanged(string filePath, string newHash)
1160+
{
1161+
if (!Enabled)
1162+
throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");
1163+
1164+
_newCache!.FileHashes[filePath] = newHash;
1165+
return !_oldCache!.FileHashes.TryGetValue(filePath, out string? oldHash) || oldHash != newHash;
1166+
}
1167+
10951168
public bool ShouldCopy(ProxyFile proxyFile, [NotNullWhen(true)] out string? cause)
10961169
{
1170+
if (!Enabled)
1171+
throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");
1172+
10971173
cause = null;
10981174

10991175
string newHash = Utils.ComputeHash(proxyFile.TempFile);
@@ -1151,6 +1227,9 @@ public bool CopyOutputFileIfChanged()
11511227

11521228
try
11531229
{
1230+
if (!File.Exists(TempFile))
1231+
throw new LogAsErrorException($"Could not find the temporary file {TempFile} for target file {TargetFile}. Look for any errors/warnings generated earlier in the build.");
1232+
11541233
if (!_cache.ShouldCopy(this, out string? cause))
11551234
{
11561235
_cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");

0 commit comments

Comments
 (0)