Skip to content
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

Assembly load error in Android app referencing another project referencing a DLL assembly #10154

Closed
ist-22 opened this issue Sep 15, 2022 · 25 comments · Fixed by dotnet/android#7642 or dotnet/android#7947
Labels
external partner/android Issues for the Android SDK platform/android 🤖 t/bug Something isn't working

Comments

@ist-22
Copy link

ist-22 commented Sep 15, 2022

Description

If a MAUI Android project has a reference to a .NET Standard project in the same solution; and the .NET Standard project has a reference to a .NET Standard assembly, calling a code that eventually calls a code in the assembly fails to load the assembly.

Steps to Reproduce

  1. Create a new MAUI solution and project.
  2. Add a .NET Standard project to the solution.
  3. Add a .NET Standard assembly reference to this project created in Step 2.
  4. Add a method to the .NET Standard project (created in Step 2) that calls something in the referenced (in Step 3) assembly.
  5. Add a project reference to the MAUI app that was created in Step 1 that references to the project created in Step 2.
  6. Call the method that was created in step 4.

i.e. The reference chain: MAUI csproj --> .NET Std. csproj --> .NET Std. DLL

Link to public reproduction project repository

https://github.com/ist-22/10154-repro
app has a project reference to lib1
lib1 has an assembly reference to lib2 (so lib2 needs to be compiled first)

Version with bug

6.0.400

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 11

Did you find any workaround?

Add the .NET Standard assembly reference also to the MAUI app project.
In the repository uncomment lines 14..18 in app/app.csproj -- this adds an assembly reference to lib2 in app

Relevant log output

No response

@ist-22 ist-22 added the t/bug Something isn't working label Sep 15, 2022
@drasticactions drasticactions added the s/needs-repro Attach a solution or code which reproduces the issue label Sep 16, 2022
@ghost
Copy link

ghost commented Sep 16, 2022

Hi @ist-22. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@drasticactions
Copy link
Contributor

Are you sure this is a MAUI (The UI Framework) issue, or an issue with whatever device you're trying to target? Did you try doing this with a dotnet new android app?

@ist-22
Copy link
Author

ist-22 commented Sep 16, 2022

I don't think it's a device/target issue (if this implies architecture) since adding a reference to the assembly in the top MAUI app can act as a workaround.

However, you are right about the MAUI/UI framework question and pointing to the Android project. Yes the issue happens in a project created with dotnet new android, too; so I guess it's not MAUI specific and maybe more related to Xamarin.Android?

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-repro Attach a solution or code which reproduces the issue labels Sep 16, 2022
@rachelkang rachelkang added the partner/android Issues for the Android SDK label Sep 16, 2022
@jonpryor
Copy link
Member

Could you please attach a project that exhibits this scenario?

@jonpryor jonpryor added the s/needs-repro Attach a solution or code which reproduces the issue label Sep 26, 2022
@ghost
Copy link

ghost commented Sep 26, 2022

Hi @ist-22. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@jonpryor jonpryor removed the s/needs-attention Issue has more information and needs another look label Sep 26, 2022
@ist-22
Copy link
Author

ist-22 commented Sep 29, 2022

@jonpryor -- Updated the issue description with the repo link

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-repro Attach a solution or code which reproduces the issue labels Sep 29, 2022
@PureWeen
Copy link
Member

@jonpryor @jonathanpeppers thoughts?

@jonathanpeppers
Copy link
Member

jonathanpeppers commented Oct 28, 2022

Does this problem go away if you use net6.0 class libraries (with no platform, vanilla net6.0) instead of netstandard?

@michalss
Copy link

michalss commented Nov 3, 2022

I have same issue with dynamic assembly loading..

@ist-22
Copy link
Author

ist-22 commented Nov 3, 2022

Does this problem go away if you use net6.0 class libraries (with no platform, vanilla net6.0) instead of netstandard?

@jonathanpeppers No, still happens.

@jonathanpeppers
Copy link
Member

Can you update the repro? https://github.com/ist-22/10154-repro

@rachelkang rachelkang added s/needs-info Issue needs more info from the author and removed s/needs-attention Issue has more information and needs another look labels Nov 18, 2022
@ghost
Copy link

ghost commented Nov 18, 2022

Hi @ist-22. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@ist-22
Copy link
Author

ist-22 commented Nov 22, 2022

