I'm not entirely sure what posessed me to use
`Environment.GetCommandLineArgs()` to determine the location of
`setup-windows.exe`, from which the installation prefix is derived.
It seemed like a good idea at the time?
Regardless, the problem with using `Environment.GetCommandLineArgs()`
in this fashion is that you can't execute the utility without
providing the full path name:
> cd C:\xa-sdk\oss-xamarin.android_v7.4.99.60_Darwin-x86_64_master_4f3d604\bin\Debug\bin
> .\setup-windows.exe
setup-windows.exe: This program must be run from the `bin` directory.
This is decidedly dumb.
Update the logic to instead use
`typeof(SetupWindows).Assembly.Location`. which will behave in a more
reasonable fashion.
Additionally, update `UsingJenkinsBuildArtifacts.md` so that the
mentioned macOS+Windows versions actually contain `setup-windows.exe`,
instead of hand-waving things away.
Finally, fix `Xamarin.Android.sln` so that Visual Studio for Mac
doesn't show an "Invalid configuration mapping" error status on the
`setup-windows` project.
I really didn't want to do this. I wanted the Xamarin.Android SDK on
Windows to be usable side-by-side, so that multiple
`oss-xamarin.android*.zip` files could be extracted, and developers
could switch between them by overriding MSBuild properties.
This is what was documented in commit 87ca2737, by overriding the
`$(TargetFrameworkRootPath)` MSBuild property.
There's just one "minor" problem with that approach: it only works if
the project that is being built, and *all* project dependencies, are
Xamarin.Android projects. If *any* other kind of dependency is brought
in, this approach will no longer work, as the `GetReferenceAssemblies`
target -- which looks for assemblies *rooted within
`$(TargetFrameworkRootPath)`* -- won't be able to find them.
Unfortunately, *everything of interest* doesn't fit within this
restriction. A Xamarin.Forms app, or any other app using PCL
assemblies, quickly runs afoul of it:
error MSB3644: The reference assemblies for framework ".NETPortable,Version=v4.5,Profile=Profile259" were not found.
Consequently, the instructions from commit 87ca2737 are borderline
worthless. There is only one way to actually build such projects, and
that's to install Xamarin.Android *system-wide*, so that MSBuild's
`GetReferenceAssemblies` target can find everything it needs.
:-(
Thus, we need an "installer." I was hoping for a simple `.cmd` file,
but that stymied me. Then I hoped for a PowerShell script, but
installation requires access to the `%VSINSTALLDIR%` environment
variable, which is only exported from
Visual Studio Developer Command Prompts, and all the solutions I found
to import the VS command prompt environment info into PowerShell
looked decidedly ugly.
Which brings us to a minimal effort command-line installer:
`setup-windows.exe`. This utility backs up existing installs, then
creates symbolic links within the system-wide directories, pointing
them into the extracted `oss-xamarin.android*.zip` contents which
contains `setup-windows.exe`.
`setup-windows.exe /uninstall` is also provided, to put directories
back the way they were found.
`Documentation/UsingJenkinsBuildArtifacts.md` has been updated
accordingly.
Build `netstandard.dll` and `System.Drawing.Primitives.dll` facades
The old project for `System.Drawing.Primitives` was removed in favor
of building the assembly with the existing logic in Mono.
Also add them to the bundle since we can't rebuild them if we just
have the bundle.
Update facades list in `MobileProfile.cs`;
`netstandard.dll` and `System.ValueTuple.dll` were missing.
Visual Studio 2017 uses `.vsix` files for Xamarin.Android SDK support.
A `.vsix` file is [ZIP container with additional metadata][0], and the
[Microsoft.VSSDK.BuildTools NuGet package][1] contains various MSBuild
targets and tools to assist in creating `.vsix` files.
Add a new `build-tools/create-vsix/create-vsix.csproj` project to
create the `bin/Build$(Configuration)/Xamarin.Android.Sdk*.vsix` file,
so that we can plausibly provide per-build OSS Xamarin.Android
releases that work with Visual Studio 2017.
Unfortunately those tools were written on Windows, and not really well
tested on macOS or Linux... In particular, there are case-sensitivity
and directory-separator-char issues with the tooling, necessitating
that `MONO_IOMAP=all` be exported in order to run them:
MONO_IOMAP=all MONO_OPTIONS=--arch=64 msbuild \
build-tools/create-vsix/create-vsix.csproj /p:CreateVsixContainer=True
Meanwhile, we're still attempting to allow things to be built with
`xbuild` [^3]. Thread this needle by "special-casing" the
`$(BuildDependsOn)`, `$(CopyVsixManifestFileDependsOn)`, and
`$(DetokenizeVsixManifestFileDependsOn)` MSBuild properties so that
when the `$(CreateVsixContainer)` MSBuild property is False -- the
default -- no `.vsix` package will be created, and `xbuild` will be
able to build the project.
Similar needle threading is needed to "support" building on Linux: the
Microsoft.VSSDK.BuildTools NuGet package uses case in an inconsistent
fashion -- or is it `nuget`? -- which results in
[failures when building on Linux][2] because `nuget` extracts e.g.:
packages/Microsoft.VSSDK.BuildTools.15.0.26201/tools/vssdk/Microsoft.VsSDK.targets
while Microsoft.VSSDK.BuildTools attempts to `<Import/>` the file:
packages/Microsoft.VSSDK.BuildTools.15.0.26201/tools/VSSDK/Microsoft.VsSDK.targets
`vssdk` != `VSSDK` on case-sensitive filesystems, so this results in
an error on Ubuntu (and presumably case-senstive macOS as well).
Handle the Linux case by extending the `xbuild` case: *even when*
`$(CreateVsixContainer)` is True, we won't actually build the `.vsix`
unless the `$(VsSDKInstall)` directory exists, which is the
`tools/VSSDK` path.
Building with `$(CreateVsixContainer)` set to True will require using
`msbuild` with `MONO_IOMAP=all` and `MONO_OPPTIONS=--arch=64`
exported, while using a case-insensitive filesystem.
The new `make create-vsix CONFIGURATIONS=Debug` target can be used to
explicitly create the `Xamarin.Android.Sdk*.vsix` file.
One problem with creating `.vsix` files: macOS still defaults to using
a 32-bit process for `mono`, and `make create-vsix` regularly fails
for me with an `OutOfMemoryException` when attempting to generate a
`.vsix` file ~600-700+MB in size. (Why so large? In part because for
Debug configuration we're including un-`strip`'d native libraries;
`libmonosgen-2.0.dll` is *237MB* in size (!); it's closer to 5MB when
`strip`'d, but any un-`strip`'d MXE-generated native libraries to the
`.vsix` file quickly results in `OutOfMemoryException`s.)
A 64-bit mono can be used by using `mono64` -- which isn't easily done
with `msbuild` -- or by using `mono --arch=64`, which *can* be done
with `msbuild` by exporting `MONO_OPTIONS=--arch=64`.
The `make create-vsix` target does this.
Finally, once we have a `.visx` being created, we then examine the
*contents* of the `.vsix` file, which is...weird:
$MSBuild/Xamarin/Android/Xamarin.Android.CSharp.targets # Desirable
$ReferenceAssemblies/Microsoft/Framework/MonoAndroid/v1.0/Xamarin.Android.CSharp.targets # wat?
$ReferenceAssemblies/Microsoft/Framework/MonoAndroid/v1.0/Facades/Xamarin.Android.CSharp.targets # really?!
$ReferenceAssemblies/Microsoft/Framework/MonoAndroid/v7.1/Xamarin.Android.CSharp.targets # !!!!
Turns Out™, the problem was due to `@(ProjectReference)`. We're
(ab)using `@(ProjectReference)` to explicitly specify project
dependencies, and the (implicit) project build order.
Unexpectedly (forgetten?), MSBuild *also* copies the *outputs* of
`@(ProjectReference)`s into the `$(OutputPath)` of the current
project.
Since e.g. `Mono.Posix.csproj` had a reference on
`Xamarin.Android.Build.Tasks.csproj`, the result of this is that the
`$(OutputPath)` of `Mono.Posix.csproj` --
`$prefix/lib/xbuild-frameworks/MonoAndroid/v1.0` -- also contained the
`%(None.CopyToOutputDirectory)` items from
`Xamarin.Android.Build.Tasks.csproj`.
Oops.
The fix is to set `%(ProjectReference.Private)` to False, which
disables this copying of additional and undesirable files.
[0]: https://msdn.microsoft.com/en-us/library/dd997148.aspx
[1]: https://www.nuget.org/packages/Microsoft.VSSDK.BuildTools/
[2]: https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-anroid-linux-pr-builder/297/consoleText
[^3]: But for how much longer?
Rather than have all the msbuild tests in an assembly this commit
moves them into a shared project. This means the source code for
the tests can be easily reused upstream. It also means we can make
use of things like partial classes to change the tests depending on
which repo we are running them from. For example having tests in this
repo only run in Release mode but have both Release and Debug upstream.
... with some changes:
- It turned out that we don't have Ionic.Zip, so it is replaced with
LibZipSharp (with new entry removal methods thanks to grendel).
- We don't import README.samples that indicates some internals.
javadoc-to-mdoc is a tool that converts Javadocs or DroidDocs into
monodoc format. Then it can be converted to C# docs (csc /doc) or HTML
using "mdoc" tool in mono (we also build and ship it within `lib/mandroid`).
**Background**: many Android SDK features rely on [ProGuard][0], a
Java bytecode manipulation library. Some of these features include
linking (shrinking) bytecode and [multidex][1].
Xamarin.Android has historically relied on the version of ProGuard
bundled with the Android SDK, which is how the `<Proguard/>` task and
`@(ProguardConfiguration)` build actions work.
Unfortunately, the version of ProGuard bundled with the Android SDK
is, at this point, *ancient* (ProGuard v4.7; current version is v5.3),
and (more importantly) ProGuard v4.7 doesn't support JDK 1.8 bytecode.
Meaning if the end-developer references a Java library targeting
JDK 1.8 -- e.g. the Android Support Library v25 -- then the version of
ProGuard bundled with the Android SDK *cannot process that library*.
This in turn means that the `<Proguard/>` task and
`@(ProguardConfiguration)` build actions don't work/are useless.
Native Java development "works around" this problem by using Gradle as
the build system, and Java projects are configured to download and
install a newer version of ProGuard via Gradle.
Xamarin.Android projects do not use Gradle, nor will they.
(At least not in the foreseeable future.)
**Solution**: We have communicated with Eric Lafortune, maintainer of
ProGuard, about distributing a newer ProGuard with Xamarin.Android.
He replied:
> From a licensing point of view, you shouldn't need an exception to
> the GPL in many practical configurations:
>
> * if you indeed run ProGuard in a separate JVM
The `<Proguard/>` task is a [`ToolTask`][2], which operates by
creating a new process to invoke the specified tool. We thus are
*already* using ProGuard within a "separate JVM", and no architectural
change is required to comply with ProGuard's licensing without
altering Xamarin.Android's licensing.
The current ProGuard sources (v5.3.2) have been imported into the
[xamarin/proguard][3] module, and we will be using the
`xamarin/proguard` module as a git submodule for build purposes.
[0]: https://www.guardsquare.com/en/proguard
[1]: https://developer.android.com/studio/build/multidex.html
[2]: https://msdn.microsoft.com/en-us/library/microsoft.build.utilities.tooltask.aspx
[3]: http://github.com/xamarin/proguard/
The #runtime team is requesting that Xamarin.Android support
"auto-provisioning": the ability to track dependency *versions*, and
if there is a "version mismatch", *install* a dependency that
satisfies the minimum.
The primary use case is Mono itself: there is a desire to build
xamarin-android *itself* against mono/master, to see what breaks.
(Does the C# compiler break things? Does the JIT hang? etc.)
Extend the existing `@(RequiredProgram)` infrastructure to attempt to
support this:
* `%(RequiredProgram.MinimumVersion)`: The minimum program version
that is supported.
* `%(RequiredProgram.MaximumVersion)`: The maximum program version
that is supported.
* `%(RequiredProgram.CurrentVersionCommand)`: A command to execute to
obtain the current program version.
Defaults to `%(RequiredProgram.Identity) --version`.
* `%(RequiredProgram.MinimumUrl)`: URL to download "something" to use
to obtain a version of the program that satisfies
`%(MinimumVersion)`.
* `%(RequiredProgram.Install)`: Command to execute which installs the
minimum version of the program.
`%(RequiredProgram.CurrentVersionCommand)`,
`%(RequiredProgram.MinimumUrl)`, and `%(RequiredProgram.Install)` can
all be "overridden" for platform-specific values by prefixing the
value with `$(HostOSName)` and/or `$(HostOS)`. Thus, macOS-specific
values can be stored into e.g. `%(RequiredProgram.DarwinMinimumUrl)`
and `%(RequiredProgram.DarwinInstall)`.
Auto-Provisioning is controlled by the new `$(AutoProvision)`
property, which can be provided in `Configuration.Override.props`.
It defaults to `False`.
If the `$(AutoProvisionUsesSudo)` MSBuild property is True, then
`sudo` is used to invoke all install commands.
Context: https://github.com/xamarin/xamarin-android/pull/341
`remap-assembly-ref` is utility which changes assembly references.
For example, consider `mdoc.exe`:
$ monodis --assemblyref bin/Debug/lib/mandroid-internal/mdoc.exe
AssemblyRef Table
...
4: Version=0.10.0.0
Name=Mono.Cecil
Flags=0x00000000
Public Key:
...
By policy, we *don't* want to reference `Mono.Cecil.dll`, because in
times past all IDE add-ins would be loaded into the same process, and
if an assembly wasn't strong-named -- `Mono.Cecil.dll` isn't --
then all references to `Mono.Cecil.dll` would resolve to the same
file, even if used from different add-ins which expected different
Cecil versions, and things would break in horrific ways.
Lots of hair-pulling ensued, and our response was to "vendorize"
Cecil into [`Xamarin.Android.Cecil`][0], which is just Mono.Cecil
with a different assembly name.
However, we didn't want to have to rebuild everything from source
"just because" the assembly name changed, and `mdoc.exe` is such an
assembly. It's built as part of mono, references `Mono.Cecil.dll`,
and we need it to instead reference `Xamarin.Android.Cecil.dll`.
Enter `remap-assembly-ref.exe`:
$ mono bin/BuildDebug/remap-assembly-ref.exe
Usage: <input assembly filename> <output assembly filename> <source assembly ref> <target assembly>
`remap-assembly-ref.exe` will read `<input assembly filename>`, alter
all references to `<source assembly ref>` to the name and version
information contained in `<target assembly>`, and write the modified
assembly to `<output assembly filename>`:
$ mono bin/BuildDebug/remap-assembly-ref.exe \
bin/Debug/lib/mandroid-internal/mdoc.exe \
bin/Debug/lib/mandroid/mdoc.exe \
"Mono.Cecil" \
bin/Debug/lib/mandroid/Xamarin.Android.Cecil.dll
# Behold, altered assembly name reference!
$ monodis --assemblyref bin/Debug/lib/mandroid/mdoc.exe
...
4: Version=0.10.0.0
Name=Xamarin.Android.Cecil
Flags=0x00000001
Public Key:
0x00000000: 00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00
...
*Note*: `remap-assembly-ref.exe` is *brittle*. It does no validation
that the types and members referenced from `<source assembly ref>`
actually exist in `<target assembly>`, so it's entirely possible
that the resulting assembly *will not work*.
[0]: https://github.com/xamarin/java.interop/tree/master/src/Xamarin.Android.Cecil
Context: fbfd676c10
Stage 3 of the cunning plan is to (attempt to) use the mono bundle
introduced in commit fbfd676c.
This "simple" desire (ha!) re-raises the architectural project
dependency issue "solved" in fbfd676c, but first, a simple question:
What should download the mono bundle?
There are two plausible answers:
1. `make prepare` can (somehow) handle it.
2. MSBuild can (somehow) handle it.
Both are plausible. The problem with using `make` targets (1) is there
is increased potential for "duplication" -- duplication of the bundle
filename, downloading it, and extracting it. Plus, `make` isn't
"Windows friendly", in that GNU make isn't (normally) present with
Visual Studio. (`NMAKE` is, but the Makefiles in this project are not
compatible with `NMAKE`.)
Which brings us to MSBuild (2): can it handle the task?
To tackle that, we need to be able to have an MSBuild task project
which has *no dependencies*, so that it can download and extract the
mono bundle *before anything else runs*, as it may be downloading
contents which mean that other projects don't *need* to run.
The need for a "pre-bootstrap" task assembly -- called `xa-prep-tasks`
-- thus "undoes" *some* of the logic regarding `libzip-windows.mdproj`
and the `<Zip/>` task from fbfd676c: it isn't *possible* to rely on
`libzip` from a "pre-build" state, as `libzip` is one of the things in
the mono bundle, so now we need *two* "bootstrap" task assemblies:
one without a `libzip` dependency -- `xa-prep-tasks.dll` -- and one
*with* a `libzip` dependency --
`Xamarin.Android.Tools.BootstrapTasks.dll`
Move tasks which don't currently require `libzip` -- or won't in the
future, or laziness -- from `Xamarin.Android.Tools.BootstrapTasks.dll`
and move them into `xa-prep-tasks.dll`.
With that architectural compromise in place, add `xa-prep-tasks` as a
`@(ProjectReference)` to various projects to help ensure it's built
*first*, and rearchitect `bundle.mdproj` so that
`xa-prep-tasks.targets` and `bundle.targets` can use the same targets
to compute the bundle filename, now in
`build-tools/bundle/bundle-path.targets`.
Add a post-build step to `xa-prep-tasks.csproj` which downloads and
extracts the expected mono bundle.
One "problem" (feature?) is that the new `<SystemUnzip/>` task doesn't
report errors as errors when unzip'ing the file. This turns out to be
fine here because when downloading the mono bundle from Azure we don't
get a 404 *anyway* -- Azure instead returns an XML document containing
an error message (wat?!). We can thus ignore most error handling
entirely...though we're *also* ignoring any checking for invalid
downloads, which is something we should address in the future.
Update the varioous project files so that they won't attempt to
rebuild binaries that were present in the mono bundle.
Building Mono on Windows is a PITA, and commit 0c073f67 added support
to cross-compile Mono for Windows so that Mono could be built from
macOS or Linux.
The unstated goal of that work was to -- eventually -- build the Mono
runtimes *for Windows* on e.g. macOS, *bundle* those runtimes, and
then we could update the build process to *download* a pre-built
bundle of mono runtimes so that we can ~sanely build xamarin-android
on Windows without building *Mono* on Windows.
Part Two of this cunning plan involves creating the bundle of built
mono runtimes. This in turn got rather complicated because instead of
doing the *simple* thing of invoking **zip**(1), I instead wanted to
create and use a `<Zip/>` MSBuild task, as this would allow us to
specify in one place -- `mono-runtimes.targets` -- which files should
be bundled, instead of bundling e.g. everything in `bin/Debug`, which
would grab considerably more than just the mono bits.
Adding a `<Zip/>` task in turn involves determining which Zip
implementation to use, and what assembly to put it in; the obvious
answer is to use `libzip`/`libZipSharp.dll` and place it into
`Xamarin.Android.Tools.BootstrapTasks.dll` -- which is how we did it
-- but this answer was tricky, due to a circular dependency problem:
`libzip.mdproj` depends on `android-runtimes.mdproj` which depends on
`Xamarin.Android.Tools.BootstrapTasks.dll`. Consequently, adding
`libzip.mdproj` as a dependency to
`Xamarin.Android.Tools.BootstrapTasks.dll` *doesn't work*.
(Question: Why use libzip? Why not use System.IO.Compression?
Answer: Because I'd like to be able to update the
`<UnzipDirectoryChildren/>` task to use `libzip` instead of
"shelling out" to **zip**(1) *like an animal*.)
Step 1 is removing the circular dependency. `libzip.mdproj` depends on
`android-runtimes.mdproj` because of MXE: `android-runtimes.mdproj`
configures the MXE build environment to generate a Windows
`libzip.dll` native library, if needed.
The fix: introduce a new `libzip-windows.mdproj` project which
performs the MXE-based build. This allows `libzip.mdproj` to no longer
depend on `android-runtimes.mdproj`; `libzip-windows.mdproj` does.
This also involves "refactoring" `libzip.mdproj` so that the build
logic can be shared with `libzip-windows.mdproj`.
Additionally, improve `libzip.mdproj` so that the native libraries are
also set as a `@(Content)` build action. This allows e.g.
`libzip.3.0.dylib` to be properly copied into `bin/BuildDebug`, as
that's the `$(OutputPath)` of
`Xamarin.Android.Tools.BootstrapTasks.csproj`.
Step 2 is adding `libZipSharp.csproj` as a `@(ProjectReference)` to
`Xamarin.Android.Tools.BootstrapTasks.csproj`. This requires bumping
`Xamarin.Android.Tools.BootstrapTasks.csproj` to target .NET 4.5.1,
which allows us to add the new `<Zip/>` task, as well as a new
`<GitCommit/>` task (so we can determine the abbreviated commit hash
of the mono checkout, for encoding into the filename).
Step 3 is updating `mono-runtimes.targets` so that the Windows
cross-compilers are built into `bin/$(Configuration)/lib/mandroid`,
as this matches what is currently done internally.
Step 4 is adding a new `build-tools/bundle.mdproj` project with a
`CreateBundle` targtet, which will create the file:
$(OutputPath)\bundle-$(Configuration)-libzip=$(libzip-hash),llvm=$(llvm-hash),mono=$(mono-hash).zip`.
Uploading the `bundle-*.zip` files will be handled by Jenkins as a
post-build task.
Not yet done: *using* `bundle-*.zip` so that mono/etc. builds can be
skipped, *greatly* improving build times.
We're trying to get [`make jenkins`][0] working on Jenkins, and
[it's failing][1], as one might expect when a particular repo and
associated build system has never been run on Jenkins before:
Android.App/ApplicationTest.cs(9,7): error CS0246: The type or namespace name `NUnit' could not be found. Are you missing an assembly reference?
This error occurs while building
`src/Mono.Android/Test/Mono.Android-Tests.csproj`, and happens because
the `Xamarin.Android.NUnitLite.dll` assembly isn't referenced...
because it isn't *found*:
Microsoft.Common.targets: warning : Reference 'Xamarin.Android.NUnitLite' not resolved
This returns us to a long-standing issue which I thought was mentioned
in a commit somewhere, but I can't find at present:
MSBuild doesn't support updating the assembly resolution directories
*while MSBuild is running*.
For example, we build `Xamarin.Android.NUniteLite.dll` into
`bin/$(Configuration/lib/xbuild-frameworks/MonoAndroid/v1.0`,
but "normal Xamarin.Android referencing projects" don't use a
`@(ProjectReference)` to `Xamarin.Android.NUnitLite.csproj`, but
instead a `@(Reference)` to the assembly:
<!-- src/Mono.Android/Test/Mono.Android-Tests.csproj -->
<Reference Include="Xamarin.Android.NUnitLite" />
The *requirement* for a "proper" Xamarin.Android SDK install is that
`Xamarin.Android.NUnitLite.dll` can be found through a normal
`@(Reference)`. In order to satisfy this requirement, we need to tell
MSBuild where to find it, which can be with `xbuild` via the
`$MSBuildExtensionsPath` and `$XBUILD_FRAMEWORK_FOLDERS_PATH`
*environment variables*.
*Environment variables*.
MSBuild doesn't provide a way to update environment variables, short
of writing a new Task which calls
`Environment.SetEnvironmentVariable()`, and while [this works][2], it
doesn't *actually* work [^3].
The short of all this is that it isn't possible,
*within a single `xbuild` invocation*, to both build the
Xamarin.Android SDK "environment" *and use that environment* as
intended for "normal" apps.
The fix, as is often the case, is to bend with the wind. Instead of
requiring the impossible, move
`src/Mono.Android/Test/Mono.Android-Tests.csproj` into a *new*
`Xamarin.Android-Tests.sln` solution, *out of* `Xamarin.Android.sln`.
This allows building `Xamarin.Android.sln` without error in a pristine
environment -- that is, one that doesn't already have a system-wide
Xamarin.Android install -- and separately building the tests by using
`tools/scripts/xabuild`, which *can* export environment variables to
manipulate `xbuild` behavior so that things Just Work™.
Building `Mono.Android-Tests.csproj` and similar projects
(`HelloWorld.csproj`!) can be done by using the new `make all-tests`
make target.
[0]: a16673d3eb
[1]: https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-android/20/console
[2]: https://github.com/xamarin/xamarin-android/pull/147
[^3]: [PR #147][2] isn't viable because of [xbuild's `AsssemblyResolver`][4].
There's no way to clear/invalidate `target_framework_cache`.
The idea of PR #147 was to "hack things up" so that
`Xamarin.Android.NuniteLite.dll` would be properly resolved during
the build of `Mono.Android-Tests.csproj` *when building everything*.
The problem is *this can't work*, because `xbuild` has a
"target framework cache," with no way to invalidate it, and the
cache is populated the first time the target framework is used.
Due to build ordering issues, this first use is *before*
`Xamarin.Android.NunitLite.dll` was built, and thus it doesn't
exist in the cache. The result:
Task "ResolveAssemblyReference"
....
TargetFrameworkDirectories:
/Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v7.0
/Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v1.0
/Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v1.0/Facades/
...
Primary Reference System.Xml
Reference System.Xml resolved to /Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v1.0/System.Xml.dll. CopyLocal = False
...
Primary Reference Xamarin.Android.NUnitLite
Microsoft.Common.targets: warning : Reference 'Xamarin.Android.NUnitLite' not resolved
For searchpath {TargetFrameworkDirectory}
Considered target framework dir /Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v7.0, assembly named 'Xamarin.Android.NUnitLite' not found.
Considered target framework dir /Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v1.0, assembly named 'Xamarin.Android.NUnitLite' not found.
Considered target framework dir /Volumes/Seagate4TB/work/xamarin-android/bin/Debug/lib/xbuild-frameworks/MonoAndroid/v1.0/Facades/, assembly named 'Xamarin.Android.NUnitLite' not found.
...
Consequently, the `mcs` invocation is missing a
`/reference:path/to/Xamarin.Android.NUniteLite.dll`, and
compilation fails:
Android.App/ApplicationTest.cs(9,7): error CS0246: The type or namespace name `NUnit' could not be found. Are you missing an assembly reference?
...plus 29 others...
[4]: https://github.com/mono/mono/blob/dd8aadf/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssemblyResolver.cs#L131
context: https://github.com/xamarin/java.interop/issues/58
dependent on: https://github.com/atsushieno/java.interop/commit/5583d26
Copypasting the changes on Java.Interop
We have complicated module interdependency between this and xamarin-android,
so this set of changes is to fix the situation.
So far, here is the changeset summary:
- Xamarin.Android.NamingCustomAttributes.shproj is moved to xamarin-android
- there will be new Java.Interop.NamingCustomAttributes.shproj which is for
use in this repository. It will be shrinked in the later commits to have
only required bits in Java/Interop.
Now Xamarin.Android.sln has two NamingCustomAttributes.shproj: one for
imported and then slightly stripped version of the original shared project
in xamarin-android, and renamed and then significantly stripped version
in Java.Interop.
Build adjustment comes next.
The `make jenkins` [^0] target is for use by Continuous Integration
machines, to build *everything* [^1].
This is expected to take an eternity. Think *hours*.
$ time make jenkins
...
real 130m11.608s
user 97m22.220s
sys 18m20.522s
Of particular note is that the above "everything" includes
*Release configuration builds* of everything, which is something that
didn't actually work before. (Oops.)
Bump Java.Interop so that it supports building the Release
configuration, update Xamarin.Android.sln so that all required
projects are part of the Release configuration, and update
Xamarin.Android.Build.Tasks.csproj so that `JCW_ONLY_TYPE_NAMES` isn't
defined, as this was preventing compilation.
Fix **strip**(1) use: `mono-runtimes.targets` was trying to use
`strip -S` on macOS, but the value of `%(_MonoRuntime.Strip)` was
quoted, and thus attempted to execute `"strip -S" ...`, which failed.
Move the `-S` into a new `%(_MonoRuntime.StripFlags)` value.
Fixup `mono-runtimes.targets` and related files so that
`$(MonoSourceFullPath)` is used instead of a relative path. This helps
alleviate the "mental math" of determining the relative path to the
Mono checkout. Plus, the Mono checkout is supposed to be overridable,
e.g. commit d205cab2, and using `$(MonoSourceFullPath)` supports that.
Download and install `android.jar` for all supported API levels.
Fix the `Mono.Android.csproj` build so that `Mono.Android.dll` is
stored in a per-API-level intermediate directory. Otherwise, if e.g.
API-10 is built after API-23, the API-23 version will be installed,
but the JCW build will fail. Additionally, API-24 requires using
`javac -source 1.8 -target 1.8`, not 1.6.
Fix `Mono.Android/metadata` to use the correct `merge.SourceFile`
filename of `Profiles/api-24.xml.in`. Without that fix, API-24 won't
build because `NumericShaper.GetContextualShaper()` is emitted twice,
and the C# compiler doesn't like that.
Disable use of `-lz` when building for Windows. Windows doesn't
contain a `Z.DLL` to link against.
[^0]: https://en.wikipedia.org/wiki/Leeroy_Jenkins
[^1]: https://www.youtube.com/watch?v=hooKVstzbz0
Commit 4ec06ac9 *broke* generation of `.apk` files when migrating
from `Ionic.Zip.dll` to `System.IO.Compression` because this:
apk.AddFile (assembly.ItemSpec, "assemblies", compressionLevel: CompressionLevel.NoCompression);
doesn't work the way we thought/hoped it would.
Specifically, we require that assemblies be *stored*, uncompressed,
within the `.apk`, as we **mmap**(2) the `.apk` and hand off the
memory addresses of the loaded assemblies to mono for execution.
`CompressionLevel.NoCompression`, despite saying "no compression",
does *not* mean "store". It means "add the file 'normally', but tell
`DeflateStream` to use 'no compression'."
The result is that the zip entry doesn't say it's stored:
$ unzip -lv bin/Debug/*-Signed.apk | grep assemblies/
64512 Defl:N 64512 67% 06-16-16 10:38 6d65706b assemblies/Scratch.DebugRelease.dll
Which in turn means that the resulting `.apk` is *unusable*.
The fix? `System.IO.Compression` can't be used for this, and
`Ionic.Zip.dll` had other problems (why we tried to use
`System.IO.Compression` in the first place!), so instead we'll
use [libZip][0] and [LibZipSharp][1] to handle `.apk` files.
libzip is a well-maintained OSS library for manipulating zip files,
and LibZipSharp is a C# wrapper around libzip.
Add libzip and LibZipSharp to the build system so that they're
available on required paltforms, and update the `BuildApk` and
related tasks to use LibZipSharp instead of System.IO.Compression.
This allows assemblies to be properly stored in the `.apk`,
allowing apps to execute as intended.
$ unzip -lv bin/Debug/*-Signed.apk | grep assembl
64512 Stored 64512 0% 06-29-16 08:31 6d65706b assemblies/Scratch.DebugRelease.dll
[0]: http://www.nih.at/libzip/
[1]: https://github.com/grendello/LibZipSharp/
OpenTK sources are built using our own project file in order to leave
the upstream sources untouched. Only OpenTK 1.0 is provided in hope that
nobody is interested in 0.9 anymore :) For this reason compilation items
are placed in a separate project file. It also makes it easier to share
the "wrapper" project file for different versions of the library.
A new configuration property is added, $(OpenTKSourceDirectory), which,
surprisingly, points to the OpenTK submodule directory.
Fix the solution and project files so that `msbuild` may be used to
build the solution instead of requiring `xbuild`.
There were a few issues that `msbuild` didn't like:
1. MSBuild doesn't like the "extra" configuration mappings in
Xamarin.Android.sln.
2. MSBuild doesn't like the presence of `.dll` within `@(Reference)`
entries. `<Reference Include="System.dll" />` is Bad™, so
Don't Do That™.™.
3. Turning `$(AndroidSupportedAbis)` into an item group is...broken.
(1) and (2) are straightforward fixes. (3) requires some explanation.
`src/monodroid` needs to *only* build `libmonodroid.so` for the
non-"host" ABIs within `$(AndroidSupportedAbis)`. It needs this
restriction because non-selected ABIs may not be configured in
`$(AndroidNdkDirectory)`, and thus can't be built.
This *could* be done by following
`build-tools/mono-runtimes/mono-runtimes.projitems` and doing lots of
`Condition`s on `$(AndroidSupportedAbisForConditionalChecks)`:
<_MonoRuntime Include="armeabi-v7a" Condition="$(AndroidSupportedAbisForConditionalChecks.Contains(':armeabi-v7a:'))" />
...
However, that's kinda ugly when *all* we need is the ABI name, so
`monodroid.projitems` was "cute":
<PropertyGroup>
<_SupportedAbis>$(AndroidSupportedAbis.Replace(':', ';'))</_SupportedAbis>
</PropertyGroup>
<ItemGroup>
<_MonoRuntime Include="$(_SupportedAbis)" Exclude="@(HostOSName)" />
</ItemGroup>
<!-- @(_MonoRuntime) is `armeabi-v7a` by default -->
This works...on xbuild, but *not* `msbuild`. Doh!
(`msbuild` is "smart" and doesn't treat the `;` as an item separator,
so if `$(AndroidSupportedAbis)` is `host-Darwin;armeabi-v7a` then
MSBuild treats the `;` as part of the filename -- NOT a filename
separator -- and `@(_MonoRuntime)` contains *one* item with
an `%(Identity)` of `host-Darwin;armeabi-v7a`. On the one hand, this
is kinda awesome and answers the question "how can you have a filename
that contains `;`?", but on the other hand it broke my project!)
The only fix I could think of was to use `.Split(':')`:
<_MonoRuntime Include="$(AndroidSupportedAbis.Split(':'))" Exclude="@(HostOSName)" />
That provides desired behavior with `msbuild`, but `xbuild` doesn't
support it and appears to either *ignore* it, or treat it literally,
in that `@(_MonoRuntime)` would contain a *single* item with the
literal value `$(AndroidSupportedAbis.Split(':'))` (argh!).
Fortunately, there's a "cute" workaround: using `.Split()` within an
item's `Include` attribute doesn't work, but using `.Split()` within a
property group declaration *does* work:
<PropertyGroup>
<_SupportedAbis>$(AndroidSupportedAbis.Split(':'))</_SupportedAbis>
</PropertyGroup>
<ItemGroup>
<_MonoRuntime Include="$(_SupportedAbis)" Exclude="@(HostOSName)" />
</ItemGroup>
<!-- @(_MonoRuntime) is `armeabi-v7a` by default -->
This implies that a property value isn't limited to string values, but
(as here) can be string *arrays*, which is interesting.
~~~
All that aside, while exploring the proper fix for (3) (it took a
remarkably long time to run across it), I decided to reconsider the
property and item arrangement here.
The prior approach was to have a single `$(AndroidSupportedAbis)`
MSBuild property which controlled *both* Android target ABIs and host
ABIs. This worked...but wasn't entirely scalable (separate moving
parts need to be kept in sync). Additionally, we need to add AOT
cross-compiler support, which logically would be controlled by the
same/similar mechanism, so a value of "build everything" would start
to look insane:
msbuild /p:AndroidSupportedAbis=armeabi:armeabi-v7a:arm64-v8a:x86:x86_64:host-Darwin:host-Win64:cross-Darwin-arm:cross-Darwin-arm64:cross-Darwin-x86:cross-Darwin-x86_64:cross-Win64-arm:cross-Win64-arm64:cross-Win64-x86:cross-Win64-x86_64
And that's assuming I'm not missing anything, or that we don't add
MIPS support in the future, or...
Blech.
Furthermore, Xamarin.Android *already* uses
[`$(AndroidSupportedAbis)` in its build system][0], which means a
top-level override of `$(AndroidSupportedAbis)` would also impact all
projects which build `.apk` files, e.g.
`src/Mono.Android/Test/Mono.Android-Tests.csproj`, which might not be
desirable.
In short, I think we're overloading "Android supported ABIs," and it
should be split up into smaller, easier to rationalize, chunks.
Thus, leave `$(AndroidSupportedAbis)` to Xamarin.Android's tasks, and
replace it with *two* new properties:
* `$(AndroidSupportedHostJitAbis)`: The "host" ABIs to build.
* `$(AndroidSupportedTargetJitAbis)`: The "target" ABIs to build.
AOT support, when added, would use a new
`$(AndroidSupportedHostAotAbis)` property, thus keeping the set of
acceptable values small and more easily rationalizable.
Finally, "split up" these new Abis properties into corresponding Abi
item groups, to allow consistent and reusable "mapping" of ABI names
to filesystem locations, etc. The new `@(AndroidSupportedHostAotAbi)`
and `@(AndroidSupportedTargetJitAbi)` item groups are derived from
their corresponding values. (Note singular from plural in naming.)
[0]: https://developer.xamarin.com/guides/android/under_the_hood/build_process/#AndroidSupportedAbis
Proper execution of Xamarin.Android.Build.Tasks requires that
`$prefix/lib/mandroid/class-parse.exe` exist. Unfortunately, nothing
*created* that file, which means a default build didn't work. :-(
Bump to Java.Interop/b6431ac8, which adds support for
`Java.Interop/Configuration.Override.props`, update the `make prepare`
target to create `external/Java.Interop/Configuration.Override.props`,
and add Java.Interop's `class-parse` and `logcat-parse` to
`Xamarin.Android.sln`. This causes `class-parse` to (1) be built, and
(2) place its output into `bin/$(Configuration)/lib/mandroid`, as
required by `Xamarin.Android.Build.Tasks.dll`.
Fix monodroid SDK detection that depended on non-existent file.
We don't build DebugRuntime apk in this new source set anymore, so
do not look for the file in our SDK sanity checker.
Additional warning logs are added because, we should do that.
(Otherwise we will keep ignorant of the actual cause of the
problem forever.)
System.EnterpriseServices.dll is an empty shell. It contains no types.
It isn't *directly* used by *anything*.
The entire reason it exists is so that Visual Studio (2010?)-
generated Web Services would compile, as the generated project would
contain an assembly reference to System.EnterpriseServices.dll, even
though that assembly wasn't actually *used*.
I suspect that this thread is relevant:
http://lists.ximian.com/pipermail/monodroid/2010-October/001398.html
> I had the same problem, I fixed it by removing the reference to
> System.EnterpriseServices.dll. That assembly isn't in MonoDroid P5
> and it falls back to your GAC to find it which causes chaos later.
Import monodroid/tests/runtime from monodroid/9c5b3712.
Add a toplevel `make run-apk-tests` target to "full stack" tests, in
which a .apk is created, installed, and executed on an attached
Android device. `make run-apk-tests` requires that `adb` be in $PATH,
and uses GNU make(1) features, and...
Additionally, tools/scripts/xabuild *must* be used to execute the
`SignAndroidPackage` target, to ensure that the local/"in tree"
assemblies are used. There is no "within xbuild" facility to alter
where target framework assemblies are resolved from, i.e no MSBuild
properties currently control the resolution order, only environment
variables, and MSBuild can't *set* environment variables...
The $(ADB_TARGET) variable can be used to control on which target
Android device the tests will be installed and executed on:
# Install & run tests on *only* connected USB device
$ make run-apk-tests ADB_TARGET=-d
# Install & run tests on *only* connected emulator
$ make run-apk-tests ADB_TARGET=-e
# Install & run tests on specified device, listed via `adb devices`.
$ make run-apk-tests ADB_TARGET="-s 036533824381cfcb"
Sorry potential/future Windows developers. *Running* tests will
require manual testing or running on OS X or Linux for now...
Note: These tests DO NOT PASS. In fact, they *crash*:
$ make run-apk-tests
...
Target RunTests:
Executing: "$HOME/android-toolchain/sdk/platform-tools/adb" shell am instrument -w Mono.Android_Tests/xamarin.android.runtimetests.TestInstrumentation
INSTRUMENTATION_RESULT: shortMsg=Process crashed.
INSTRUMENTATION_CODE: 0
$ adb logcat
...
E mono : Unhandled Exception:
E mono : System.ObjectDisposedException: Cannot access a disposed object.
E mono : Object name: 'System.Net.Sockets.Socket'.
E mono : at System.Net.Sockets.Socket.ThrowIfDisposedAndClosed () <0xa93923f0 + 0x00054> in <filename unknown>:0
E mono : at System.Net.Sockets.Socket.AcceptAsync (System.Net.Sockets.SocketAsyncEventArgs e) <0x9b8f9680 + 0x0001b> in <filename unknown>:0
E mono : at System.Net.EndPointListener.Accept (System.Net.Sockets.Socket socket, System.Net.Sockets.SocketAsyncEventArgs e) <0x9b8f95d0 + 0x0003f> in <filename unknown>:0
E mono : at System.Net.EndPointListener.ProcessAccept (System.Net.Sockets.SocketAsyncEventArgs args) <0x9b8e0340 + 0x0007f> in <filename unknown>:0
E mono : at System.Net.EndPointListener.OnAccept (System.Object sender, System.Net.Sockets.SocketAsyncEventArgs e) <0x9b8e0310 + 0x00017> in <filename unknown>:0
E mono : at System.Net.Sockets.SocketAsyncEventArgs.OnCompleted (System.Net.Sockets.SocketAsyncEventArgs e) <0x9b8e02c8 + 0x0003b> in <filename unknown>:0
E mono : at System.Net.Sockets.SocketAsyncEventArgs.Complete () <0x9b8e02a0 + 0x0001f> in <filename unknown>:0
E mono : at System.Net.Sockets.Socket.<AcceptAsyncCallback>m__0 (System.IAsyncResult ares) <0x9b8dfd40 + 0x002af> in <filename unknown>:0
E mono : at System.Net.Sockets.SocketAsyncResult+<Complete>c__AnonStorey0.<>m__0 (System.Object _) <0xa892f720 + 0x0002b> in <filename unknown>:0
E mono : at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () <0xa938f6b8 + 0x0002f> in <filename unknown>:0
E mono : at System.Threading.ThreadPoolWorkQueue.Dispatch () <0xa938e358 + 0x001bb> in <filename unknown>:0
E mono : at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () <0xa938e1a8 + 0x00007> in <filename unknown>:0
Looks like a Socket and/or ThreadPool bug in mono.
This commit adds basic support for the MSBuild Unit Tests. The goal
of these tests is to ensure that the build of an android applicaition
works consistently on all the supported platforms.
The basic layout of a Unit test is as follows
[Test]
public void BuildReleaseApplication ()
{
var proj = new XamarinAndroidApplicationProject () {
IsRelease = true,
};
using (var b = CreateApkBuilder (Path.Combine ("temp", TestContext.CurrentContext.Test.Name))) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
}
}
It is a standard Unit test. First we create a XamarinAndroidApplicatonProject
and set its properties. You can use the proj to add new source files,
references, assets, resources and nuget packages. By default you will get
a standard "HelloWorld" android application.
Once you have the project you will need to create either a Apk or Dll builder
via the helper methods
CreateApkBuilder
CreateDllBuilder
CreateApkBuilder will create an apk and by default will execute the
`SignAndroidPackage` build target. the CreateDllBuilder will produce
a dll. The source files are created in a temp directory relative to
the build output of the unit tests. By default this is
src/Xamarin.Android.Build.Tasks/UnitTests/Xamarin.Android.Build.Tests/bin/$(Configuraiton)/temp
Once you have a builder you can then call Build passing in the project. There are
also methods available for Clean and Save. Running any of these will cause
the Unit test to shell out to xbuild/msbuild and attempt to build the project.
Test results are written to a build.log file.
If a unit test passes then the test directory is removed. If a test fails
the directory and the build.log will be left in place for investigation.
- Xamarin.Android.sln: Disable Java.Interop build from here. It is built
from _BuildJavaInterop target
- Mono.Android.csproj: Add a ProjectReference to mono-runtimes project,
but don't *use* the reference. We just need it be built before
Mono.Android, so that we can resolve against the built mscorlib and
friends here.
- Mono.Android.targets: Mono.Android build was failing because it could
not find the referenced Java.Interop.dll in ResolveAssemblyReferences.
This was because _BuildJavaInterop was set to build
`BeforeTargets=ResolveReferences`. But the *real* resolution of
references is done in targets defined in `ResolveReferencesDependsOn`.
And that runs *before* the `BeforeTargets`! Which means that
_BuildJavaInterop was running too late!
Solution: `BeforeTargets=BeforeResolveReferences` - JonP !
Added Xamarin.Android.Build.Tasks and its supporting Libraries.
Code has also been updated to make uses of the new libraries in
the Java.Interop repository
Note : Xamarin.Android.Build.Tasks has a compile time code
generation step which generates Profile.g.cs. This file contains
the list of SharedRuntimeAssemblies. If building manually you will
need to make sure that Xamarin.Android.Tools.BootstrapTasks is
built first.
This is required because of a bug in xbuild. It tries to look for the
Java.Interop projects which are referenced from the other projects, in
the sln itself, because it needs to figure out a build order for *all*
the projects. It should probably allow projects not being present in sln
and build them via the reference.
`api-merge` is a tool to support emitting *forward compatible*
binding assemblies.
Way back in the dark days before Mono for Android 4.2, a binding
assembly was only backward compatible: if Mono.Android.dll was
targeting API-8, the resulting app could run on API-8 *and later*.
The opposite was *not* true: you *might not* be able to use
Mono.Android.dll for API-10 and run it on an API-8 device.
This is *forward compatibility*, the ability for "a system" to accept
"input" intended for a later version [0].
*Java* apps were forward compatible. Mono for Android prior to 4.2
did *not* generate forward compatible apps.
The reason why was that a binding assembly directly bound what was in
the specified API level [1], so if a member "moved" in a
*backward compatible* manner -- moving
android.view.MotionEvent.getDeviceId() to the newly introduced
android.view.InputEvent base class -- it would not be
*forward compatible* for the binding assemblies we emitted, as the
e.g. API-10 binding assembly would attempt to resolve the type
android.view.InputEvent, which didn't exist in API-8, and would result
in an exception.
`api-merge` was the fix: instead of a Mono.Android.dll targeting a
*specific* API level, it would instead take the API descriptions for
the target API level *and all prior API levels*. It would thus have
enough information to track member "movements", and with a bit of
Java.Interop/tools/generator "magic" forward compatibility was
provided starting in Mono for Android 4.2 [2].
[0]: https://en.wikipedia.org/wiki/Forward_compatibility
[1]: http://lists.ximian.com/pipermail/monodroid/2011-November/007350.html
[2]: https://developer.xamarin.com/releases/android/mono_for_android_4/mono_for_android_4.2/#Improved_API_level_support
(*Now* things are starting to come together...)
One of the important infrastructural pieces of Xamarin.Android is
Mono.Android.dll, a "binding" for the Android SDK `android.jar` file.
Bindings are generated by using the `generator` tool from
Java.Interop, along with tools/jnienv-gen, and various other programs.
This in turn requires adding a git submodule for Java.Interop.
Stitch all these pieces together so that we can take an
*API description* (stored in Profiles\api-*.xml.in) and generate a
binding for that API description.
The generated files are located in
obj\$(Configuration)\android-$(ApiLevel), and the resulting
Mono.Android.dll is copied to
$(topdir)\bin\$(Configuration)\xbuild-frameworks\MonoAndroid\$(XAFrameworkVersion).
One SNAFU: currently, Mono.Android.csproj conditinally <Import/>s a
Mono.Android.projitems file generated by Java.Interop's generator
tool, which contains a list of all the generated files.
When the project is first loaded, Mono.Android.projitems will not
exist, so on that initial build, source code will be generated but
xbuild won't re-read Mono.Android.projitems (once it exists). This
will result in a failing build.
Simply rebuild the project to get a valid build, or use the `make all`
Makefile target.
Add a new build-tools/mono-runtimes project to build the required Mono
runtimes for Xamarin.Android app execution on Android devices.
Xamarin.Android 6.0 ("cycle 6") includes Mono runtimes compiled for
five architectures:
* armeabi (armv5)
* armeabi-v7a (armv7)
* arm64-v8a (aarch64)
* x86
* x86_64
An additional "host" architecture is also built to build the
"monodroid" profile assemblies such as mscorlib.dll.
In the interest of expediency and not requiring that *six* different
runtimes be built -- which can greatly increase build times and
storage requirements -- the initial xamarin-android open-source build
will only build the armeabi-v7a (armv7) and "host" architectures.
Additionally, the <UnzipDirectoryChildren/> task needed to be altered
to use unzip(1) on OS X instead of using
System.IO.Compression.ZipFile, because the ZipFile implementation
included with Mono 4.4 doesn't respect file attributes such as the +x,
causing all files to be extracted as 644 (rw-r--r--), resulting
in an NDK toolchain which wouldn't work. To resolve this issue,
execution on OS X and Linux now uses unzip(1).
~~ Adding new architectures ~~
To build Mono for a new architecture, edit
build-tools/mono-runtimes/mono-runtimes.projitems and add a new
@(_MonoRuntime) entry for the desired architecture.
TODO: Figure out if there's a reasonable way to "opt-in" or "opt-out"
of CPU architectures so that it isn't an "all or nothing" prospect.
This might be a suspect idea, but lets see if we can make this work.
[The Joel Test: 12 Steps to Better Code][0] outlines 12 steps
to better code. The first two steps are:
1. Do you use source control?
2. Can you make a build in one step?
github is being used for source control, so (1) is handled, but how
simple can we make (2)? How easy can we make it to build
Xamarin.Android upon a fresh checkout?
The ideal to strive for is simple:
Load Xamarin.Android.sln into your IDE and Build the project.
I *know* we're not going to be able to do this, if only because we're
going to be using git submodules, which will require a separate
`git submodule init` invocation [1].
Knowing we can't reach that level of simplicitly doesn't mean we
shouldn't *try* to reach it for all other parts of the build system.
Which brings us to the Android NDK and SDK. The Android NDK will be
required in order to build native code, such as libmonodroid.so, while
the Android SDK will be required in order to compile
Java Callable Wrappers (née Android Callable Wrappers [2]) and
eventual samples and unit tests.
There are three ways we can deal with the Android NDK and SDK:
1. Complicate the "build" process by requiring that developers go to
the Android SDK Download Page [3], download and install
"somewhere" the required bits, and then configure the
Xamarin.Android build to use these bits.
2. Complicate the "build" process by requiring that developers run
the Xamarin Unified Installer [4], let it install everything
required, then configure the Xamarin.Android build to use those
bits.
3. Painstakingly determine which files are actually required, then
automatically download and extract those files into a "well-known"
location known by the Xamarin.Android build process.
(1) and (2) can be infuriating. Let's give (3) a try. :-)
Add a Xamarin.Android.Tools.BootstrapTasks project which contains
MSBuild tasks to facilitate downloading the Android SDK and NDK files.
Add an android-toolchain project which uses
Xamarin.Android.Tools.BootstrapTasks to download a painstakingly
determined set of files and install them "somewhere".
Unfortunately [5] the "somewhere" to download and install these files
needs to be in a known absolute path, so I've arbitrary decided to
download the files into $(HOME)\android-archives and install them into
$(HOME)\android-toolchain. On windows, this is
%HOMEDRIVE%%HOMEPATH%\android-archives and
%HOMEDRIVE%%HOMEPATH%\android-toolchain.
These locations may be modified by creating a
Configuration.Override.props file; see README.md for details.
TL;DR: This setup is able to magically download the Android NDK and
SDK files and install them for later use in a reasonably overridable
location, all within MSBuild.
[0]: http://www.joelonsoftware.com/articles/fog0000000043.html
[1]: Though maybe there's some MSBuild-fu we can use to address that.
[2]: https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/android_callable_wrappers/
[3]: http://developer.android.com/sdk/index.html
[4]: https://www.xamarin.com/download
[5]: Because I couldn't find a reliable way to use $(SolutionDir) when
only building a project, and relative paths would require an
in-tree installation location, which might not work.