dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.89k stars 523 forks source link

Pre-build event should run before checking for AndroidManifest.xml (XA1018) #9021

Open metal450 opened 1 week ago

metal450 commented 1 week ago

Android framework version

Other

Affected platform version

VS 2022 / net7.0-android

Description

In my Xamarin app, I have a pre-build event that runs a script which generates AssemblyInfo.cs (Windows), Info.plist (iOS) and AndroidManifest.xml (Android) from a template, filling in some version info (i.e. git commit hash, etc). The repo includes the template file, i.e. AndroidManifest.template.xml; AndroidManifest.xml itself is .gitignored, since its contents change with each build.

On Windows & iOS, this works great. However on Android, it seems to check for the existence of AndroidManifest.xml before running the pre-build event, saying:

1>C:\Program Files\dotnet\packs\Microsoft.Android.Sdk.Windows\33.0.95\tools\Xamarin.Android.Common.targets(574,3): error XA1018: Specified AndroidManifest file does not exist: D:\Work\USRadar\_Solution_\RadarMP\RadarMP.Android\AndroidManifest.xml.

Since it checks for this before pre-build, the script can't run & generate AndroidManifest.xml from the template. I therefore have to manually create a "dummy" AndroidManifest.xml every time I checkout the repo, just so the first-time build will run the pre-build event & generate the real AndroidManifest.xml. All builds after that work.

How can I get the Android project to run the pre-build event, before checking for that file, similar to Windows & iOS?

Steps to Reproduce

Please see description

Did you find any workaround?

No response

Relevant log output

No response

dellis1972 commented 1 week ago

The reason this check is done is because we need to get the packageName from the AndroidManifest.xml file before we do a bunch of stuff as it effects things like the final package output, the native code we build and a ton of other things.

You might want to try to put your target into the BuildDependsOn chain.

<PropertyGroup>
<BuildDependsOn>
    YourTargetNameHere;
    $(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>

Depending on when that gets evaluated it should let you generate the file earlier.

We currently do not have a public "pre android build stuff" event which you can hook into. They are all private (and subject to change).

metal450 commented 1 week ago

The reason this check is done is because we need to get the packageName from the AndroidManifest.xml file before we do a bunch of stuff as it effects things like the final package output, the native code we build and a ton of other things.

Sure, the need to check for it makes sense - it's just the timing. Why does it need to check for the packageName earlier than the preBuild event - shouldn't it do the preBuild event first, then do that after? (the pre-build event is described as "commands that run before the build starts").

You might want to try to put your target into the BuildDependsOn chain.

So my script command was entered in Project properties -> Build -> Events -> Pre-build event. Looking at the project file, it ends up in the <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> element. So if I'm understanding correctly, I tried both:

<PropertyGroup>
<BuildDependsOn>
    PreBuild;
    $(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>

and

<PropertyGroup>
<BuildDependsOn>
    PreBuildEvent;
    $(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>

but it still failed. Did you mean something different?

metal450 commented 3 days ago

Also tried sticking it in

<Target Name="CustomPreBuild" BeforeTargets="BeforeBuild">

as apparently BeforeBuild is even earlier than PreBuildEvent. No dice. Also tried BeforeTarget=Initialize & BeforeTarget=InitializeBuildStatus.

Still not following why this needs to be done even before prebuild, based on your description above.

dellis1972 commented 2 days ago

So I'm trying to find a work around for you because in order to support this we would need to change the order of our entire build system . We hook into the BuildDependsOn event as well.

<PropertyGroup>
<BuildDependsOn>
    InsertATonOfAndroidSpecificStuffHere;
    AndroidEventMoreStuff;
    $(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>

The problem here is the PreBuild event is getting included by the $(BuildDependsOn) bit of xml, so it happens after all of our stuff. See https://github.com/dotnet/android/blob/ddb215b762d94801b61b72acc879b2eefe3e9e46/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets#L29.

I'll have to have a think about how to work around this.

metal450 commented 2 days ago

I figured something out that works:

<Project Sdk="Microsoft.NET.Sdk" InitialTargets="MakeAndroidManifestFromTemplate" >

And then:

<Target Name="MakeAndroidManifestFromTemplate">
    <Exec Command="..." />
</Target>

It gets created as soon as you launch Visual Studio (rather than when you build).