@jonathanpeppers -- repro updated.

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels Nov 22, 2022
@mattleibow mattleibow removed the s/needs-attention Issue has more information and needs another look label Dec 1, 2022
@mattleibow
Copy link
Member

Should we move this to the Android repo? Does this happen with iOS or Windows? Or even a plain old net7.0 console app?

@ist-22
Copy link
Author

ist-22 commented Dec 1, 2022

It doesn't happen with a Windows or console app -- can't test iOS.

@jonathanpeppers
Copy link
Member

In VS Windows, the above repro doesn't even build for me, unless I change lib1.csproj:

<Reference Include="lib2">
  <HintPath>..\lib2\bin\$(Configuration)\net7.0\lib2.dll</HintPath>
</Reference>

To this instead:

<ItemGroup>
  <ProjectReference Include="..\lib2\lib2.csproj" />
</ItemGroup>

If you have a <Reference/> to a random .dll file, the .NET SDK does not go "locate" this file and bring it to the top-level project (application project). I would use <ProjectReference> instead of <Reference> in this case, as the .NET SDK supports this. I suspect you'd have the same issue build a .NET 7 self-contained console app -- mobile apps are always self-contained.

If you are hitting this situation with a NuGet package, we can make suggestions on what to do there. Let me know, thanks!

@ist-22
Copy link
Author

ist-22 commented Dec 13, 2022

@jonathanpeppers The scenario in this issue is about a case where the user doesn't have access to the source code of the external library (i.e. lib2 in the repro), so adding a project reference is really not an option.

I guess your build failed since lib2 hadn't been built first (this was in the original message but sorry if it wasn't clear enough). The source code of lib2 in the repro is not very relevant to the issue itself and it is only there so that someone who runs the repro doesn't have to call a random DLL.

As I mentioned, this problem doesn't happen with a Windows or console app, also (as long as lib2 has been compiled) IntelliSense is also fine. In all cases (Windows, console and even Android) lib2.dll is copied into bin so I suspect it somehow is not packed into the APK properly.

@jonathanpeppers
Copy link
Member

@ist-22 does the same problem happen in a console app? You would publish it such as dotnet publish --self-contained -r win-x64, for example, and I bet that lib2.dll won't be in the build output. You would get a crash at runtime that the assembly isn't found. There isn't any logic in the .NET SDK that loads assemblies and automatically finds references.

So your options are:

  1. Make a NuGet package, and put lib2.dll alongside lib1.dll.
  2. Reference lib2.dll from the application project.

Does that make sense? Let me know if I missed something here.

@jonathanpeppers
Copy link
Member

Here is another case we saw the same behavior in the .NET SDK: #11364 (comment)

@ist-22
Copy link
Author

ist-22 commented Dec 13, 2022

@jonathanpeppers it does not happen in a console or Windows app, and lib2.dll is always (even in Android) in the build output. What I can't verify (and suspect where the problem) is that whether it is packed into the APK or not.

Also:

  1. If I run dotnet publish --self-contained -r win-x64 I can still see lib2.dll under win-x64\publish and it doesn't crash when run,
  2. Separately, the scenario works fine in a Xamarin.Forms project (i.e. no crash/assembly error).

I appreciate the options/workarounds (I believe the second one is what I suggested as a workaround in my original message) but considering that it works fine with other project types, it makes me think that the expected behaviour is that assembly references inside referenced projects should be OK.

@jonathanpeppers
Copy link
Member

Can you share the console app you made? Is it on a branch of the original repro? Thanks.

@ist-22
Copy link
Author

ist-22 commented Dec 13, 2022

Repro updated; cmd is the new console app project. Reference chain is: cmd -- (proj. ref.) --> lib1 -- (bin. ref.) --> lib2.
If you run below, lib2 is copied into cmd's publish output.

dotnet build lib2
dotnet publish cmd --self-contained -r win-x64

@jonathanpeppers
Copy link
Member

Ok, I tracked this down to:

https://github.com/dotnet/msbuild/blob/a2490dd3f78cce4abc8f9e6f1b5268437332818f/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2322

In the console app we see:

image

But in the Android app we see:

image

Putting this in app.csproj (main Android app) also works around the issue:

<PropertyGroup>
  <_ResolveReferenceDependencies>true</_ResolveReferenceDependencies>
</PropertyGroup>

Still figuring out what these settings are for, and how we can fix this.

@jonathanpeppers
Copy link
Member

It appears we need this change in Microsoft.Android.Sdk.AssemblyResolution.targets:

   <Target Name="_ComputeFilesToPublishForRuntimeIdentifiers"
