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

How to use ref with .netframework and packagereference #12972

Closed
adrianm64 opened this issue Oct 27, 2023 · 11 comments
Closed

How to use ref with .netframework and packagereference #12972

adrianm64 opened this issue Oct 27, 2023 · 11 comments

Comments

@adrianm64
Copy link

NuGet Product Used

dotnet.exe, NuGet.exe, Visual Studio Package Management UI, Visual Studio Package Manager Console

Product Version

nuget 6.7.0.127

Worked before?

No response

Impact

It's more difficult to complete my work

Repro Steps & Context

I have a package containing a compile time assembly which must not be copied to the output folder.
The package is used in 3 types of applications

  1. .NetFramework packages.config
  2. .NetFramework PackageReference
  3. SDK PackageReference

With package.config (1) I can set CopyLocal to false
With SDK (3) I use the "ref" folder in the package
But I have not found a way to solve type 2.
CopyLocal is not available and "ref" does not seem to work.

My .nuspec file contains

<file src="MyAssembly.dll" target="lib/net48" />
<file src="MyAssembly.dll" target="ref/net48" />
<file src="MyAssembly.dll" target="ref/netstandard2.1" />

"lib" is needed for type 1, "ref/netstandard2.1" is for type 3, both work fine.
Have tried with and without the "ref/netstandard2.1" line.

Created asset file for case 2 looks like

"MyAssembly/1.0.0": {
     "type": "package",
      "compile": {
          "ref/net48/MyAssembly.dll": { }
      },
      "runtime": {
        "lib/net48/MyAssembly.dll": { }
      }
    },

Verbose Logs

verbose nuget log just says "checking compatability.." and "all packages compatible ..."
@kartheekp-ms
Copy link
Contributor

Can you please try if the following approach works for all your 3 scenarios?

Nuspec file has only assets under lib folder.

<file src="MyAssembly.dll" target="lib/net48" />
<file src="MyAssembly.dll" target="lib/netstandard2.1" />

The following setting refers to the contents of the lib folder and controls whether the consuming project can compile against the assemblies within the folder. This effectively removes the dependencies from the consuming project's output folder.

<PackageReference Include="MyAssembly" Version="1.0.0" >
  <IncludeAssets>compile</IncludeAssets>
</PackageReference>

For more information on how to control dependency assets in PackageReference please refer to https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets docs.

@adrianm64
Copy link
Author

@kartheekp-ms, thank you for your reply but I don't understand.

Do you mean the consumers of the package should manually edit the .csproj file and add the <IncludeAssets>compile</IncludeAssets> tag?

Even if that would technically work when the package is a top level reference (for transitive references there is nothing in .csproj file to add the tag to) it is worse than the current situation where "ref" folder solves it automatically for SDK projects.

@ghost ghost added WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. and removed WaitingForCustomer Applied when a NuGet triage person needs more info from the OP labels Oct 28, 2023
@nkolev92 nkolev92 added Functionality:Restore and removed Product:NuGet.exe NuGet.exe Functionality:VisualStudioUI Package Manager UI et al Product:VS.PMConsole Product:dotnet.exe WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. labels Nov 1, 2023
@nkolev92
Copy link
Member

nkolev92 commented Nov 1, 2023

@adrianm64

Have you had a chance to look at https://learn.microsoft.com/en-us/nuget/create-packages/select-assemblies-referenced-by-projects?
The complement to ref is the runtimes folder.

@nkolev92 nkolev92 added the WaitingForCustomer Applied when a NuGet triage person needs more info from the OP label Nov 1, 2023
@adrianm64
Copy link
Author

adrianm64 commented Nov 2, 2023

@nkolev92, yes I have read that document but it describes the opposite problem, including an asset as runtime but not compile.

