Skip to content

Commit d77d943

Browse files
dellis1972jonathanpeppers
authored andcommitted
[Xamarin.Android.Build.Tasks] Bump ZipFlushFilesLimit (#7957)
Context: https://dev.azure.com/DevDiv/DevDiv/_workitems/edit/1782014 Context: 9166e03 On Windows customers are seeing the following error: error XABLD7000: Xamarin.Tools.Zip.ZipException: Renaming temporary file failed: Permission denied at Xamarin.Tools.Zip.ZipArchive.Close() in /Users/runner/work/1/s/LibZipSharp/Xamarin.Tools.Zip/ZipArchive.cs:line 939 We have traced this to the use of the [`MoveFileExW()`][0] / `MoveFileExA()` API: when libzip is trying to move its temp file to the final location, Windows is raising this error: Renaming temporary file failed: Permission denied Turning off Anti Virus seems to help, however adding an exclusion does not. This is very confusing, so we are unsure why this error is being raised. The process has the correct permissions and the file being moved is in the same directory, so its not a TEMP folder issue. Perhaps it's the number of temp files we create? Part of the `<BuildApk/>` system is that as we add files we `Flush()` the zip file to commit those changes to disk. This is partly to work around how libzip works: it does not write any data to the main file until [`zip_close()`][1] is called. To work around issues around too many files being open (9166e03), we added this flush. The limit of 50 files was picked out of a hat. Try pushing the limit up a bit to see if that helps. Additionally, introduce the following two (private!) MSBuild properties: * `$(_ZipFlushFilesLimit)`: Call `Flush()` after `$(_ZipFlushFilesLimit)` files have been added to the `.apk`. * `$(_ZipFlushSizeLimit)`: Call `Flush()` after `$(_ZipFlushSizeLimit)` bytes of data have been added to the `.apk`. [0]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefileexw [1]: https://libzip.org/documentation/zip_close.html
1 parent d96a7a1 commit d77d943

File tree

4 files changed

+89
-28
lines changed

4 files changed

+89
-28
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ public class BuildApk : AndroidTask
9595

9696
public bool UseAssemblyStore { get; set; }
9797

98+
public string ZipFlushFilesLimit { get; set; }
99+
100+
public string ZipFlushSizeLimit { get; set; }
101+
98102
[Required]
99103
public string ProjectFullPath { get; set; }
100104

@@ -136,6 +140,12 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
136140
refresh = false;
137141
}
138142
using (var apk = new ZipArchiveEx (apkOutputPath, File.Exists (apkOutputPath) ? FileMode.Open : FileMode.Create )) {
143+
if (int.TryParse (ZipFlushFilesLimit, out int flushFilesLimit)) {
144+
apk.ZipFlushFilesLimit = flushFilesLimit;
145+
}
146+
if (int.TryParse (ZipFlushSizeLimit, out int flushSizeLimit)) {
147+
apk.ZipFlushSizeLimit = flushSizeLimit;
148+
}
139149
if (refresh) {
140150
for (long i = 0; i < apk.Archive.EntryCount; i++) {
141151
ZipEntry e = apk.Archive.ReadEntry ((ulong) i);
@@ -205,7 +215,6 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
205215
AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, $"{AssembliesPath}rc.bin", compressionMethod: UncompressedMethod);
206216
}
207217

208-
int count = 0;
209218
foreach (var file in files) {
210219
var item = Path.Combine (file.archivePath.Replace (Path.DirectorySeparatorChar, '/'));
211220
existingEntries.Remove (item);
@@ -215,12 +224,7 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
215224
continue;
216225
}
217226
Log.LogDebugMessage ("\tAdding {0}", file.filePath);
218-
apk.Archive.AddFile (file.filePath, item, compressionMethod: compressionMethod);
219-
count++;
220-
if (count >= ZipArchiveEx.ZipFlushFilesLimit) {
221-
apk.Flush();
222-
count = 0;
223-
}
227+
apk.AddFileAndFlush (file.filePath, item, compressionMethod: compressionMethod);
224228
}
225229

226230
var jarFiles = (JavaSourceFiles != null) ? JavaSourceFiles.Where (f => f.ItemSpec.EndsWith (".jar", StringComparison.OrdinalIgnoreCase)) : null;
@@ -235,7 +239,6 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
235239
var jarFilePaths = libraryProjectJars.Concat (jarFiles != null ? jarFiles.Select (j => j.ItemSpec) : Enumerable.Empty<string> ());
236240
jarFilePaths = MonoAndroidHelper.DistinctFilesByContent (jarFilePaths);
237241