-      DependsOnTargets="_FixupIntermediateAssembly;ResolveReferences;ComputeFilesToPublish;$(_RunAotMaybe)"
+      DependsOnTargets="BuildOnlySettings;_FixupIntermediateAssembly;ResolveReferences;ComputeFilesToPublish;$(_RunAotMaybe)"
       Returns="@(ResolvedFileToPublish)">

The BuildOnlySettings target sets $(BuildingProject) to true, preventing $(_FindDependencies) from being set to false.

I need to write a regression test for this, but will have a PR shortly.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Dec 15, 2022
Fixes: dotnet/maui#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:

dotnet/maui#11364

I added a test for this scenario.
jonpryor pushed a commit to dotnet/android that referenced this issue Jan 4, 2023
)

Fixes: dotnet/maui#10154

Context? dotnet/maui#11364

If you have a solution setup with:

  * `ApplicationA.csproj` has a `@(ProjectReference)` to
    `LibraryB.csproj`.
  * `LibraryB.csproj` which has a `@(Reference)` to `LibraryC.dll`,
    built by-
  * `LibraryC.csproj`

The app will crash at runtime, due to a missing `LibraryC.dll`.

The workaround is for `LibraryB.csproj` to use `@(ProjectReference)`
to `LibraryC.csproj` instead of `@(Reference)` to `LibraryC.dll`.

However, it appears the same situation works in a .NET 7
self-contained console app:

	% dotnet publish --self-contained -r win-x64
	…
	% ls -1 .\bin\Debug\net7.0\win-x64\publish\LibraryC.dll
	LibraryC.dll

The underlying issue appears to be due to [`$(_FindDependencies)`][0]:

	<_FindDependencies Condition="'$(BuildingProject)' != 'true' and '$(_ResolveReferenceDependencies)' != 'true'">false</_FindDependencies>

In the console app, `$(BuildingProject)`=true and
`$(_FindDependencies)` is empty.

In the Android app, `$(BuildingProject)`=false and
`$(_FindDependencies)` is `false`.

It appears that the `BuildOnlySettings` target *should* be running in
Android apps when we do an "inner" build per `$(RuntimeIdentifier)`.
Simply updating `_ComputeFilesToPublishForRuntimeIdentifiers` so that
the `BuildOnlySettings` target is in `DependsOnTargets` fixes this.
However, this also causes satellite assemblies to now be automatically
found by the .NET SDK.  Update `@(_AndroidResolvedSatellitePaths)` so
that `@(ReferenceSatellitePaths)` is only included on Classic builds,
preventing duplicate entries.

[0]: https://github.com/dotnet/msbuild/blob/a2490dd3f78cce4abc8f9e6f1b5268437332818f/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2322
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Jan 5, 2023
…tnet#7642)

Fixes: dotnet/maui#10154

Context? dotnet/maui#11364

If you have a solution setup with:

  * `ApplicationA.csproj` has a `@(ProjectReference)` to
    `LibraryB.csproj`.
  * `LibraryB.csproj` which has a `@(Reference)` to `LibraryC.dll`,
    built by-
  * `LibraryC.csproj`

The app will crash at runtime, due to a missing `LibraryC.dll`.

The workaround is for `LibraryB.csproj` to use `@(ProjectReference)`
to `LibraryC.csproj` instead of `@(Reference)` to `LibraryC.dll`.

However, it appears the same situation works in a .NET 7
self-contained console app:

	% dotnet publish --self-contained -r win-x64
	…
	% ls -1 .\bin\Debug\net7.0\win-x64\publish\LibraryC.dll
	LibraryC.dll

The underlying issue appears to be due to [`$(_FindDependencies)`][0]:

	<_FindDependencies Condition="'$(BuildingProject)' != 'true' and '$(_ResolveReferenceDependencies)' != 'true'">false</_FindDependencies>

In the console app, `$(BuildingProject)`=true and
`$(_FindDependencies)` is empty.

In the Android app, `$(BuildingProject)`=false and
`$(_FindDependencies)` is `false`.

It appears that the `BuildOnlySettings` target *should* be running in
Android apps when we do an "inner" build per `$(RuntimeIdentifier)`.
Simply updating `_ComputeFilesToPublishForRuntimeIdentifiers` so that
the `BuildOnlySettings` target is in `DependsOnTargets` fixes this.
However, this also causes satellite assemblies to now be automatically
found by the .NET SDK.  Update `@(_AndroidResolvedSatellitePaths)` so
that `@(ReferenceSatellitePaths)` is only included on Classic builds,
preventing duplicate entries.

