In the past it might seem that Windows users of MonoGame get all the cool stuff, and Mac / Linux users are left out in the cold. To be honest for a while that was true, but the great news is that more recently that has begun to change. On going community efforts have resulted in both MacOS and Linux installers which will download and install templates into the Xamarin Studio and MonoDevelop. They also install the Pipeline tool which is a GUI you can use to build your content for your game.

All that was great, but again Windows has something that Mac and Linux developers just didn’t have access to. Automatic Content Building, this is where you just include the .mgcb file in your project, set its Build Action to “MonoGameContentReference” and providing you created the project via one of the templates it would “just work”. Your .xnb files would appear as if by magic in your Output Directory without all that messy manual linking of .xnbs.

So how does it work.. Well to fully understand we need to dig into MSBuild a bit ūüôā I know recently I’ve been talking about MSBuild allot but thats because in my day job (@Xamarin) I’m dealing with it ALLOT! So its topical from my point of view ūüėČ

So if you dig into your csproj which was created in Visual Studio via one of the MonoGame templates  you will see a number of things. The first is a <MonoGamePlatform> element. This element is used later to tell the MGCB (MonoGame Content Builder) which platform it needs to build for.  Next up is the <MonoGameContentReference> element which will contain a link to the .mgcb file.. This again is used later to tell MGCB which files to build. Note that you are not just limited to one of these. If you have multiple assets and different resolutions (e.g @2x stuff for iOS) you can have a separate .mgcb file for those and include that in your project. The system will collect ALL the outputs (just make sure they build into different intermediate directories).

The last piece of this system is the “MonoGame.Content.Builder.targets” file. This is the core of the system and you should be able to see the Import near the bottom of your .csproj. This .targets file is responsible for going through ALL the MonoGameContentReference Items in the csproj and calling MGCB.exe for each of them to build the content, it will also pass


to the .exe to that it will build the assets for the correct platform. This is all done in the BeforeBuild msbuild event, so it will happen before the code is even built, just like the old XNA content references used to do. But this time you don’t need to do any fiddling to get this to work on a command line, it will just work. Now calling an .exe on a build in a .targets isn’t exactly magic, the magic bit is right here

<Target Name="BuildContent" DependsOnTargets="Prepare;RunContentBuilder"
<CreateItem Include="$(ParentOutputDir)\%(ExtraContent.RecursiveDir)%(ExtraContent.Filename)%(ExtraContent.Extension)"
Condition="'%(ExtraContent.Filename)' != ''">
<Output TaskParameter="Include" ItemName="Content" Condition="'$(MonoGamePlatform)' != 'Android' And '$(MonoGamePlatform)' != 'iOS' And '$(MonoGamePlatform)' != 'MacOSX'" />
<Output TaskParameter="Include" ItemName="BundleResource" Condition="'$(MonoGamePlatform)' == 'MacOSX' Or '$(MonoGamePlatform)' == 'iOS'" />
<Output TaskParameter="Include" ItemName="AndroidAsset" Condition="'$(MonoGamePlatform)' == 'Android'" />

This part is responsible for adding the resulting .xnb files to the appropriate ItemGroup for the platform that we are targeting. So in the case of a Desktop build like Windows, Linix we use Content. For iOS and Mac we use BundleResource and for Android we use AndroidAsset. Because we are doing this just before the Build process, when those target platforms actually build the content later they will pick up the items we added in addition to any other items that the projects themselves included.

Now the really interesting bit is that code above was not how it originally looked.. The problem with the old code was it didn’t work with xbuild, which is what is used on Mac and Linux. So it just wouldn’t work. But now the entire .targets file will run quite happily on Mac and Linux and have intact been included in the latest unstable installers. So if you want to try it out go and download the latest development installers and give it a go.

If you have an existing project and you want to upgrade to use the new content pipeline system you will need to do the following

  1. Open your Application .csproj in an Editor.
  2. In the first <PropertyGroup> section add <MonoGamePlatform>$(Platform)</MonoGamePlatform>
    where $(platform) is the system you are targeting e.g Windows, iOS, Android.
  3. Add the following lines right underneath the <MonoGamePlatform /> element <MonoGameInstallDirectory Condition="'$(OS)' != 'Unix' ">$(MSBuildProgramFiles32)</MonoGameInstallDirectory>
    <MonoGameInstallDirectory Condition="'$(OS)' == 'Unix' ">$(MSBuildExtensionsPath)</MonoGameInstallDirectory>
  4. Find the <Import/> element for the CSharp (or FSharp) targets and underneath add <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />

Now providing you have the latest development release this should all work. So if you have an old project go ahead and give it a try, its well worth it ūüôā

One of the many requests I see from customers of Xamarin.Android is the ability to “run a script” after the package has been built and signed, or do some process before the build process. Allot of people¬†end up trying to use the CustomCommands which are available in the IDE to do this work, and shy away from getting down and dirty with MSBuild. The thing is, sometimes MSBuild is the only way to get certain things done.

Most people already know about the “SignAndroidPackage” target which is available on Xamarin.Android projects. If not basically you can use this target to create a Signed package, most people would use this on a Continuous Integration (CI) server. You can use it like so

  msbuild MyProject.csproj /t:SignAndroidPackage

Note that this target is ONLY available on the .csproj NOT the .sln but that is ok because MSBuild does its just of resolving projects etc and it should build fine. You can also pass in addition parameters to define your KeyStore etc, more information can be found in the excellent documentation on the subject.

Now we want to run a script after this has been done, Custom Commands can’t be used as they run at the wrong time in the build process, so what do we do? This is where MSBuild targets come in. What we need to do is add a custom MSBuild target which hooks into the build process After the SignAndroidPackage has run.

First thing to do is open the .csproj of your application in your favourite editor and find the last </Project> element right at the bottom of the file. Next we define a new target just above the </Project> element like so

<Target Name="AfterAndroidSignPackage" AfterTargets="SignAndroidPackage">
   <Message Text="AfterSignAndroidPackage Target Ran" />

Save the .csproj and then run the command

  msbuild MyProject.csproj /t:SignAndroidPackage

and you should see the text “AfterSignAndroidPackage Target Ran” in the build output. ¬†If not add a /v:d argument to the command line, which switches the MSBuild output to verbose mode. The key thing here is the Target defines the “AfterTargets” attribute which tells MSBuild when to run this script. In this case after the SignAndroidPackage, there is also a “BeforeTargets” attribute which will run a target.. you guessed it.. before the target(s) listed in the attribute.

Now we have a target that will run after the package has been signed. Next this to do is to get it to run our script, for this we can use the MSBuild Exec task. One thing to note is that its more than likely that some of your developers are running on Mac as well as Windows so we need to make sure that any command we do run will work on both systems.

Fortunately we have a way of conditionally executing targets using the Condition attribute. This means we can do something like

<Exec Condition=" '$(OS)' != 'Unix' " Command="dir" />
<Exec Condition=" '$(OS)' == 'Unix' " Command="ls" />

this allows us to run separate commands based on the OS we are running (in this case a directory listing). As you can see we can easily extend this to run a batch file or shell script. So the final target would be

<Target Name="AfterAndroidSignPackage" AfterTargets="SignAndroidPackage">
  <Exec Condition=" '$(OS)' != 'Unix' " Command="dir" />
  <Exec Condition=" '$(OS)' == 'Unix' " Command="ls" />

MSBuild targets can also define Inputs and Outputs which will allow the build engine to decide if it needs to run the target at all. That is something that you can read up on if you are interested in learning more, but for now if you just want to “run a script after the apk is built” this is all you need.