From 5ee304c07472b56f9785e8921af75c70dcfa8db4 Mon Sep 17 00:00:00 2001
From: Jonathan Pryor <jonpryor@vt.edu>
Date: Wed, 4 Mar 2020 10:28:03 -0500
Subject: [PATCH] [build] Use `$(AndroidPlatformId)` when appropriate

Context: 485e39b173c5c9516b91bd1e850781124a6fb4bd
Context: eb08bb464567f70d1977d0a349d38e336842d867
...and doubtless others...

Three MSBuild properties control the `android.jar` which is bound and
the `$(TargetFrameworkVersion)` of `Mono.Android.dll`:

  * `$(AndroidApiLevel)`: The API level that is bound.  Must be an int.
  * `$(AndroidFrameworkVersion)`: The `$(TargetFrameworkVersion)` of
    the generated `Mono.Android.dll`.  Must be *mostly* parseable by
    `System.Version` except with a leading `v`, e.g. `v10.0`.
  * `$(AndroidPlatformId)`: The "ID" of the API level.

*Most* of the time, `$(AndroidApiLevel)` and `$(AndroidPlatformId)`
will be *identical*: for API-29, they're both `29`.

Where they differ is for new *preview* API levels, such as API-R:
`$(AndroidApiLevel)` will be 30, but `$(AndroidPlatformId)` is `R`.
The distinction is important because various filesystem paths within
the Android SDK use the "id" and *not* the API level when they differ,
e.g. the API-R `android.jar` is installed into:

	$(AndroidSdkDirectory)/platforms/android-R/android.jar

We thus need to be *careful* when distinguishing between
`$(AndroidApiLevel)` and `$(AndroidPlatformId)`, using the former when
an integer is *required*, and using the latter whenever it refers to
filesystem paths.

Unfortunately, we *haven't* been careful, because these values really
only differ for ~4 months out of the year, and for only one
`$(TargetFrameworkVersion)` version.

Start bringing some sanity...and finding bugs while we do so:

`api-xml-adjuster.targets` should use `%(AndroidApiInfo.Id)` and *not*
`%(AndroidApiLevel.Level)`, as it references filesystem locations.
Consequently, `src/Mono.Android/Profiles/api-30.params.txt` must be
renamed to `src/Mono.Android/Profiles/api-R.params.txt` so that it
correctly embeds the `$(AndroidPlatformId)` value.

`Mono.Android.targets` should likewise use `$(AndroidPlatformId)` and
not `$(AndroidApiLevel)` when using filesystem paths from the SDK.

For good measure, `Mono.Android.csproj` now overrides
`$(IntermediateOutputPath)` to contain `$(AndroidPlatformId)`, because
why not (MOAR CONSISTENCY!).

These changes, unfortunately, introduce breakage, which will need to
be addressed:

*Because* API-R was installed into
`$(AndroidSdkDirectory)/platforms/android-R`, `api-versions.xml`
*was not previously used* because `Mono.Android.targets` was using
`$(AndroidApiLevel)`, and `platforms/android-30/data/api-version.xml`
does not yet exist.  (It will come June!  But not now.)  As it didn't
exist, it hit the fallback path and used
`platform-tools/api/api-versions.xml` (4cd20605c).  You would *think*
this wouldn't be a problem, but the API-R `api-versions.xml` is
*missing* members relative to platform-tools, resulting in members
*missing* `RegisterAttribute.ApiSince` values, which
`Microsoft.DotNet.ApiCompat.exe` reports, e.g.:

	CannotChangeAttribute : Attribute 'Android.Runtime.RegisterAttribute' on 'Java.Lang.StringBuilder.TrimToSize()' changed from '[RegisterAttribute("trimToSize", "()V", "", ApiSince=9)]' in the contract to '[RegisterAttribute("trimToSize", "()V", "")]' in the implementation

xamarin/java.interop@568d24ac added support to allow
`generator --apiversions` to be specified multiple times.  Take
advantage of this new support to pass in the `api-versions.xml` files
from *both* `platforms/android-R` *and* `platform-tools/api` when
binding API levels > API-29.  (Attempting to do this for *all*
versions which have both resulted in bizarre API compat errors, as
the `RegisterAttribute.ApiSince` value was *cleared*. ?!)
This works around the deficiency in API-R's `api-versions.xml` and
allows us to retain correct `RegisterAttribute.ApiSince` values.

Aside: to manually build the API-R binding, use:

	msbuild /p:AndroidPlatformId=R /p:AndroidApiLevel=30 /p:AndroidFrameworkVersion=v10.0.99 src/Mono.Android/Mono.Android.csproj /v:diag > b.txt
---
 .../api-xml-adjuster/api-xml-adjuster.targets     | 10 +++++-----
 src/Mono.Android/Mono.Android.csproj              |  2 +-
 src/Mono.Android/Mono.Android.targets             | 15 ++++++++++++---
 .../{api-30.params.txt => api-R.params.txt}       |  0
 4 files changed, 18 insertions(+), 9 deletions(-)
 rename src/Mono.Android/Profiles/{api-30.params.txt => api-R.params.txt} (100%)

diff --git a/build-tools/api-xml-adjuster/api-xml-adjuster.targets b/build-tools/api-xml-adjuster/api-xml-adjuster.targets
index 8432970bf27..4b24b03288a 100644
--- a/build-tools/api-xml-adjuster/api-xml-adjuster.targets
+++ b/build-tools/api-xml-adjuster/api-xml-adjuster.targets
@@ -10,12 +10,12 @@
   <Target Name="_DefineApiFiles">
     <ItemGroup>
       <_Api