[0]: https://github.com/dotnet/msbuild/blob/a2490dd3f78cce4abc8f9e6f1b5268437332818f/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2322
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Jan 20, 2023
…ce)` (dotnet#7642)"

This reverts commit 22f2001.

.NET MAUI is hitting a build failure such as:

    Unable to open file 'obj\Release\net8.0-android\android-x64\aot\x86_64\Microsoft.Maui.Controls.resources\temp.s': Permission denied

In 22f2001, we began passing satellite assemblies to the AOT compiler,
on accident? I am unsure how a test in xamarin-android didn't catch
this, but it may be something that only happens via a
`@(ProjectReference)` as occurred in .NET MAUI.

For now, let's revert the change and revisit later.

We should reopen dotnet/maui#10154 after
this is merged.
jonathanpeppers added a commit to dotnet/android that referenced this issue Jan 22, 2023
…ce)` (#7642)" (#7726)

This reverts commit 22f2001.

.NET MAUI is hitting a build failure such as:

    Unable to open file 'obj\Release\net8.0-android\android-x64\aot\x86_64\Microsoft.Maui.Controls.resources\temp.s': Permission denied

In 22f2001, we began passing satellite assemblies to the AOT compiler,
on accident? I am unsure how a test in xamarin-android didn't catch
this, but it may be something that only happens via a
`@(ProjectReference)` as occurred in .NET MAUI.

For now, let's revert the change and revisit later.

We should reopen dotnet/maui#10154 after
this is merged.
@jonathanpeppers
Copy link
Member

The first attempt to fix this in xamarin/xamarin-android/main didn't work out. (It caused a different issue) Reopening to look into this further.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Apr 11, 2023
Fixes: dotnet/maui#10154

This reverts commit c1efcb5.

I added `.resx` files to the new test, but I'm not seeing the error we saw in dotnet/maui:

    Unable to open file 'obj\Release\net8.0-android\android-x64\aot\x86_64\Microsoft.Maui.Controls.resources\temp.s': Permission denied
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Apr 11, 2023
Context: https://github.com/dotnet/msbuild/blob/c75672a6e7387bc5c1f99c166e9277351144b14c/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2327
Fixes: dotnet/maui#10154

This partially reverts c1efcb5.

Previously, we removed this change because it broke .NET MAUI's build
with:

    Unable to open file 'obj\Release\net8.0-android\android-x64\aot\x86_64\Microsoft.Maui.Controls.resources\temp.s': Permission denied

The problem being that the .NET SDK was placing satellite assemblies
in the `@(ResolvedFileToPublish)` item group.

Let's apply our change from before, but also set
`$(ResolveAssemblyReferencesFindRelatedSatellites)` to `false` for
our "inner build" per `$(RuntimeIdentifier)`.

This solves the original issue for dotnet/maui#10154 without changing
behavior of satellite assemblies.

I added `.resx` files in our new test, and assert that satellite
assemblies make to apps for good measure.
jonathanpeppers added a commit to dotnet/android that referenced this issue Apr 12, 2023
)

Context: https://github.com/dotnet/msbuild/blob/c75672a6e7387bc5c1f99c166e9277351144b14c/src/Tasks/Microsoft.Common.CurrentVersion.targets#L2327
Fixes: dotnet/maui#10154

This partially reverts c1efcb5.

Previously, we removed this change because it broke .NET MAUI's build
with:

    Unable to open file 'obj\Release\net8.0-android\android-x64\aot\x86_64\Microsoft.Maui.Controls.resources\temp.s': Permission denied

The problem being that the .NET SDK was placing satellite assemblies
in the `@(ResolvedFileToPublish)` item group.

Let's apply our change from before, but also set
`$(ResolveAssemblyReferencesFindRelatedSatellites)` to `false` for
our "inner build" per `$(RuntimeIdentifier)`.

This solves the original issue for dotnet/maui#10154 without changing
behavior of satellite assemblies.

I added `.resx` files in our new test, and assert that satellite
assemblies make to apps for good measure.
@ghost ghost locked as resolved and limited conversation to collaborators May 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
external partner/android Issues for the Android SDK platform/android 🤖 t/bug Something isn't working
Projects
None yet
9 participants