238-
count = 0;
239242
foreach (var jarFile in jarFilePaths) {
240243
using (var stream = File.OpenRead (jarFile))
241244
using (var jar = ZipArchive.Open (stream)) {
@@ -273,14 +276,9 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut
273276
data = d.ToArray ();
274277
}
275278
Log.LogDebugMessage ($"Adding {path} from {jarFile} as the archive file is out of date.");
276-
apk.Archive.AddEntry (data, path);
279+
apk.AddEntryAndFlush (data, path);
277280
}
278281
}
279-
count++;
280-
if (count >= ZipArchiveEx.ZipFlushFilesLimit) {
281-
apk.Flush();
282-
count = 0;
283-
}
284282
}
285283
// Clean up Removed files.
286284
foreach (var entry in existingEntries) {
@@ -372,7 +370,6 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<str
372370
storeGenerator = null;
373371
}
374372

375-
int count = 0;
376373
AssemblyStoreAssemblyInfo storeAssembly = null;
377374

378375
//
@@ -393,7 +390,6 @@ void AddAssemblies (ZipArchiveEx apk, bool debug, bool compress, IDictionary<str
393390
AddAssembliesFromCollection (ResolvedUserAssemblies);
394391

395392
// Add framework assemblies
396-
count = 0;
397393
AddAssembliesFromCollection (ResolvedFrameworkAssemblies);
398394

399395
if (!UseAssemblyStore) {
@@ -483,12 +479,6 @@ void AddAssembliesFromCollection (ITaskItem[] assemblies)
483479

484480
if (UseAssemblyStore) {
485481
storeGenerator.Add (assemblyStoreApkName, storeAssembly);
486-
} else {
487-
count++;
488-
if (count >= ZipArchiveEx.ZipFlushFilesLimit) {
489-
apk.Flush();
490-
count = 0;
491-
}
492482
}
493483
}
494484
}
@@ -555,7 +545,7 @@ bool AddFileToArchiveIfNewer (ZipArchiveEx apk, string file, string inArchivePat
555545
return false;
556546
}
557547
Log.LogDebugMessage ($"Adding {file} as the archive file is out of date.");
558-
apk.Archive.AddFile (file, inArchivePath, compressionMethod: compressionMethod);
548+
apk.AddFileAndFlush (file, inArchivePath, compressionMethod: compressionMethod);
559549
return true;
560550
}
561551

@@ -579,7 +569,7 @@ void AddAssemblyConfigEntry (ZipArchiveEx apk, string assemblyPath, string confi
579569
source.CopyTo (dest);
580570
dest.WriteByte (0);
581571
dest.Position = 0;
582-
apk.Archive.AddEntry (inArchivePath, dest, compressionMethod);
572+
apk.AddEntryAndFlush (inArchivePath, dest, compressionMethod);
583573
}
584574
}
585575

@@ -626,7 +616,7 @@ void AddNativeLibraryToArchive (ZipArchiveEx apk, string abi, string filesystemP
626616
return;
627617
}
628618
Log.LogDebugMessage ($"Adding native library: {filesystemPath} (APK path: {archivePath})");
629-
apk.Archive.AddEntry (archivePath, File.OpenRead (filesystemPath), compressionMethod);
619+
apk.AddEntryAndFlush (archivePath, File.OpenRead (filesystemPath), compressionMethod);
630620
}
631621

632622
void AddRuntimeLibraries (ZipArchiveEx apk, string [] supportedAbis)

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,46 @@ public void CheckExcludedFilesAreMissing ()
928928
}
929929
}
930930

