xamarin-macios/tools/mtouch/Assembly.cs

362 строки
12 KiB
C#

// Copyright 2013 Xamarin Inc. All rights reserved.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.IO;
using Mono.Cecil;
using Xamarin.Utils;
namespace Xamarin.Bundler {
public enum AssemblyBuildTarget
{
StaticObject,
DynamicLibrary,
Framework,
}
public class AotInfo
{
public AOTTask Task;
public LinkTask LinkTask;
public List<string> BitcodeFiles = new List<string> (); // .bc files produced by the AOT compiler
public List<string> AsmFiles = new List<string> (); // .s files produced by the AOT compiler.
public List<string> AotDataFiles = new List<string> (); // .aotdata files produced by the AOT compiler
public List<string> ObjectFiles = new List<string> (); // .o files produced by the AOT compiler
}
public partial class Assembly
{
public AssemblyBuildTarget BuildTarget;
public string BuildTargetName;
public bool IsCodeShared;
public bool BundleInContainerApp;
public Dictionary<Abi, AotInfo> AotInfos = new Dictionary<Abi, AotInfo> ();
HashSet<string> dependency_map;
bool has_dependency_map;
public bool HasDependencyMap {
get {
return has_dependency_map;
}
}
public HashSet<string> DependencyMap {
get {
return dependency_map;
}
}
public bool IsAOTCompiled {
get {
return App.IsAOTCompiled (Identity);
}
}
// Recursively list all the assemblies the specified assembly depends on.
HashSet<string> ComputeDependencies (List<Exception> warnings)
{
if (dependency_map != null)
return dependency_map;
dependency_map = new HashSet<string> ();
has_dependency_map = true;
foreach (var ar in AssemblyDefinition.MainModule.AssemblyReferences) {
var found = false;
if (ar.FullName == AssemblyDefinition.FullName)
continue;
// find the dependent assembly
foreach (var a in Target.Assemblies) {
if (a == this)
continue;
if (a.AssemblyDefinition.Name.Name == ar.Name) {
// gotcha
if (!dependency_map.Contains (a.FullPath)) {
dependency_map.Add (a.FullPath);
dependency_map.UnionWith (a.ComputeDependencies (warnings));
}
found = true;
break;
}
}
if (!found) {
warnings.Add (new MonoTouchException (3005, false, "The dependency '{0}' of the assembly '{1}' was not found. Please review the project's references.",
ar.FullName, AssemblyDefinition.FullName));
has_dependency_map = false;
}
}
return dependency_map;
}
public void ComputeDependencyMap (List<Exception> exceptions)
{
ComputeDependencies (exceptions);
}
public delegate bool StripAssembly (string path);
// returns false if the assembly was not copied (because it was already up-to-date).
public bool CopyAssembly (string source, string target, bool copy_debug_symbols = true, StripAssembly strip = null)
{
var copied = false;
try {
var strip_assembly = strip != null && strip (source);
if (!Application.IsUptodate (source, target) && (strip_assembly || !Cache.CompareAssemblies (source, target))) {
copied = true;
if (strip_assembly) {
Driver.FileDelete (target);
Directory.CreateDirectory (Path.GetDirectoryName (target));
MonoTouch.Tuner.Stripper.Process (source, target);
} else {
Application.CopyFile (source, target);
}
} else {
Driver.Log (3, "Target '{0}' is up-to-date.", target);
}
// Update the debug symbols file even if the assembly didn't change.
if (copy_debug_symbols) {
if (File.Exists (source + ".mdb"))
Application.UpdateFile (source + ".mdb", target + ".mdb", true);
var spdb = Path.ChangeExtension (source, "pdb");
if (File.Exists (spdb))
Application.UpdateFile (spdb, Path.ChangeExtension (target, "pdb"), true);
}
CopyConfigToDirectory (Path.GetDirectoryName (target));
} catch (Exception e) {
throw new MonoTouchException (1009, true, e, "Could not copy the assembly '{0}' to '{1}': {2}", source, target, e.Message);
}
return copied;
}
public void CopyDebugSymbolsToDirectory (string directory)
{
string mdb_src = FullPath + ".mdb";
if (File.Exists (mdb_src)) {
string mdb_target = Path.Combine (directory, FileName + ".mdb");
Application.UpdateFile (mdb_src, mdb_target);
}
var spdb = Path.ChangeExtension (FullPath, "pdb");
if (File.Exists (spdb))
Application.UpdateFile (spdb, Path.Combine (directory, Path.ChangeExtension (FileName, "pdb")), true);
}
public void CopyMSymToDirectory (string directory)
{
string msym_src = FullPath + ".aotid.msym";
var dirInfo = new DirectoryInfo (msym_src);
if (!dirInfo.Exists) // got no aot data
return;
var subdirs = dirInfo.GetDirectories();
foreach (var subdir in subdirs) {
var destPath = Path.Combine (directory, subdir.Name.ToUpperInvariant ());
var destInfo = new DirectoryInfo (destPath);
if (!destInfo.Exists)
Directory.CreateDirectory (destPath);
var files = subdir.GetFiles ();
foreach (FileInfo file in files) {
string temppath = Path.Combine (destPath, file.Name);
file.CopyTo(temppath, true);
}
}
}
public void CopyConfigToDirectory (string directory)
{
string config_src = FullPath + ".config";
if (File.Exists (config_src)) {
string config_target = Path.Combine (directory, FileName + ".config");
Application.UpdateFile (config_src, config_target, true);
}
}
// this will copy (and optionally strip) the assembly and almost all the related files:
// * debug file (.mdb)
// * config file (.config)
// * satellite assemblies (<language id>/.dll)
//
// Aot data is copied separately, because we might want to copy aot data
// even if we don't want to copy the assembly (if 32/64-bit assemblies are identical,
// only one is copied, but we still want the aotdata for both).
public void CopyToDirectory (string directory, bool reload = true, bool check_case = false, bool only_copy = false, bool copy_debug_symbols = true, StripAssembly strip = null)
{
var target = Path.Combine (directory, FileName);
var fileNameNoExtension = Path.GetFileNameWithoutExtension (FileName);
var assemblyName = AssemblyDefinition.Name.Name;
if (check_case && fileNameNoExtension != assemblyName && string.Equals (fileNameNoExtension, assemblyName, StringComparison.OrdinalIgnoreCase)) {
// Fix up the name of the target file to match the assembly name.
target = Path.Combine (directory, assemblyName + Path.GetExtension (FileName));
}
// our Copy code deletes the target (so copy'ing over itself is a bad idea)
if (directory != Path.GetDirectoryName (FullPath))
CopyAssembly (FullPath, target, copy_debug_symbols: copy_debug_symbols, strip: strip);
CopySatellitesToDirectory (directory);
if (!only_copy) {
if (reload) {
LoadAssembly (target);
} else {
FullPath = target;
}
}
}
public void CopyAotDataFilesToDirectory (string directory)
{
foreach (var aotdata in AotInfos.Values.SelectMany ((info) => info.AotDataFiles))
Application.UpdateFile (aotdata, Path.Combine (directory, Path.GetFileName (aotdata)));
}
/*
* Runs the AOT compiler, creating one of the following:
* [not llvm] => .s + .aotdata
* [is llvm-only] => .bc + .aotdata
* [is llvm] =>
* [is llvm creating assembly code] => .s + -llvm.s + .aotdata
* [is llvm creating object code] => .s + -llvm.o + .aotdata
*/
public void CreateAOTTask (Abi abi)
{
// Check if we've already created the AOT tasks.
if (AotInfos.ContainsKey (abi))
return;
var assembly_path = FullPath;
var build_dir = Path.GetDirectoryName (assembly_path);
var arch = abi.AsArchString ();
var asm_dir = Path.Combine (App.Cache.Location, arch);
var asm = Path.Combine (asm_dir, Path.GetFileName (assembly_path)) + ".s";
var data = Path.Combine (asm_dir, Path.GetFileNameWithoutExtension (assembly_path)) + ".aotdata" + "." + arch;
var llvm_aot_ofile = "";
var asm_output = (string) null;
var other_output = string.Empty;
var is_llvm = (abi & Abi.LLVM) == Abi.LLVM;
Directory.CreateDirectory (asm_dir);
if (!File.Exists (assembly_path))
throw new MonoTouchException (3004, true, "Could not AOT the assembly '{0}' because it doesn't exist.", assembly_path);
var aotInfo = new AotInfo ();
AotInfos.Add (abi, aotInfo);
if (App.EnableLLVMOnlyBitCode) {
// In llvm-only mode, the AOT compiler emits a .bc file and no .s file for JITted code
llvm_aot_ofile = Path.Combine (asm_dir, Path.GetFileName (assembly_path)) + ".bc";
aotInfo.BitcodeFiles.Add (llvm_aot_ofile);
other_output = Path.Combine (asm_dir, Path.GetFileName (assembly_path)) + "-output";
} else if (is_llvm) {
if (Driver.GetLLVMAsmWriter (App)) {
llvm_aot_ofile = Path.Combine (asm_dir, Path.GetFileName (assembly_path)) + "-llvm.s";
aotInfo.AsmFiles.Add (llvm_aot_ofile);
} else {
llvm_aot_ofile = Path.Combine (asm_dir, Path.GetFileName (assembly_path)) + "-llvm.o";
aotInfo.ObjectFiles.Add (llvm_aot_ofile);
}
asm_output = asm;
} else {
asm_output = asm;
}
if (!string.IsNullOrEmpty (asm_output))
aotInfo.AsmFiles.Add (asm_output);
aotInfo.AotDataFiles.Add (data);
var aotCompiler = Driver.GetAotCompiler (App, Target.Is64Build);
var aotArgs = Driver.GetAotArguments (App, assembly_path, abi, build_dir, asm_output ?? other_output, llvm_aot_ofile, data);
var task = new AOTTask
{
Assembly = this,
AssemblyName = assembly_path,
AddBitcodeMarkerSection = BuildTarget != AssemblyBuildTarget.StaticObject && App.EnableMarkerOnlyBitCode,
AssemblyPath = asm,
ProcessStartInfo = Driver.CreateStartInfo (App, aotCompiler, aotArgs, Path.GetDirectoryName (assembly_path)),
AotInfo = aotInfo,
};
if (App.Platform == ApplePlatform.WatchOS) {
// Visual Studio for Mac sets this environment variable, and it confuses the AOT compiler.
// So unset it.
// See https://github.com/mono/mono/issues/11765
task.ProcessStartInfo.EnvironmentVariables ["MONO_THREADS_SUSPEND"] = null;
}
aotInfo.Task = task;
}
public bool CanSymLinkForApplication ()
{
if (EnableCxx || NeedsGccExceptionHandling || ForceLoad)
return false;
if (LinkerFlags != null && LinkerFlags.Count > 0)
return false;
if (LinkWith != null && LinkWith.Count > 0)
return false;
return true;
}
public bool Symlink ()
{
bool symlink_failed = false;
string target = Path.Combine (Target.TargetDirectory, Path.GetFileName (FullPath));
string source = FullPath;
if (!Driver.SymlinkAssembly (App, source, target, Path.GetDirectoryName (target))) {
symlink_failed = true;
CopyAssembly (source, target);
}
if (Satellites != null) {
foreach (var a in Satellites) {
string s_target_dir = Path.Combine (Target.TargetDirectory, Path.GetFileName (Path.GetDirectoryName (a)));
string s_target = Path.Combine (s_target_dir, Path.GetFileName (a));
if (!Driver.SymlinkAssembly (App, a, s_target, s_target_dir)) {
CopyAssembly (a, s_target);
}
}
}
return symlink_failed;
}
public void LoadAssembly (string filename)
{
try {
AssemblyDefinition = Target.Resolver.Load (filename);
FullPath = AssemblyDefinition.MainModule.FileName;
if (symbols_loaded.HasValue && symbols_loaded.Value) {
symbols_loaded = null;
LoadSymbols ();
}
Driver.Log (3, "Loaded '{0}'", FullPath);
} catch (Exception e) {
// cecil might not be able to load the assembly, e.g. bug #758
throw new MonoTouchException (1010, true, e, "Could not load the assembly '{0}': {1}", FullPath, e.Message);
}
}
}
}