[msbuild] Improve logic to clean up app bundle for unwanted files. (#15080)

The current directory at launch is the root directory of the app bundle. This
means that any files written to the current directory when an app is executed,
will be placed there. This becomes a problem when the app is rebuilt (and
resigned), because a valid macOS app bundle doesn't have any files in the root
directory of the app bundle, so signing fails.

We have logic to automatically crash crash reports from the app bundle, but it
turns out this is a more common problem with other types of files (and
folders), so improve the logic a bit:

* Add support for setting a property to automatically clean up everything from
  an app bundle we don't think should be there (which is anything not in a
  Contents/ subdirectory).
* Use the same property to add support for disabling any cleaning (we already
  clean mono's crash reports by default).
* Improve detection of unwanted files to include directories inside the app
  bundle, not only files.

Ref: https://github.com/dotnet/maui/issues/7353
This commit is contained in:
Rolf Bjarne Kvinge 2022-05-30 11:34:45 +02:00 коммит произвёл GitHub
Родитель 96116c400d
Коммит cf7c6b5980
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 44 добавлений и 9 удалений

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

@ -300,6 +300,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<!-- Delete any crash dumps in the app bundle that might exist. Ref: https://github.com/xamarin/xamarin-macios/issues/12320 --> <!-- Delete any crash dumps in the app bundle that might exist. Ref: https://github.com/xamarin/xamarin-macios/issues/12320 -->
<!-- Use a task to collect the files, so that we get the correct behavior on Windows --> <!-- Use a task to collect the files, so that we get the correct behavior on Windows -->
<!-- This is enabled by default, but can be disabled by setting EnableAutomaticAppBundleRootDirectoryCleanup=false -->
<GetFileSystemEntries <GetFileSystemEntries
SessionId="$(BuildSessionId)" SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS')" Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS')"
@ -312,22 +313,48 @@ Copyright (C) 2018 Microsoft. All rights reserved.
</GetFileSystemEntries> </GetFileSystemEntries>
<Delete <Delete
SessionId="$(BuildSessionId)" SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS')" Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS') And '$(EnableAutomaticAppBundleRootDirectoryCleanup)' != 'false'"
Files="@(_MonoCrashDumpsInAppBundle)" Files="@(_MonoCrashDumpsInAppBundle)"
/> />
<!-- Warn about any files that are left --> <!-- Warn about any files that are left -->
<!-- These can be deleted automatically by setting EnableAutomaticAppBundleRootDirectoryCleanup=true (in which case we won't show the warning) -->
<GetFileSystemEntries <GetFileSystemEntries
SessionId="$(BuildSessionId)" SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS')" Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS')"
DirectoryPath="$(AppBundleDir)" DirectoryPath="$(AppBundleDir)"
Pattern="*" Pattern="*"
Recursive="false" Recursive="true"
IncludeDirectories="false" IncludeDirectories="true"
> >
<Output TaskParameter="Entries" ItemName="_FilesInAppBundleRootDirectory" /> <Output TaskParameter="Entries" ItemName="_FilesInAppBundleRootDirectory" />
</GetFileSystemEntries> </GetFileSystemEntries>
<Warning Text="Found files in the root directory of the app bundle. This will likely cause codesign to fail. Files:%0a@(_FilesInAppBundleRootDirectory, '%0a')" Condition="@(_FilesInAppBundleRootDirectory->Count()) &gt; 0"/> <!-- Remove anything in the Contents subdirectory from the list of files -->
<!-- You would think that it would be possible to remove using something like this:
<_FilesInAppBundleRootDirectory Remove="$(AppBundleDir)/Contents/**" />
but that doesn't remove directories, only files -->
<ItemGroup>
<_FilesInAppBundleRootDirectoryToRemove Include="@(_FilesInAppBundleRootDirectory)" Condition="$([MSBuild]::ValueOrDefault('%(Identity)', '').StartsWith('$(AppBundleDir)/Contents/'))" />
<_FilesInAppBundleRootDirectory Remove="@(_FilesInAppBundleRootDirectoryToRemove);$(AppBundleDir)/Contents" />
</ItemGroup>
<!-- We may have both files and directories, so just run both the Delete and RemoveDir task on all entries we're to delete -->
<Delete
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS') And '$(EnableAutomaticAppBundleRootDirectoryCleanup)' == 'true'"
Files="@(_FilesInAppBundleRootDirectory)"
/>
<!-- Create a separate item group for directories, excluding anything with length <= 1. This is an additional protection against accidentally wiping out the hard drive: https://github.com/dotnet/msbuild/issues/4105 -->
<ItemGroup>
<_DirectoriesInAppBundleRootDirectory Include="@(_FilesInAppBundleRootDirectory)" Condition="$([MSBuild]::ValueOrDefault('%(Identity)', '').Length) &gt; 1" />
</ItemGroup>
<RemoveDir
SessionId="$(BuildSessionId)"
Condition="'$(IsMacEnabled)' == 'true' And ('$(_PlatformName)' == 'MacCatalyst' Or '$(_PlatformName)' == 'macOS') And '$(EnableAutomaticAppBundleRootDirectoryCleanup)' == 'true'"
Directories="@(_DirectoriesInAppBundleRootDirectory)"
/>
<Warning
Text="Found files in the root directory of the app bundle. This will likely cause codesign to fail. Files:%0a@(_FilesInAppBundleRootDirectory, '%0a')"
Condition="'$(EnableAutomaticAppBundleRootDirectoryCleanup)' != 'true' And @(_FilesInAppBundleRootDirectory->Count()) &gt; 0"/>
</Target> </Target>
<Target Name="_CleanBindingResourcePackage"> <Target Name="_CleanBindingResourcePackage">

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

