[mtouch] Fix case where `copyfile` could fail silently (#7633)

Copying the framework could fail (error 260) and the failure was never
reported so the build succeeded - but without updating (completely) the
framework.

The exact reason it fails is unknown :( but we can recover from it by
deleting the target and copying (everything) back to the expected
(target) location.

Build logs will now indicate when this fails and will try to recover
before reporting a build error. Best case it works :) worse case we'll
be aware something is wrong (which is better than ignoring)

This is half the fix from https://github.com/xamarin/xamarin-macios/pull/7544
which was reverted due to the second half causing a regression (under
investigation).

Reference: https://github.com/xamarin/xamarin-macios/issues/7514 (partial fix)
This commit is contained in:
Sebastien Pouliot 2019-12-23 09:18:04 -05:00 коммит произвёл GitHub
Родитель 2c0366cc0d
Коммит 2694579530
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 28 добавлений и 5 удалений

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

@ -77,8 +77,20 @@ namespace Xamarin.Bundler {
public static void UpdateDirectory (string source, string target)
{
if (!Directory.Exists (target))
Directory.CreateDirectory (target);
// first chance, try to update existing content inside `target`
if (TryUpdateDirectory (source, target, out var err))
return;
// 2nd chance, nuke `target` then copy everything
Log (1, "Could not update `{0}` content (error #{1} : {2}), trying to overwrite everything...", target, err, strerror (err));
Directory.Delete (target, true);
if (!TryUpdateDirectory (source, target, out err))
throw CreateError (1022, "Could not copy the directory '{0}' to '{1}': (error #{2} : {3})", source, target, err, strerror (err));
}
static bool TryUpdateDirectory (string source, string target, out int errno)
{
Directory.CreateDirectory (target);
// Mono's File.Copy can't handle symlinks (the symlinks are followed instead of copied),
// so we need to use native functions directly. Luckily OSX provides exactly what we need.
@ -87,13 +99,19 @@ namespace Xamarin.Bundler {
CopyFileCallbackDelegate del = CopyFileCallback;
copyfile_state_set (state, CopyFileState.StatusCB, Marshal.GetFunctionPointerForDelegate (del));
int rv = copyfile (source, target, state, CopyFileFlags.Data | CopyFileFlags.Recursive | CopyFileFlags.Nofollow | CopyFileFlags.Clone);
if (rv != 0)
throw CreateError (1022, "Could not copy the directory '{0}' to '{1}': {2}", source, target, strerror (Marshal.GetLastWin32Error ()));
if (rv == 0) {
errno = 0; // satisfy compiler and make sure not to pick up some older error code
return true;
} else {
errno = Marshal.GetLastWin32Error (); // might not be very useful since the callback signaled an error (CopyFileResult.Quit)
return false;
}
} finally {
copyfile_state_free (state);
}
}
// do not call `Marshal.GetLastWin32Error` inside this method since it's called while the p/invoke is executing and will return `260`
static CopyFileResult CopyFileCallback (CopyFileWhat what, CopyFileStep stage, IntPtr state, string source, string target, IntPtr ctx)
{
// Console.WriteLine ("CopyFileCallback ({0}, {1}, 0x{2}, {3}, {4}, 0x{5})", what, stage, state.ToString ("x"), source, target, ctx.ToString ("x"));
@ -102,6 +120,10 @@ namespace Xamarin.Bundler {
if (!IsUptodate (source, target)) {
if (stage == CopyFileStep.Finish)
Log (1, "Copied {0} to {1}", source, target);
else if (stage == CopyFileStep.Err) {
Log (1, "Could not copy the file '{0}' to '{1}'", source, target);
return CopyFileResult.Quit;
}
return CopyFileResult.Continue;
} else {
Log (3, "Target '{0}' is up-to-date", target);
@ -113,7 +135,8 @@ namespace Xamarin.Bundler {
case CopyFileWhat.CopyXattr:
return CopyFileResult.Continue;
case CopyFileWhat.Error:
throw CreateError (1021, "Could not copy the file '{0}' to '{1}': {2}", source, target, strerror (Marshal.GetLastWin32Error ()));
Log (1, "Could not copy the file '{0}' to '{1}'", source, target);
return CopyFileResult.Quit;
default:
return CopyFileResult.Continue;
}