[macos] Teach mmp to correctly swap arch specific assemblies (#2503)

- https://bugzilla.xamarin.com/show_bug.cgi?id=58826
- If the first instance of Xamarin.Mac.dll or another arch specific assembly
is found as a library reference, it would not be handled correctly. This caused
the "reference" assembly to be copied in, which is 64-bit.
- This causes startup crashes in 32-bit applications
This commit is contained in:
Chris Hamons 2017-08-23 09:31:02 -05:00 коммит произвёл GitHub
Родитель 52119f8a1a
Коммит 125fe51ad7
6 изменённых файлов: 101 добавлений и 52 удалений

Просмотреть файл

@ -1,3 +1,5 @@
namespace FSharpXM45Library namespace FSharpXM45Library
type Class1() = type Class1() =
member this.X = "F#" member this.X = "F#"
%CODE%

Просмотреть файл

@ -10,3 +10,4 @@ namespace Library
} }
} }
%CODE%

Просмотреть файл

@ -34,14 +34,15 @@ namespace Xamarin.MMP.Tests
public bool FSharp { get; set; } public bool FSharp { get; set; }
public bool XM45 { get; set; } public bool XM45 { get; set; }
public bool DiagnosticMSBuild { get; set; } public bool DiagnosticMSBuild { get; set; }
public string ProjectName { get; set; } public string ProjectName { get; set; } = "";
public string TestCode { get; set; } public string TestCode { get; set; } = "";
public string TestDecl { get; set; } public string TestDecl { get; set; } = "";
public string CSProjConfig { get; set; } public string CSProjConfig { get; set; } = "";
public string References { get; set; } public string References { get; set; } = "";
public string AssemblyName { get; set; } public string ReferencesBeforePlatform { get; set; } = "";
public string ItemGroup { get; set; } public string AssemblyName { get; set; } = "";
public string SystemMonoVersion { get; set; } public string ItemGroup { get; set; } = "";
public string SystemMonoVersion { get; set; } = "";
// Binding project specific // Binding project specific
public string APIDefinitionConfig { get; set; } public string APIDefinitionConfig { get; set; }
@ -53,16 +54,6 @@ namespace Xamarin.MMP.Tests
public UnifiedTestConfig (string tmpDir) public UnifiedTestConfig (string tmpDir)
{ {
TmpDir = tmpDir; TmpDir = tmpDir;
ProjectName = "";
TestCode = "";
TestDecl = "";
CSProjConfig = "";
References = "";
AssemblyName = "";
APIDefinitionConfig = "";
StructsAndEnumsConfig = "";
ItemGroup = "";
SystemMonoVersion = "";
} }
} }
@ -150,7 +141,11 @@ namespace Xamarin.MMP.Tests
static string ProjectTextReplacement (UnifiedTestConfig config, string text) static string ProjectTextReplacement (UnifiedTestConfig config, string text)
{ {
return text.Replace ("%CODE%", config.CSProjConfig).Replace ("%REFERENCES%", config.References).Replace ("%NAME%", config.AssemblyName ?? Path.GetFileNameWithoutExtension (config.ProjectName)).Replace ("%ITEMGROUP%", config.ItemGroup); return text.Replace ("%CODE%", config.CSProjConfig)
.Replace ("%REFERENCES%", config.References)
.Replace ("%REFERENCES_BEFORE_PLATFORM%", config.ReferencesBeforePlatform)
.Replace ("%NAME%", config.AssemblyName ?? Path.GetFileNameWithoutExtension (config.ProjectName))
.Replace ("%ITEMGROUP%", config.ItemGroup);
} }
public static string RunEXEAndVerifyGUID (string tmpDir, Guid guid, string path) public static string RunEXEAndVerifyGUID (string tmpDir, Guid guid, string path)
@ -196,11 +191,14 @@ namespace Xamarin.MMP.Tests
string sourceDir = FindSourceDirectory (); string sourceDir = FindSourceDirectory ();
string sourceFileName = config.FSharp ? "Component1.fs" : "MyClass.cs"; string sourceFileName = config.FSharp ? "Component1.fs" : "MyClass.cs";
string projectSuffix = config.FSharp ? ".fsproj" : ".csproj"; string projectSuffix = config.FSharp ? ".fsproj" : ".csproj";
File.Copy (Path.Combine (sourceDir, sourceFileName), Path.Combine (config.TmpDir, sourceFileName), true);
CopyFileWithSubstitutions (Path.Combine (sourceDir, sourceFileName), Path.Combine (config.TmpDir, sourceFileName), text => {
return text.Replace ("%CODE%", config.TestCode);
});
return CopyFileWithSubstitutions (Path.Combine (sourceDir, config.ProjectName + projectSuffix), Path.Combine (config.TmpDir, config.ProjectName + projectSuffix), text => { return CopyFileWithSubstitutions (Path.Combine (sourceDir, config.ProjectName + projectSuffix), Path.Combine (config.TmpDir, config.ProjectName + projectSuffix), text => {
return ProjectTextReplacement (config, text); return ProjectTextReplacement (config, text);
}); });
} }
public static string GetUnifiedExecutableProjectName (UnifiedTestConfig config) public static string GetUnifiedExecutableProjectName (UnifiedTestConfig config)
@ -220,10 +218,10 @@ namespace Xamarin.MMP.Tests
return GenerateEXEProject (config); return GenerateEXEProject (config);
} }
public static string GenerateAndBuildUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, string configuration = null) public static string GenerateAndBuildUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, bool useMSBuild = false, string configuration = null)
{ {
string csprojTarget = GenerateUnifiedExecutableProject (config); string csprojTarget = GenerateUnifiedExecutableProject (config);
return BuildProject (csprojTarget, isUnified: true, diagnosticMSBuild: config.DiagnosticMSBuild, shouldFail: shouldFail, configuration: configuration); return BuildProject (csprojTarget, isUnified: true, diagnosticMSBuild: config.DiagnosticMSBuild, shouldFail: shouldFail, useMSBuild: useMSBuild, configuration: configuration);
} }
public static string RunGeneratedUnifiedExecutable (UnifiedTestConfig config) public static string RunGeneratedUnifiedExecutable (UnifiedTestConfig config)
@ -233,7 +231,7 @@ namespace Xamarin.MMP.Tests
return RunEXEAndVerifyGUID (config.TmpDir, config.guid, exePath); return RunEXEAndVerifyGUID (config.TmpDir, config.guid, exePath);
} }
public static OutputText TestUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, string configuration = null) public static OutputText TestUnifiedExecutable (UnifiedTestConfig config, bool shouldFail = false, bool useMSBuild = false, string configuration = null)
{ {
// If we've already generated guid bits for this config, don't tack on a second copy // If we've already generated guid bits for this config, don't tack on a second copy
if (config.guid == Guid.Empty) if (config.guid == Guid.Empty)
@ -242,7 +240,7 @@ namespace Xamarin.MMP.Tests
config.TestCode += GenerateOutputCommand (config.TmpDir, config.guid); config.TestCode += GenerateOutputCommand (config.TmpDir, config.guid);
} }
string buildOutput = GenerateAndBuildUnifiedExecutable (config, shouldFail, configuration); string buildOutput = GenerateAndBuildUnifiedExecutable (config, shouldFail, useMSBuild, configuration);
if (shouldFail) if (shouldFail)
return new OutputText (buildOutput, ""); return new OutputText (buildOutput, "");