@ -526,19 +526,27 @@ namespace Xamarin.Tests {
// Create a file that isn't a crash report. // Create a file that isn't a crash report.
File.WriteAllText (Path.Combine (appPath, "otherfile.txt"), "A file"); File.WriteAllText (Path.Combine (appPath, "otherfile.txt"), "A file");
var otherFileInDir = Path.Combine (appPath, "otherdir", "otherfile.log");
Directory.CreateDirectory (Path.GetDirectoryName (otherFileInDir)!);
File.WriteAllText (otherFileInDir, "A log");
// Build again - this time it'll fail // Build again - this time it'll fail
var rv = DotNet.Build (project_path, properties); var rv = DotNet.Build (project_path, properties);
var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray (); var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray ();
Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success"); Assert.AreNotEqual (0, rv.ExitCode, "Unexpected success");
Assert.AreEqual (1, warnings.Length, "Warning Count"); Assert.AreEqual (1, warnings.Length, "Warning Count");
Assert.AreEqual ($"Found files in the root directory of the app bundle. This will likely cause codesign to fail. Files:\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherfile.txt", warnings [0].Message, "Warning"); Assert.AreEqual ($"Found files in the root directory of the app bundle. This will likely cause codesign to fail. Files:\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherfile.txt\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherdir\nbin/Debug/{Configuration.DotNetTfm}-maccatalyst/maccatalyst-x64/MySimpleApp.app/otherdir/otherfile.log", warnings [0].Message, "Warning");
// Remove the offending file // Build again, asking for automatic removal of the extraneous files.
File.Delete (Path.Combine (appPath, "otherfile.txt")); var enableAutomaticCleanupProperties = new Dictionary<string, string> (properties);
enableAutomaticCleanupProperties ["EnableAutomaticAppBundleRootDirectoryCleanup"] = "true";
rv = DotNet.AssertBuild (project_path, enableAutomaticCleanupProperties);
warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).ToArray ();
Assert.AreEqual (0, warnings.Length, "Warning Count");
// Build yet again // Verify that the files were in fact removed.
DotNet.AssertBuild (project_path, properties); Assert.That (Path.Combine (appPath, "otherfile.txt"), Does.Not.Exist, "otherfile");
Assert.That (Path.GetDirectoryName (otherFileInDir), Does.Not.Exist, "otherdir");
} }
[Test] [Test]