diff --git a/external/Xamarin.MacDev b/external/Xamarin.MacDev
index 188089e8e69d..f0e6305bf153 160000
--- a/external/Xamarin.MacDev
+++ b/external/Xamarin.MacDev
@@ -1 +1 @@
-Subproject commit 188089e8e69df1a271d4a1260a30868c58643936
+Subproject commit f0e6305bf153798374817e8634a3cdcb395631b3
diff --git a/msbuild/Xamarin.MacDev.Tasks/Decompress.cs b/msbuild/Xamarin.MacDev.Tasks/Decompress.cs
index b28428f41faa..b4e9a293ef7a 100644
--- a/msbuild/Xamarin.MacDev.Tasks/Decompress.cs
+++ b/msbuild/Xamarin.MacDev.Tasks/Decompress.cs
@@ -89,7 +89,30 @@ public static bool IsCompressed (string path)
/// True if successfully decompressed, false otherwise.
public static bool TryDecompress (TaskLoggingHelper log, string zip, string resource, string decompressionDir, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? decompressedResource)
{
- decompressedResource = Path.Combine (decompressionDir, resource);
+ return TryDecompress (log, zip, resource, decompressionDir, null, null, createdFiles, cancellationToken, out decompressedResource);
+ }
+
+ ///
+ /// Extracts the specified resource (may be either a file or a directory) from the given zip file.
+ /// A stamp file will be created to avoid re-extracting unnecessarily.
+ ///
+ /// Fails if:
+ /// * The resource is or contains a symlink and we're executing on Windows.
+ /// * The resource isn't found inside the zip file.
+ ///
+ ///
+ /// The zip to search in
+ /// The relative path inside the zip to extract (may be a file or a directory).
+ /// The location on disk to store the extracted results
+ /// The name of the extracted resource (will be combined with ). The default is .
+ /// The cancellation token (if any=
+ /// The location on disk to the extracted resource
+ /// True if successfully decompressed, false otherwise.
+ public static bool TryDecompress (TaskLoggingHelper log, string zip, string resource, string decompressionDir, string? decompressionName, UnzipFilter? filter, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? decompressedResource)
+ {
+ if (string.IsNullOrEmpty (decompressionName))
+ decompressionName = resource;
+ decompressedResource = Path.Combine (decompressionDir, decompressionName);
var stampFile = decompressedResource.TrimEnd ('\\', '/') + ".stamp";
@@ -105,11 +128,11 @@ public static bool TryDecompress (TaskLoggingHelper log, string zip, string reso
bool rv;
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
- rv = TryDecompressUsingSystemIOCompression (log, zip, resource, decompressionDir, cancellationToken);
+ rv = TryDecompressUsingSystemIOCompression (log, zip, resource, decompressionDir, filter, cancellationToken);
} else if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION"))) {
- rv = TryDecompressUsingSystemIOCompression (log, zip, resource, decompressionDir, cancellationToken);
+ rv = TryDecompressUsingSystemIOCompression (log, zip, resource, decompressionDir, filter, cancellationToken);
} else {
- rv = TryDecompressUsingUnzip (log, zip, resource, decompressionDir, cancellationToken);
+ rv = TryDecompressUsingUnzip (log, zip, resource, decompressionDir, filter, cancellationToken);
}
if (rv) {
@@ -132,41 +155,21 @@ public static bool TryDecompress (TaskLoggingHelper log, string zip, string reso
// The dir separator character in zip files is always "/", even on Windows
const char zipDirectorySeparator = '/';
- static bool TryDecompressUsingUnzip (TaskLoggingHelper log, string zip, string resource, string decompressionDir, CancellationToken? cancellationToken)
- {
- Directory.CreateDirectory (decompressionDir);
- var args = new List {
- "-u", "-o",
- "-d", decompressionDir,
- zip,
- };
-
- if (!string.IsNullOrEmpty (resource)) {
- using var archive = ZipFile.OpenRead (zip);
- resource = resource.Replace ('\\', zipDirectorySeparator);
- var entry = archive.GetEntry (resource);
- if (entry is null) {
- entry = archive.GetEntry (resource + zipDirectorySeparator);
- if (entry is null) {
- log.LogError (MSBStrings.E7112 /* Could not find the file or directory '{0}' in the zip file '{1}'. */, resource, zip);
- return false;
- }
- }
-
- var zipPattern = entry.FullName;
- if (zipPattern.Length > 0 && zipPattern [zipPattern.Length - 1] == zipDirectorySeparator) {
- zipPattern += "*";
- }
-
- args.Add (zipPattern);
- }
+ ///
+ /// A filter to determine whether an entry in a zip file should be extracted or not.
+ /// Returns the relative target path for the entry (relative to the target directory).
+ ///
+ /// The name of the entry inside the zip file. The path separator will always be '/'.
+ /// Whether the entry is a directory.
+ ///
+ public delegate string? UnzipFilter (string entryPath, bool isDirectory);
- var rv = XamarinTask.ExecuteAsync (log, "unzip", args, cancellationToken: cancellationToken).Result;
- return rv.ExitCode == 0;
- }
+ delegate bool DecompressImplementation (TaskLoggingHelper log, string zip, ZipArchiveEntry entry, string targetPath, CancellationToken? cancellationToken);
- static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string zip, string resource, string decompressionDir, CancellationToken? cancellationToken)
+ static bool TryDecompressFiltered (TaskLoggingHelper log, string zip, string resource, string decompressionDir, UnzipFilter? filter, DecompressImplementation decompress, CancellationToken? cancellationToken)
{
+ log.LogMessage (MessageImportance.Low, $"TryDecompressFiltered (zip={zip}, resource={resource}, decompressionDir={decompressionDir})\n{Environment.StackTrace}");
+
var rv = true;
// canonicalize input
@@ -182,38 +185,35 @@ static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string
if (entryPath.Length == 0)
continue;
- if (string.IsNullOrEmpty (resource)) {
- // an empty resource means extract everything, so we want this
- } else if (entryPath.StartsWith (resourceAsDir, StringComparison.Ordinal)) {
+ var isDir = entryPath [entryPath.Length - 1] == zipDirectorySeparator;
+ var canonicalizedEntryPath = entryPath.Replace (zipDirectorySeparator, Path.DirectorySeparatorChar);
+
+ if (string.IsNullOrEmpty (resource) || canonicalizedEntryPath == resource || canonicalizedEntryPath.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);
+ log.LogMessage (MessageImportance.Low, "Did not extract {0} because it didn't match the resource {1}", canonicalizedEntryPath, resource);
// but otherwise nope
continue;
}
- // Check if the file or directory is a symlink, and show an error if so. Symlinks are only supported
- // on non-Windows platforms.
- var entryAttributes = ((uint) GetExternalAttributes (entry)) >> 16;
- const uint S_IFLNK = 0xa000; // #define S_IFLNK 0120000 /* symbolic link */
- var isSymlink = (entryAttributes & S_IFLNK) == S_IFLNK;
- if (isSymlink) {
- log.LogError (MSBStrings.E7113 /* Can't process the zip file '{0}' on this platform: the file '{1}' is a symlink. */, zip, entryPath);
- rv = false;
+ var relativeTargetPath = filter is null ? canonicalizedEntryPath : filter (canonicalizedEntryPath, isDir);
+ if (string.IsNullOrEmpty (relativeTargetPath)) {
+ log.LogMessage (MessageImportance.Low, "Did not extract {0} because the filter filtered it out.", entryPath);
+ // but otherwise nope
continue;
}
- var isDir = entryPath [entryPath.Length - 1] == zipDirectorySeparator;
- var targetPath = Path.Combine (decompressionDir, entryPath.Replace (zipDirectorySeparator, Path.DirectorySeparatorChar));
+ // canonicalize the target path
+ var targetPath = Path.GetFullPath (Path.Combine (decompressionDir, relativeTargetPath));
+
+ log.LogMessage (MessageImportance.Low, "Extracting '{0}' to '{1}' => '{2}'.", entryPath, relativeTargetPath, targetPath);
- // canonicalize the path
- targetPath = Path.GetFullPath (targetPath);
// validate that the unzipped file is inside the target directory
- var decompressionDirectoryPath = decompressionDir.Trim (Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
+ var decompressionDirectoryPath = decompressionDir.TrimEnd (Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
if (!targetPath.StartsWith (decompressionDirectoryPath)) {
+ log.LogMessage (MessageImportance.Low, $"targetPath: {targetPath}");
+ log.LogMessage (MessageImportance.Low, $"decompressionDirectoryPath: {decompressionDirectoryPath}");
log.LogWarning (7144, null, MSBStrings.W7144 /* Did not extract {0} because it would write outside the target directory. */, entryPath);
continue;
}
@@ -222,18 +222,110 @@ static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string
Directory.CreateDirectory (targetPath);
} else {
Directory.CreateDirectory (Path.GetDirectoryName (targetPath));
- using var streamWrite = File.OpenWrite (targetPath);
- using var streamRead = entry.Open ();
+ File.Delete (targetPath);
+ if (!decompress (log, zip, entry, targetPath, cancellationToken)) {
+ rv = false;
+ continue;
+ }
+ log.LogMessage (MessageImportance.Low, "Extracted {0} into {1}", entryPath, targetPath);
+ }
+ }
+
+ return rv;
+ }
+
+ static bool DecompressFileEntryWithStream (TaskLoggingHelper log, string zip, ZipArchiveEntry entry, string targetPath, CancellationToken? cancellationToken)
+ {
+ // Check if the file or directory is a symlink, and show an error if so. Symlinks are only supported
+ // on non-Windows platforms.
+ var entryPath = entry.FullName;
+ var entryAttributes = ((uint) GetExternalAttributes (entry)) >> 16;
+ const uint S_IFLNK = 0xa000; // #define S_IFLNK 0120000 /* symbolic link */
+ var isSymlink = (entryAttributes & S_IFLNK) == S_IFLNK;
+ if (isSymlink) {
+ log.LogError (MSBStrings.E7113 /* Can't process the zip file '{0}' on this platform: the file '{1}' is a symlink. */, zip, entryPath);
+ return false;
+ }
+
+ using var streamWrite = File.OpenWrite (targetPath);
+ using var streamRead = entry.Open ();
#if NET
- streamRead.CopyToAsync (streamWrite, cancellationToken ?? CancellationToken.None).Wait ();
+ streamRead.CopyToAsync (streamWrite, cancellationToken ?? CancellationToken.None).Wait ();
#else
- streamRead.CopyToAsync (streamWrite, 81920 /* default buffer size according to docs */, cancellationToken ?? CancellationToken.None).Wait ();
+ streamRead.CopyToAsync (streamWrite, 81920 /* default buffer size according to docs */, cancellationToken ?? CancellationToken.None).Wait ();
#endif
- log.LogMessage (MessageImportance.Low, "Extracted {0} into {1}", entryPath, targetPath);
+ return true;
+ }
+
+ static bool DecompressFileEntryWithUnzip (TaskLoggingHelper log, string zip, ZipArchiveEntry entry, string targetPath, CancellationToken? cancellationToken)
+ {
+ // Check if the file or directory is a symlink, and show an error if so. Symlinks are only supported
+ // on non-Windows platforms.
+ var entryPath = entry.FullName;
+ var targetDirectory = Path.GetDirectoryName (targetPath);
+
+ var args = new List {
+ "-u", "-o", "-j",
+ "-d", targetDirectory,
+ zip,
+ entryPath,
+ };
+
+ var rv = XamarinTask.ExecuteAsync (log, "unzip", args, cancellationToken: cancellationToken).Result;
+ if (rv.ExitCode != 0)
+ return false;
+
+ if (entry.Name != Path.GetFileName (targetPath))
+ File.Move (Path.Combine (targetDirectory, entry.Name), targetPath);
+
+ return true;
+ }
+
+ static bool TryDecompressUsingUnzip (TaskLoggingHelper log, string zip, string resource, string decompressionDir, UnzipFilter? filter, CancellationToken? cancellationToken)
+ {
+ if (filter is null)
+ return TryDecompressUsingUnzip (log, zip, resource, decompressionDir, cancellationToken);
+
+ return TryDecompressFiltered (log, zip, resource, decompressionDir, filter, DecompressFileEntryWithUnzip, cancellationToken);
+ }
+
+ // Does not support filtering nor extracting partial contents into a custom directory hierarchy.
+ static bool TryDecompressUsingUnzip (TaskLoggingHelper log, string zip, string resource, string decompressionDir, CancellationToken? cancellationToken)
+ {
+ Directory.CreateDirectory (decompressionDir);
+ var args = new List {
+ "-u", "-o",
+ "-d", decompressionDir,
+ zip,
+ };
+
+ if (!string.IsNullOrEmpty (resource)) {
+ using var archive = ZipFile.OpenRead (zip);
+ resource = resource.Replace ('\\', zipDirectorySeparator);
+ var entry = archive.GetEntry (resource);
+ if (entry is null) {
+ entry = archive.GetEntry (resource + zipDirectorySeparator);
+ if (entry is null) {
+ log.LogError (MSBStrings.E7112 /* Could not find the file or directory '{0}' in the zip file '{1}'. */, resource, zip);
+ return false;
+ }
}
+
+ var zipPattern = entry.FullName;
+ if (zipPattern.Length > 0 && zipPattern [zipPattern.Length - 1] == zipDirectorySeparator) {
+ zipPattern += "*";
+ }
+
+ args.Add (zipPattern);
}
- return rv;
+ var rv = XamarinTask.ExecuteAsync (log, "unzip", args, cancellationToken: cancellationToken).Result;
+ return rv.ExitCode == 0;
+ }
+
+ static bool TryDecompressUsingSystemIOCompression (TaskLoggingHelper log, string zip, string resource, string decompressionDir, UnzipFilter? filter, CancellationToken? cancellationToken)
+ {
+ return TryDecompressFiltered (log, zip, resource, decompressionDir, filter, DecompressFileEntryWithStream, cancellationToken);
}
///
diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/ResolveNativeReferences.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/ResolveNativeReferences.cs
index f42cfc369862..c0eea8d62476 100644
--- a/msbuild/Xamarin.MacDev.Tasks/Tasks/ResolveNativeReferences.cs
+++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/ResolveNativeReferences.cs
@@ -16,6 +16,7 @@
using Xamarin.Localization.MSBuild;
using Xamarin.Messaging.Build.Client;
using Xamarin.Utils;
+using System.Text.RegularExpressions;
#nullable enable
@@ -48,6 +49,11 @@ public class ResolveNativeReferences : XamarinTask, ITaskCallback {
[Required]
public string? Architectures { get; set; }
+ public bool UseExperimentalIntermediateDirectory { get; set; }
+
+ // list of regexp of files to exclude when extracting
+ public ITaskItem [] ExtractionFilters { get; set; } = Array.Empty ();
+
[Required]
public string FrameworksDirectory { get; set; } = string.Empty;
@@ -80,6 +86,9 @@ string GetIntermediateDecompressionDir (ITaskItem item)
string GetIntermediateDecompressionDir (string item)
{
+ if (UseExperimentalIntermediateDirectory)
+ return IntermediateOutputPath;
+
return Path.Combine (IntermediateOutputPath, Path.GetFileName (item));
}
@@ -195,7 +204,7 @@ void ProcessNativeReference (ITaskItem item, string name, List native
// (compressed) xcframework
if (name.EndsWith (".xcframework", StringComparison.OrdinalIgnoreCase) || name.EndsWith (".xcframework.zip", StringComparison.OrdinalIgnoreCase)) {
- if (!TryResolveXCFramework (Log, TargetFrameworkMoniker, SdkIsSimulator, Architectures, name, GetIntermediateDecompressionDir (item), createdFiles, cancellationToken, out var nativeLibraryPath))
+ if (!TryResolveXCFramework (Log, TargetFrameworkMoniker, SdkIsSimulator, Architectures, name, GetIntermediateDecompressionDir (item), ExtractionFilters, createdFiles, cancellationToken, out var nativeLibraryPath))
return;
var nr = new TaskItem (item);
SetMetadataNativeLibrary (nr, nativeLibraryPath);
@@ -205,7 +214,7 @@ void ProcessNativeReference (ITaskItem item, string name, List native
// compressed framework
if (name.EndsWith (".framework.zip", StringComparison.OrdinalIgnoreCase)) {
- if (!CompressionHelper.TryDecompress (Log, name, Path.GetFileNameWithoutExtension (name), GetIntermediateDecompressionDir (item), createdFiles, cancellationToken, out var frameworkPath))
+ if (!TryDecompress (name, Path.GetFileNameWithoutExtension (name), GetIntermediateDecompressionDir (item), createdFiles, cancellationToken, out var frameworkPath))
return;
var nr = new TaskItem (item);
nr.ItemSpec = GetActualLibrary (frameworkPath);
@@ -302,20 +311,20 @@ void ProcessSidecar (ITaskItem r, string resources, List native_frame
return;
var isCompressed = CompressionHelper.IsCompressed (resources);
- XmlDocument document = new XmlDocument ();
+ var document = new XmlDocument ();
document.LoadXmlWithoutNetworkAccess (manifestContents);
foreach (XmlNode referenceNode in document.GetElementsByTagName ("NativeReference")) {
ITaskItem t = new TaskItem (r);
var name = referenceNode.Attributes ["Name"].Value.Trim ('\\', '/');
if (name.EndsWith (".xcframework", StringComparison.Ordinal) || name.EndsWith (".xcframework.zip", StringComparison.Ordinal)) {
- if (!TryResolveXCFramework (Log, TargetFrameworkMoniker, SdkIsSimulator, Architectures, resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out var nativeLibraryPath))
+ if (!TryResolveXCFramework (Log, TargetFrameworkMoniker, SdkIsSimulator, Architectures, resources, name, GetIntermediateDecompressionDir (resources), ExtractionFilters, createdFiles, cancellationToken, out var nativeLibraryPath))
continue;
SetMetadataNativeLibrary (t, nativeLibraryPath);
} else if (name.EndsWith (".framework", StringComparison.Ordinal)) {
string? frameworkPath;
if (!isCompressed) {
frameworkPath = Path.Combine (resources, name);
- } else if (!CompressionHelper.TryDecompress (Log, resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out frameworkPath)) {
+ } else if (!TryDecompress (resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out frameworkPath)) {
continue;
}
t.ItemSpec = GetActualLibrary (frameworkPath);
@@ -327,7 +336,7 @@ void ProcessSidecar (ITaskItem r, string resources, List native_frame
string? dylibPath;
if (!isCompressed) {
dylibPath = Path.Combine (resources, name);
- } else if (!CompressionHelper.TryDecompress (Log, resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out dylibPath)) {
+ } else if (!TryDecompress (resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out dylibPath)) {
continue;
}
t.ItemSpec = dylibPath;
@@ -338,7 +347,7 @@ void ProcessSidecar (ITaskItem r, string resources, List native_frame
string? aPath;
if (!isCompressed) {
aPath = Path.Combine (resources, name);
- } else if (!CompressionHelper.TryDecompress (Log, resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out aPath)) {
+ } else if (!TryDecompress (resources, name, GetIntermediateDecompressionDir (resources), createdFiles, cancellationToken, out aPath)) {
continue;
}
t.ItemSpec = aPath;
@@ -375,7 +384,7 @@ void ProcessSidecar (ITaskItem r, string resources, List native_frame
/// A full path to the resolved native library within the xcframework. If 'resourcePath' is compressed, this will point to where the native library is decompressed on disk.
///
/// True if a native library was successfully found. Otherwise false, and an error will have been printed to the log.
- public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFrameworkMoniker, bool isSimulator, string? architectures, string path, string intermediateDecompressionDir, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeLibraryPath)
+ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFrameworkMoniker, bool isSimulator, string? architectures, string path, string intermediateDecompressionDir, ITaskItem [] filters, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeLibraryPath)
{
string resourcePath;
string xcframework;
@@ -387,7 +396,7 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFr
resourcePath = Path.GetDirectoryName (path);
xcframework = Path.GetFileName (path);
}
- return TryResolveXCFramework (log, targetFrameworkMoniker, isSimulator, architectures, resourcePath, xcframework, intermediateDecompressionDir, createdFiles, cancellationToken, out nativeLibraryPath);
+ return TryResolveXCFramework (log, targetFrameworkMoniker, isSimulator, architectures, resourcePath, xcframework, intermediateDecompressionDir, filters, createdFiles, cancellationToken, out nativeLibraryPath);
}
///
@@ -402,7 +411,7 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFr
/// A full path to the resolved native library within the xcframework. If 'resourcePath' is compressed, this will point to where the native library is decompressed on disk.
///
/// True if a native library was successfully found. Otherwise false, and an error will have been printed to the log.
- public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFrameworkMoniker, bool isSimulator, string? architectures, string resourcePath, string xcframework, string intermediateDecompressionDir, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeLibraryPath)
+ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFrameworkMoniker, bool isSimulator, string? architectures, string resourcePath, string xcframework, string intermediateDecompressionDir, ITaskItem [] filters, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeLibraryPath)
{
nativeLibraryPath = null;
@@ -412,29 +421,30 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFr
var isCompressed = CompressionHelper.IsCompressed (resourcePath);
var xcframeworkPath = isCompressed ? resourcePath : Path.Combine (resourcePath, xcframework);
- if (!TryResolveXCFramework (log, plist, xcframeworkPath, targetFrameworkMoniker, isSimulator, architectures!, cancellationToken, out var nativeLibraryRelativePath))
+ if (!TryResolveXCFramework (log, plist, xcframeworkPath, targetFrameworkMoniker, isSimulator, architectures!, cancellationToken, out var nativeRelativePath))
return false;
if (!isCompressed && CompressionHelper.IsCompressed (xcframework)) {
var zipPath = Path.Combine (resourcePath, xcframework);
var xcframeworkName = Path.GetFileNameWithoutExtension (xcframework);
- if (!CompressionHelper.TryDecompress (log, zipPath, xcframeworkName, intermediateDecompressionDir, createdFiles, cancellationToken, out var decompressedXcframeworkPath))
+ var resource = Path.Combine (xcframeworkName, nativeRelativePath);
+ if (!TryDecompress (log, zipPath, resource, filters, intermediateDecompressionDir, createdFiles, cancellationToken, out var decompressedFrameworkPath))
return false;
- nativeLibraryPath = Path.Combine (intermediateDecompressionDir, xcframeworkName, nativeLibraryRelativePath);
+ nativeLibraryPath = decompressedFrameworkPath;
return true;
}
if (!isCompressed) {
- nativeLibraryPath = Path.Combine (resourcePath, xcframework, nativeLibraryRelativePath);
+ nativeLibraryPath = Path.Combine (resourcePath, xcframework, nativeRelativePath);
return true;
}
- var zipResource = Path.Combine (xcframework, Path.GetDirectoryName (nativeLibraryRelativePath));
- if (!CompressionHelper.TryDecompress (log, resourcePath, zipResource, intermediateDecompressionDir, createdFiles, cancellationToken, out var decompressedPath))
+ var zipResource = Path.Combine (xcframework, nativeRelativePath);
+ if (!TryDecompress (log, resourcePath, zipResource, filters, intermediateDecompressionDir, createdFiles, cancellationToken, out var decompressedPath))
return false;
- nativeLibraryPath = Path.Combine (intermediateDecompressionDir, xcframework, nativeLibraryRelativePath);
+ nativeLibraryPath = Path.Combine (intermediateDecompressionDir, Path.GetFileName (zipResource));
return true;
} catch (Exception e) {
@@ -451,14 +461,16 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, string targetFr
/// The log to log any errors and/or warnings.
/// The plist inside the xcframework.
/// The path to the xcframework. This is only used for error messages, so it can also point to a compressed xcframework.
- /// If we're targeting the simulator
/// The target framework moniker.
- /// The target architectures
- /// A relative path to the resolved native library within the xcframework.
+ /// If we're targeting the simulator.
+ /// The target architectures.
+ /// A cancellation token.
+ /// The relative path to the library (.framework/.dylib/.a) inside the xcframework.
/// True if a native library was successfully found. Otherwise false, and an error will have been printed to the log.
- public static bool TryResolveXCFramework (TaskLoggingHelper log, PDictionary plist, string xcframeworkPath, string targetFrameworkMoniker, bool isSimulator, string architectures, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeLibraryPath)
+ public static bool TryResolveXCFramework (TaskLoggingHelper log, PDictionary plist, string xcframeworkPath, string targetFrameworkMoniker, bool isSimulator, string architectures, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? nativeRelativePath)
{
- nativeLibraryPath = null;
+ nativeRelativePath = null;
+
var platform = PlatformFrameworkHelper.GetFramework (targetFrameworkMoniker);
string platformName;
switch (platform) {
@@ -522,9 +534,9 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, PDictionary pli
return false;
}
}
- var library_path = (PString?) item ["LibraryPath"];
- var library_identifier = (PString?) item ["LibraryIdentifier"];
- nativeLibraryPath = GetActualLibrary (Path.Combine (library_identifier!, library_path!));
+ var library_path = (string) (PString) item ["LibraryPath"]!;
+ var library_identifier = (string?) (PString?) item ["LibraryIdentifier"];
+ nativeRelativePath = Path.Combine (library_identifier!, library_path);
return true;
}
@@ -532,6 +544,43 @@ public static bool TryResolveXCFramework (TaskLoggingHelper log, PDictionary pli
return false;
}
+ bool TryDecompress (string zip, string resource, string decompressionDir, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? decompressedResource)
+ {
+ return TryDecompress (Log, zip, resource, ExtractionFilters, decompressionDir, createdFiles, cancellationToken, out decompressedResource);
+ }
+
+ static bool TryDecompress (TaskLoggingHelper log, string zip, string resource, IEnumerable? extractionFilters, string decompressionDir, List createdFiles, CancellationToken? cancellationToken, [NotNullWhen (true)] out string? decompressedResource)
+ {
+ var regexps = extractionFilters?.
+ Select (v => v.ItemSpec)?.
+ Select (v => new Regex (v, RegexOptions.Compiled))?.
+ ToArray ();
+
+ var decompressionName = Path.GetFileName (resource);
+ var directoryComponentToRemove = Path.GetDirectoryName (resource);
+
+ if (!string.IsNullOrEmpty (directoryComponentToRemove))
+ directoryComponentToRemove = directoryComponentToRemove.TrimEnd ('\\', '/') + "/";
+
+ var filter = new CompressionHelper.UnzipFilter ((entryPath, isDirectory) => {
+ if (regexps is not null) {
+ foreach (var exp in regexps) {
+ if (exp.IsMatch (entryPath)) {
+ log.LogMessage (MessageImportance.Low, "Did not extract {0} because the filter '{1}' filtered it out.", entryPath, exp);
+ return null;
+ }
+ }
+ }
+
+ if (string.IsNullOrEmpty (resource) || string.IsNullOrEmpty (decompressionName))
+ return entryPath;
+
+ var targetPath = entryPath.Substring (directoryComponentToRemove.Length);
+ return targetPath;
+ });
+
+ return CompressionHelper.TryDecompress (log, zip, resource, decompressionDir, decompressionName, filter, createdFiles, cancellationToken, out decompressedResource);
+ }
public void Cancel ()
{
if (ShouldExecuteRemotely ()) {
diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets
index 984ba1c916a5..1dff2f12cd80 100644
--- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets
+++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets
@@ -164,6 +164,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
References="@(ReferencePath)"
SdkIsSimulator="$(_SdkIsSimulator)"
TargetFrameworkMoniker="$(_ComputedTargetFrameworkMoniker)"
+ UseExperimentalIntermediateDirectory="$(_UseExperimentalIntermediateDirectory)"
>
diff --git a/tests/dotnet/UnitTests/ProjectTest.cs b/tests/dotnet/UnitTests/ProjectTest.cs
index 8354884182b2..d08feafab7e7 100644
--- a/tests/dotnet/UnitTests/ProjectTest.cs
+++ b/tests/dotnet/UnitTests/ProjectTest.cs
@@ -2437,6 +2437,43 @@ public void CompressedXCFrameworkInBindingProjectApp (ApplePlatform platform)
var appExecutable = GetNativeExecutable (platform, appPath);
Assert.That (appExecutable, Does.Exist, "There is an executable");
+ var objDir = GetObjDir (project_path, platform, runtimeIdentifiers);
+ var sidecarDir = Path.Combine (objDir, "BindingWithCompressedXCFramework.resources");
+ var frameworks = new string [] {
+ "XStaticArTest",
+ "XStaticObjectTest",
+ "XTest",
+ };
+ foreach (var fw in frameworks) {
+ var fwDir = Path.Combine (sidecarDir, $"{fw}.framework");
+ var stampFile = Path.Combine (sidecarDir, $"{fw}.framework.stamp");
+ Assert.That (stampFile, Does.Exist.IgnoreDirectories, "Stamp file");
+ Assert.That (fwDir, Does.Exist.IgnoreFiles, "Framework directory");
+ // assert the maximum length of any file extracted from the compressed sidecar (we don't want the max length to be too high, because we run into MAX_PATH issues on Windows)
+ var allFilesInFrameworkDirectory = Directory.GetFileSystemEntries (fwDir, "*", SearchOption.AllDirectories);
+ var maxLength = allFilesInFrameworkDirectory.Select (v => v.Length).Max ();
+ foreach (var fw2 in allFilesInFrameworkDirectory.OrderBy (v => v))
+ Console.WriteLine ($"{fw2.Length}: {fw2}");
+ maxLength -= fwDir.Length;
+ maxLength -= fw.Length;
+ maxLength -= 1; // directory separator
+ Console.WriteLine ($"fwDir: {fwDir}");
+ Console.WriteLine ($"fw: {fw} {fw.Length}");
+ Assert.That (maxLength, Is.GreaterThanOrEqualTo (0), "Length A");
+ switch (platform) {
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ Assert.That (maxLength, Is.LessThanOrEqualTo ("/Versions/A/Resources/Info.plist".Length), "Length B");
+ break;
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ Assert.That (maxLength, Is.LessThanOrEqualTo ("/Info.plist".Length), "Length B");
+ break;
+ default:
+ throw new NotImplementedException ();
+ }
+ }
+
if (CanExecute (platform, properties)) {
ExecuteWithMagicWordAndAssert (appExecutable);
}
diff --git a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ResolveNativeReferencesTaskTest.cs b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ResolveNativeReferencesTaskTest.cs
index ec39d36da20e..cc52a15a821b 100644
--- a/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ResolveNativeReferencesTaskTest.cs
+++ b/tests/msbuild/Xamarin.MacDev.Tasks.Tests/TaskTests/ResolveNativeReferencesTaskTest.cs
@@ -1,10 +1,16 @@
using System;
+using System.Collections.Generic;
using System.IO;
+using System.IO.Compression;
+using System.IO.Enumeration;
+using System.Linq;
using System.Threading;
-
+using Microsoft.Build.Experimental.ProjectCache;
+using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NUnit.Framework;
+using Xamarin.Tests;
using Xamarin.Utils;
#nullable enable
@@ -12,52 +18,774 @@
namespace Xamarin.MacDev.Tasks.Tests {
[TestFixture]
- public class ResolveNativeReferencesTaskTest {
+ public class ResolveNativeReferencesTaskTest : TestBase {
TaskLoggingHelper log = new TaskLoggingHelper (new TestEngine (), "ResolveNativeReferences");
// single arch request (subset are fine)
- [TestCase (TargetFramework.DotNet_iOS_String, false, "arm64", "ios-arm64/Universal.framework/Universal")]
- [TestCase (TargetFramework.DotNet_iOS_String, true, "x86_64", "ios-arm64_x86_64-simulator/Universal.framework/Universal")] // subset
- [TestCase (TargetFramework.DotNet_MacCatalyst_String, false, "x86_64", "ios-arm64_x86_64-maccatalyst/Universal.framework/Universal")] // subset
- [TestCase (TargetFramework.DotNet_tvOS_String, false, "arm64", "tvos-arm64/Universal.framework/Universal")]
- [TestCase (TargetFramework.DotNet_tvOS_String, true, "x86_64", "tvos-arm64_x86_64-simulator/Universal.framework/Universal")] // subset
- [TestCase (TargetFramework.DotNet_macOS_String, false, "x86_64", "macos-arm64_x86_64/Universal.framework/Universal")] // subset
+ [TestCase (TargetFramework.DotNet_iOS_String, false, "arm64", "ios-arm64/Universal.framework/Universal", "ios-arm64/Universal.framework")]
+ [TestCase (TargetFramework.DotNet_iOS_String, true, "x86_64", "ios-arm64_x86_64-simulator/Universal.framework/Universal", "ios-arm64_x86_64-simulator/Universal.framework")] // subset
+ [TestCase (TargetFramework.DotNet_MacCatalyst_String, false, "x86_64", "ios-arm64_x86_64-maccatalyst/Universal.framework/Universal", "ios-arm64_x86_64-maccatalyst/Universal.framework")] // subset
+ [TestCase (TargetFramework.DotNet_tvOS_String, false, "arm64", "tvos-arm64/Universal.framework/Universal", "tvos-arm64/Universal.framework")]
+ [TestCase (TargetFramework.DotNet_tvOS_String, true, "x86_64", "tvos-arm64_x86_64-simulator/Universal.framework/Universal", "tvos-arm64_x86_64-simulator/Universal.framework")] // subset
+ [TestCase (TargetFramework.DotNet_macOS_String, false, "x86_64", "macos-arm64_x86_64/Universal.framework/Universal", "macos-arm64_x86_64/Universal.framework")] // subset
// multiple arch request (all must be present)
- [TestCase (TargetFramework.DotNet_macOS_String, false, "x86_64, arm64", "macos-arm64_x86_64/Universal.framework/Universal")]
+ [TestCase (TargetFramework.DotNet_macOS_String, false, "x86_64, arm64", "macos-arm64_x86_64/Universal.framework/Universal", "macos-arm64_x86_64/Universal.framework")]
// failure to resolve requested architecture
- [TestCase (TargetFramework.DotNet_iOS_String, true, "i386, x86_64", null)] // i386 not available
+ [TestCase (TargetFramework.DotNet_iOS_String, true, "i386, x86_64", null, null)] // i386 not available
// failure to resolve mismatched variant
- [TestCase (TargetFramework.DotNet_macOS_String, true, "x86_64", null)] // simulator not available on macOS
- public void Xcode12_x (string targetFrameworkMoniker, bool isSimulator, string architecture, string expected)
+ [TestCase (TargetFramework.DotNet_macOS_String, true, "x86_64", null, null)] // simulator not available on macOS
+ public void Xcode12_x (string targetFrameworkMoniker, bool isSimulator, string architecture, string expected, string expectedNativeRelativePath)
{
// on Xcode 12.2+ you get arm64 for all (iOS, tvOS) simulators
var path = Path.Combine (Path.GetDirectoryName (GetType ().Assembly.Location)!, "Resources", "xcf-xcode12.2.plist");
var plist = PDictionary.FromFile (path)!;
- var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", targetFrameworkMoniker, isSimulator, architecture, null, out var frameworkPath);
+ var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", targetFrameworkMoniker, isSimulator, architecture, null, out var nativeRelativePath);
Assert.AreEqual (result, !string.IsNullOrEmpty (expected), "result");
- Assert.That (frameworkPath, Is.EqualTo (expected), "frameworkPath");
+ Assert.That (nativeRelativePath, Is.EqualTo (expectedNativeRelativePath), "frameworkPath");
}
- [TestCase (TargetFramework.DotNet_iOS_String, false, "ARMv7", "ios-arm64_armv7_armv7s/XTest.framework/XTest")]
- public void PreXcode12 (string targetFrameworkMoniker, bool isSimulator, string architecture, string expected)
+ [TestCase (TargetFramework.DotNet_iOS_String, false, "ARMv7", "ios-arm64_armv7_armv7s/XTest.framework/XTest", "ios-arm64_armv7_armv7s/XTest.framework")]
+ public void PreXcode12 (string targetFrameworkMoniker, bool isSimulator, string architecture, string expected, string expectedNativeRelativePath)
{
var path = Path.Combine (Path.GetDirectoryName (GetType ().Assembly.Location)!, "Resources", "xcf-prexcode12.plist");
var plist = PDictionary.FromFile (path)!;
- var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", targetFrameworkMoniker, isSimulator, architecture, null, out var frameworkPath);
+ var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", targetFrameworkMoniker, isSimulator, architecture, null, out var nativeRelativePath);
Assert.AreEqual (result, !string.IsNullOrEmpty (expected), "result");
- Assert.That (frameworkPath, Is.EqualTo (expected), "frameworkPath");
+ Assert.That (nativeRelativePath, Is.EqualTo (expectedNativeRelativePath), "frameworkPath");
}
[Test]
public void BadInfoPlist ()
{
var plist = new PDictionary ();
- var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", TargetFramework.DotNet_iOS_String, false, "x86_64", null, out var frameworkPath);
+ var result = ResolveNativeReferences.TryResolveXCFramework (log, plist, "N/A", TargetFramework.DotNet_iOS_String, false, "x86_64", null, out var nativeRelativePath);
Assert.IsFalse (result, "Invalid Info.plist");
}
+
+ [TestCase (ApplePlatform.iOS, false)]
+ [TestCase (ApplePlatform.iOS, true)]
+ [TestCase (ApplePlatform.MacOSX, false)]
+ [TestCase (ApplePlatform.TVOS, false)]
+ [TestCase (ApplePlatform.TVOS, true)]
+ [TestCase (ApplePlatform.MacCatalyst, false)]
+ public void CompressedNativeReference (ApplePlatform platform, bool useSystemIOCompression)
+ {
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+
+ var tmpdir = Cache.CreateTemporaryDirectory ();
+
+ var item = new TaskItem (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XTest.xcframework.zip"));
+ item.SetMetadata ("Kind", "Framework");
+
+ var task = CreateTask ();
+ task.Architectures = "ARM64";
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ task.FrameworksDirectory = "";
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ task.FrameworksDirectory = "Contents/Frameworks/";
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+ task.IntermediateOutputPath = tmpdir;
+ task.NativeReferences = new TaskItem [] {
+ item,
+ };
+ task.SdkIsSimulator = false;
+ task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform).ToString ();
+
+ var originalSystemIOCompression = Environment.GetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION");
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", "1");
+
+ try {
+ Assert.IsTrue (task.Execute (), "Execute");
+
+ var expectedFiles = new List () {
+ Path.Combine ("XTest.xcframework.zip"),
+ Path.Combine ("XTest.xcframework.zip", "XTest.framework"),
+ Path.Combine ("XTest.xcframework.zip", "XTest.framework", "XTest"),
+ Path.Combine ("XTest.xcframework.zip", "XTest.framework.stamp"),
+ };
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Info.plist"));
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Resources"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions", "A", "Resources", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions", "A", "XTest"));
+ expectedFiles.Add (Path.Combine ("XTest.xcframework.zip", "XTest.framework", "Versions", "Current"));
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+
+ var files = new FileSystemEnumerable (
+ directory: tmpdir,
+ transform: (ref FileSystemEntry entry) => entry.ToFullPath (),
+ options: new EnumerationOptions {
+ RecurseSubdirectories = true,
+ }) {
+ ShouldRecursePredicate = (ref FileSystemEntry entry) => {
+ return entry.ToFileSystemInfo ().LinkTarget is null;
+ }
+ }
+ .Select (v => v [(tmpdir.Length + 1)..])
+ .OrderBy (v => v)
+ .ToArray ();
+
+ var expectedFilesSorted = expectedFiles.OrderBy (v => v).ToArray ();
+
+ Assert.That (files, Is.EqualTo (expectedFilesSorted), "Unzipped files");
+
+ var nativeFrameworks = task.NativeFrameworks?.OrderBy (v => v.ItemSpec).ToArray () ?? Array.Empty ();
+ var nativeFrameworkNames = nativeFrameworks.Select (v => v.ItemSpec).ToArray ();
+ var expectedNativeFrameworkNames = new string [] {
+ Path.Combine (tmpdir, "XTest.xcframework.zip", "XTest.framework/XTest"),
+ };
+ Assert.That (nativeFrameworkNames, Is.EqualTo (expectedNativeFrameworkNames), "Native frameworks");
+ } finally {
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", originalSystemIOCompression);
+ }
+ }
+
+ [TestCase (ApplePlatform.iOS, false)]
+ [TestCase (ApplePlatform.iOS, true)]
+ [TestCase (ApplePlatform.MacOSX, false)]
+ [TestCase (ApplePlatform.TVOS, false)]
+ [TestCase (ApplePlatform.TVOS, true)]
+ [TestCase (ApplePlatform.MacCatalyst, false)]
+ public void SidecarFromReferenceWithCompressedNativeReferences (ApplePlatform platform, bool useSystemIOCompression)
+ {
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+
+ var tmpdir = Cache.CreateTemporaryDirectory ();
+ var inputdir = Path.Combine (tmpdir, "input");
+ var outputdir = Path.Combine (tmpdir, "output");
+
+ var dll = Path.Combine (inputdir, "BindingWithCompressedXCFramework.dll");
+ var sidecar = Path.Combine (inputdir, "BindingWithCompressedXCFramework.resources");
+ Directory.CreateDirectory (sidecar);
+ var manifest =
+ $"""
+
+
+
+
+ ../../../test-libraries/.libs/XTest.xcframework.zip
+
+ Framework
+
+
+
+
+
+
+
+
+
+ ../../../test-libraries/.libs/XStaticArTest.xcframework.zip
+
+ Static
+
+
+
+
+
+
+
+
+
+ ../../../test-libraries/.libs/XStaticObjectTest.xcframework.zip
+
+ Static
+
+
+
+
+
+
+
+ """;
+ File.WriteAllText (Path.Combine (sidecar, "manifest"), manifest);
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XTest.xcframework.zip"), Path.Combine (sidecar, "XTest.xcframework.zip"));
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XStaticArTest.xcframework.zip"), Path.Combine (sidecar, "XStaticArTest.xcframework.zip"));
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XStaticObjectTest.xcframework.zip"), Path.Combine (sidecar, "XStaticObjectTest.xcframework.zip"));
+
+ var item = new TaskItem (dll);
+
+ var task = CreateTask ();
+ task.Architectures = "ARM64";
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ task.FrameworksDirectory = "";
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ task.FrameworksDirectory = "Contents/Frameworks/";
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+ task.IntermediateOutputPath = outputdir;
+ task.References = new TaskItem [] {
+ item,
+ };
+ task.SdkIsSimulator = false;
+ task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform).ToString ();
+
+ var originalSystemIOCompression = Environment.GetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION");
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", "1");
+
+ try {
+ Assert.IsTrue (task.Execute (), "Execute");
+
+ var expectedFiles = new List ();
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "XStaticArTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "XStaticObjectTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "XTest"));
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "XStaticArTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "XStaticObjectTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "Resources", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "XTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "Current"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "XTest"));
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+
+ var files = new FileSystemEnumerable (
+ directory: task.IntermediateOutputPath,
+ transform: (ref FileSystemEntry entry) => entry.ToFullPath (),
+ options: new EnumerationOptions {
+ RecurseSubdirectories = true,
+ }) {
+ ShouldRecursePredicate = (ref FileSystemEntry entry) => {
+ return entry.ToFileSystemInfo ().LinkTarget is null;
+ }
+ }
+ .Select (v => v [(task.IntermediateOutputPath.Length + 1)..])
+ .OrderBy (v => v)
+ .ToArray ();
+
+ var expectedFilesSorted = expectedFiles.OrderBy (v => v).ToArray ();
+
+ Assert.That (files, Is.EqualTo (expectedFilesSorted), "Unzipped files");
+
+ var nativeFrameworks = task.NativeFrameworks?.OrderBy (v => v.ItemSpec).ToArray () ?? Array.Empty ();
+ var nativeFrameworkNames = nativeFrameworks.Select (v => v.ItemSpec).ToArray ();
+ var expectedNativeFrameworkNames = new string [] {
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XStaticArTest.framework/XStaticArTest"),
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework/XStaticObjectTest"),
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XTest.framework/XTest"),
+ };
+ Assert.That (nativeFrameworkNames, Is.EqualTo (expectedNativeFrameworkNames), "Native frameworks");
+ } finally {
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", originalSystemIOCompression);
+ }
+ }
+
+ [TestCase (ApplePlatform.iOS, false)]
+ [TestCase (ApplePlatform.iOS, true)]
+ [TestCase (ApplePlatform.MacOSX, false)]
+ [TestCase (ApplePlatform.TVOS, false)]
+ [TestCase (ApplePlatform.TVOS, true)]
+ [TestCase (ApplePlatform.MacCatalyst, false)]
+ public void CompressedSidecarFromReference (ApplePlatform platform, bool useSystemIOCompression)
+ {
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+
+ var tmpdir = Cache.CreateTemporaryDirectory ();
+ var inputdir = Path.Combine (tmpdir, "input");
+ var outputdir = Path.Combine (tmpdir, "output");
+
+ var dll = Path.Combine (inputdir, "bindings-test.dll");
+ var sidecar = Path.Combine (inputdir, Path.GetFileNameWithoutExtension (dll) + ".resources.zip");
+ var manifest =
+ $"""
+
+
+
+ CoreLocation Foundation ModelIO
+ /Users/rolf/work/dotnet/macios/msbuild/macios/tests/test-libraries/.libs/libtest.xcframework
+
+ Static
+
+
+
+
+
+
+
+
+
+ /Users/rolf/work/dotnet/macios/msbuild/macios/tests/test-libraries/.libs/SwiftTest.xcframework
+
+ Framework
+
+ true
+
+
+
+
+
+
+
+ /Users/rolf/work/dotnet/macios/msbuild/macios/tests/test-libraries/.libs/SwiftTest2.xcframework
+
+ Framework
+
+ true
+
+
+
+
+
+ """;
+
+ Directory.CreateDirectory (Path.GetDirectoryName (sidecar)!);
+ using var stream = File.OpenWrite (sidecar);
+ using (var archive = new ZipArchive (stream, ZipArchiveMode.Create)) {
+ archive.CreateEntryFromString (manifest, "manifest");
+ } // dispose here to make sure everything is written to disk
+ var rootDirectory = Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs");
+ ZipHelpers.AddDirectoryToZipFile (sidecar, Path.Combine (rootDirectory, "libtest.xcframework"), rootDirectory);
+ ZipHelpers.AddDirectoryToZipFile (sidecar, Path.Combine (rootDirectory, "SwiftTest.xcframework"), rootDirectory);
+ ZipHelpers.AddDirectoryToZipFile (sidecar, Path.Combine (rootDirectory, "SwiftTest2.xcframework"), rootDirectory);
+
+ var item = new TaskItem (dll);
+
+ var task = CreateTask ();
+ task.Architectures = "ARM64";
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ task.FrameworksDirectory = "";
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ task.FrameworksDirectory = "Contents/Frameworks/";
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+ task.IntermediateOutputPath = outputdir;
+ task.References = new TaskItem [] {
+ item,
+ };
+ task.SdkIsSimulator = false;
+ task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform).ToString ();
+
+ var originalSystemIOCompression = Environment.GetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION");
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", "1");
+
+ try {
+ Assert.IsTrue (task.Execute (), "Execute");
+
+ var expectedFiles = new List ();
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "libtest.a"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "libtest.a.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "SwiftTest"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "SwiftTest2"));
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "libtest.a"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "libtest.a.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Resources"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "SwiftTest"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions", "A", "Resources", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions", "A", "SwiftTest"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest.framework", "Versions", "Current"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Resources"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "SwiftTest2"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions", "A", "Resources", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions", "A", "SwiftTest2"));
+ expectedFiles.Add (Path.Combine ("bindings-test.resources.zip", "SwiftTest2.framework", "Versions", "Current"));
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+
+ var files = new FileSystemEnumerable (
+ directory: task.IntermediateOutputPath,
+ transform: (ref FileSystemEntry entry) => entry.ToFullPath (),
+ options: new EnumerationOptions {
+ RecurseSubdirectories = true,
+ }) {
+ ShouldRecursePredicate = (ref FileSystemEntry entry) => {
+ return entry.ToFileSystemInfo ().LinkTarget is null;
+ }
+ }
+ .Select (v => v [(task.IntermediateOutputPath.Length + 1)..])
+ .OrderBy (v => v)
+ .ToArray ();
+
+ var expectedFilesSorted = expectedFiles.OrderBy (v => v).ToArray ();
+
+ Assert.That (files, Is.EqualTo (expectedFilesSorted), "Unzipped files");
+
+ var nativeFrameworks = task.NativeFrameworks?.OrderBy (v => v.ItemSpec).ToArray () ?? Array.Empty ();
+ var nativeFrameworkNames = nativeFrameworks.Select (v => v.ItemSpec).ToArray ();
+ var expectedNativeFrameworkNames = new string [] {
+ Path.Combine (outputdir, "bindings-test.resources.zip", "libtest.a"),
+ Path.Combine (outputdir, "bindings-test.resources.zip", "SwiftTest.framework/SwiftTest"),
+ Path.Combine (outputdir, "bindings-test.resources.zip", "SwiftTest2.framework/SwiftTest2"),
+ };
+ Assert.That (nativeFrameworkNames, Is.EqualTo (expectedNativeFrameworkNames), "Native frameworks");
+ } finally {
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", originalSystemIOCompression);
+ }
+ }
+
+ static void AddFileToZip (ZipArchive archive, string pathInZip, string contents)
+ {
+ var entry = archive.CreateEntry (pathInZip);
+ using var entryStream = entry.Open ();
+ using var writer = new StreamWriter (entryStream);
+ writer.Write (contents);
+ }
+
+ static void StuffZipWithFiles (string zipFile)
+ {
+ using var stream = File.Open (zipFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+ using var archive = new ZipArchive (stream, ZipArchiveMode.Update);
+ var manifests = archive.Entries.Where (v => {
+ if (v.Name != "Info.plist")
+ return false;
+ var dir = Path.GetDirectoryName (v.FullName);
+ if (dir?.EndsWith (".xcframework", StringComparison.Ordinal) != true)
+ return false;
+
+ if (Path.GetFileName (dir) != dir)
+ return false; // inside an unexpected subdirectory somewhere
+
+ return true;
+ }).ToArray ();
+
+ var frameworks = new List<(string Path, string Platform)> ();
+ foreach (var manifest in manifests) {
+ using var manifestStream = manifest.Open ();
+ var dict = (PDictionary) PDictionary.FromStream (manifestStream)!;
+ var availableLibraries = dict.Get ("AvailableLibraries")!;
+ foreach (PDictionary lib in availableLibraries) {
+ var libraryIdentifier = (string) lib.GetString ("LibraryIdentifier")!;
+ var libraryPath = (string) lib.GetString ("LibraryPath")!;
+ var platform = (string) lib.GetString ("SupportedPlatform")!;
+ var platformVariant = (string) lib.GetString ("SupportedPlatformVariant")!;
+
+ if (platformVariant == "maccatalyst")
+ platform = platformVariant;
+
+ frameworks.Add ((Path.Combine (Path.GetDirectoryName (manifest.FullName)!, libraryIdentifier, libraryPath), platform));
+ }
+ }
+
+ foreach (var (path, platform) in frameworks) {
+ var isDesktop = platform == "macos" || platform == "maccatalyst";
+ var fwName = Path.GetFileNameWithoutExtension (path);
+ var architectures = AfterFirst (Path.GetFileName (Path.GetDirectoryName (path)!)!, '-').Split ('_');
+ string infix = "";
+ var resourceInfix = "";
+ if (isDesktop) {
+ infix = Path.Combine ("Versions", "A");
+ resourceInfix = "Resources";
+ }
+ AddFileToZip (archive, Path.Combine (path, infix, "Headers", "MyHeader.h"), "// myheader");
+ AddFileToZip (archive, Path.Combine (path, infix, "PrivateHeaders", "MyPrivateHeader.h"), "// myprivateheader");
+ AddFileToZip (archive, Path.Combine (path, infix, resourceInfix, "PrivacyInfo.xcprivacy"), "");
+ AddFileToZip (archive, Path.Combine (path, infix, "Modules", "module.modulemap"), "// modulemap");
+ foreach (var arch in architectures)
+ AddFileToZip (archive, Path.Combine (path, infix, "Modules", fwName + ".swiftmodule", $"{arch}-{platform}.swiftinterface"), "// swiftinterface");
+ AddFileToZip (archive, Path.Combine (path, infix, "dSYMs", fwName + ".dSYM", "Contents", "Resources", "DWARF", fwName), "// dsym");
+ }
+ }
+
+ static string AfterFirst (string value, char needle)
+ {
+ var idx = value.IndexOf (needle);
+ if (idx == -1)
+ return value;
+ return value [(idx + 1)..];
+ }
+
+ [TestCase (ApplePlatform.iOS, false)]
+ [TestCase (ApplePlatform.iOS, true)]
+ [TestCase (ApplePlatform.MacOSX, false)]
+ [TestCase (ApplePlatform.TVOS, false)]
+ [TestCase (ApplePlatform.TVOS, true)]
+ [TestCase (ApplePlatform.MacCatalyst, false)]
+ public void SidecarFromReferenceWithCompressedNativeReferencesAndThenFiltered (ApplePlatform platform, bool useSystemIOCompression)
+ {
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+
+ var tmpdir = Cache.CreateTemporaryDirectory ();
+ var inputdir = Path.Combine (tmpdir, "input");
+ var outputdir = Path.Combine (tmpdir, "output");
+
+ var dll = Path.Combine (inputdir, "BindingWithCompressedXCFramework.dll");
+ var sidecar = Path.Combine (inputdir, "BindingWithCompressedXCFramework.resources");
+ Directory.CreateDirectory (sidecar);
+ var manifest =
+ $"""
+
+
+
+
+ ../../../test-libraries/.libs/XTest.xcframework.zip
+
+ Framework
+
+
+
+
+
+
+
+
+
+ ../../../test-libraries/.libs/XStaticArTest.xcframework.zip
+
+ Static
+
+
+
+
+
+
+
+
+
+ ../../../test-libraries/.libs/XStaticObjectTest.xcframework.zip
+
+ Static
+
+
+
+
+
+
+
+ """;
+ File.WriteAllText (Path.Combine (sidecar, "manifest"), manifest);
+
+ var XTestFrameworkZipPath = Path.Combine (sidecar, "XTest.xcframework.zip");
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XTest.xcframework.zip"), XTestFrameworkZipPath);
+ StuffZipWithFiles (XTestFrameworkZipPath);
+
+ var XStaticArTestFrameworkZipPath = Path.Combine (sidecar, "XStaticArTest.xcframework.zip");
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XStaticArTest.xcframework.zip"), XStaticArTestFrameworkZipPath);
+ StuffZipWithFiles (XStaticArTestFrameworkZipPath);
+
+ var XStaticObjectTestFrameworkZipPath = Path.Combine (sidecar, "XStaticObjectTest.xcframework.zip");
+ File.Copy (Path.Combine (Configuration.RootPath, "tests", "test-libraries", ".libs", "XStaticObjectTest.xcframework.zip"), XStaticObjectTestFrameworkZipPath);
+ StuffZipWithFiles (XStaticObjectTestFrameworkZipPath);
+
+ var item = new TaskItem (dll);
+
+ var task = CreateTask ();
+ task.Architectures = "ARM64";
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ task.FrameworksDirectory = "";
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ task.FrameworksDirectory = "Contents/Frameworks/";
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+ task.IntermediateOutputPath = outputdir;
+ task.References = new TaskItem [] {
+ item,
+ };
+ task.SdkIsSimulator = false;
+ task.TargetFrameworkMoniker = TargetFramework.GetTargetFramework (platform).ToString ();
+ task.ExtractionFilters = new [] {
+ new TaskItem (".*/Headers/.*"),
+ new TaskItem ("Modules/.*"),
+ new TaskItem ("dSYMs/.*"),
+ new TaskItem ("PrivateHeaders/.*"),
+ new TaskItem ("PrivateHeaders/.*"),
+ };
+
+ var originalSystemIOCompression = Environment.GetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION");
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", "1");
+
+ try {
+ Assert.IsTrue (task.Execute (), "Execute");
+
+ var expectedFiles = new List ();
+ switch (platform) {
+ case ApplePlatform.iOS:
+ case ApplePlatform.TVOS:
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "XStaticArTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "XStaticObjectTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "XTest"));
+ break;
+ case ApplePlatform.MacCatalyst:
+ case ApplePlatform.MacOSX:
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "XStaticArTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticArTest.framework", "Versions", "A", "Resources", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "XStaticObjectTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework", "Versions", "A", "Resources", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework.stamp"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "Resources"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "Resources", "Info.plist"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "Resources", "PrivacyInfo.xcprivacy"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "A", "XTest"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "Versions", "Current"));
+ expectedFiles.Add (Path.Combine ("BindingWithCompressedXCFramework.resources", "XTest.framework", "XTest"));
+ break;
+ default:
+ throw new NotSupportedException ($"Unsupported platform: {platform}");
+ }
+
+ // Get all extracted files, but don't recurse into directories that are symlinks
+ var files = new FileSystemEnumerable (
+ directory: task.IntermediateOutputPath,
+ transform: (ref FileSystemEntry entry) => entry.ToFullPath (),
+ options: new EnumerationOptions {
+ RecurseSubdirectories = true,
+ }) {
+ ShouldRecursePredicate = (ref FileSystemEntry entry) => {
+ return entry.ToFileSystemInfo ().LinkTarget is null;
+ }
+ }
+ .Select (v => v [(task.IntermediateOutputPath.Length + 1)..])
+ .OrderBy (v => v)
+ .ToArray ();
+
+ var expectedFilesSorted = expectedFiles.OrderBy (v => v).ToArray ();
+
+ Assert.That (files, Is.EqualTo (expectedFilesSorted), "Unzipped files");
+
+ var nativeFrameworks = task.NativeFrameworks?.OrderBy (v => v.ItemSpec).ToArray () ?? Array.Empty ();
+ var nativeFrameworkNames = nativeFrameworks.Select (v => v.ItemSpec).ToArray ();
+ var expectedNativeFrameworkNames = new string [] {
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XStaticArTest.framework/XStaticArTest"),
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XStaticObjectTest.framework/XStaticObjectTest"),
+ Path.Combine (outputdir, "BindingWithCompressedXCFramework.resources", "XTest.framework/XTest"),
+ };
+ Assert.That (nativeFrameworkNames, Is.EqualTo (expectedNativeFrameworkNames), "Native frameworks");
+ } finally {
+ if (useSystemIOCompression)
+ Environment.SetEnvironmentVariable ("XAMARIN_USE_SYSTEM_IO_COMPRESSION", originalSystemIOCompression);
+ }
+ }
+
+ }
+}
+
+static class ZipHelpers {
+ public static void AddDirectoryToZipFile (string zipFile, string sourceDirectory, string rootDirectory)
+ {
+ var task = Execution.RunWithStringBuildersAsync ("zip", [zipFile, "--symlink", "-r", sourceDirectory.Substring (rootDirectory.Length + 1)], workingDirectory: rootDirectory);
+ task.Wait ();
+ Assert.That (task.Result.ExitCode, Is.EqualTo (0), "Zip command failed");
+ }
+
+ public static void CreateEntryFromString (this ZipArchive archive, string content, string entryName)
+ {
+ var entry = archive.CreateEntry (entryName);
+ using var stream = entry.Open ();
+ using var writer = new StreamWriter (stream);
+ writer.Write (content);
}
}