To reiterate, the document says with PackageReference, assets are evaluated as (in C# terms)

compile = ref/<tfm> ?? lib/<tfm>
runtime = runtime/<tfm> ?? lib/<tfm>

while packages.config only use assemblies in lib/<tfm> and ignores the other folders.

So to support packages.config, I must have the assembly in the lib folder.
But that breaks PackageReference since it will then use the assembly in the lib folder as a runtime asset (according to the rules above).

This does not happen for SDK projects if I use different tfms

lib/net45
ref/netstandard2.1

Probably because the ref, lib and runtime folders are evaluated together and the lib/net45 is excluded since netstandard2.1 is a better match.

But using different tfms does not work in .NetFramework projects with packagereference.
(probably because it lib,ref and runtime folders are evaluated independently).

lib/net45
ref/net48

creates an asset file like

      "MyAssembly/1.0.0": {
        "type": "package",
        "compile": {
          "ref/net48/MyAssembly.dll": { }
        },
        "runtime": {
          "lib/net45/MyAssembly.dll": {}
        },

This might be by design but it means it is impossible to support both packages.config and PackageReference in the same package for reference-only assemblies.

@ghost ghost added WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. and removed WaitingForCustomer Applied when a NuGet triage person needs more info from the OP labels Nov 2, 2023
@zivkan
Copy link
Member

zivkan commented Nov 2, 2023

I think these docs, still in review, are what you're looking for: NuGet/docs.microsoft.com-nuget#3092

@ghost ghost added WaitingForCustomer Applied when a NuGet triage person needs more info from the OP and removed WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. labels Nov 2, 2023
@adrianm64
Copy link
Author

ok, thanks. The conclusion is I need several packages.

Just one more detail. Is using lib/net45 and ref/netstandard2.1 only working by accident and should actually add a runtime asset?

@ghost ghost added WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. and removed WaitingForCustomer Applied when a NuGet triage person needs more info from the OP labels Nov 2, 2023
@zivkan
Copy link
Member

zivkan commented Nov 2, 2023

Near the top of the docs, there's a heading "understanding NuGet asset selection". There's a table that explains how compile and runtime assets are selected.

@ghost ghost added WaitingForCustomer Applied when a NuGet triage person needs more info from the OP and removed WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. labels Nov 2, 2023
@adrianm64
Copy link
Author

Problem is that it says

runtime: Managed assemblies copied to the output directory. runtimes/{rid}/lib/{tfm}/ if it exists, otherwise lib/{tfm}/.

But that is only how it works for .NetFramework projects. SDK projects has an extra constraint that lib/{tfm}/ is only used if same {tfm} is specified in both liband ref folders.

So either the documentation or implementation is wrong for SDK projects.

@ghost ghost added WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. and removed WaitingForCustomer Applied when a NuGet triage person needs more info from the OP labels Nov 2, 2023
@nkolev92
Copy link
Member

nkolev92 commented Nov 2, 2023

@adrianm64
It doesn't have to be the same framework, it just needs to be compatible with the pivot framework.

In your package you have a ref assembly directly compatible with netcore frameworks.

@ghost ghost added WaitingForCustomer Applied when a NuGet triage person needs more info from the OP and removed WaitingForClientTeam Customer replied, needs attention from client team. Do not apply this label manually. labels Nov 2, 2023
@zivkan
Copy link
Member

zivkan commented Nov 2, 2023

I think you're referring to this comment: #12972 (comment)

Is that right?

I think the problem is that .NETCoreApp (.NET Core and .NET 5+) is not directly compatible with .NET Framework. The .NET SDK defines asset target fallback. However, since NuGet finds that netstandard2.1 asset, asset target fallback is not used, hence the .NET Framework runtime asset is NOT compatible. Your package needs netstandard2.1 runtime assets.

To see AssetTargetFallback in action, you need a package with only .NET Framework assets, then reference the package from a project targeting .NET Framework. NuGet will raise a warning that the package was used, but might not be fully compatible, because there are bunch of .NET Framework APIs that don't work on .NET (Core), and if the package uses any of those APIs, the app will crash at runtime, which is basically what different TFM versions and TFM compatibility is trying to avoid.

But to reiterate the point, in order to reenforce its understanding, Asset Target Fallback works in two passes. First, NuGet attempts to do asset selection without any asset target fallback. if any assets match, from any asset class (compile, runtime, content files, build) finds a compatible asset, then asset target fallback will NOT be used. Only when NONE of the asset classes find any matches, then NuGet will try asset target fallback.

Therefore, projects that are compatible with .NET Standard 2.1, they will not select .NET Framework assets in a different asset class. This is by design because .NET Framework and .NET Core App are not "guaranteed" to be compatible. In fact, there are known API and runtime differences.

@adrianm64
Copy link
Author

Thank you for the clarification. I understand now and my package will continue to work as is.

I told the customer it is not possible to support reference only assemblies for .NetFramework with PackageReference in the same package. They need to add my package as a top level reference and manually edit the .csproj, which they did and it works fine.

(Using a separate package for this case is not an option since my package is usually a transitive dependency).

@ghost ghost removed the WaitingForCustomer Applied when a NuGet triage person needs more info from the OP label Nov 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants