Skip to content

Commit

Permalink
[msbuild/tests/dotnet] Add support for xcframeworks for Hot Restart a…
Browse files Browse the repository at this point in the history
…nd add tests. Fixes #16571. (#18103)

Rework Hot Restart builds to use as much as possible of the normal build
logic, because this is the easiest way to make sure the Hot Restart build is
as close as possible to normal builds (and we don't end up missing features).

This is done by executing selected parts of a normal build, and at the end we
have a new task that computes where each file goes in the various output
directories Hot Restart uses (HotRestartAppBundlePath, HotRestartContentDir,
HotRestartAppContentDir, etc.)

This PR also:

* Adds a test that runs on Windows and verifies that everything consumed in a
  build is placed in the correct location for a Hot Restart build (this is a
  variation of the BundleStructure test we already have).
* Removes tasks that aren't used anymore.
* Misc fixes to make sure existing code works on Windows.

---

This PR is best reviewed commit-by-commit.

Ref: #13924

* Fixes #16571
* Fixes #16001
* Fixes #10784
* Fixes #17579
* Contributes towards #13924.
  • Loading branch information
rolfbjarne authored May 3, 2023
2 parents 9159d0d + 312076a commit 6e7128b
Show file tree
Hide file tree
Showing 30 changed files with 650 additions and 452 deletions.
11 changes: 8 additions & 3 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@
<_FrameworkToPublish Update="@(_FrameworkToPublish)">
<TargetDirectory>$(_RelativePublishDir)$(_AppBundleFrameworksDir)\%(Filename)%(Extension).framework</TargetDirectory>
<SourceDirectory>%(RelativeDir)</SourceDirectory>
<PublishFolderType>AppleFramework</PublishFolderType>
</_FrameworkToPublish>
</ItemGroup>

Expand Down Expand Up @@ -1500,9 +1501,10 @@
<ResolvedFileToPublish Remove="$(ProjectRuntimeConfigFilePath)" Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'" />
</ItemGroup>

<!-- This task is executed on Windows as well, for hotrestart builds -->
<ComputeBundleLocation
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true'"
Condition="'$(IsMacEnabled)' == 'true' Or '$(IsHotRestartBuild)' == 'true'"
AssemblyDirectory="$(_AppContentsRelativePath)"
BundleResource="@(BundleResource)"
BundlerDebug="$(_BundlerDebug)"
Expand Down Expand Up @@ -1577,11 +1579,13 @@
<ResolvedFileToPublish RelativePath="$(_RelativeAppBundlePath)\%(RelativePath)" />
</ItemGroup>

<!-- This task is executed on Windows as well, for hotrestart builds -->
<!-- resolve any .xcframeworks and binding resource packages -->
<ResolveNativeReferences
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true'"
Condition="'$(IsMacEnabled)' == 'true' Or '$(IsHotRestartBuild)' == 'true'"
Architectures="$(TargetArchitectures)"
FrameworksDirectory="$(_AppFrameworksRelativePath)"
IntermediateOutputPath="$(DeviceSpecificIntermediateOutputPath)"
NativeReferences="@(_UnresolvedXCFrameworks);@(_AppleBindingResourcePackage);@(_CompressedAppleFrameworks);@(_CompressedAppleBindingResourcePackage)"
SdkIsSimulator="$(_SdkIsSimulator)"
Expand Down Expand Up @@ -1641,9 +1645,10 @@
</_CompressedPlugIns>
</ItemGroup>

<!-- This task is executed from Windows as well when using HotRestart -->
<Unzip
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true'"
Condition="'$(IsMacEnabled)' == 'true' Or '$(IsHotRestartBuild)' == 'true'"
ZipFilePath="%(_CompressedPlugIns.Identity)"
ExtractionPath="%(_CompressedPlugIns.ExtractionPath)"
>
Expand Down
5 changes: 5 additions & 0 deletions msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1492,4 +1492,9 @@
<data name="E7113" xml:space="preserve">
<value>Can't process the zip file '{0}' on this platform: the file '{1}' is a symlink.</value>
</data>

<data name="E7114" xml:space="preserve">
<value>The "{0}" task was not given a value for the parameter "{1}", which is required when building on this platform.</value>
</data>

</root>
13 changes: 10 additions & 3 deletions msbuild/Xamarin.MacDev.Tasks/Decompress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO.Compression;
using System.Reflection;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

using Xamarin.Bundler;
Expand Down Expand Up @@ -86,7 +87,7 @@ public static bool TryDecompress (TaskLoggingHelper log, string zip, string reso
{
decompressedResource = Path.Combine (decompressionDir, resource);

var stampFile = decompressedResource + ".stamp";
var stampFile = decompressedResource.TrimEnd ('\\', '/') + ".stamp";

if (FileCopier.IsUptodate (zip, stampFile, XamarinTask.GetFileCopierReportErrorCallback (log), XamarinTask.GetFileCopierLogCallback (log), check_stamp: false))
return true;
Expand Down Expand Up @@ -117,6 +118,8 @@ public static bool TryDecompress (TaskLoggingHelper log, string zip, string reso
createdFiles.Add (decompressedResource);
} else if (Directory.Exists (decompressedResource)) {
createdFiles.AddRange (Directory.GetFiles (decompressedResource, "*", SearchOption.AllDirectories));
} else {
log.LogWarning ("The extracted file or directory '{0}' could not be found." /* The extracted file or directory '{0}' could not be found. */, decompressedResource);
}

return rv;
Expand Down Expand Up @@ -168,11 +171,14 @@ static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string
if (entryPath.Length == 0)
continue;

if (entryPath.StartsWith (resourceAsDir, StringComparison.Ordinal)) {
if (string.IsNullOrEmpty (resource)) {
// an empty resource means extract everything, so we want this
} else if (entryPath.StartsWith (resourceAsDir, StringComparison.Ordinal)) {
// yep, we want this entry
} else if (entryPath == resource) {
// we want this one too
} else {
log.LogMessage (MessageImportance.Low, "Did not extract {0} because it didn't match the resource {1}", entryPath, resource);
// but otherwise nope
continue;
}
Expand All @@ -189,14 +195,15 @@ static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string
}

var isDir = entryPath [entryPath.Length - 1] == zipDirectorySeparator;
var targetPath = Path.Combine (decompressionDir, entryPath);
var targetPath = Path.Combine (decompressionDir, entryPath.Replace (zipDirectorySeparator, Path.DirectorySeparatorChar));
if (isDir) {
Directory.CreateDirectory (targetPath);
} else {
Directory.CreateDirectory (Path.GetDirectoryName (targetPath));
using var streamWrite = File.OpenWrite (targetPath);
using var streamRead = entry.Open ();
streamRead.CopyTo (streamWrite);
log.LogMessage (MessageImportance.Low, "Extracted {0} into {1}", entryPath, targetPath);
}
}

Expand Down
30 changes: 21 additions & 9 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/CompileAppManifestTaskBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public abstract class CompileAppManifestTaskBase : XamarinTask {

public string DebugIPAddresses { get; set; } = String.Empty;

[Required]
public string DefaultSdkVersion { get; set; } = String.Empty;

public ITaskItem [] FontFilesToRegister { get; set; } = Array.Empty<ITaskItem> ();
Expand Down Expand Up @@ -75,7 +74,6 @@ public abstract class CompileAppManifestTaskBase : XamarinTask {
[Required]
public bool SdkIsSimulator { get; set; }

[Required]
public string SdkVersion { get; set; } = String.Empty;

public string SupportedOSPlatformVersion { get; set; } = String.Empty;
Expand All @@ -95,6 +93,10 @@ public string SdkPlatform {
}
}

bool OnWindows {
get => Environment.OSVersion.Platform == PlatformID.Win32NT;
}

public override bool Execute ()
{
PDictionary plist;
Expand Down Expand Up @@ -276,6 +278,9 @@ bool SetMinimumOSVersion (PDictionary plist)
// Nothing is specified in the Info.plist - use SupportedOSPlatformVersion, and if that's not set, then use the sdkVersion
if (!string.IsNullOrEmpty (convertedSupportedOSPlatformVersion)) {
minimumOSVersion = convertedSupportedOSPlatformVersion;
} else if (OnWindows && string.IsNullOrEmpty (SdkVersion)) {
// When building on Windows (Hot Restart), we're not using any Xcode version, so there's no SdkVersion either, so use the min OS version we support if the project doesn't specify anything.
minimumOSVersion = Xamarin.SdkVersions.GetMinVersion (Platform).ToString ();
} else {
minimumOSVersion = SdkVersion;
}
Expand Down Expand Up @@ -305,14 +310,21 @@ bool SetMinimumOSVersion (PDictionary plist)

bool Compile (PDictionary plist)
{
var currentSDK = Sdks.GetAppleSdk (Platform);
if (!OnWindows) {
if (string.IsNullOrEmpty (DefaultSdkVersion)) {
Log.LogError (MSBStrings.E7114 /* The "{0}" task was not given a value for the parameter "{1}", which is required when building on this platform. */, GetType ().Name, "DefaultSdkVersion");
return false;
}

sdkVersion = AppleSdkVersion.Parse (DefaultSdkVersion);
if (!currentSDK.SdkIsInstalled (sdkVersion, SdkIsSimulator)) {
Log.LogError (null, null, null, null, 0, 0, 0, 0, MSBStrings.E0013, Platform, sdkVersion);
return false;
var currentSDK = Sdks.GetAppleSdk (Platform);

sdkVersion = AppleSdkVersion.Parse (DefaultSdkVersion);
if (!currentSDK.SdkIsInstalled (sdkVersion, SdkIsSimulator)) {
Log.LogError (null, null, null, null, 0, 0, 0, 0, MSBStrings.E0013, Platform, sdkVersion);
return false;
}
SetXcodeValues (plist, currentSDK);
}
SetXcodeValues (plist, currentSDK);

switch (Platform) {
case ApplePlatform.iOS:
Expand Down Expand Up @@ -408,7 +420,7 @@ void Validation (PDictionary plist)

var supportedDevices = plist.GetUIDeviceFamily ();
var macCatalystOptimizedForMac = (supportedDevices & IPhoneDeviceType.MacCatalystOptimizedForMac) == IPhoneDeviceType.MacCatalystOptimizedForMac;
if (macCatalystOptimizedForMac) {
if (macCatalystOptimizedForMac && !OnWindows) {
if (Platform != ApplePlatform.MacCatalyst) {
LogAppManifestError (MSBStrings.E7098 /* The UIDeviceFamily value '6' is not valid for this platform. It's only valid for Mac Catalyst. */);
return; // no need to look for more errors, they will probably not make much sense.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public override bool Execute ()

// Figure out the relative directory inside the app bundle where the item is supposed to be placed.
var relativePath = string.Empty;
var virtualProjectPath = GetVirtualAppBundlePath (item);
switch (publishFolderType) {
case PublishFolderType.Assembly:
relativePath = AssemblyDirectory;
Expand All @@ -119,16 +120,23 @@ public override bool Execute ()
continue;
case PublishFolderType.CompressedAppleFramework:
relativePath = FrameworksDirectory;
virtualProjectPath = Path.GetFileNameWithoutExtension (item.ItemSpec);
if (virtualProjectPath.EndsWith (".xcframework", StringComparison.OrdinalIgnoreCase))
virtualProjectPath = Path.ChangeExtension (virtualProjectPath, ".framework");
break;
case PublishFolderType.AppleBindingResourcePackage:
// Nothing to do here, this is handled fully in the targets file
break;
case PublishFolderType.CompressedAppleBindingResourcePackage:
// Nothing to do here, this is handled fully in the targets file
virtualProjectPath = RemoveExtension (virtualProjectPath, ".zip");
break;
case PublishFolderType.PlugIns:
relativePath = PlugInsDirectory;
break;
case PublishFolderType.CompressedPlugIns:
relativePath = PlugInsDirectory;
virtualProjectPath = string.Empty;
break;
case PublishFolderType.RootDirectory:
break;
Expand All @@ -151,7 +159,6 @@ public override bool Execute ()
}

// Compute the relative path of the item relative to the root of the app bundle
var virtualProjectPath = GetVirtualAppBundlePath (item);
relativePath = Path.Combine (relativePath, virtualProjectPath);
item.SetMetadata ("RelativePath", relativePath);
}
Expand All @@ -163,7 +170,7 @@ public override bool Execute ()
var items = entry.Value;
var item = new TaskItem (entry.Key);
item.SetMetadata ("PublishFolderType", "AppleFramework");
item.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (entry.Key)));
item.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.ChangeExtension (Path.GetFileName (entry.Key), "framework")));
list.Add (item);
}

Expand All @@ -172,6 +179,13 @@ public override bool Execute ()
return !Log.HasLoggedErrors;
}

static string RemoveExtension (string path, string extension)
{
if (path.EndsWith (extension, StringComparison.OrdinalIgnoreCase))
return path.Substring (0, path.Length - extension.Length);
return path;
}

// Check if the input, or any of it's parent directories is either an *.xcframework, or a *.framework
static bool TryGetFrameworkDirectory (string path, out string? frameworkDirectory)
{
Expand Down
19 changes: 19 additions & 0 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/ResolveNativeReferencesBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public abstract class ResolveNativeReferencesBase : XamarinTask {
[Required]
public string? Architectures { get; set; }

[Required]
public string FrameworksDirectory { get; set; } = string.Empty;

[Required]
public string IntermediateOutputPath { get; set; } = string.Empty;

Expand Down Expand Up @@ -142,12 +145,16 @@ void ProcessNativeReference (ITaskItem item, string name, List<ITaskItem> native
var nr = new TaskItem (item);
nr.ItemSpec = GetActualLibrary (name);
nr.SetMetadata ("Kind", "Framework");
nr.SetMetadata ("PublishFolderType", "AppleFramework");
nr.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (nr.ItemSpec))));
native_frameworks.Add (nr);
return;
} else if (parentDirectory.EndsWith (".framework", StringComparison.OrdinalIgnoreCase) && Path.GetFileName (name) == Path.GetFileNameWithoutExtension (parentDirectory)) {
var nr = new TaskItem (item);
nr.ItemSpec = GetActualLibrary (name);
nr.SetMetadata ("Kind", "Framework");
nr.SetMetadata ("PublishFolderType", "AppleFramework");
nr.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (nr.ItemSpec))));
native_frameworks.Add (nr);
return;
}
Expand All @@ -157,6 +164,7 @@ void ProcessNativeReference (ITaskItem item, string name, List<ITaskItem> native
var nr = new TaskItem (item);
nr.ItemSpec = name;
nr.SetMetadata ("Kind", "Dynamic");
nr.SetMetadata ("PublishFolderType", "DynamicLibrary");
native_frameworks.Add (nr);
return;
}
Expand All @@ -166,6 +174,7 @@ void ProcessNativeReference (ITaskItem item, string name, List<ITaskItem> native
var nr = new TaskItem (item);
nr.ItemSpec = name;
nr.SetMetadata ("Kind", "Static");
nr.SetMetadata ("PublishFolderType", "StaticLibrary");
native_frameworks.Add (nr);
return;
}
Expand All @@ -177,6 +186,8 @@ void ProcessNativeReference (ITaskItem item, string name, List<ITaskItem> native
var nr = new TaskItem (item);
nr.ItemSpec = GetActualLibrary (frameworkPath);
nr.SetMetadata ("Kind", "Framework");
nr.SetMetadata ("PublishFolderType", "AppleFramework");
nr.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (nr.ItemSpec))));
native_frameworks.Add (nr);
return;
}
Expand All @@ -188,6 +199,8 @@ void ProcessNativeReference (ITaskItem item, string name, List<ITaskItem> native
var nr = new TaskItem (item);
nr.ItemSpec = GetActualLibrary (frameworkPath);
nr.SetMetadata ("Kind", "Framework");
nr.SetMetadata ("PublishFolderType", "AppleFramework");
nr.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (nr.ItemSpec))));
native_frameworks.Add (nr);
return;
}
Expand Down Expand Up @@ -272,6 +285,8 @@ void ProcessSidecar (ITaskItem r, string resources, List<ITaskItem> native_frame
continue;
t.ItemSpec = GetActualLibrary (frameworkPath);
t.SetMetadata ("Kind", "Framework");
t.SetMetadata ("PublishFolderType", "AppleFramework");
t.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (t.ItemSpec))));
break;
}
case ".framework": {
Expand All @@ -283,6 +298,8 @@ void ProcessSidecar (ITaskItem r, string resources, List<ITaskItem> native_frame
}
t.ItemSpec = GetActualLibrary (frameworkPath);
t.SetMetadata ("Kind", "Framework");
t.SetMetadata ("PublishFolderType", "AppleFramework");
t.SetMetadata ("RelativePath", Path.Combine (FrameworksDirectory, Path.GetFileName (Path.GetDirectoryName (t.ItemSpec))));
break;
}
case ".dylib": // macOS
Expand All @@ -294,6 +311,7 @@ void ProcessSidecar (ITaskItem r, string resources, List<ITaskItem> native_frame
}
t.ItemSpec = dylibPath;
t.SetMetadata ("Kind", "Dynamic");
t.SetMetadata ("PublishFolderType", "DynamicLibrary");
break;
case ".a": // static library
string? aPath;
Expand All @@ -304,6 +322,7 @@ void ProcessSidecar (ITaskItem r, string resources, List<ITaskItem> native_frame
}
t.ItemSpec = aPath;
t.SetMetadata ("Kind", "Static");
t.SetMetadata ("PublishFolderType", "StaticLibrary");
break;
default:
Log.LogWarning (MSBStrings.W7105 /* Unexpected extension '{0}' for native reference '{1}' in binding resource package '{2}'. */, Path.GetExtension (name), name, r.ItemSpec);
Expand Down
3 changes: 3 additions & 0 deletions msbuild/Xamarin.MacDev.Tasks/Xamarin.MacDev.Tasks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
<Link>Versions.dotnet.g.cs</Link>
</Compile>
<Compile Remove="Errors.designer.cs" /> <!-- The 'CoreResGen' target will add it again from the EmbeddedResource item, this avoids a warning about the file being compiled twice -->
<Compile Include="..\..\tools\common\SdkVersions.cs">
<Link>external\SdkVersions.cs</Link>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
Loading

6 comments on commit 6e7128b

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.