Skip to content

Commit

Permalink
Creating .NET MAUI Project sometimes causes error *.png is being used…
Browse files Browse the repository at this point in the history
… by another process

Fixes dotnet#25207
Context
dotnet/android-tools#245
dotnet/android#9409

So there is a problem where the design time builds (DTB) of android sometimes
lock files. This can happen when two processes try to write to the same
file. This is not a great experience for our users as it just fails the build.

So lets try a few things to fix this.

1. Move the Resizetizer output for a DTB into the android
`$(IntermediateOutputPath)designtime` folder. This will keep the DTB files
completely separate. This should prevent clashes.
2. Add some retry code to the `SkiaSharpTools.Save` method. This will catch
`UnauthorizedAccessException` exceptions as well as specific `IOException` types
(Access Denied and Sharing Violation). This will allow us to catch when this happens
and retry the write. There is a small delay before attempting to write the file
again.

Note these code uses the Identical code as we are going to use in Android.
We have introduced two new environment variables which can be used to
control the new behavior.

1. `DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS`. Integer, controls the number of times
we try to write the file. The default is 10.
2. `DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS`. Integer, controls the delay in
milliseconds between retry attempts.  Default is 1000ms (or 1 second).
  • Loading branch information
dellis1972 committed Nov 29, 2024
1 parent 37a2d9d commit b4ef833
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 18 deletions.
73 changes: 71 additions & 2 deletions src/SingleProject/Resizetizer/src/SkiaSharpTools.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,54 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using SkiaSharp;