-          Condition=" Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(AndroidApiInfo.Level).params.txt') "
+          Condition=" Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(AndroidApiInfo.Id).params.txt') "
           Include="@(AndroidApiInfo)">
       </_Api>
     </ItemGroup>
     <CreateItem Include="@(_Api)"
-        AdditionalMetadata="ParameterDescription=$(_TopDir)\src\Mono.Android\Profiles\api-%(_Api.Level).params.txt;ClassParseXml=$(_OutputPath)api\api-%(_Api.Level).xml.class-parse;ApiAdjustedXml=$(_OutputPath)api\api-%(_Api.Level).xml.in">
+        AdditionalMetadata="ParameterDescription=$(_TopDir)\src\Mono.Android\Profiles\api-%(_Api.Id).params.txt;ClassParseXml=$(_OutputPath)api\api-%(_Api.Id).xml.class-parse;ApiAdjustedXml=$(_OutputPath)api\api-%(_Api.Id).xml.in">
       <Output TaskParameter="Include" ItemName="ApiFileDefinition"/>
     </CreateItem>
   </Target>
@@ -30,8 +30,8 @@
     </PropertyGroup>
     <MakeDir Directories="$(_OutputPath)api" />
     <Exec
-        Condition="Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(ApiFileDefinition.Level).params.txt')"
-        Command="$(ManagedRuntime) $(ManagedRuntimeArgs) $(ClassParse) $(AndroidSdkDirectory)\platforms\android-%(ApiFileDefinition.Id)\android.jar -platform=%(ApiFileDefinition.Level) -parameter-names=&quot;%(ApiFileDefinition.ParameterDescription)&quot; -o=&quot;%(ApiFileDefinition.ClassParseXml)&quot;"
+        Condition="Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(ApiFileDefinition.Id).params.txt')"
+        Command="$(ManagedRuntime) $(ManagedRuntimeArgs) $(ClassParse) $(AndroidSdkDirectory)\platforms\android-%(ApiFileDefinition.Id)\android.jar -platform=%(ApiFileDefinition.Id) -parameter-names=&quot;%(ApiFileDefinition.ParameterDescription)&quot; -o=&quot;%(ApiFileDefinition.ClassParseXml)&quot;"
     />
   </Target>
   <Target Name="_AdjustApiXml"
@@ -43,7 +43,7 @@
       <ApiXmlAdjuster>$(_TopDir)\bin\Build$(Configuration)\api-xml-adjuster.exe</ApiXmlAdjuster>
     </PropertyGroup>
     <Exec
-        Condition="Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(ApiFileDefinition.Level).params.txt')"
+        Condition="Exists('$(_TopDir)\src\Mono.Android\Profiles\api-%(ApiFileDefinition.Id).params.txt')"
         Command="$(ManagedRuntime) $(ManagedRuntimeArgs) $(ApiXmlAdjuster) %(ApiFileDefinition.ClassParseXml) %(ApiFileDefinition.ApiAdjustedXml)"
     />
   </Target>
diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj
index a6dbe692964..f6851d03598 100644
--- a/src/Mono.Android/Mono.Android.csproj
+++ b/src/Mono.Android/Mono.Android.csproj
@@ -97,7 +97,7 @@
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>
     <ImplicitlyExpandDesignTimeFacades>False</ImplicitlyExpandDesignTimeFacades>
-    <IntermediateOutputPath>$(IntermediateOutputPath)android-$(AndroidApiLevel)\</IntermediateOutputPath>
+    <IntermediateOutputPath>$(IntermediateOutputPath)android-$(AndroidPlatformId)\</IntermediateOutputPath>
   </PropertyGroup>
   <Import Project="Mono.Android.targets" />
   <PropertyGroup>
diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets
index 373e64eac0f..51dbd6718bc 100644
--- a/src/Mono.Android/Mono.Android.targets
+++ b/src/Mono.Android/Mono.Android.targets
@@ -79,9 +79,18 @@
       Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems">
     <MakeDir Directories="$(IntermediateOutputPath)mcw" />
     <PropertyGroup>
-      <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidApiLevel)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidApiLevel)\data\api-versions.xml"</_ApiVersions>
-      <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions>
+      <_PlatformIdVersions>$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml</_PlatformIdVersions>
     </PropertyGroup>
+    <ItemGroup>
+      <_ApiVersion
+          Condition=" '$(AndroidApiLevel)' > 29 Or !Exists ('$(_PlatformIdVersions)')"
+          Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"
+      />
+      <_ApiVersion
+          Condition="Exists('$(_PlatformIdVersions)')"
+          Include="$(_PlatformIdVersions)"
+      />
+    </ItemGroup>
     <PropertyGroup>
       <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator>
       <_GenFlags>--public --product-version=7</_GenFlags>
@@ -91,7 +100,7 @@
       <_Fixup>--fixup=metadata</_Fixup>
       <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1>
       <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2>
-      <_Versions>--apiversions=$(_ApiVersions)</_Versions>
+      <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions>
       <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations>
       <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly>
       <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap>
diff --git a/src/Mono.Android/Profiles/api-30.params.txt b/src/Mono.Android/Profiles/api-R.params.txt
similarity index 100%
rename from src/Mono.Android/Profiles/api-30.params.txt
rename to src/Mono.Android/Profiles/api-R.params.txt