Просмотреть файл

@ -27,6 +27,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
%REFERENCES_BEFORE_PLATFORM%
<Reference Include="Xamarin.Mac" /> <Reference Include="Xamarin.Mac" />
%REFERENCES% %REFERENCES%
</ItemGroup> </ItemGroup>

Просмотреть файл

@ -728,5 +728,29 @@ namespace Xamarin.MMP.Tests
TI.TestUnifiedExecutable (test); TI.TestUnifiedExecutable (test);
}); });
} }
[Test]
public void Unified32BitWithXMRequiringLibrary_ShouldReferenceCorrectXM_AndNotCrash ()
{
RunMMPTest (tmpDir => {
TI.UnifiedTestConfig libConfig = new TI.UnifiedTestConfig (tmpDir) {
ProjectName = "UnifiedLibrary",
TestCode = "namespace Library { public static class Foo { public static void Bar () { var v = new Foundation.NSObject (); } } }"
};
string csprojTarget = TI.GenerateUnifiedLibraryProject (libConfig);
TI.BuildProject (csprojTarget, isUnified: true);
string referenceCode = string.Format (@"<Reference Include=""UnifiedLibrary""><HintPath>{0}</HintPath></Reference>", Path.Combine (tmpDir, "bin/Debug/UnifiedLibrary.dll"));
TI.UnifiedTestConfig test = new TI.UnifiedTestConfig (tmpDir) {
CSProjConfig = @"<PlatformTarget>x86</PlatformTarget><XamMacArch>i386</XamMacArch>",
ReferencesBeforePlatform = referenceCode,
TestCode = "Library.Foo.Bar ();"
};
TI.TestUnifiedExecutable (test);
});
}
} }
} }

Просмотреть файл

