Skip to content

Commit 3d999d3

Browse files
jonathanpeppersdellis1972
authored andcommitted
[Xamarin.Android.Build.Tasks] timestamp fixes for incremental builds (#1930)
Context: https://github.com/xamarin/Xamarin.Forms/blob/42c07d1ae5aa56eb574b7d169499f1a9af7ec080/Xamarin.Forms.ControlGallery.Android/Xamarin.Forms.ControlGallery.Android.csproj While working on build performance, I noticed two timestamp issues that are preventing some targets in Xamarin.Android from building incrementally. I spotted both of these while timing builds of the `Xamarin.Forms.ControlGallery.Android` project. It is a good test subject, because it builds PCLs, and seven (or so) Xamarin.Android library projects. ~~ _LinkAssembliesNoShrink ~~ While timing builds, I noticed the following when `_LinkAssembliesNoShrink` runs: Target Name=_LinkAssembliesNoShrink Project=PagesGallery.Droid.csproj Building target "_LinkAssembliesNoShrink" partially, because some output files are out of date with respect to their input files. [ResolvedUserAssemblies: Input=C:\Users\myuser\Desktop\Git\Xamarin.Forms\PagesGallery\PagesGallery.Droid\bin\Debug\PagesGallery.Droid.dll, Output=obj\\Debug\android\assets\PagesGallery.Droid.dll] Input file is newer than output file. ... Skip linking unchanged file: C:\Users\myuser\Desktop\Git\Xamarin.Forms\PagesGallery\PagesGallery.Droid\bin\Debug\PagesGallery.Droid.dll It seems there is a case where MSBuild is thinking the target needs to run (due to timestamp values), but the `LinkAssemblies` MSBuild task does not copy the file. It actually doesn't need to do any work at all here, since this was a build with no changes. The fix here is to apply a timestamp for files that get skipped. This prevents the target from running again when it doesn't need to. I also removed a spot where a `copysrc` variable looks like it was being set an extra time--did not seem to be needed. ~~ _GenerateJavaStubs ~~ After fixing `_LinkAssembliesNoShrink`, I noticed another timestamp issue: Building target "_GenerateJavaStubs" completely. Input file "obj\\Debug\android\assets\PagesGallery.Droid.dll" is newer than output file "obj\\Debug\android\typemap.jm". Looking at the `GenerateJavaStubs` MSBuild task, it was never setting the timestamp on either `typemap.mj` or `typemap.jm`. Fixing these two issues, I was able to get a build with no-changes in this Xamarin.Forms project down from ~23 seconds to ~13 seconds. I also updated the `CheckTimestamps` test to validate these changes.
1 parent 0ab8ec1 commit 3d999d3

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ void UpdateWhenChanged (string path, Action<Stream> generator)
306306
var np = path + ".new";
307307
using (var o = File.OpenWrite (np))
308308
generator (o);
309-
Files.CopyIfChanged (np, path);
309+
MonoAndroidHelper.CopyIfChanged (np, path);
310+
MonoAndroidHelper.SetLastAccessAndWriteTimeUtc (path, DateTime.UtcNow, Log);
310311
File.Delete (np);
311312
}
312313
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,18 @@ bool Execute (DirectoryAssemblyResolver res)
152152
foreach (var assembly in ResolvedAssemblies) {
153153
var copysrc = assembly.ItemSpec;
154154
var filename = Path.GetFileName (assembly.ItemSpec);
155+
var assemblyDestination = Path.Combine (copydst, filename);
155156

156157
if (options.LinkNone) {
157158
if (skiplist.Any (s => Path.GetFileNameWithoutExtension (filename) == s)) {
158159
// For skipped assemblies, skip if there is existing file in the destination.
159160
// We cannot just copy the linker output from *current* run output, because
160161
// it always renew the assemblies, in *different* binary values, whereas
161162
// the dll in the OptionalDestinationDirectory must retain old and unchanged.
162-
if (File.Exists (Path.Combine (copydst, filename)))
163+
if (File.Exists (assemblyDestination)) {
164+
MonoAndroidHelper.SetLastAccessAndWriteTimeUtc (assemblyDestination, DateTime.UtcNow, Log);
163165
continue;
164-
copysrc = assembly.ItemSpec;
166+
}
165167
} else {
166168
// Prefer fixup assemblies if exists, otherwise just copy the original.
167169
copysrc = Path.Combine (OutputDirectory, filename);
@@ -171,7 +173,6 @@ bool Execute (DirectoryAssemblyResolver res)
171173
else if (!MonoAndroidHelper.IsForceRetainedAssembly (filename))
172174
continue;
173175

174-
var assemblyDestination = Path.Combine (copydst, filename);
175176
if (MonoAndroidHelper.CopyIfChanged (copysrc, assemblyDestination)) {
176177
MonoAndroidHelper.SetLastAccessAndWriteTimeUtc (assemblyDestination, DateTime.UtcNow, Log);
177178
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public void CheckTimestamps ()
158158
var output = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath);
159159
if (Directory.Exists (output))
160160
Directory.Delete (output, true);
161-
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
161+
Assert.IsTrue (b.Build (proj), "first build should have succeeded.");
162162

163163
//Absolutely non of these files should be *older* than the starting time of this test!
164164
var files = Directory.EnumerateFiles (intermediate, "*", SearchOption.AllDirectories).ToList ();
@@ -167,6 +167,21 @@ public void CheckTimestamps ()
167167
var info = new FileInfo (file);
168168
Assert.IsTrue (info.LastWriteTimeUtc > start, $"`{file}` is older than `{start}`, with a timestamp of `{info.LastWriteTimeUtc}`!");
169169
}
170+
171+
//Build again after a code change, checking a few files
172+
proj.MainActivity = proj.DefaultMainActivity.Replace ("clicks", "CLICKS");
173+
proj.Touch ("MainActivity.cs");
174+
start = DateTime.UtcNow;
175+
Assert.IsTrue (b.Build (proj), "second build should have succeeded.");
176+
177+
foreach (var file in new [] { "typemap.mj", "typemap.jm" }) {
178+
var info = new FileInfo (Path.Combine (intermediate, "android", file));
179+
Assert.IsTrue (info.LastWriteTimeUtc > start, $"`{file}` is older than `{start}`, with a timestamp of `{info.LastWriteTimeUtc}`!");
180+
}
181+
182+
//One last build with no changes
183+
Assert.IsTrue (b.Build (proj), "third build should have succeeded.");
184+
Assert.IsTrue (b.Output.IsTargetSkipped ("_LinkAssembliesNoShrink"), "`_LinkAssembliesNoShrink` should be skipped!");
170185
}
171186
}
172187

0 commit comments

Comments
 (0)