Android framework version
Likely affects any current SDK-based Android app path using the current targets. I noticed this while inspecting the current main branch and would expect it to matter for net8.0-android, net9.0-android, and net10.0-android.
Affected platform version
Current main as of commit 0d3996623b902210d15d6327e25ddf14d30d398b.
Description
There seems to be an incremental build bug around manifest overlays generated via the public BeforeGenerateAndroidManifest extension point.
The scenario is:
- A custom target is added to
$(BeforeGenerateAndroidManifest).
- That target generates or updates an overlay file and adds it to
@(AndroidManifestOverlay).
_ManifestMerger later consumes @(AndroidManifestOverlay) via ManifestOverlayFiles="@(AndroidManifestOverlay)".
This looks like a supported scenario:
build-properties.md documents BeforeGenerateAndroidManifest as running directly before _GenerateJavaStubs.
- In
Microsoft.Android.Sdk.TypeMap.LlvmIr.targets, _GenerateJavaStubs is wired as:
<Target Name="_GenerateJavaStubs"
DependsOnTargets="$(_GenerateJavaStubsDependsOnTargets);$(BeforeGenerateAndroidManifest)"
_ManifestMerger then runs after _GenerateJavaStubs.
However, _ManifestMerger's incremental Inputs do not include @(AndroidManifestOverlay), even though the task consumes those items:
<Target Name="_ManifestMerger"
Inputs="$(IntermediateOutputPath)AndroidManifest.xml;@(ExtractedManifestDocuments);$(_AndroidBuildPropertiesCache);@(_AndroidMSBuildAllProjects)"
Outputs="$(IntermediateOutputPath)android\AndroidManifest.xml">
<ManifestMerger
AndroidManifest="$(IntermediateOutputPath)AndroidManifest.xml"
OutputManifestFile="$(IntermediateOutputPath)android\AndroidManifest.xml"
LibraryManifestFiles="@(ExtractedManifestDocuments)"
ManifestOverlayFiles="@(AndroidManifestOverlay)"
... />
</Target>
Because of that, if only the overlay changes, the custom target can run and update the overlay file, but _ManifestMerger can still be considered up-to-date and skipped. The merged manifest under obj/.../android/AndroidManifest.xml then does not reflect the updated overlay.
That makes the public BeforeGenerateAndroidManifest hook unreliable for generated overlays unless callers use brittle workarounds.
Steps to Reproduce
- Create an Android app.
- Add a custom target to
$(BeforeGenerateAndroidManifest).
- Have that target generate
$(IntermediateOutputPath)CustomOverlay.xml from some input file and add it to @(AndroidManifestOverlay).
- Build once.
- Change only the custom target's input so the generated overlay content changes.
- Build again.
- Observe that the custom target may run and update the overlay file, but
_ManifestMerger can still be skipped because @(AndroidManifestOverlay) is not part of its declared Inputs.
Did you find any workaround?
A workaround is to also touch or rewrite $(IntermediateOutputPath)AndroidManifest.xml when the overlay changes, since that file is in _ManifestMerger's Inputs.
That works, but it is brittle and leaks an implementation detail of the current target graph into consumers that are otherwise trying to use the documented public hook.
Relevant log output
No binlog attached here, but the issue is visible from the target definitions above:
Microsoft.Android.Sdk.TypeMap.LlvmIr.targets wires $(BeforeGenerateAndroidManifest) into _GenerateJavaStubs
Xamarin.Android.Common.targets consumes @(AndroidManifestOverlay) in _ManifestMerger but omits it from the target Inputs
Proposed solution
Include @(AndroidManifestOverlay) in _ManifestMerger's Inputs so its incremental behavior matches the inputs actually consumed by the ManifestMerger task.
Something along these lines:
<Target Name="_ManifestMerger"
Inputs="$(IntermediateOutputPath)AndroidManifest.xml;@(ExtractedManifestDocuments);@(AndroidManifestOverlay);$(_AndroidBuildPropertiesCache);@(_AndroidMSBuildAllProjects)"
Outputs="$(IntermediateOutputPath)android\AndroidManifest.xml">
It would also be great to add a regression test covering a generated overlay added through BeforeGenerateAndroidManifest, so that a change in just the overlay causes _ManifestMerger to rerun.
Android framework version
Likely affects any current SDK-based Android app path using the current targets. I noticed this while inspecting the current
mainbranch and would expect it to matter fornet8.0-android,net9.0-android, andnet10.0-android.Affected platform version
Current
mainas of commit0d3996623b902210d15d6327e25ddf14d30d398b.Description
There seems to be an incremental build bug around manifest overlays generated via the public
BeforeGenerateAndroidManifestextension point.The scenario is:
$(BeforeGenerateAndroidManifest).@(AndroidManifestOverlay)._ManifestMergerlater consumes@(AndroidManifestOverlay)viaManifestOverlayFiles="@(AndroidManifestOverlay)".This looks like a supported scenario:
build-properties.mddocumentsBeforeGenerateAndroidManifestas running directly before_GenerateJavaStubs.Microsoft.Android.Sdk.TypeMap.LlvmIr.targets,_GenerateJavaStubsis wired as:_ManifestMergerthen runs after_GenerateJavaStubs.However,
_ManifestMerger's incrementalInputsdo not include@(AndroidManifestOverlay), even though the task consumes those items:Because of that, if only the overlay changes, the custom target can run and update the overlay file, but
_ManifestMergercan still be considered up-to-date and skipped. The merged manifest underobj/.../android/AndroidManifest.xmlthen does not reflect the updated overlay.That makes the public
BeforeGenerateAndroidManifesthook unreliable for generated overlays unless callers use brittle workarounds.Steps to Reproduce
$(BeforeGenerateAndroidManifest).$(IntermediateOutputPath)CustomOverlay.xmlfrom some input file and add it to@(AndroidManifestOverlay)._ManifestMergercan still be skipped because@(AndroidManifestOverlay)is not part of its declaredInputs.Did you find any workaround?
A workaround is to also touch or rewrite
$(IntermediateOutputPath)AndroidManifest.xmlwhen the overlay changes, since that file is in_ManifestMerger'sInputs.That works, but it is brittle and leaks an implementation detail of the current target graph into consumers that are otherwise trying to use the documented public hook.
Relevant log output
No binlog attached here, but the issue is visible from the target definitions above:
Microsoft.Android.Sdk.TypeMap.LlvmIr.targetswires$(BeforeGenerateAndroidManifest)into_GenerateJavaStubsXamarin.Android.Common.targetsconsumes@(AndroidManifestOverlay)in_ManifestMergerbut omits it from the targetInputsProposed solution
Include
@(AndroidManifestOverlay)in_ManifestMerger'sInputsso its incremental behavior matches the inputs actually consumed by theManifestMergertask.Something along these lines:
It would also be great to add a regression test covering a generated overlay added through
BeforeGenerateAndroidManifest, so that a change in just the overlay causes_ManifestMergerto rerun.