931+
[Test]
932+
[TestCase (1, -1)]
933+
[TestCase (5, -1)]
934+
[TestCase (50, -1)]
935+
[TestCase (100, -1)]
936+
[TestCase (512, -1)]
937+
[TestCase (1024, -1)]
938+
[TestCase (-1, 1)]
939+
[TestCase (-1, 5)]
940+
[TestCase (-1, 10)]
941+
[TestCase (-1, 100)]
942+
[TestCase (-1, 200)]
943+
public void BuildApkWithZipFlushLimits (int filesLimit, int sizeLimit)
944+
{
945+
var proj = new XamarinAndroidApplicationProject {
946+
IsRelease = false,
947+
PackageReferences = {
948+
KnownPackages.SupportDesign_27_0_2_1,
949+
KnownPackages.SupportV7CardView_27_0_2_1,
950+
KnownPackages.AndroidSupportV4_27_0_2_1,
951+
KnownPackages.SupportCoreUtils_27_0_2_1,
952+
KnownPackages.SupportMediaCompat_27_0_2_1,
953+
KnownPackages.SupportFragment_27_0_2_1,
954+
KnownPackages.SupportCoreUI_27_0_2_1,
955+
KnownPackages.SupportCompat_27_0_2_1,
956+
KnownPackages.SupportV7AppCompat_27_0_2_1,
957+
KnownPackages.SupportV7MediaRouter_27_0_2_1,
958+
},
959+
};
960+
proj.SetProperty ("EmbedAssembliesIntoApk", "true");
961+
if (filesLimit > 0)
962+
proj.SetProperty ("_ZipFlushFilesLimit", filesLimit.ToString ());
963+
if (sizeLimit > 0)
964+
proj.SetProperty ("_ZipFlushSizeLimit", (sizeLimit * 1024 * 1024).ToString ());
965+
using (var b = CreateApkBuilder ()) {
966+
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
967+
968+
}
969+
}
970+
931971
[Test]
932972
public void ExtractNativeLibsTrue ()
933973
{

src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEx.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ namespace Xamarin.Android.Tasks
88
public class ZipArchiveEx : IDisposable
99
{
1010

11-
public static int ZipFlushSizeLimit = 50 * 1024 * 1024;
12-
public static int ZipFlushFilesLimit = 50;
11+
const int DEFAULT_FLUSH_SIZE_LIMIT = 100 * 1024 * 1024;
12+
const int DEFAULT_FLUSH_FILES_LIMIT = 512;
1313

1414
ZipArchive zip;
1515
string archive;
@@ -24,6 +24,9 @@ public ZipArchive Archive {
2424

2525
public bool CreateDirectoriesInZip { get; set; } = true;
2626

27+
public int ZipFlushSizeLimit { get; set; } = DEFAULT_FLUSH_SIZE_LIMIT;
28+
public int ZipFlushFilesLimit { get; set; } = DEFAULT_FLUSH_FILES_LIMIT;
29+
2730
public ZipArchiveEx (string archive) : this (archive, FileMode.CreateNew)
2831
{
2932
}
@@ -65,7 +68,31 @@ void AddFileAndFlush (string filename, long fileLength, string archiveFileName,
6568
{
6669
filesWrittenTotalSize += fileLength;
6770
zip.AddFile (filename, archiveFileName, compressionMethod: compressionMethod);
68-
if ((filesWrittenTotalSize >= ZipArchiveEx.ZipFlushSizeLimit || filesWrittenTotalCount >= ZipArchiveEx.ZipFlushFilesLimit) && AutoFlush) {
71+
if ((filesWrittenTotalSize >= ZipFlushSizeLimit || filesWrittenTotalCount >= ZipFlushFilesLimit) && AutoFlush) {
72+
Flush ();
73+
}
74+
}
75+
76+
public void AddFileAndFlush (string filename, string archiveFileName, CompressionMethod compressionMethod)
77+
{
78+
var fi = new FileInfo (filename);
79+
AddFileAndFlush (filename, fi.Length, archiveFileName, compressionMethod);
80+
}
81+
82+
public void AddEntryAndFlush (byte[] data, string archiveFileName)
83+
{
84+
filesWrittenTotalSize += data.Length;
85+
zip.AddEntry (data, archiveFileName);
86+
if ((filesWrittenTotalSize >= ZipFlushSizeLimit || filesWrittenTotalCount >= ZipFlushFilesLimit) && AutoFlush) {
87+
Flush ();
88+
}
89+
}
90+
91+
public void AddEntryAndFlush (string archiveFileName, Stream data, CompressionMethod method)
92+
{
93+
filesWrittenTotalSize += data.Length;
94+
zip.AddEntry (archiveFileName, data, method);
95+
if ((filesWrittenTotalSize >= ZipFlushSizeLimit || filesWrittenTotalCount >= ZipFlushFilesLimit) && AutoFlush) {
6996
Flush ();
7097
}
7198
}

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,8 @@ because xbuild doesn't support framework reference assemblies.
20902090
CheckedBuild="$(_AndroidCheckedBuild)"
20912091
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
20922092
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
2093+
ZipFlushFilesLimit="$(_ZipFlushFilesLimit)"
2094+
ZipFlushSizeLimit="$(_ZipFlushSizeLimit)"
20932095
UseAssemblyStore="$(AndroidUseAssemblyStore)">
20942096
<Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
20952097
</BuildApk>
@@ -2124,6 +2126,8 @@ because xbuild doesn't support framework reference assemblies.
21242126
CheckedBuild="$(_AndroidCheckedBuild)"
21252127
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
21262128
ExcludeFiles="@(AndroidPackagingOptionsExclude)"
2129+
ZipFlushFilesLimit="$(_ZipFlushFilesLimit)"
2130+
ZipFlushSizeLimit="$(_ZipFlushSizeLimit)"
21272131
UseAssemblyStore="$(AndroidUseAssemblyStore)">
21282132
<Output TaskParameter="OutputFiles" ItemName="BaseZipFile" />
21292133
</BuildBaseAppBundle>

0 commit comments

Comments
 (0)