From c694855b6c50970408909d7780edd318b5392ea5 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 15 Dec 2022 12:31:36 -0600 Subject: [PATCH 1/2] [Xamarin.Android.Build.Tasks] fix cases of missing `@(Reference)` Fixes: https://github.com/dotnet/maui/issues/10154 If you have a solution setup with: * `ApplicationA` project reference -> * `LibraryB` reference -> * `LibraryC` The app will crash at runtime, due to a missing `LibraryC.dll`. You can solve the problem by changing `@(Reference)` to a `@(ProjectReference)`. However, it appears the same situation works in a .NET 7 self-contained console app: dotnet publish --self-contained -r win-x64 ... ls -n .\bin\Debug\net7.0\win-x64\publish\LibraryC.dll LibraryC.dll The underlying issue appears to be: https://github.com/dotnet/msbuild/blob/a2490dd3f78cce4abc8f9e6f1b5268437332818f/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2322 In the console app, `$(BuildingProject)` is `true` and `$(_FindDependencies)` is empty. In the android app, `$(BuildingProject)` is `false` and `$(_FindDependencies)` is `false`. It appears the `BuildOnlySettings` target *should* be running in Android apps when we do an "inner" build per `$(RuntimeIdentifier)`. Simply adding a dependency for the `_ComputeFilesToPublishForRuntimeIdentifiers` MSBuild target solves this issue. This likely might fix other issues, such as: https://github.com/dotnet/maui/issues/11364 I added a test for this scenario. --- ...oft.Android.Sdk.AssemblyResolution.targets | 2 +- .../Xamarin.Android.Build.Tests/XASdkTests.cs | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 3ccabedc09d..e15b10a264f 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -44,7 +44,7 @@ _ResolveAssemblies MSBuild target. diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index 68cb7728107..81f094ee126 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -1053,6 +1053,67 @@ public void DotNetIncremental ([Values (true, false)] bool isRelease, [Values (" } } + [Test] + public void ProjectDependencies ([Values(true, false)] bool projectReference) + { + // Setup dependencies App A -> Lib B -> Lib C + var path = Path.Combine ("temp", TestName); + + var libB = new XASdkProject (outputType: "Library") { + ProjectName = "LibraryB", + IsRelease = true, + }; + libB.Sources.Clear (); + libB.Sources.Add (new BuildItem.Source ("Foo.cs") { + TextContent = () => @"public class Foo { + public Foo () { + var bar = new Bar(); + } + }", + }); + + var libC = new XASdkProject (outputType: "Library") { + ProjectName = "LibraryC", + IsRelease = true, + }; + libC.Sources.Clear (); + libC.Sources.Add (new BuildItem.Source ("Bar.cs") { + TextContent = () => "public class Bar { }", + }); + + // Add a @(Reference) or @(ProjectReference) + if (projectReference) { + libB.AddReference (libC); + } else { + libB.OtherBuildItems.Add (new BuildItem.Reference ($@"..\{libC.ProjectName}\bin\Release\{libC.TargetFramework}\{libC.ProjectName}.dll")); + } + + // Build libraries + var libCBuilder = CreateDotNetBuilder (libC, Path.Combine (path, libC.ProjectName)); + Assert.IsTrue (libCBuilder.Build (), $"{libC.ProjectName} should succeed"); + var libBBuilder = CreateDotNetBuilder (libB, Path.Combine (path, libB.ProjectName)); + Assert.IsTrue (libBBuilder.Build (), $"{libB.ProjectName} should succeed"); + + var appA = new XASdkProject { + ProjectName = "AppA", + IsRelease = true, + Sources = { + new BuildItem.Source ("Bar.cs") { + TextContent = () => "public class Bar : Foo { }", + } + } + }; + appA.AddReference (libB); + var appBuilder = CreateDotNetBuilder (appA, Path.Combine (path, appA.ProjectName)); + Assert.IsTrue (appBuilder.Build (), $"{appA.ProjectName} should succeed"); + + var apkPath = Path.Combine (FullProjectDirectory, appA.OutputPath, $"{appA.PackageName}-Signed.apk"); + FileAssert.Exists (apkPath); + var helper = new ArchiveAssemblyHelper (apkPath); + helper.AssertContainsEntry ($"assemblies/{libB.ProjectName}.dll"); + helper.AssertContainsEntry ($"assemblies/{libC.ProjectName}.dll"); + } + [Test] public void SignAndroidPackage () { From e9b5957ac8c740e4448540b12ee5c61ca1483c66 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 16 Dec 2022 10:31:52 -0600 Subject: [PATCH 2/2] Prevent duplicates in @(_AndroidResolvedSatellitePaths) It appears that after we fixed `BuildOnlySettings` in the inner builds, that satellite assemblies for assembly references are now automatically found by the .NET SDK. Conditionally add to `@(_AndroidResolvedSatellitePaths)` to avoid duplicate satellite assemblies in .NET 6+. --- src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 875dc59abe9..07f3598ffef 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1972,7 +1972,8 @@ because xbuild doesn't support framework reference assemblies. DependsOnTargets="_ResolveAssemblies" > - <_AndroidResolvedSatellitePaths Include="@(ReferenceSatellitePaths)" /> + + <_AndroidResolvedSatellitePaths Condition=" '$(UsingAndroidNETSdk)' != 'true' " Include="@(ReferenceSatellitePaths)" /> <_AndroidResolvedSatellitePaths Include="@(IntermediateSatelliteAssembliesWithTargetPath->'$(OutDir)%(Culture)\$(TargetName).resources.dll')" />