-
Notifications
You must be signed in to change notification settings - Fork 538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bump to xamarin/xamarin-android-tools/main@76c076fc #7685
Conversation
Context: dotnet/maui#11605 Context: 8bc7a3e Changes: http://github.com/xamarin/xamarin-android-tools/compare/9f02d77692bca8c6585941de03750d5eaaca5c5a...47f95ab99f6201d956eecfa1c7b2fd5fa7e43946 * dotnet/android-tools@47f95ab: Fix CS0121 ambiguity errors. (dotnet/android-tools#200) * dotnet/android-tools@76c076f: Add support for Project Specific RegisterTaskObject. (dotnet/android-tools#199) In dotnet/maui#11605, when `$(AndroidEnableMarshalMethods)`=True (8bc7a3e), the build may fail if the `.sln` contains more than one Android "App" project. We tracked this down to "undesired sharing" between project builds; the `obj` provided to [`IBuildEngine4.RegisterTaskObject()`][0] can be visible across project builds. Consider [`<GeneratePackageManagerJava/>`][1]: var marshalMethodsState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<MarshalMethodsState> (".:!MarshalMethods!:.", RegisteredTaskObjectLifetime.Build); Further consider a `.sln` with two "App" projects, as the "App" project build hits `<GeneratePackageManagerJava/>`. The lifetime of `.Build` is *not* tied to the the `Build` target of a given `.csproj`; rather, it's for the *build process*. This can result in: 1. `dotnet build Project.sln` is run; `Project.sln` references `App1.csproj` and `App2.csproj`. 2. `App1.csproj` is built. 3. `App1.csproj` calls `<GeneratePackageManagerJava/>`. 4. `App2.csproj` is later built as part in the process, and *also* calls `<GeneratePackageManagerJava/>`. In particular note the key within `<GeneratePackageManagerJava/>`: `".:!MarshalMethods!:."`. This value is unchanged, and means that that when `App2.csproj` is built, it will be using the same key as was used with `App1.csproj`, and thus could be inadvertently using data intended for `App1.csproj`! This could result build errors: …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: System.InvalidOperationException: Unable to translate assembly name 'Xamarin.AndroidX.Activity' to its index …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: at Xamarin.Android.Tasks.MarshalMethodsNativeAssemblyGenerator.WriteNativeMethods(LlvmIrGenerator generator, Dictionary`2 asmNameToIndex, LlvmIrVariableReference get_function_pointer_ref) …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: at Xamarin.Android.Tasks.MarshalMethodsNativeAssemblyGenerator.Write(LlvmIrGenerator generator) …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: at Xamarin.Android.Tasks.LLVMIR.LlvmIrComposer.Write(AndroidTargetArch arch, StreamWriter output, String fileName) …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: at Xamarin.Android.Tasks.GeneratePackageManagerJava.AddEnvironment() …\Xamarin.Android.Common.targets(1717,3): error XAGPM7009: at Xamarin.Android.Tasks.GeneratePackageManagerJava.RunTask() The sharing of `RegisterTaskObject()` data across project builds is rarely desirable. There are a few instances where it is safe to share the registered objects between projects, e.g. `java` version information (keyed on `java` path). However, most of the time it is specific to the project that is being built. Historically we have probably got away with this because "most" users only have one project. dotnet/android-tools@76c076f updated the `MSBuildExtensions` extension methods so that [`IBuildEngine.ProjectFileOfTaskNode`][2] would be part of the `RegisterTaskObject()` key. Review use of the `.RegisterTaskObjectAssemblyLocal()`, `.GetRegisteredTaskObjectAssemblyLocal()`, and `.UnregisterTaskObjectAssemblyLocal()` extension methods to ensure that `IBuildEngine.ProjectFileOfTaskNode` is used or excluded, as appropriate. This should fix the XAGPM7009 build errors. TODO: add unit test to trigger this build scenario. [0]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.framework.ibuildengine4.registertaskobject?view=msbuild-17-netcore [1]: https://github.com/xamarin/xamarin-android/blob/c92ae5eb9fdcb3a2fd7c20f5b42dddf8b3ea781a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs#L407 [2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.framework.ibuildengine.projectfileoftasknode?view=msbuild-17-netcore
Also TODO: run all the unit tests. (I forget the voodoo to do that.) |
@dellis1972: could you please add unit test to trigger the described scenario? I believe you have something lying around? |
"All tests enabled" build is at: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=7184637&view=results |
This looks good. Should we wait for the monodroid changes? |
Draft of monodroid changes https://github.com/xamarin/monodroid/pull/1281 |
I don't think we need to wait, unless the it takes so long to get this PR "good" that monodroid can come along for the ride… I don't think that this PR is "good" though; consider Aapt2DaemonInstances(6,6,6,50):
Or ReportAaptErrorsInOriginalFileName:
|
The First unit test is using GetRegisteredTaskObjectAssemblyLocal but is not providing the |
Should have got a Daemon Expected: not null But was: null at Xamarin.Android.Build.Tests.Aapt2Tests.Aapt2DaemonInstances(Int32 maxInstances, Int32 expectedMax, Int32 expectedInstances, Int32 numLayouts) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/Aapt2Tests.cs:line 109 at InvokeStub_Aapt2Tests.Aapt2DaemonInstances(Object, Ob The tests *also* need to use the correct & consistent arguments to `engine.GetRegisteredTaskObjectAssemblyLocal<Appt2Daemon>(…)`.
I apply this patch: diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
index acd5230b3..70fb6d838 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
@@ -497,11 +497,47 @@ namespace Xamarin.Android.Tasks
static readonly string ResourceCaseMapKey = $"{nameof (MonoAndroidHelper)}_ResourceCaseMap";
- public static void SaveResourceCaseMap (IBuildEngine4 engine, Dictionary<string, string> map) =>
+ public static void SaveResourceCaseMap (IBuildEngine4 engine, Dictionary<string, string> map) {
+ engine.LogMessageEvent (new BuildMessageEventArgs
+ ($"# jonp: SaveResourceCaseMap: key={ResourceCaseMapKey}; engine.ProjectFileOfTaskNode={engine.ProjectFileOfTaskNode}",
+ helpKeyword: "",
+ senderName: "SaveResourceCaseMap",
+ importance: MessageImportance.Normal
+ ));
+ var s = new System.Text.StringBuilder();
+ foreach (var e in map) {
+ s.AppendLine ($"{e.Key}={e.Value}");
+ }
+ engine.LogMessageEvent (new BuildMessageEventArgs
+ ($"# jonp: SaveResourceCaseMap: map={s}",
+ helpKeyword: "",
+ senderName: "SaveResourceCaseMap",
+ importance: MessageImportance.Normal
+ ));
engine.RegisterTaskObjectAssemblyLocal (ResourceCaseMapKey, map, RegisteredTaskObjectLifetime.Build);
+ }
+
+ public static Dictionary<string, string> LoadResourceCaseMap (IBuildEngine4 engine) {
- public static Dictionary<string, string> LoadResourceCaseMap (IBuildEngine4 engine) =>
- engine.GetRegisteredTaskObjectAssemblyLocal<Dictionary<string, string>> (ResourceCaseMapKey, RegisteredTaskObjectLifetime.Build) ?? new Dictionary<string, string> (0);
+ var map = engine.GetRegisteredTaskObjectAssemblyLocal<Dictionary<string, string>> (ResourceCaseMapKey, RegisteredTaskObjectLifetime.Build) ?? new Dictionary<string, string> (0);
+ engine.LogMessageEvent (new BuildMessageEventArgs
+ ($"# jonp: LoadResourceCaseMap: key={ResourceCaseMapKey}; engine.ProjectFileOfTaskNode={engine.ProjectFileOfTaskNode}; found? {map != null}",
+ helpKeyword: "",
+ senderName: "LoadResourceCaseMap",
+ importance: MessageImportance.Normal
+ ));
+ var s = new System.Text.StringBuilder();
+ foreach (var e in map) {
+ s.AppendLine ($"{e.Key}={e.Value}");
+ }
+ engine.LogMessageEvent (new BuildMessageEventArgs
+ ($"# jonp: LoadResourceCaseMap: map={s}",
+ helpKeyword: "",
+ senderName: "LoadResourceCaseMap",
+ importance: MessageImportance.Normal
+ ));
+ return map;
+ }
public static string FixUpAndroidResourcePath (string file, string resourceDirectory, string resourceDirectoryFullPath, Dictionary<string, string> resource_name_case_map)
{
And I see the following fascinating output in
In particular, note the
They're not the same! Consequently, the map that Thus, Which means the underlying idea of dotnet/android-tools@76c076f will not work; we still can have potential unexpected data sharing, because the MSBuild file which invokes |
Behold, docs!
Apparently "project file" does not mean |
Given that
I'm not entirely certain that the added complexity of (2) is actually worth it; (1) may be best. |
Reverts: 47f95ab Reverts: 76c076f Context: dotnet/android#7685 (comment) The problem is that we misunderstood [`IBuildEngine.ProjectFileOfTaskNode`]: we *thought* (hoped?) that it would return `$(MSBuildProjectFullPath)`, the path to the `.csproj` being built. In actuality it returns `$(MSBuildThisFileFullPath)` for the file containing the Task invocation. Meaning if e.g. `Xamarin.Android.Common.targets` contains the Task invocation, then IBuildEngine.ProjectFileOfTaskNode` would be the path to `Xamarin.Android.Common.targets`, *not* the path of the `.csproj`. This in turn means it doesn't actually solve the problem we had. We will instead need to audit all use of `IBuildEngine4.RegisterTaskObject()` & related to ensure that the keys provided *also* contain paths to project-specific files, if necessary. Revert 47f95ab and 76c076f, as the changes are not needed. [0]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.framework.ibuildengine.projectfileoftasknode?view=msbuild-17-netcore
Reverts: 47f95ab Reverts: 76c076f Context: dotnet/android#7685 (comment) The problem is that we misunderstood [`IBuildEngine.ProjectFileOfTaskNode`][0]: we *thought* (hoped?) that it would return `$(MSBuildProjectFullPath)`, the path to the `.csproj` being built. In actuality it returns `$(MSBuildThisFileFullPath)` for the file containing the Task invocation. Meaning if e.g. `Xamarin.Android.Common.targets` contains the Task invocation, then IBuildEngine.ProjectFileOfTaskNode` would be the path to `Xamarin.Android.Common.targets`, *not* the path of the `.csproj`. This in turn means it doesn't actually solve the problem we had. We will instead need to audit all use of `IBuildEngine4.RegisterTaskObject()` & related to ensure that the keys provided *also* contain paths to project-specific files, if necessary. Revert 47f95ab and 76c076f, as the changes are not needed. [0]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.framework.ibuildengine.projectfileoftasknode?view=msbuild-17-netcore
Closing this branch, as the approach doesn't work as we hoped. |
Context: dotnet/maui#11605
Context: 8bc7a3e
Changes: http://github.com/xamarin/xamarin-android-tools/compare/9f02d77692bca8c6585941de03750d5eaaca5c5a...47f95ab99f6201d956eecfa1c7b2fd5fa7e43946
In dotnet/maui#11605, when
$(AndroidEnableMarshalMethods)
=True (8bc7a3e), the build may fail if the.sln
contains more than one Android "App" project. We tracked this down to "undesired sharing" between project builds; theobj
provided toIBuildEngine4.RegisterTaskObject()
can be visible across project builds. Consider<GeneratePackageManagerJava/>
:Further consider a
.sln
with two "App" projects, as the "App" project build hits<GeneratePackageManagerJava/>
. The lifetime of.Build
is not tied to the theBuild
target of a given.csproj
; rather, it's for the build process. This can result in:dotnet build Project.sln
is run;Project.sln
referencesApp1.csproj
andApp2.csproj
.App1.csproj
is built.App1.csproj
calls<GeneratePackageManagerJava/>
.App2.csproj
is later built as part in the process, and also calls<GeneratePackageManagerJava/>
.In particular note the key within
<GeneratePackageManagerJava/>
:".:!MarshalMethods!:."
. This value is unchanged, and means that that whenApp2.csproj
is built, it will be using the same key as was used withApp1.csproj
, and thus could be inadvertently using data intended forApp1.csproj
!This could result build errors:
The sharing of
RegisterTaskObject()
data across project builds is rarely desirable. There are a few instances where it is safe to share the registered objects between projects, e.g.java
version information (keyed onjava
path). However, most of the time it is specific to the project that is being built. Historically we have probably got away with this because "most" users only have one project.dotnet/android-tools@76c076f updated the
MSBuildExtensions
extension methods so thatIBuildEngine.ProjectFileOfTaskNode
would be part of theRegisterTaskObject()
key.Review use of the
.RegisterTaskObjectAssemblyLocal()
,.GetRegisteredTaskObjectAssemblyLocal()
, and.UnregisterTaskObjectAssemblyLocal()
extension methods to ensure thatIBuildEngine.ProjectFileOfTaskNode
is used or excluded, as appropriate. This should fix the XAGPM7009 build errors.TODO: add unit test to trigger this build scenario.