namespace Microsoft.Maui.Resizetizer
{
internal abstract class SkiaSharpTools
{
const int ERROR_ACCESS_DENIED = -2147024891;
const int ERROR_SHARING_VIOLATION = -2147024864;
const int DEFAULT_FILE_WRITE_RETRY_ATTEMPTS = 10;
const int DEFAULT_FILE_WRITE_RETRY_DELAY_MS = 1000;

static int fileWriteRetry = -1;
static int fileWriteRetryDelay = -1;

/// <summary>
/// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS to
/// see if a custom value for the number of times to retry writing a file has been
/// set.
/// </summary>
/// <returns>The value of DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS or the default of DEFAULT_FILE_WRITE_RETRY_ATTEMPTS</returns>
public static int GetFileWriteRetryAttempts ()
{
if (fileWriteRetry == -1) {
var retryVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS");
if (string.IsNullOrEmpty (retryVariable) || !int.TryParse (retryVariable, out fileWriteRetry))
fileWriteRetry = DEFAULT_FILE_WRITE_RETRY_ATTEMPTS;
}
return fileWriteRetry;
}

/// <summary>
/// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS to
/// see if a custom value for the delay between trying to write a file has been
/// set.
/// </summary>
/// <returns>The value of DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS or the default of DEFAULT_FILE_WRITE_RETRY_DELAY_MS</returns>
public static int GetFileWriteRetryDelay ()
{
if (fileWriteRetryDelay == -1) {
var delayVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS");
if (string.IsNullOrEmpty (delayVariable) || !int.TryParse (delayVariable, out fileWriteRetryDelay))
fileWriteRetryDelay = DEFAULT_FILE_WRITE_RETRY_DELAY_MS;
}
return fileWriteRetryDelay;
}

static SkiaSharpTools()
{
// DO NOT DELETE!
Expand Down Expand Up @@ -150,8 +192,35 @@ void Draw(SKBitmap tempBitmap, double additionalScale, SKSize originalSize, floa

void Save(string destination, SKBitmap tempBitmap)
{
using var stream = File.Create(destination);
tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
int attempt = 0;
int attempts = GetFileWriteRetryAttempts ();
int delay = GetFileWriteRetryDelay ();
while (attempt <= attempts)
{
try
{
using var stream = File.Create(destination);
tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100);
}
catch (Exception ex)
{
switch (ex)
{
case UnauthorizedAccessException:
case IOException:
var code = Marshal.GetHRForException(ex);
if ((code != ERROR_ACCESS_DENIED && code != ERROR_SHARING_VIOLATION) || attempt == attempts)
{
throw;
}
break;
default:
throw;
}
attempt++;
Thread.Sleep(delay);
}
}
}

public abstract SKSize GetOriginalSize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,6 @@
_CleanResizetizer;
</CleanDependsOn>

<_ResizetizerInputsFile>$(IntermediateOutputPath)mauiimage.inputs</_ResizetizerInputsFile>
<_ResizetizerOutputsFile>$(IntermediateOutputPath)mauiimage.outputs</_ResizetizerOutputsFile>
<_ResizetizerStampFile>$(IntermediateOutputPath)mauiimage.stamp</_ResizetizerStampFile>
<_MauiFontInputsFile>$(IntermediateOutputPath)mauifont.inputs</_MauiFontInputsFile>
<_MauiFontStampFile>$(IntermediateOutputPath)mauifont.stamp</_MauiFontStampFile>
<_MauiSplashInputsFile>$(IntermediateOutputPath)mauisplash.inputs</_MauiSplashInputsFile>
<_MauiSplashStampFile>$(IntermediateOutputPath)mauisplash.stamp</_MauiSplashStampFile>
<_MauiManifestStampFile>$(IntermediateOutputPath)mauimanifest.stamp</_MauiManifestStampFile>

<_ResizetizerIntermediateOutputRoot>$(IntermediateOutputPath)resizetizer\</_ResizetizerIntermediateOutputRoot>
<_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\</_MauiIntermediateImages>
<_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\</_MauiIntermediateFonts>
<_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\</_MauiIntermediateSplashScreen>
<_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\</_MauiIntermediateManifest>

<_ResizetizerPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</_ResizetizerPlatformIdentifier>
<_ResizetizerNoTargetPlatform Condition="'$(_ResizetizerPlatformIdentifier)' == ''">True</_ResizetizerNoTargetPlatform>
<_ResizetizerPlatformIsAndroid Condition="'$(_ResizetizerPlatformIdentifier)' == 'android'">True</_ResizetizerPlatformIsAndroid>
Expand All @@ -84,6 +69,24 @@
<_ResizetizerPlatformIsWindows Condition="$(_ResizetizerPlatformIdentifier.Contains('windows')) == 'True'">True</_ResizetizerPlatformIsWindows>
<_ResizetizerPlatformIsTizen Condition="'$(_ResizetizerPlatformIdentifier)' == 'tizen'">True</_ResizetizerPlatformIsTizen>

<_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' And '$(_ResizetizerPlatformIsAndroid)' == 'True' And '$(DesignTimeBuild)' == 'True' ">$(IntermediateOutputPath)designtime</_ResizetizerIntermediateOutputPath>
<_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' " >$(IntermediateOutputPath)</_ResizetizerIntermediateOutputPath>

<_ResizetizerInputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.inputs</_ResizetizerInputsFile>
<_ResizetizerOutputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.outputs</_ResizetizerOutputsFile>
<_ResizetizerStampFile>$(_ResizetizerIntermediateOutputPath)mauiimage.stamp</_ResizetizerStampFile>
<_MauiFontInputsFile>$(_ResizetizerIntermediateOutputPath)mauifont.inputs</_MauiFontInputsFile>
<_MauiFontStampFile>$(_ResizetizerIntermediateOutputPath)mauifont.stamp</_MauiFontStampFile>
<_MauiSplashInputsFile>$(_ResizetizerIntermediateOutputPath)mauisplash.inputs</_MauiSplashInputsFile>
<_MauiSplashStampFile>$(_ResizetizerIntermediateOutputPath)mauisplash.stamp</_MauiSplashStampFile>
<_MauiManifestStampFile>$(_ResizetizerIntermediateOutputPath)mauimanifest.stamp</_MauiManifestStampFile>

<_ResizetizerIntermediateOutputRoot>$(_ResizetizerIntermediateOutputPath)resizetizer\</_ResizetizerIntermediateOutputRoot>
<_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\</_MauiIntermediateImages>
<_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\</_MauiIntermediateFonts>
<_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\</_MauiIntermediateSplashScreen>
<_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\</_MauiIntermediateManifest>

<ResizetizerIncludeSelfProject Condition="'$(ResizetizerIncludeSelfProject)' == ''">False</ResizetizerIncludeSelfProject>

<_ResizetizerDefaultInvalidFilenamesErrorMessage>One or more invalid file names were detected. File names must be lowercase, start and end with a letter character, and contain only alphanumeric characters or underscores: </_ResizetizerDefaultInvalidFilenamesErrorMessage>
Expand Down Expand Up @@ -504,7 +507,7 @@
</ItemGroup>

<!-- Stamp file for Outputs -->
<MakeDir Directories="$(IntermediateOutputPath)"/>
<MakeDir Directories="$(_ResizetizerIntermediateOutputPath)"/>
<Touch Files="$(_MauiSplashStampFile)" AlwaysCreate="True" />

<ItemGroup>
Expand Down

0 comments on commit b4ef833

Please sign in to comment.