Monday, July 25, 2016

Android xbuild: 'OutputPath' property is not set

Background

My team and I are building a Xamarin Forms mobile application that includes iOS and Android projects. I've set up automated CI builds and I've created some custom build configurations for CI and Production (Store) builds. The idea is to use one solution level build configuration that configures both iOS and Android projects appropriately with one build script.

Here's the Ad-hoc (deploy to test devices) Build Config:
(The first project is the PCL which simply the App name with no suffix).

Here's the Production (Store) Build Config:


This all works great in Visual Studio. But not in Bitrise (my chosen online mobile build system).

The Error Message

Here's the command line used to build the Android project extracted from the build output in Bitrise:
xbuild /t:PackageForAndroid /p:Configuration"PlayStore" "./XXXXXXXX/XXXXXXXX.Droid/XXXXXXXX.Droid.csproj" /verbosity:minimal /nologo

Within a few seconds produces this error message.
/Library/Frameworks/Mono.framework/Versions/4.4.0/lib/mono/4.5/Microsoft.Common.targets: error : 'OutputPath' property is not set for this project. Usually this is caused by invalid Configuration/Platform combination. Original values: Configuration: PlayStore Platform: AnyCPU.

There's been reports of this error on the Xamarin Forums (and here) that include other build system like Jenkins.  It looks like there has been bugs in xbuild and Xamarin Studio that have been investigated and fixed.  So its no fault of Bitrise.

The build is given "AppStore" as the target build config, and "RealDevices" as the platform.  The solution file is successfully parsed and based on what we can see from the command line above, it has passed the right target and platform to xbuild (see Store Build Config screenshot above).

The iOS project builds fine.

The diagnostic process on Bitrise

It seems unlikely that the bugs discussed in the forum posts mentioned above are still a problem, the posts are dated some time ago. The suggestions center around App code problems rather than real bugs with Xamarin or Mono.

Bitrise only offers limited options to pass parameters to the Xamarin-Builder step. Basically only build configuration, platform, and solution path.  This is where their generic Script step comes in handy.  Delete the Xamarin-Builder step and replace it with the Script step and use the command line to invoke xbuild directly:

xbuild /t:PackageForAndroid /p:Configuration="PlayStore" /p:Platform="AnyCPU" "./XXXXXX/XXXXXXXX.Droid/XXXXXXXX.Droid.csproj" /verbosity:detailed /nologo /p:OutputPath="bin/PlayStore"

This produced the same results, with nothing new of value.
Running it locally on a Mac also produced the same results.

I then realised that because the build of the Android project is triggered with a build config of "PlayStore" it is passing this value to the its dependency the PCL project.  But its supposed to be built in "Store" config according to the solution file. It doesn't have a config in it's csproj file named "PlayStore" hence the error.

The Solution

xbuild is not building the solution in the same was as msbuild, Visual Studio or Xamarin Studio.  It is using the Android csproj file as its entry point not the solution file.  So the Build Config name provided to build the Android project has to be the same Config name provided to build the PCL.

I added a "PlayStore" Build Config to the PCL and bingo. 
The only downside is that I have two Build Configs to maintain for the PCL: PlayStore and AppStore.

Thursday, July 21, 2016

AndroidDeploymentException: [INSTALL_FAILED_UPDATE_INCOMPATIBLE]

Xamarin.AndroidTools.AndroidDeploymentException: InternalError ---> Mono.AndroidTools.InstallFailedException: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE]

I've come across this problem several times with different physical Android devices.  This message is thrown when a real production Google Play App with the same App Id has been installed and then you try to install a debug version.  It will happen with any means of deploying a debug version including with Visual Studio, Xamarin Studio, and the command tools.

I think what is happening is that Android is not happy with a debug version being deployed over the top of a properly signed and deployed version of a Play Store App.  Android stores a flag to block further install attempts for that App Id.  When an App deployment attempt is made, the flag that Android created is not reset.

To fix this, you will need to reinstall the App from Google Play and then uninstall it normally. After that you'll be able to debug your App with Visual Studio.

