[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
type Class1() =
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 XM45 { get; set; }
public bool DiagnosticMSBuild { get; set; }
public string ProjectName { get; set; }
public string TestCode { get; set; }
public string TestDecl { get; set; }
public string CSProjConfig { get; set; }
public string References { get; set; }
public string AssemblyName { get; set; }
public string ItemGroup { get; set; }
public string SystemMonoVersion { get; set; }
public string ProjectName { get; set; } = "";
public string TestCode { get; set; } = "";
public string TestDecl { get; set; } = "";
public string CSProjConfig { get; set; } = "";
public string References { get; set; } = "";
public string ReferencesBeforePlatform { get; set; } = "";
public string AssemblyName { get; set; } = "";
public string ItemGroup { get; set; } = "";
public string SystemMonoVersion { get; set; } = "";
// Binding project specific
public string APIDefinitionConfig { get; set; }
@ -53,16 +54,6 @@ namespace Xamarin.MMP.Tests
public UnifiedTestConfig (string 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)
{
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)
@ -196,11 +191,14 @@ namespace Xamarin.MMP.Tests
string sourceDir = FindSourceDirectory ();
string sourceFileName = config.FSharp ? "Component1.fs" : "MyClass.cs";
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 ProjectTextReplacement (config, text);
});
return ProjectTextReplacement (config, text);
});
}
public static string GetUnifiedExecutableProjectName (UnifiedTestConfig config)
@ -220,10 +218,10 @@ namespace Xamarin.MMP.Tests
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);
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)
@ -233,7 +231,7 @@ namespace Xamarin.MMP.Tests
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 (config.guid == Guid.Empty)
@ -242,7 +240,7 @@ namespace Xamarin.MMP.Tests
config.TestCode += GenerateOutputCommand (config.TmpDir, config.guid);
}
string buildOutput = GenerateAndBuildUnifiedExecutable (config, shouldFail, configuration);
string buildOutput = GenerateAndBuildUnifiedExecutable (config, shouldFail, useMSBuild, configuration);
if (shouldFail)
return new OutputText (buildOutput, "");

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

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

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

@ -728,5 +728,29 @@ namespace Xamarin.MMP.Tests
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 AppPath { get { return Path.Combine (macos_dir, app_name); } }
public static string Arch => arch;
static string icon;
static string certificate_name;
@ -122,12 +123,6 @@ namespace Xamarin.Bundler {
static Version MinimumMonoVersion = new Version (4, 2, 0);
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) {
Console.WriteLine ("mmp - Xamarin.Mac Packer");
Console.WriteLine ("Copyright 2010 Novell Inc.");
@ -1867,9 +1862,7 @@ namespace Xamarin.Bundler {
static void GatherAssemblies () {
foreach (string asm in references) {
var assembly = BuildTarget.Resolver.AddAssembly (SwapOutReferenceAssembly (asm));
if (assembly == null)
ErrorHelper.Warning (1501, "Can not resolve reference: {0}", asm);
AssemblyDefinition assembly = AddAssemblyPathToResolver (asm);
ProcessAssemblyReferences (assembly);
}
if (BuildTarget.Resolver.Exceptions.Count > 0)
@ -1894,33 +1887,63 @@ namespace Xamarin.Bundler {
resolved_assemblies.Add (fqname);
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);
}
}
static string SwapOutReferenceAssembly (string assembly)
static AssemblyDefinition AddAssemblyPathToResolver (string path)
{
// Inject the correct Xamarin.Mac.dll - the one in the framework
// directory is a reference assembly only (stripped of IL, containing
// only API/metadata) and the correct one based on the target
// architecture needs to replace it
string fileName = Path.GetFileName (assembly);
if (AssemblySwapInfo.AssemblyNeedsSwappedOut (path))
path = AssemblySwapInfo.GetSwappedAssemblyPath (path);
if (assembly.Contains ("OpenTK.dll") && IsUnifiedFullXamMacFramework)
return assembly;
if (IsUnified &&
xammac_reference_assemblies.Contains (fileName)) {
switch (arch) {
var assembly = BuildTarget.Resolver.AddAssembly (path);
if (assembly == null)
ErrorHelper.Warning (1501, "Can not resolve reference: {0}", path);
return assembly;
}
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 "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:
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;
}
}
}