314 строки
10 KiB
C#
314 строки
10 KiB
C#
/*
|
|
* Copyright 2016 Microsoft Inc
|
|
*
|
|
* Authors:
|
|
* Chris Hamons <chris.hamons@xamarin.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using Xamarin.Utils;
|
|
|
|
using Profile = Mono.Tuner.Profile;
|
|
|
|
namespace Xamarin.Bundler {
|
|
|
|
public interface IFileEnumerator
|
|
{
|
|
IEnumerable<string> Files { get; }
|
|
string RootDir { get; }
|
|
}
|
|
|
|
public class FileSystemEnumerator : IFileEnumerator
|
|
{
|
|
DirectoryInfo Info;
|
|
public IEnumerable<string> Files => Info.GetFiles ().Select (x => x.FullName);
|
|
public string RootDir { get; private set; }
|
|
|
|
public FileSystemEnumerator (string path)
|
|
{
|
|
RootDir = path;
|
|
Info = new DirectoryInfo (path);
|
|
}
|
|
}
|
|
|
|
public delegate int RunCommandDelegate (string path, IList<string> args, string[] env = null, StringBuilder output = null, bool suppressPrintOnErrors = false);
|
|
|
|
public enum AOTCompilerType {
|
|
Invalid,
|
|
Bundled64,
|
|
System64,
|
|
}
|
|
|
|
public enum AOTCompilationType {
|
|
Default,
|
|
None,
|
|
All,
|
|
Core,
|
|
SDK,
|
|
Explicit
|
|
}
|
|
|
|
public enum AOTKind {
|
|
Default,
|
|
Standard,
|
|
Hybrid
|
|
}
|
|
|
|
public class AOTOptions
|
|
{
|
|
public bool IsAOT => CompilationType != AOTCompilationType.Default && CompilationType != AOTCompilationType.None;
|
|
public bool IsHybridAOT => IsAOT && Kind == AOTKind.Hybrid;
|
|
|
|
public AOTCompilationType CompilationType { get; private set; } = AOTCompilationType.Default;
|
|
public AOTKind Kind { get; private set; } = AOTKind.Standard;
|
|
|
|
public List <string> IncludedAssemblies { get; private set; } = new List <string> ();
|
|
public List <string> ExcludedAssemblies { get; private set; } = new List <string> ();
|
|
|
|
public AOTOptions (string options)
|
|
{
|
|
// Syntax - all,core,sdk,none or "" if explicit then optional list of +/-'ed assemblies
|
|
// Sections seperated by ,
|
|
string [] optionParts = options.Split (',');
|
|
for (int i = 0 ; i < optionParts.Length ; ++i) {
|
|
string option = optionParts [i];
|
|
|
|
AOTKind kind = AOTKind.Default;
|
|
// Technically '|' is valid in a file name, so |hybrid.dll would be as well.
|
|
// So check the left hand side for a valid option and pass if not
|
|
if (option.Contains ("|")) {
|
|
string [] optionTypeParts = option.Split ('|');
|
|
if (optionTypeParts.Length != 2)
|
|
throw new MonoMacException (20, true, Errors.MX0020, "--aot", "{none, all, core, sdk}{|hybrid}, then an optional explicit list of assemblies.");
|
|
switch (optionTypeParts [0]) {
|
|
case "none":
|
|
case "core":
|
|
case "sdk":
|
|
case "all": {
|
|
option = optionTypeParts [0];
|
|
switch (optionTypeParts [1]) {
|
|
case "hybrid":
|
|
if (option != "all")
|
|
throw new MonoMacException (114, true, Errors.MM0114);
|
|
kind = AOTKind.Hybrid;
|
|
break;
|
|
case "standard":
|
|
kind = AOTKind.Standard;
|
|
break;
|
|
default:
|
|
throw new MonoMacException (20, true, Errors.MX0020, "--aot", "{none, all, core, sdk}{|hybrid}, then an optional explicit list of assemblies.");
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (option) {
|
|
case "none":
|
|
CompilationType = AOTCompilationType.None;
|
|
if (kind != AOTKind.Default)
|
|
Kind = kind;
|
|
continue;
|
|
case "all":
|
|
CompilationType = AOTCompilationType.All;
|
|
if (kind != AOTKind.Default)
|
|
Kind = kind;
|
|
continue;
|
|
case "sdk":
|
|
CompilationType = AOTCompilationType.SDK;
|
|
if (kind != AOTKind.Default)
|
|
Kind = kind;
|
|
continue;
|
|
case "core":
|
|
CompilationType = AOTCompilationType.Core;
|
|
if (kind != AOTKind.Default)
|
|
Kind = kind;
|
|
continue;
|
|
}
|
|
|
|
if (option.StartsWith ("+", StringComparison.Ordinal)) {
|
|
if (CompilationType == AOTCompilationType.Default)
|
|
CompilationType = AOTCompilationType.Explicit;
|
|
IncludedAssemblies.Add (option.Substring (1));
|
|
continue;
|
|
}
|
|
if (option.StartsWith ("-", StringComparison.Ordinal)) {
|
|
if (CompilationType == AOTCompilationType.Default)
|
|
CompilationType = AOTCompilationType.Explicit;
|
|
ExcludedAssemblies.Add (option.Substring (1));
|
|
continue;
|
|
}
|
|
throw new MonoMacException (20, true, Errors.MX0020, "--aot", "{none, all, core, sdk}{|hybrid}, then an optional explicit list of assemblies.");
|
|
}
|
|
if (CompilationType == AOTCompilationType.Default)
|
|
throw new MonoMacException (20, true, Errors.MX0020, "--aot", "{none, all, core, sdk}{|hybrid}, then an optional explicit list of assemblies.");
|
|
|
|
}
|
|
}
|
|
|
|
public class AOTCompiler
|
|
{
|
|
// Allows tests to stub out actual compilation and parallelism
|
|
public RunCommandDelegate RunCommand { get; set; } = Driver.RunCommand;
|
|
public ParallelOptions ParallelOptions { get; set; } = new ParallelOptions () { MaxDegreeOfParallelism = Driver.Concurrency };
|
|
public string XamarinMacPrefix { get; set; } = Driver.FrameworkDirectory; // FrameworkDirectory assumes GetExecutingAssembly in ways that are not valid for tests, so we must stub out
|
|
|
|
AOTOptions options;
|
|
AOTCompilerType compilerType;
|
|
bool IsRelease;
|
|
bool IsModern;
|
|
|
|
public AOTCompiler (AOTOptions options, AOTCompilerType compilerType, bool isModern, bool isRelease)
|
|
{
|
|
this.options = options;
|
|
this.compilerType = compilerType;
|
|
this.IsModern = isModern;
|
|
this.IsRelease = isRelease;
|
|
}
|
|
|
|
public void Compile (string path)
|
|
{
|
|
Compile (new FileSystemEnumerator (path));
|
|
}
|
|
|
|
public void Compile (IFileEnumerator files)
|
|
{
|
|
if (!options.IsAOT)
|
|
throw ErrorHelper.CreateError (0099, Errors.MX0099, $"\"AOTBundle with aot: {options.CompilationType}\" ");
|
|
|
|
var monoEnv = new string [] {"MONO_PATH", files.RootDir };
|
|
|
|
List<string> filesToAOT = GetFilesToAOT (files);
|
|
Parallel.ForEach (filesToAOT, ParallelOptions, file => {
|
|
var cmd = new List<string> ();
|
|
cmd.Add (options.IsHybridAOT ? "--aot=hybrid" : "--aot");
|
|
if (IsModern)
|
|
cmd.Add ("--runtime=mobile");
|
|
cmd.Add (file);
|
|
if (RunCommand (MonoPath, cmd, monoEnv) != 0)
|
|
throw ErrorHelper.CreateError (3001, Errors.MX3001, "AOT", file);
|
|
});
|
|
|
|
if (IsRelease && options.IsHybridAOT) {
|
|
Parallel.ForEach (filesToAOT, ParallelOptions, file => {
|
|
if (RunCommand (StripCommand, new [] { file }) != 0)
|
|
throw ErrorHelper.CreateError (3001, Errors.MX3001, "strip", file);
|
|
});
|
|
}
|
|
|
|
if (IsRelease) {
|
|
// mono --aot creates .dll.dylib.dSYM directories for each assembly AOTed
|
|
// There isn't an easy was to disable this behavior, so clean up under release
|
|
Parallel.ForEach (filesToAOT, ParallelOptions, file => {
|
|
if (RunCommand (DeleteDebugSymbolCommand, new [] { "-r", file + ".dylib.dSYM/" }, monoEnv) != 0)
|
|
throw ErrorHelper.CreateError (3001, Errors.MX3001, "delete debug info from", file);
|
|
});
|
|
}
|
|
}
|
|
|
|
List<string> GetFilesToAOT (IFileEnumerator files)
|
|
{
|
|
// Make a dictionary of included/excluded files to track if we've missed some at the end
|
|
Dictionary <string, bool> includedAssemblies = new Dictionary <string, bool> ();
|
|
foreach (var item in options.IncludedAssemblies)
|
|
includedAssemblies [item] = false;
|
|
|
|
Dictionary <string, bool> excludedAssemblies = new Dictionary <string, bool> ();
|
|
foreach (var item in options.ExcludedAssemblies)
|
|
excludedAssemblies [item] = false;
|
|
|
|
var aotFiles = new List<string> ();
|
|
|
|
foreach (var file in files.Files) {
|
|
string fileName = Path.GetFileName (file);
|
|
string extension = Path.GetExtension (file);
|
|
if (extension != ".exe" && extension != ".dll")
|
|
continue;
|
|
|
|
if (excludedAssemblies.ContainsKey (fileName)) {
|
|
excludedAssemblies [fileName] = true;
|
|
continue;
|
|
}
|
|
|
|
if (includedAssemblies.ContainsKey (fileName)) {
|
|
includedAssemblies [fileName] = true;
|
|
aotFiles.Add (file);
|
|
continue;
|
|
}
|
|
|
|
switch (options.CompilationType) {
|
|
case AOTCompilationType.All:
|
|
aotFiles.Add (file);
|
|
break;
|
|
case AOTCompilationType.SDK:
|
|
string fileNameNoExtension = Path.GetFileNameWithoutExtension (fileName);
|
|
if (Profile.IsSdkAssembly (fileNameNoExtension) || fileName == "Xamarin.Mac.dll")
|
|
aotFiles.Add (file);
|
|
break;
|
|
case AOTCompilationType.Core:
|
|
if (fileName == "Xamarin.Mac.dll" || fileName == "System.dll" || fileName == "mscorlib.dll")
|
|
aotFiles.Add (file);
|
|
break;
|
|
case AOTCompilationType.Explicit:
|
|
break; // In explicit, only included includedAssemblies included
|
|
default:
|
|
throw ErrorHelper.CreateError (0099, Errors.MX0099, $"\"GetFilesToAOT with aot: {options.CompilationType}\"" );
|
|
}
|
|
}
|
|
|
|
var unusedIncludes = includedAssemblies.Where (pair => !pair.Value).Select (pair => pair.Key).ToList ();
|
|
if (unusedIncludes.Count > 0)
|
|
throw ErrorHelper.CreateError (3009, Errors.MM3009, String.Join (" ", unusedIncludes));
|
|
|
|
var unusedExcludes = excludedAssemblies.Where (pair => !pair.Value).Select (pair => pair.Key).ToList ();
|
|
if (unusedExcludes.Count > 0)
|
|
throw ErrorHelper.CreateError (3010, Errors.MM3010, String.Join (" ", unusedExcludes));
|
|
|
|
return aotFiles;
|
|
}
|
|
|
|
public const string StripCommand = "/Library/Frameworks/Mono.framework/Commands/mono-cil-strip";
|
|
public const string DeleteDebugSymbolCommand = "/bin/rm";
|
|
|
|
string MonoPath
|
|
{
|
|
get {
|
|
switch (compilerType) {
|
|
case AOTCompilerType.Bundled64:
|
|
return Path.Combine (XamarinMacPrefix, "bin", "mono-sgen");
|
|
case AOTCompilerType.System64:
|
|
return "/Library/Frameworks/Mono.framework/Commands/mono64";
|
|
default:
|
|
throw ErrorHelper.CreateError (0099, Errors.MX0099, $"\"MonoPath with compilerType: {compilerType}\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|