Skip to content

Commit

Permalink
[Xamarin.Android.Cecil] Fix project rebuilds
Browse files Browse the repository at this point in the history
A problem with any build system are "cascading rebuilds": when a
project changes and is rebuilt, that requires that all projects which
depend on it are *also* rebuilt. The end result is that if a "core"
project changes, everything else in the solution will need to be
rebuilt, *just in case* something "important" changed.

MSBuild (and make!) use file timestamps to determine whether or not
targets should be re-executed. Consequently, the way to avoid
cascading rebuilds is to ensure that assembly timestamps don't change
unless they have to, e.g. they were actually rebuilt.
[The MSBuild `<Target/>` element][0] can use the `@Inputs` and
`@Outputs` attributes to control when a target is run, relying on file
timestamps to determine when a target needs to be rerun.

Enter `Xamarin.Android.Cecil.targets`'s `BuildCecil` target:

	<Target Name="BuildCecil"
	    Outputs="$(CecilAssemblies)">
	  ...

*This is a bug*: It provides the `@Outputs` attribute but not the
`@Inputs` attribute. In this scenario, MSBuild treats the target as if
the outputs are *always* "older" than the inputs, and thus *always*
re-executes the target:

	$ xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	$ ls -l bin/Debug/Xamarin.Android.Cecil.dll
	-rw-r--r--  1 jon  staff  374784 Apr  3 13:46 bin/Debug/Xamarin.Android.Cecil.dll
	$ xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	$ ls -l bin/Debug/Xamarin.Android.Cecil.dll
	-rw-r--r--  1 jon  staff  374784 Apr  3 13:47 bin/Debug/Xamarin.Android.Cecil.dll

This is the foundation for cascading rebuilds: *Nothing has changed*,
yet the timestamp is updated. This in turn requires that everything
which depends on `Xamarin.Android.Cecil.dll` -- which is **A LOT** --
also needs to be rebuilt.

Fix `Xamarin.Android.Cecil.targets` so that the `BuildCecil` target
has an `@Inputs` attribute specified. This allows the `BuildCecil`
target to be *skipped* if nothing has changed.

This also *greatly* decreases the time for rebuilds:

	# pre patch; note: times are roughly the same:
	$ time xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	real	0m4.574s
	user	0m5.829s
	sys	0m1.755s
	$ time xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	real	0m4.552s
	user	0m5.617s
	sys	0m1.833s

	# post patch: `BuildCecil` takes 1/10 the time!
	$ time xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	real	0m4.707s
	user	0m5.655s
	sys	0m1.793s
	$ time xbuild /nologo /v:quiet src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.csproj
	real	0m0.492s
	user	0m0.399s
	sys	0m0.074s

[0]: https://msdn.microsoft.com/en-us/library/t50z2hka.aspx
  • Loading branch information
jonpryor authored and radekdoulik committed Apr 3, 2017
1 parent 68233f9 commit 5eeb287
Showing 1 changed file with 15 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/Xamarin.Android.Cecil/Xamarin.Android.Cecil.targets
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@
<CecilOutputPath>$([System.IO.Path]::GetFullPath ($(CecilOutputPath)))</CecilOutputPath>
<CecilAssemblies>$(OutputPath)\Xamarin.Android.Cecil.dll;$(OutputPath)\Xamarin.Android.Cecil.Mdb.dll</CecilAssemblies>
</PropertyGroup>
<ItemGroup>
<_CecilProject Include="$(CecilDirectory)\Mono.Cecil.csproj" />
<_CecilProject Include="$(CecilDirectory)\symbols\mdb\Mono.Cecil.Mdb.csproj" />
<_CecilSource Include="$(CecilDirectory)\**\*.cs" />
</ItemGroup>
<Target Name="BuildCecil"
Inputs="@(_CecilSource)"
Outputs="$(CecilAssemblies)">
<MSBuild
Projects="$(CecilDirectory)\Mono.Cecil.csproj;$(CecilDirectory)\symbols\mdb\Mono.Cecil.Mdb.csproj"
Projects="@(_CecilProject)"
Targets="Clean;Build"
StopOnFirstFailure="True"
Properties="Configuration=net_4_0_Debug;OutputPath=$(CecilOutputPath);BuildingSolutionFile=false" />
Expand All @@ -20,4 +26,12 @@
<Target Name="Build" DependsOnTargets="BuildCecil" Returns="$(CecilOutputPath)\$(AssemblyName).dll">
<MakeDir Directories="obj\$(Configuration)" />
</Target>
<Target Name="Clean" Returns="$(CecilOutputPath)\$(AssemblyName).dll">
<MSBuild
Projects="@(_CecilProject)"
Targets="Clean"
StopOnFirstFailure="True"
Properties="Configuration=net_4_0_Debug;OutputPath=$(CecilOutputPath);BuildingSolutionFile=false"
/>
</Target>
</Project>

0 comments on commit 5eeb287

Please sign in to comment.