@ -109,6 +109,7 @@ namespace Xamarin.Bundler {
static string BundleName { get { return custom_bundle_name != null ? custom_bundle_name : "MonoBundle"; } } static string BundleName { get { return custom_bundle_name != null ? custom_bundle_name : "MonoBundle"; } }
static string AppPath { get { return Path.Combine (macos_dir, app_name); } } static string AppPath { get { return Path.Combine (macos_dir, app_name); } }
public static string Arch => arch;
static string icon; static string icon;
static string certificate_name; static string certificate_name;
@ -122,12 +123,6 @@ namespace Xamarin.Bundler {
static Version MinimumMonoVersion = new Version (4, 2, 0); static Version MinimumMonoVersion = new Version (4, 2, 0);
const string pkg_config = "/Library/Frameworks/Mono.framework/Commands/pkg-config"; const string pkg_config = "/Library/Frameworks/Mono.framework/Commands/pkg-config";
static HashSet<string> xammac_reference_assemblies = new HashSet<string> {
"Xamarin.Mac.dll",
"Xamarin.Mac.CFNetwork.dll",
"OpenTK.dll"
};
static void ShowHelp (OptionSet os) { static void ShowHelp (OptionSet os) {
Console.WriteLine ("mmp - Xamarin.Mac Packer"); Console.WriteLine ("mmp - Xamarin.Mac Packer");
Console.WriteLine ("Copyright 2010 Novell Inc."); Console.WriteLine ("Copyright 2010 Novell Inc.");
@ -1867,9 +1862,7 @@ namespace Xamarin.Bundler {
static void GatherAssemblies () { static void GatherAssemblies () {
foreach (string asm in references) { foreach (string asm in references) {
var assembly = BuildTarget.Resolver.AddAssembly (SwapOutReferenceAssembly (asm)); AssemblyDefinition assembly = AddAssemblyPathToResolver (asm);
if (assembly == null)
ErrorHelper.Warning (1501, "Can not resolve reference: {0}", asm);
ProcessAssemblyReferences (assembly); ProcessAssemblyReferences (assembly);
} }
if (BuildTarget.Resolver.Exceptions.Count > 0) if (BuildTarget.Resolver.Exceptions.Count > 0)
@ -1894,33 +1887,63 @@ namespace Xamarin.Bundler {
resolved_assemblies.Add (fqname); resolved_assemblies.Add (fqname);
foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) { foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) {
var reference_assembly = BuildTarget.Resolver.Resolve (SwapOutReferenceAssembly (reference.FullName)); AssemblyDefinition reference_assembly = AddAssemblyReferenceToResolver (reference.Name);
ProcessAssemblyReferences (reference_assembly); ProcessAssemblyReferences (reference_assembly);
} }
} }
static string SwapOutReferenceAssembly (string assembly) static AssemblyDefinition AddAssemblyPathToResolver (string path)
{ {
// Inject the correct Xamarin.Mac.dll - the one in the framework if (AssemblySwapInfo.AssemblyNeedsSwappedOut (path))
// directory is a reference assembly only (stripped of IL, containing path = AssemblySwapInfo.GetSwappedAssemblyPath (path);
// only API/metadata) and the correct one based on the target
// architecture needs to replace it
string fileName = Path.GetFileName (assembly);
if (assembly.Contains ("OpenTK.dll") && IsUnifiedFullXamMacFramework) var assembly = BuildTarget.Resolver.AddAssembly (path);
return assembly; if (assembly == null)
if (IsUnified && ErrorHelper.Warning (1501, "Can not resolve reference: {0}", path);
xammac_reference_assemblies.Contains (fileName)) { return assembly;
switch (arch) { }
static AssemblyDefinition AddAssemblyReferenceToResolver (string reference)
{
if (AssemblySwapInfo.ReferencedNeedsSwappedOut (reference))
return BuildTarget.Resolver.AddAssembly (AssemblySwapInfo.GetSwappedReference (reference));
return BuildTarget.Resolver.Resolve (reference);
}
}
public static class AssemblySwapInfo {
static HashSet<string> xammac_reference_assemblies_names = new HashSet<string> {
"Xamarin.Mac",
"Xamarin.Mac.CFNetwork",
"OpenTK"
};
public static bool AssemblyNeedsSwappedOut (string path) => NeedsSwappedCore (Path.GetFileNameWithoutExtension (path));
public static bool ReferencedNeedsSwappedOut (string reference) => NeedsSwappedCore (reference);
static bool NeedsSwappedCore (string name)
{
if (name.Contains ("OpenTK") && Driver.IsUnifiedFullXamMacFramework)
return false;
return Driver.IsUnified && xammac_reference_assemblies_names.Contains (name);
}
public static string GetSwappedAssemblyPath (string path) => GetSwappedPathCore (Path.GetFileNameWithoutExtension (path));
public static string GetSwappedReference (string reference) => GetSwappedPathCore (reference);
static string GetSwappedPathCore (string name)
{
string flavor = (Driver.IsUnifiedFullSystemFramework || Driver.IsUnifiedFullXamMacFramework) ? "full" : "mobile";
switch (Driver.Arch) {
case "i386": case "i386":
case "x86_64": case "x86_64":
return Path.Combine (GetXamMacPrefix (), "lib", arch, (IsUnifiedFullSystemFramework || IsUnifiedFullXamMacFramework) ? "full" : "mobile", fileName); return Path.Combine (Driver.GetXamMacPrefix (), "lib", Driver.Arch, flavor, name + ".dll");
default: default:
throw new MonoMacException (5205, true, "Invalid architecture '{0}'. " + throw new MonoMacException (5205, true, "Invalid architecture '{0}'. " +
"Valid architectures are i386 and x86_64 (when --profile=mobile).", arch); "Valid architectures are i386 and x86_64 (when --profile=mobile).", Driver.Arch);
}
} }
return assembly;
} }
} }
} }