Matthew Soucoup: Code Mill Minute: Setting Up Xamarin Studio To Work With TFS Online Git Source Control
Michael Hutchinson: Build Time Code Generation in MSBuild
Build-time code generation is a really powerful way to automate repetitive parts of your code. It can save time, reduce frustration, and eliminate a source of copy/paste bugs.
This is something I’m familiar with due to my past work on MonoDevelop’s tooling for ASP.NET, T4 and Moonlight, and designing and/or implementing similar systems for Xamarin.iOS and Xamarin.Android. However, I haven’t seen any good documentation on it, so I decided to write an article to outline the basics.
This isn’t just something for custom project types, it’s also something that you can include in NuGets, since they can include MSBuild logic.
Background
The basic idea is to generate C# code from other files in the project, and include it in the build. This can be to generate helpers, for example CodeBehind for views (ASPX, XAML), or to process simple DSLs (T4), or any other purpose you can imagine.
MSBuild makes this pretty easy. You can simply hook a custom target before the Compile
target, and have it emit a Compile
item based on whatever input items you want. For the purposes of this guide I’m going to assume you’re comfortable with enough MSBuild to understand that – if you’re not, the MSDN docs are pretty good for the basics.
The challenge is to include the generated C# in code completion, and update it automatically.
An IDE plugin can do this fairly easily – see for example the Generator mechanism used by T4, and the *.designer.cs
file generated by the old Windows Forms and ASP.NET designers. However, doing it this way has several downsides, for example you have to check their output into source control, and they won’t update if you edit files outside the IDE. Build-time generation, as used for XAML, is a better option in most cases.
This article describes how to implement the same model used by WPF/Silverlight/Xamarin.Forms XAML.
Generating the Code
First, you need a build target that updates the generated files, emits them into the intermediate output directory, and injects them to the Compile
ItemGroup
. For the purposes of this article I’ll call it UpdateGeneratedFiles
and assume that it’s processing ResourceFile
items and emitting a file called GeneratedCode.g.cs
. In a real implementation, you should use unique names won’t conflict with other targets, items and files.
For example:
<Target Name="UpdateGeneratedFiles" DependsOnTargets="_UpdateGeneratedFiles" Condition=="'@(ResourceFile)' != ''" > <ItemGroup> <Compile Include="$(IntermediateOutputDir)GeneratedFile.g.cs" /> <FileWrites Include="$(IntermediateOutputDir)GeneratedFile.g.cs" /> </ItemGroup> </Target> <Target Name="_UpdateGeneratedFiles" Inputs="$(MSBuildProjectFile);@(ResourceFile)" Outputs="$(IntermediateOutputDir)GeneratedFile.g.cs" > <FileGenerationTask Inputs="@(ResourceFile)" Output="$(IntermediateOutputDir)GeneratedFile.g.cs" > </Target>
A quick breakdown:
The UpdateGeneratedFiles
target runs if you have any ResourceFile
items. It injects the generated file into the build as a Compile
item, and also injects a FileWrites
item so the file is recorded for incremental clean. It depends on the ‘real’ generation target, _UpdateGeneratedFiles
, so that the file is generated before the UpdateGeneratedFiles
target runs.
The _UpdateGeneratedFiles
target has Inputs
and Outputs
set, so that it is incremental. The target will be skipped if the output file exists is newer than all of the input files – the project file and the resource files.
The project file is included in the inputs list because its write time will change if the list of resource files changes.
The _UpdateGeneratedFiles
target simply runs a tasks that generates the output file from the input files.
Note that the generated file has the suffix .g.cs
. This is the convention for built-time generated files. The .designer.cs
suffix is used for user-visible files generated at design-time by the designer.
Hooking into the Build
The UpdateGeneratedFiles
target is added to the dependencies of the CoreCompile
target by prepending it to the CoreCompileDependsOn
property.
<PropertyGroup> <CoreCompileDependsOn>UpdateGeneratedFiles;$(CoreCompileDependsOn)</CoreCompileDependsOn> </PropertyGroup>
This means that whenever the the project is compiled, the generated file is generated or updated if necessary, and the injected Compile
item is injected before the compiler is called, so is passed to the compiler – though it never exists in the project file itself.
Live Update on Project Change
So how do the types from the generated file show up in code completion before the project has been compiled? This takes advantage of the way that Visual Studio initializes its in-process compiler that’s used for code completion.
When the project is loaded in Visual Studio, or when the project file is changed, Visual Studio runs the CoreCompile
target. It intercepts the call to the compiler via a host hook in the the MSBuild Csc
task and uses the file list and arguments to initialize the in-process compiler.
Because UpdateGeneratedFiles
is a dependency of CoreCompile
, this means that the generated file is updated before the code completion system is initialized, and the injected file is passed to the code completion system.
Note that the UpdateGeneratedFiles
target has to be fast, or it will add latency to code completion availability when first loading the project or after cleaning it.
Live Update on File Change
So, the generated code is updated whenever the project changes. But what happens when the contents of the ResourceFile
files that it depends on change?
This is handled via Generator metadata on each of the ResourceFile
files:
<ItemGroup> <ResourceFile Include="Foo.png"> <Generator>MSBuild:UpdateGeneratedFiles</Generator> </ResourceFile> </ItemGroup>
This takes advantage of another Visual Studio feature. Whenever the file is saved, VS runs the UpdateGeneratedFiles
target. The code completion system detects the change to the generated file and reparses it.
This metadata has to be applied to the items by the IDE (or the user). It may be possible for the build targets to apply it automatically using an ItemDefinitionGroup
but I haven’t tested whether VS respects this for Generator
metadata.
Xamarin Studio/MonoDevelop
But we have another problem. What about Xamarin Studio/MonoDevelop?
Although Xamarin Studio respects Generator
metadata, it doesn’t have an in-process compiler. It doesn’t run CoreCompile
, nor does it intercept the Csc
file list, so its code completion system won’t see the generated file at all.
The solution – for now – is to add explicit support in a Xamarin Studio addin to run the UpdateGeneratedFiles
target on project load and when the resource files change, parse the generated file and inject it into the type system directly.
Migration
Migrating automatically from a designer-generation system to a build-generation system has a few implications.
You either have to force migration of the project to the new system via an IDE, or handle the old system and make the migration optional – e.g. toggled by the presence of the old files. You have to update the project templates and samples, and you have to build a migration system that removes the designer files from the project and adds Generator metadata to existing files.
Nic Wise: Love and Free Apps
This is the age of writing iOS apps for love.
Well, that’s not true for everybody. Well-established and awesome companies such as Omni, Panic, Flexibits, AgileBits, Tapbots, and The Iconfactory have a business writing iOS apps. (They do it for love, but not just for
Wallace B. (Wally) McClure: Refining the Economics of Your App for the Right Market
Url: https://visualstudiomagazine.com/articles/2015/06/30/app-to-market-part-2-right-app.aspx
In part 1, we looked at researching whether your brilliant idea was worth pursuing as a business venture and it all starts with seeing if the need exists. Now that you’ve validated that there is a need for the product, only then is it time to start building things. Let’s take the next step, which is to work through the basics of the economics of your idea.