Tuesday, July 19, 2016

How to create a signed and aligned APK using Visual Studio

This article guides you through creating an APK file for your Xamarin.Android App that is ready to publish on Google Play.

Creating an Android APK that is signed and aligned can mostly be achieved with Visual Studio.  To sign it and align it, command line tools must be used, but the APK can be created with Visual Studio.

You're App does need to be signed to be submitted to Google Play, but it doesn't necessarily need to be "aligned".  It is highly recommended however. Its basically a very cheap optimisation that will minimise the amount of RAM your App will need at runtime.

Step 1 - Prepare your App for Release

This is out of scope for this article. Check out this link:

Step 2 - Build a Release build of your App

Build your Android App in Release Mode.  Ensure the platform drop down is set to "Any CPU".

Step 3 - Export to APK

This does not automatically occur when you build a Release build. Creating the APK must be triggered manually by selecting a context menu item when you right click the Android Project in Solution Explorer.


The APK will be created in your bin\Release\ folder for the Android project.


Step 4 - Sign the APK

Ensure you have the JDK path in your environment Path variable.  My JDK is installed here:
C:\Program Files (x86)\Java\jdk1.7.0_55\bin

The "signed" file you see in the folder has been automatically signed with a Developer certificate.  This isn't suitable for publishing purposes, so you'll need a certificate that identifies you as a developer.

To sign your APK, you'll need your Java KeyStore (JKS) file.  These files usually have a JKS extension or a keystore extension.  If you don't have one you can create one using the instructions in the Google reference above. In summary the command line is: keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Once you have a JKS file you need to keep it in a safe place you must use the same file to sign your App everytime. This file uniquely identifies you as a developer.

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks my_application.apk alias_name

There are three things you need to specify in this command:
Replace "my-release-key.jks" with the filename to your JKS file.
Replace "my_application.apk" with your app's APK filename.
Replace the "alias_name" with the alias name you used when creating your JKS file.
You'll also be prompted for the keystore password.

Step 5 - Align the APK

Finally use the Zipalign tool to optimise your APK file.
Zipalign isn't in the same folder as the JDK, for me its in the Android tools folder here:
C:\Program Files (x86)\Android\android-sdk\build-tools\22.0.1
This also should be added to your environment path if it isn't already.

zipalign -v 4 my_application.apk my_application_aligned.apk


That's it.

Verify an APK is signed with correct certificate

How do I determine if the APK produced by the build system has been signed correctly with the production certificate?

jarsigner -verify -verbose -certs my_application.apk >output.txt
You will need to ensure that the java jdk is included in your Path.  You'll find it, here:
C:\Program Files (x86)\Java\jdk1.7.0_55\bin\jarsigner.exe
If you don't then its likely you haven't correctly installed the Android SDK.

This will produce a lot of output that will show the certificate used to sign each file, which is usually the same certificate.  Open the output.txt file with a text editor.

s      98829 Mon Jul 18 15:45:06 NZST 2016 META-INF/MANIFEST.MF

      X.509, CN=CERTIFICATE_NAME, OU=Department, O=Company Ltd, L=Auckland, ST=Auckland, C=NZ
      [certificate is valid from 01/01/16 10:06 PM to 31/12/43 11:06 PM]
       98950 Mon Jul 18 15:45:06 NZST 2016 META-INF/CERT.SF
        1408 Mon Jul 18 15:45:06 NZST 2016 META-INF/CERT.RSA
sm      7364 Wed Dec 31 16:00:00 NZDT 1980 AndroidManifest.xml

      X.509, CN=CERTIFICATE_NAME, OU=Department, O=Company Ltd, L=Auckland, ST=Auckland, C=NZ
      [certificate is valid from 01/01/16 10:06 PM to 31/12/43 11:06 PM]

Ensure the certificate name is what you expect, and there is no mention of "Debug" in the output file.

I find it useful to do a search and replace for the "CN=CERTIFICATE_NAME" part, and replace with "@@@@@@" or whatever.  Then search for "CN=", and find any, that means some files are signed with a different certificate.