[macos] Fix target framework detection issues in mmp (#2096)

- Also detect mismatches between referenced XM.dll and target framework selected
This commit is contained in:
Chris Hamons 2017-05-30 09:23:28 -05:00 коммит произвёл GitHub
Родитель a6cf8c5edc
Коммит 0be72f3c13
10 изменённых файлов: 131 добавлений и 16 удалений

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

@ -92,6 +92,8 @@ The easiest way to get exact version information is to use the **Xamarin Studio*
<h3><a name="MM1406">MM1406: Target framework '{0}' is invalid when targetting Xamarin.Mac 4.5 .NET framwork.</h3>
<h3><a name="MM1407">MM1407: Mismatch between Xamarin.Mac reference '{0}' and target framework selected '{1}'.</h3>
## MM15xx: Assembly gathering (not requiring linker) errors
<h3><a name="MM1501">MM1501: Can not resolve reference: {0}</h3>

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

@ -977,6 +977,8 @@ Things to try to solve this:
* Reboot the Mac.
* Synchronize the device with iTunes (this will remove any crash reports from the device).
<!--- 1407 used by mmp -->
### MT16xx: MachO
<!--

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

@ -113,9 +113,11 @@ namespace Xamarin.Mac.Tasks
args.AddQuoted ("/name:" + ApplicationName);
if (TargetFrameworkIdentifier == "Xamarin.Mac")
args.Add ("/profile:Xamarin.Mac");
else if (TargetFrameworkVersion.StartsWith ("v", StringComparison.Ordinal))
args.Add ("/profile:" + TargetFrameworkVersion.Substring (1));
args.Add ("/profile:Xamarin.Mac,Version=v2.0,Profile=Mobile");
else if (UseXamMacFullFramework)
args.Add ($"/profile:Xamarin.Mac,Version={TargetFrameworkVersion},Profile=Full");
else
args.Add ($"/profile:Xamarin.Mac,Version={TargetFrameworkVersion},Profile=System");
XamMacArch arch;
if (!Enum.TryParse (Architecture, true, out arch))

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

@ -85,11 +85,6 @@ namespace Xamarin.MMP.Tests
}
public static string RunAndAssert (string exe, string args, string stepName, bool shouldFail = false, Func<string> getAdditionalFailInfo = null)
{
return RunAndAssert (exe, new StringBuilder (args), stepName, shouldFail, getAdditionalFailInfo);
}
public static string RunAndAssert (string exe, StringBuilder args, string stepName, bool shouldFail = false, Func<string> getAdditionalFailInfo = null)
{
StringBuilder output = new StringBuilder ();
Environment.SetEnvironmentVariable ("MONO_PATH", null);
@ -103,6 +98,11 @@ namespace Xamarin.MMP.Tests
return output.ToString ();
}
public static string RunAndAssert (string exe, StringBuilder args, string stepName, bool shouldFail = false, Func<string> getAdditionalFailInfo = null)
{
return RunAndAssert (exe, args.ToString (), stepName, shouldFail, getAdditionalFailInfo);
}
// In most cases we generate projects in tmp and this is not needed. But nuget and test projects can make that hard
public static void CleanUnifiedProject (string csprojTarget, bool useMSBuild = false)
{
@ -156,14 +156,13 @@ namespace Xamarin.MMP.Tests
{
// Assert that the program actually runs and returns our guid
Assert.IsTrue (File.Exists (path), string.Format ("{0} did not generate an exe?", path));
string output = RunAndAssert (path, (StringBuilder)null, "Run");
string output = RunAndAssert (path, (string)null, "Run");
string guidPath = Path.Combine (tmpDir, guid.ToString ());
Assert.IsTrue(File.Exists (guidPath), "Generated program did not create expected guid file: " + output);
// Let's delete the guid file so re-runs inside same tests are accurate
File.Delete (guidPath);
return output;
}

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

@ -70,6 +70,7 @@
<Link>unit\aot.cs</Link>
</Compile>
<Compile Include="src\FrameworkLinksTests.cs" />
<Compile Include="src\TargetFrameworkDetectionTests.cs" />
<Compile Include="src\LinkerTests.cs" />
<Compile Include="src\Extensions.cs" />
<Compile Include="..\mtouch\Cache.cs">

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

@ -12,7 +12,7 @@ namespace Xamarin.MMP.Tests
[TestFixture]
public partial class MMPTests
{
void RunMMPTest (Action <string> test)
public static void RunMMPTest (Action <string> test)
{
test (Cache.CreateTemporaryDirectory ());
}

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

@ -0,0 +1,82 @@
using System;
using System.IO;
using System.Linq;
using NUnit.Framework;
using Xamarin.Tests;
using Xamarin.Utils;
namespace Xamarin.MMP.Tests
{
[TestFixture]
public class TargetFrameworkDetectionTests
{
TargetFramework [] XMTargetFrameworks = { TargetFramework.Xamarin_Mac_2_0_Mobile, TargetFramework.Xamarin_Mac_4_5_Full, TargetFramework.Xamarin_Mac_4_5_System };
string CreateTestExe (string tmpDir)
{
string path = Path.Combine (tmpDir, "b.exe");
File.WriteAllText (Path.Combine (tmpDir, "b.cs"), "public static class EntryPoint { public static void Main () {} }");
TI.RunAndAssert ("/Library/Frameworks/Mono.framework/Commands/mcs", string.Format ("-out:{0} {1}/b.cs", path, tmpDir), "CreateTestExe");
return path;
}
string GetTestMMPInvocation (string tmpDir, string libPath, TargetFramework targetFramework, bool correctReference = true)
{
string xmReference = correctReference ? GetXMReference (targetFramework) : GetWrongXMReference (targetFramework);
return $"-v -v -v -v -v --output={tmpDir} --arch=x86_64 --sdkroot {Configuration.xcode_root} --minos 10.7 {libPath} --sdk 10.12 --nolink -p --profile:{targetFramework} -a:{xmReference}";
}
string GetWrongXMReference (TargetFramework target)
{
if (target.Profile == "Mobile")
return GetXMReference (TargetFramework.Xamarin_Mac_4_5_Full);
else
return GetXMReference (TargetFramework.Xamarin_Mac_2_0_Mobile);
}
string GetXMReference (TargetFramework target)
{
switch (target.Profile) {
case "Mobile":
return $"-a:{Configuration.SdkRootXM}/lib/mono/Xamarin.Mac/Xamarin.Mac.dll";
case "Full":
case "System":
return $"-a:{Configuration.SdkRootXM}/lib/mono/4.5/Xamarin.Mac.dll";
default:
throw new System.InvalidOperationException ();
}
}
string MMPPath => TI.FindRootDirectory () + "/Library/Frameworks/Xamarin.Mac.framework/Commands/mmp";
[Test]
public void LongProfileStrings_SelectCorrectProfile ()
{
MMPTests.RunMMPTest (tmpDir => {
foreach (var targetProfile in XMTargetFrameworks) {
string libPath = CreateTestExe (tmpDir);
string args = GetTestMMPInvocation (tmpDir, libPath, targetProfile);
string mmpOutput = TI.RunAndAssert (MMPPath, args, "mmp invoke");
bool mobile = targetProfile.Profile == "Mobile";
string referenceLine = mmpOutput.Split (Environment.NewLine.ToCharArray ()).First (x => x.StartsWith ("Added assembly ", StringComparison.Ordinal) && x.Contains ("Xamarin.Mac.dll"));
Assert.True (referenceLine.Contains ("mobile") == mobile, "Selected Reference Line Unexpected: {0} with {1}", referenceLine, targetProfile);
}
});
}
[Test]
public void LongProfileStrings_ErrorsWhenWrongXMPassedToRightProfile ()
{
MMPTests.RunMMPTest (tmpDir => {
foreach (var targetProfile in XMTargetFrameworks) {
string libPath = CreateTestExe (tmpDir);
string args = GetTestMMPInvocation (tmpDir, libPath, targetProfile, false);
string buildResults = TI.RunAndAssert (MMPPath, args, "mmp invoke with wrong XM", shouldFail:true);
Assert.IsTrue (buildResults.Contains ("1407"), "Did not contains 1407 error expected");
}
});
}
}
}

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

@ -48,7 +48,7 @@ namespace MonoTouchFixtures.ServiceModel {
TI.BuildProject (testFolder + "/ServiceModel_Test.csproj", true);
TI.RunAndAssert (testFolder + "/bin/Debug/ServiceModel_Test.app/Contents/MacOS/ServiceModel_Test", (StringBuilder)null, "Run");
TI.RunAndAssert (testFolder + "/bin/Debug/ServiceModel_Test.app/Contents/MacOS/ServiceModel_Test", (string)null, "Run");
Assert.True (File.Exists (testResults));
using (TextReader reader = File.OpenText (testResults)) {

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

@ -182,11 +182,11 @@ $(MMP_DIRECTORIES):
GENERATE_PART_REGISTRAR = $(Q_GEN) $(SYSTEM_MONO) --debug mmp.exe --xamarin-framework-directory=$(MAC_DESTDIR)$(MAC_FRAMEWORK_CURRENT_DIR) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(OSX_SDK_VERSION) $< --registrar:static
Xamarin.Mac.registrar.mobile.i386.m: $(TOP)/src/build/mac/mobile-32/Xamarin.Mac.dll $(LOCAL_MMP)
$(GENERATE_PART_REGISTRAR) --target-framework Xamarin.Mac,v1.0 --arch=i386
$(GENERATE_PART_REGISTRAR) --target-framework Xamarin.Mac,Version=v2.0,Profile=Mobile --arch=i386
$(Q) touch Xamarin.Mac.registrar.mobile.i386.m Xamarin.Mac.registrar.mobile.i386.h
Xamarin.Mac.registrar.mobile.x86_64.m: $(TOP)/src/build/mac/mobile-64/Xamarin.Mac.dll $(LOCAL_MMP)
$(GENERATE_PART_REGISTRAR) --target-framework Xamarin.Mac,v1.0 --arch=x86_64
$(GENERATE_PART_REGISTRAR) --target-framework Xamarin.Mac,Version=v2.0,Profile=Mobile --arch=x86_64
$(Q) touch Xamarin.Mac.registrar.mobile.x86_64.m Xamarin.Mac.registrar.mobile.x86_64.h
Xamarin.Mac.registrar.full.i386.m: $(TOP)/src/build/mac/full-32/Xamarin.Mac.dll $(LOCAL_MMP)

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

@ -409,8 +409,17 @@ namespace Xamarin.Bundler {
if (!targetFramework.HasValue)
targetFramework = TargetFramework.Default;
if (TargetFramework.Identifier == TargetFramework.Xamarin_Mac_2_0.Identifier) {
// At least once instance of a TargetFramework of Xamarin.Mac,v2.0,(null) was found already. Assume any v2.0 implies a desire for Modern.
if (TargetFramework == TargetFramework.Xamarin_Mac_2_0_Mobile || TargetFramework.Version == TargetFramework.Xamarin_Mac_2_0_Mobile.Version) {
IsUnifiedMobile = true;
} else if (TargetFramework.Identifier == TargetFramework.Xamarin_Mac_4_5_Full.Identifier
&& TargetFramework.Profile == TargetFramework.Xamarin_Mac_4_5_Full.Profile) {
IsUnifiedFullXamMacFramework = true;
TargetFramework = TargetFramework.Net_4_5;
} else if (TargetFramework.Identifier == TargetFramework.Xamarin_Mac_4_5_System.Identifier
&& TargetFramework.Profile == TargetFramework.Xamarin_Mac_4_5_System.Profile) {
IsUnifiedFullSystemFramework = true;
TargetFramework = TargetFramework.Net_4_5;
} else if (!IsUnifiedFullXamMacFramework && !IsUnifiedFullSystemFramework) {
// This is a total hack. Instead of passing in an argument, we walk the refernces looking for
// the "right" Xamarin.Mac and assume you are doing something
@ -472,6 +481,7 @@ namespace Xamarin.Bundler {
if (IsUnified == IsClassic || (IsUnified && IsUnifiedCount != 1))
throw new Exception ("IsClassic/IsUnified/IsUnifiedMobile/IsUnifiedFullSystemFramework/IsUnifiedFullXamMacFramework logic regression");
ValidateXamarinMacReference ();
if (IsUnifiedFullSystemFramework || IsUnifiedFullXamMacFramework) {
switch (App.LinkMode) {
case LinkMode.None:
@ -523,6 +533,23 @@ namespace Xamarin.Bundler {
Log ("bundling complete");
}
static void ValidateXamarinMacReference ()
{
// Many Xamarin.Mac references are technically valid, so whitelisting risks breaking working project
// However, passing in Mobile / Xamarin.Mac folders and resolving full/4.5 or vice versa is
// far from expected. So catch the common cases if we can
string reference = references.FirstOrDefault (x => x.EndsWith ("Xamarin.Mac.dll"));
if (reference != null) {
bool valid = true;
if (IsUnifiedMobile)
valid = !reference.Contains ("full/") && !reference.Contains ("4.5/");
else if (IsUnifiedFullXamMacFramework || IsUnifiedFullSystemFramework)
valid = !reference.Contains ("mobile/") && !reference.Contains ("Xamarin.Mac/");
if (!valid)
throw ErrorHelper.CreateError (1407, "Mismatch between Xamarin.Mac reference '{0}' and target framework selected '{1}'.", reference, TargetFramework);
}
}
static void FixReferences (Func<string, bool> match, Func<string, string> fix)
{
var assembliesToFix = references.Where (x => match(x)).ToList ();
@ -1363,7 +1390,7 @@ namespace Xamarin.Bundler {
return ret;
}
// check that we have a reference to XamMac.dll and not to MonoMac.dll. Check various DRM license checks
// check that we have a reference to XamMac.dll and not to MonoMac.dll.
static void CheckReferences ()
{
List<Exception> exceptions = new List<Exception> ();