Some links about design-time builds:
- https://github.com/dotnet/project-system/blob/master/docs/design-time-builds.md
- https://daveaglick.com/posts/running-a-design-time-build-with-msbuild-apis
In Visual Studio 2017 (Windows), a "design-time" build is what drives
Intellisense in cases such as using `x:Name` in XAML (or
`Resource.designer.cs` in a plain Xamarin.Android app). Improving our
"design-time" build speed will improve general performance in the IDE
such as project load and how long Intellisense takes to update. Since
XamlC is not needed during a design-time build, it is an "easy-win" to
just disable it. XamlC can take a bit of time for projects with a lot
of XAML.
This might also help with: https://github.com/xamarin/Xamarin.Forms/issues/3004
Although we should probably not do anything to specifically support
NCrunch; hopefully, NCrunch is running under the context of a
design-time build.
* [XamlG] builds incrementally, add MSBuild integration tests
Context: https://github.com/xamarin/Xamarin.Forms/pull/2230
The main performance problem with the collection of MSBuild targets in
`Xamarin.Forms.targets` is they don't build incrementally. I addressed
this with `XamlC` using a "stamp" file; however, it is not quite so
easy to setup the same thing with `XamlG`.
They way "incremental" builds are setup in MSBuild, is by specifying
the `Inputs` and `Outputs` of a `<Target />`. MSBuild will partially
build a target when some outputs are not up to date, and skip it
entirely if they are all up to date.
The best docs I can find on MSBuild incremental builds:
https://msdn.microsoft.com/en-us/library/ms171483.aspx
Unfortunately a few things had to happen to make this work for
`XamlG`:
- Define a new target `_FindXamlGFiles` that is invoked before `XamlG`
- `_FindXamlGFiles` defines the `_XamlGInputs` and `_XamlGOutputs`
`<ItemGroup />`'s
- `_FindXamlGFiles` must also define `<Compile />` and `<FileWrites />`,
in case the `XamlG` target is skipped
- `XamlGTask` now needs to get passed in a list of `OutputFiles`,
since we have computed these paths ahead of time
- `XamlGTask` should validate the lengths of `XamlFiles` and
`OutputFiles` match, used error message from MSBuild proper:
a691a44f0e/src/Tasks/Copy.cs (L505)
`XamlG` now builds incrementally!
To give some context on how much improvement we can see with build
times, consider the following command:
msbuild Xamarin.Forms.ControlGallery.Android/Xamarin.Forms.ControlGallery.Android.csproj
If you run it once, it will take a while--this change will not improve
the first build. On the second build with the exact same command, it
*should* be much faster.
Before this commit, the second build on my machine takes:
40.563s
After the change:
23.692s
`XamlG` has cascading impact on build times when it isn't built
incrementally:
- The C# assembly always changes
- Hence, `XamlC` will always run
- Hence, `GenerateJavaStubs` will always run
- Hence, `javac.exe` and `dx.jar` will always run
I am making other improvements like this in Xamarin.Android itself,
that will further improve these times, such as:
https://github.com/xamarin/xamarin-android/pull/1693
~~ New MSBuild Integration Tests ~~
Added some basic MSBuild testing infrastructure:
- Tests write project files to `bin/Debug/temp/TestName`
- Each test has an `sdkStyle` flag for testing the new project system
versus the old one
- `[TearDown]` deletes the entire directory, with a retry for
`IOException` on Windows
- Used the `Microsoft.Build.Locator` NuGet package for locating
`MSBuild.exe` on Windows
- These tests take 2-5 seconds each
So for example, the simplest test, `BuildAProject` writes to
`Xamarin.Forms.Xaml.UnitTests\bin\Debug\temp\BuildAProject(False)\test.csproj`:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration>Debug</Configuration>
<Platform>AnyCPU</Platform>
<OutputType>Library</OutputType>
<OutputPath>bin\Debug</OutputPath>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="Xamarin.Forms.Core.dll">
<HintPath>..\..\Xamarin.Forms.Core.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Xaml.dll">
<HintPath>..\..\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\..\..\.nuspec\Xamarin.Forms.targets" />
<ItemGroup>
<EmbeddedResource Include="MainPage.xaml" />
</ItemGroup>
</Project>
Invokes `msbuild`, and checks the intermediate output for files being
generated.
Tested scenarios:
- Build a simple project
- Build, then build again, and make sure targets were skipped
- Build, then clean, make sure files are gone
- Build, with linked files
- Design-time build
- Call `UpdateDesignTimeXaml` directly
- Build, add a new file, build again
- Build, update timestamp on a file, build again
- XAML file with random XML content
- XAML file with invalid XML content
- A general `EmbeddedResource` that shouldn't go through XamlG
Adding these tests found a bug! `IncrementalClean` was deleting
`XamlC.stamp`. I fixed this by using `<ItemGroup />`, which will be
propery evaluated even if the target is skipped.
~~ Other Changes ~~
- `FilesWrite` is actually supposed to be `FileWrites`, see canonical
source of how `Clean` works and what `FileWrites` is here:
https://github.com/Microsoft/msbuild/issues/2408#issuecomment-321082997
- Moved `DummyBuildEngine` into `MSBuild` directory--makes sense?
maybe don't need to?
- Added a `XamlGDifferentInputOutputLengths` test to check the error
message
- Expanded `DummyBuildEngine` so you can assert against log messages
- Changed a setting in `.Xamarin.Forms.Android.sln` so the unit test
project is built
- My VS IDE monkeyed with a few files, and I kept any *good* (or
relevant) changes: `Xamarin.Forms.UnitTests.csproj`,
`Xamarin.Forms.Xaml.UnitTests\app.config`, etc.
There were some checks for `%(TargetPath)` being blank in the C# code
of `XamlGTask`. In that case it was using `Path.GetRandomFileName`,
but we can't do this if we are setting up inputs and outputs for
`XamlG`. I presume this is from the designer and/or design-time builds
before `DependsOnTargets="PrepareResourceNames"` was added. I tested
design-time builds in VS on Windows, and `$(TargetPath)` was set. To
be sure we don't break anything here, I exclude inputs to `XamlG` if
`%(TargetPath)` is somehow blank.
See relevant MSBuild code for `%(TargetPath)` here:
0515178090/src/Tasks/Microsoft.Common.CurrentVersion.targets (L2822)
~~ Future changes ~~
CssG needs the exact same setup, as it was patterned after `XamlG`.
This should probably be done in a future PR.
* [msbuild] improved lookup of Xamarin.Forms.targets in integration tests
Context: https://devdiv.visualstudio.com/DevDiv/_build?buildId=1717939
Context: https://devdiv.visualstudio.com/DevDiv/_build?buildId=1718306
It looks like the VSTS builds for release branches are running tests
in a staging directory. This means we can't reliably import
`Xamarin.Forms.targets` as what was working locally in the
Xamarin.Forms source tree.
So to fix this:
- Look for `.nuspec/Xamarin.Forms.targets`, at the default location
and then using the `BUILD_SOURCESDIRECTORY` environment variable as
a fallback
- Copy all `*.targets` files to the test directory
- Our `*.csproj` files under test can import the file from there.
We have to copy the targets files here to be sure that MSBuild can
load `Xamarin.Forms.Build.Tasks.dll`, which is also referenced by the
unit tests.
I also made the tests abort earlier if they can't find
`Xamarin.Forms.targets`.
* Call ChangeVisualState when new states are added so element is in correct
state when loaded from XAML; fixes#2625
* Fix build for UI test projects
* fix uitest build
* Added OnPlatform markup extension supporting iOS/Android/UWP for #2608
* Add Default and Other properties to OnPlatformExtension
This allows setting a default value for unknown platforms, as well
as specify values for arbitrary platforms by using a named parameter
like syntax:
`{OnPlatform Android=15, iOS=10, UWP=12, Default=11, Other=Tizen:20}`
The `Other` supports multiple semi-colon separated values. By using
this format, we can make the string more concise than if we used `=`
which would have to be escaped in quotes. For example:
`{OnPlatform Default=10, Other=Tizen:22;Xbox:20;Switch=25;PlayStation=22}`
Added unit tests that verify all the supported combinations.
* Add OnIdiomExtension
The extension allows the following syntax:
`{OnIdiom Phone=23, Tablet=25, Desktop=26, TV=30, Watch=10, Default=20}`
At least one value or `Default` must be specified. `Default` is returned
whenever the specific idiom was not specified.
* Add missing known platforms and return Default if missing
Add all strings that are provided in `Device`.
Convert individual asserts into test cases for better reporting.
Also, whenever a matching platform value isn't specified, return Default
instead of null.
* Turn OnIdiom asserts into test cases
This makes for easier to read, document and report tests.
* Add missing platforms to null check
* Make Default the content property
* Perform type conversion as expected by XamlC
Leverage the conversion that is used elsewhere, to return a
properly typed object that can be assigned directly to the
property. Update tests with typed values since now we get
integers, rather than strings out of the parser.
* Add Converter/ConverterParameter support
* Message should state that the value must be non-null
* Remove Unsupported idiom since it's not useful to set
You can use Default instead.
* Use new GetService<T> extension method for conciseness
* Don't fail if service provider is null
* Remove Other from OnPlatformExtension
As suggested, this might come back in the future in some other form.
* [Xaml] open non-generic FindByName()
* [Xaml[C]] Only set the namescope when it's different
avoid setting the NamesCope if it's the same as the parent.
Also provide an IReferenceProvider for markup extension that need to
replicate the x:Reference behavior.
* [XamlC] x:DataType in default xmlns
* [XamlC] meaningful error on missing property
throw a readable exception when part of a compiled binding path is
missing.
- fixes#2517
* Disable AndroidUseLatestPlatformSdk in VS so VS stops auto-updating Android projects;
Remove XF.targets imports from projects which don't need it;
Make XF.targets imports conditional on existence of XFBT DLL in VS to avoid errors
* More consistent check for VS
* Apply nicer VS check logic to Xamarin.Forms.Xaml.UnitTests.csproj
* Fix missing "'"
* Re-add XF.targets imports to PagesGallery native projects
* [C/XamlC] BindingMode.OneTime
Bindings with mode == OneTime:
- are only applied when the BindingContext changes
- do not subscribe to INPC
if the Binding is compiled and the mode explicitely set in the
`{Binding}` Markup extension, the setters and handlers aren't even
created.
- fixes#1686
* update docs
* [Xaml] let the previewer know the asm
in addition to the resourcePath, tell the previewer the assembly in
which we're looking for the resource.
* fix docs
* Port from old VSM branch
* Add PS and notes
* Checkpoint: entry text UWP mostly working, need to check on background colors
* Remove irrelevant samples from the EntryDisabledStatesGallery
Make Background color work on UWP Entry with VSM
* Add platform specific for disabling legacy colors on Android
* Add OnPlatform example to visual state manager gallery
* Add example OnIdiom in Visual State Manager
* Add platform specific for disabling legacy color mode on iOS Entry
* Add gallery for Button disabled states
Handling legacy colors for Buttons on Android
* Split out disabled states galleries; disabled legacy handling for Picker
* TimePicker disabled states
* DatePicker color management on Android
* Color management for pre-AppCompat button
* Button legacy color handling on iOS
* Consolidate Platform Specifics;
legacy colors working for iOS Picker and DatePicker
* Fix broken search bar color management
SearchBar color management working with VSM
Add test page for SearchBar disabled color management
Consolidate legacy color management check code into extension method on Android
* Legacy color management for Editor on Android
* Fix legacy color stuff for SearchBar Cancel button on iOS
* C# 7 cleanup
* Add colors for Cancel Button
* Make sure VisualStateGroup collections set by styles are distinct objects
* Validation example
* Make common state names consts
* Make the Windows VSM and Forms VSM work together
* Update galleries for Windows
* Make new methods internal
* Split gallery classes and add more explanation to validation example
* Remove debugging statements
* Add a quick code-only example
* Make legacy color management work for fast button renderer
* Remove old TODO
* Update docs
* Move RunTimeNamePropertyAttribute to Xamarin.Forms.Xaml namespace
* Verify XF namespace when looking for VisualState
* Use nameof
* Make common states constants public
* Cast VisualElement directly so it crashes if the property is set on the wrong type
* Collection -> IList for VisualStateManager
* Setting fromStyle to true
* Remove extraneous `private set`
* Seal VSM classes
* Use constraints instead of ==
* Add teardown method; use constraints rather than ==
* Remove null checking with GetVisualStateGroups
* Don't explicitly initialize collections on elements
* Actually, turns out that fromStyle:false *was* correct
* Direct casts
* Use GetIsDefault check in GoToState
* Validate parents in FindTypeForVisualState
* Validate group and state names on Add
* Fixed check for setter collection
* Fix issues with "duplicate" names when VisualStateGroups declared directly on VisualElements
* Add gallery example for VSGs directly on VisualElements
* Update docs
* Fix bug where initial TextColor isn't set for FastRenderer Button
* Move to explicit VisualStateGroupList in Setter
* Fix return types for unit tests
* Using string.CompareOrdinal in GetState
* Update docs
* Add check for null/empty VisualState Name properties