Skip to content

Commit

Permalink
[One .NET] prefer /manifest/@android:versionCode (#6383)
Browse files Browse the repository at this point in the history
Fixes: dotnet/sdk#21614
Fixes: #6384

Context: 84d0d9b
Context: https://github.com/xamarin/xamarin-android/blob/650f289301583003aa4acb075923bcc5627b92dc/Documentation/guides/OneDotNetSingleProject.md

In .NET 6, if [`/manifest/@android:versionCode`][0] is within
[`AndroidManifest.xml`][1], it won't be used, as it is always
overwritten by a default value:

	<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>

The other new MSBuild properties don't appear to suffer from this
problem; values within `Properties\AndroidManifest.xml` always take
precedence over MSBuild properties, [as documented in the spec][2]:

  * `$(ApplicationDisplayVersion)`
  * `$(ApplicationId)`
  * `$(ApplicationTitle)`

This is because we don't default these properties to a value when
they are blank.

Fix this scenario by updating the `<GenerateJavaStubs/>` task to only
set `ManifestDocument.VersionCode` when
`ManifestDocument.HasVersionCode` is false and the version code is set.

I added a new test for this scenario.

[0]: https://developer.android.com/guide/topics/manifest/manifest-element#vcode
[1]: https://developer.android.com/guide/topics/manifest/manifest-intro
[2]: https://github.com/xamarin/xamarin-android/blob/650f289301583003aa4acb075923bcc5627b92dc/Documentation/guides/OneDotNetSingleProject.md
  • Loading branch information
jonathanpeppers committed Oct 12, 2021
1 parent 650f289 commit 556b043
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
7 changes: 4 additions & 3 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Expand Up @@ -282,9 +282,10 @@ void Run (DirectoryAssemblyResolver res)
NeedsInternet = NeedsInternet,
InstantRunEnabled = InstantRunEnabled
};
// Only set manifest.VersionCode if it is not blank.
// We do not want to override the existing manifest in this case.
if (!string.IsNullOrEmpty (VersionCode)) {
// Only set manifest.VersionCode if there is no existing value in AndroidManifest.xml.
if (manifest.HasVersionCode) {
Log.LogDebugMessage ($"Using existing versionCode in: {ManifestTemplate}");
} else if (!string.IsNullOrEmpty (VersionCode)) {
manifest.VersionCode = VersionCode;
}
manifest.Assemblies.AddRange (userAssemblies.Values);
Expand Down
Expand Up @@ -108,5 +108,39 @@ public void AndroidManifestProperties (string versionName, string versionCode, s
Assert.AreEqual (versionNumber, assemblyFileVersion.ConstructorArguments [0].Value);
}
}

[Test]
public void AndroidManifestValuesWin ()
{
var packageName = "com.xamarin.singleproject";
var applicationLabel = "My Sweet App";
var versionName = "99.0";
var versionCode = "99";
var proj = new XamarinAndroidApplicationProject ();
proj.AndroidManifest = proj.AndroidManifest
.Replace ("package=\"${PACKAGENAME}\"", $"package=\"{packageName}\"")
.Replace ("android:label=\"${PROJECT_NAME}\"", $"android:label=\"{applicationLabel}\"")
.Replace ("android:versionName=\"1.0\"", $"android:versionName=\"{versionName}\"")
.Replace ("android:versionCode=\"1\"", $"android:versionCode=\"{versionCode}\"");
if (!Builder.UseDotNet) {
proj.SetProperty ("GenerateApplicationManifest", "true");
}
proj.SetProperty ("ApplicationId", "com.i.should.not.be.used");
proj.SetProperty ("ApplicationTitle", "I should not be used");
proj.SetProperty ("ApplicationVersion", "21");
proj.SetProperty ("ApplicationDisplayVersion", "1.1.1.1");

using var b = CreateApkBuilder ();
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
var manifest = b.Output.GetIntermediaryPath ("android/AndroidManifest.xml");
FileAssert.Exists (manifest);

using var stream = File.OpenRead (manifest);
var doc = XDocument.Load (stream);
Assert.AreEqual (packageName, doc.Root.Attribute ("package")?.Value);
Assert.AreEqual (versionName, doc.Root.Attribute (AndroidAppManifest.AndroidXNamespace + "versionName")?.Value);
Assert.AreEqual (versionCode, doc.Root.Attribute (AndroidAppManifest.AndroidXNamespace + "versionCode")?.Value);
Assert.AreEqual (applicationLabel, doc.Root.Element ("application").Attribute (AndroidAppManifest.AndroidXNamespace + "label")?.Value);
}
}
}
11 changes: 7 additions & 4 deletions src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs
Expand Up @@ -36,6 +36,7 @@ internal class ManifestDocument

static XNamespace androidNs = AndroidXmlNamespace;
static XNamespace androidToolsNs = AndroidXmlToolsNamespace;
static readonly XName versionCodeAttributeName = androidNs + "versionCode";

XDocument doc;

Expand Down Expand Up @@ -101,7 +102,7 @@ internal class ManifestDocument
/// </summary>
public string VersionCode {
get {
XAttribute attr = doc.Root.Attribute (androidNs + "versionCode");
XAttribute attr = doc.Root.Attribute (versionCodeAttributeName);
if (attr != null) {
string code = attr.Value;
if (!string.IsNullOrEmpty (code))
Expand All @@ -110,10 +111,12 @@ internal class ManifestDocument
return "1";
}
set {
doc.Root.SetAttributeValue (androidNs + "versionCode", versionCode = value);
doc.Root.SetAttributeValue (versionCodeAttributeName, versionCode = value);
}
}

public bool HasVersionCode => doc.Root.Attribute (versionCodeAttributeName) != null;

public string GetMinimumSdk () {
int defaultMinSdkVersion = MonoAndroidHelper.SupportedVersions.MinStableVersion.ApiLevel;
var minAttr = doc.Root.Element ("uses-sdk")?.Attribute (androidNs + "minSdkVersion");
Expand Down Expand Up @@ -270,8 +273,8 @@ public IList<string> Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li

manifest.SetAttributeValue (XNamespace.Xmlns + "android", "http://schemas.android.com/apk/res/android");

if (manifest.Attribute (androidNs + "versionCode") == null) {
manifest.SetAttributeValue (androidNs + "versionCode",
if (manifest.Attribute (versionCodeAttributeName) == null) {
manifest.SetAttributeValue (versionCodeAttributeName,
string.IsNullOrEmpty (versionCode) ? "1" : versionCode);
}
if (manifest.Attribute (androidNs + "versionName") == null) {
Expand Down

0 comments on commit 556b043

Please sign in to comment.