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

Forced locked mode doesn't work cross platform #9195

Open
jabbera opened this issue Feb 19, 2020 · 17 comments
Open

Forced locked mode doesn't work cross platform #9195

jabbera opened this issue Feb 19, 2020 · 17 comments
Labels
Area:RestoreRepeatableBuild The lock file features Functionality:Restore Partner:CLI-SDK Platform:Xplat Priority:2 Issues for the current backlog. Resolution:BlockedByExternal Progress on this task is blocked by an external issue. When that issue is completed this can proceed Type:Bug

Comments

@jabbera
Copy link

jabbera commented Feb 19, 2020

Details about Problem

When running a dotnet restore --locked-mode from a linux environment where dotnet restore --force-evaluate was run on windows it fails. This makes things very difficult to test cross platform. (Build on a linux agent then run tests on windows). Note: it happens either way (lockfile generate on linux causes windows to fail)

NuGet product used: dotnet.exe

NuGet version (x.x.x.xxx):

❯ dotnet nuget --version
NuGet Command Line
5.4.0.2

dotnet.exe --version (if appropriate):

❯ dotnet --version
3.1.102

OS version (i.e. win10 v1607 (14393.321)): Windows 10 Enterprise: 10.0.18363 and Ubuntu 18..04 (WSL in my case, but repro's on build agents as well)

Worked before? If so, with which NuGet version: Unknown

Detailed repro steps so we can see the same problem

  1. git clone https://github.com/jabbera/nuget-locked-mode-bug.git
  2. cd nuget-locked-mode-bug
  3. dotnet restore --locked-mode # This succeeds as it should
  4. bash
  5. dotnet restore --locked-mode # This fails with:error NU1004: The packages lock file is inconsistent with
  6. dotnet restore --force-evaluate
  7. exit
  8. git diff # This will let you see the differences between the OS's

Other suggested things

Output of the diff:

diff --git a/packages.lock.json b/packages.lock.json
index 1d02123..d72a20f 100644
--- a/packages.lock.json
+++ b/packages.lock.json
@@ -9,7 +9,6 @@
         "contentHash": "7D2TMufjGiowmt0E941kVoTIS+GTNzaPopuzM1/1LSaJAdJdBrVP0SkZW7AgDd0a2U1DjsIeaKG1wxGVBNLDMw=="             }
     },
-    ".NETCoreApp,Version=v3.1/win7-x86": {},
     ".NETFramework,Version=v4.6.2": {
       "Microsoft.NETFramework.ReferenceAssemblies": {
         "type": "Direct",
@@ -25,7 +24,6 @@
         "resolved": "1.0.0",
         "contentHash": "ONGjkFWduK13lfxUtlEl4+nYwrqDe5NF5f8qRtp5fqWiWYlqft/Ko9ht3e6Secg9y3I1yL8Xnfag/JGOOn0yoQ=="             }
-    },
-    ".NETFramework,Version=v4.6.2/win7-x86": {}
+    }
   }
 }
\ No newline at end of file
@jabbera
Copy link
Author

jabbera commented Mar 9, 2020

Hi! Is there any chance of this getting prioritized? Just trying to plan.

@nkolev92 nkolev92 added Priority:2 Issues for the current backlog. Pipeline:Backlog and removed Pipeline:New Issues labels Jul 2, 2020
@wocar
Copy link

wocar commented Aug 2, 2020

is this ever going to happen?

@jabbera
Copy link
Author

jabbera commented Aug 3, 2020

I've given up.

@kartheekp-ms
Copy link
Contributor

I can reproduce this issue on .NET Core Console App that multi-targets net472 and a .NET core tfm. I cannot reproduce this issue on a .NET Core Class Library project with multiple target frameworks.

@nkolev92 helped me to find that .NET SDK sets runtime identifier to win7-86 here. It looks like following comment in the .targets file confirms this behavior.

When building a .NETFramework exe on Windows and not given a RID, we'll pick either win7-x64 or win7-x86

image

Transferring to dotnet/SDK team for feedback.

@kartheekp-ms kartheekp-ms added Resolution:BlockedByExternal Progress on this task is blocked by an external issue. When that issue is completed this can proceed Partner:CLI-SDK labels Dec 2, 2020
@aortiz-msft aortiz-msft removed this from the Sprint 180 - 2020.11.30 milestone Jan 14, 2021
@derekssmith
Copy link

This issue is plaguing us also.

@nkolev92
Copy link
Member

Hey all,

The root problem here is that the SDK projects have different defaults depending on the environment they are being built in.
The RID scenario is one example, and the other example is the one displayed in the diff.
In particular, in order to build .NET Framework projects on linux, the SDK automatically adds a targeting pack reference which can be disabled through AutomaticallyUseReferenceAssemblyPackages. Refer to the discussion in #10456 for more detail.

Given that you are trying to build on linux, I think you need to explicitly reference the targeting pack to unblock yourself.

@jabbera
Copy link
Author

jabbera commented Feb 10, 2021

@nkolev92 I don't understand what you mean. I am explicitly referencing the targeting pack: https://github.com/jabbera/nuget-locked-mode-bug/blob/e9977b0f1438fed2377afb4554abae13f899988f/bugdemo.csproj#L10

I also don't think anything that was mentioned would actually help my scenario. I am building on Linux but then running all of my tests on multiple platforms. As part of that step I am running a dotnet restore on windows where the lock file was generated on linux. I don't see how any of these suggestions will help me.

@nkolev92
Copy link
Member

@jabbera

Did you notice @kartheekp-ms's comment about the RIDS?

And in my comment above I mention: #9195 (comment)

The root problem here is that the SDK projects have different defaults depending on the environment they are being built in.

If you are going to build on different OSs, consider including the RuntimeIdentifier in your project explicitly instead of relying on environment defaults that the SDK sets.

Hopefully that should make the dotnet restore --locked-mode call work.

@AnderssonPeter
Copy link

using <DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder> solved some of our issues when restoring on different platforms.

@Bouke
Copy link

Bouke commented Apr 21, 2021

While not a solution, I'm now using the workaround below. It disables lock files on all platforms except Windows (the primary development + deployment target). This doesn't block me from using other OS's, littering my workspace with changes to packages.lock.json:

<!-- Enable nuget lock files. -->
<RestorePackagesWithLockFile Condition="'$(OS)' == 'Windows_NT'">true</RestorePackagesWithLockFile>

<!-- On CI builds restore the exact versions as locked. -->
<RestoreLockedMode Condition="'$(OS)' == 'Windows_NT' And '$(ContinuousIntegrationBuild)' == 'true'">true</RestoreLockedMode>

<!-- Force nuget to ignore existing lock files. -->
<NuGetLockFilePath Condition="'$(OS)' != 'Windows_NT'">no.lock.file</NuGetLockFilePath>

If you want, you could also have a lock file per OS. This prevents the lock files from being changed all the time between OS's, but also that when updating your dependencies it involves updating the lock files from multiple OS's:

<!-- Enable nuget lock files. -->
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>

<!-- On CI builds restore the exact versions as locked. -->
<RestoreLockedMode Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</RestoreLockedMode>

<!-- Lock file per OS. -->
<NuGetLockFilePath>packages.$(OS).lock.json</NuGetLockFilePath>

@pinkfloydx33
Copy link

Is there any update on this or perhaps an "official" workaround?

Our primary development enviroment is windows (one dev is on macos), but we do all CI and deployments on Linux, with our primary distributable a Linux docker image. Despite the docker build/artifact we do primary development outside of docker, direct in vs/vscode. Ideally the lock file would be generated/updated there and then copied into our docker builds.

I've added DisableImplicitNuGetFallbackFolder=true and I've tried specifying rid(s) in the project file. For good measure I also tried making use of the DotNet.ReproducibleBuilds.Isolated package.

Prior to this, I had no rids specified in the project file, but my docker builds do a restore with -r linux-x64. I tried specifying multiple rids in the project file for windows/Linux and the generated lock files looked "good". But then when I try to restore in the docker image, specifying the single rid, I get an error saying that the target rid (linux-x64) doesn't match the ones in the lock file (linux-x64;win-x64)

I'd like to avoid having one lock file per OS. And I'd like to do the generation/updates of the locks on developer machines (of any flavor) via dotnet/msbuild as if docker wasn't in the picture. The work arounds by @Bouke don't really satisfy either case. If developers only used windows then perhaps his first suggestion could work.

Is there any guidance for this scenario?

@zivkan
Copy link
Member

zivkan commented Jul 11, 2022

I created an issue in dotnet/sdk to see if they're willing to remove the RID that's added when restoring/building on Windows, or alternatively help us understand if manually specifying the RID in the project file is unlikely to cause other issues, or any other recommendations they have.

I don't see it mentioned elsewhere, so specifying the RID in the project file seems to "work for me", but MSBuild being what it is, I can't promise there isn't some edge case or scenario where it doesn't work:

<PropertyGroup>
  <RuntimeIdentifiers>win7-x86</RuntimeIdentifiers>
</PropertyGroup>

It doesn't matter if you're building on Linux, specifying this in your project makes the RIDs consistent regardless of what platform you're restoring on, so locked mode should work.

Once I get feedback from the SDK team, I'll see about updating docs. For this reason, I'm going to mark this as blocked on external. I don't see what else NuGet can do. I even spent a little time trying to re-imagine lock files implemented in a different way, but I think that NuGet needs the SDK to provide us with consistent inputs. Even if we changed the lock file format, we can't solve this problem for all scenarios, only this "easy" one where there are no RID-specific packages.

@flcdrg
Copy link

flcdrg commented Jul 22, 2022

@zivkan Is that assuming you'd always do a dotnet restore?

eg. It wouldn't work if you were trying to build on a Linux box and leveraging something like the Cache task's cacheHitVar to skip restoring

@zivkan
Copy link
Member

zivkan commented Jul 22, 2022

I don't have experience with the Cache task.

Are you saying that it caches failures, and therefore will fail the build without running restore? If so, yes that's right. However, if it does not cache failures, and keeps re-running restore until the first successful restore, then there should not be any problem.

If it caches failures, the only workaround I can think of is to either change the rid (use win7-x64 instead of win7-x86, or visa-versa, or use win10-x86), as you'll need to update the lock file in the same commit as changing the csproj in that case. Alternatively, you can apply the cache task's workaround to "clear" the cache: https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops#q-can-i-clear-a-cache

@flcdrg
Copy link

flcdrg commented Jul 23, 2022

Actually, having tried that code sample on the linked page I am suspicious that it might be incorrect. Not running dotnet restore means that project.assets.json isn't created and then the build phase failed with an error.

@zivkan zivkan removed their assignment Aug 1, 2022
chklauser added a commit to chklauser/splitracker that referenced this issue Dec 24, 2022
@veikkoeeva
Copy link

veikkoeeva commented Dec 30, 2022

I think it is unclear what exactly do if one has <TargetFramework>net7.0</TargetFramework> defined and would like to publish a cross-platform library and restore dependencies in a cross-platform manner too.

Is a workaround needed? If yes, what should it be? For instance, would

<PropertyGroup Condition="$(TargetFramework.Contains('-windows')) and $(PlatformTarget) == 'x64'">
    <Platforms>x64</Platforms>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
        
<PropertyGroup Condition="$(TargetFramework.Contains('-windows')) and $(PlatformTarget) == 'x86'">
    <Platforms>x86</Platforms>
    <RuntimeIdentifier>win-x86</RuntimeIdentifier>
</PropertyGroup>

work for libraries that restore dependencies based on developers updating lock files on any OS , are built on CI as in this thread and then distributed through Nuget (and also on WASM)?

This probably starts to be a bit of an issue for libraries that would need to be mindful on package supply-chains (e.g. https://github.com/lumoin/Verifiable) and that would need to consider package locking, Nuget package sources and like that (also it would improve performance).

I'll link dotnet/sdk#1906 and dotnet/sdk#9518 and their linked issues as they give further context on the issue at large in build system. And why it's a bit tough to figure out what works or not.

@jr01
Copy link

jr01 commented May 15, 2023

In addition to the problem described above we depend on an ~700MB OS specific 3rd party vendor NuGet and want to avoid the cost for restoring the Windows NuGet when targeting Linux (on CI).

As a workaround we use a lock file per RID and -p:MyRuntimeIdentifier=... as described here dotnet/sdk#14281 (comment) with defaults for RID based on the OS platform.

<PropertyGroup>
    <MyRuntimeIdentifier Condition="$([MSBuild]::IsOSPlatform('Windows'))">win-x64</MyRuntimeIdentifier>
    <MyRuntimeIdentifier Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-x64</MyRuntimeIdentifier>
    <RuntimeIdentifier>$(MyRuntimeIdentifier)</RuntimeIdentifier>
    <NuGetLockFilePath>packages.lock.$(RuntimeIdentifier).json</NuGetLockFilePath>

    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
    <RestoreLockedMode>true</RestoreLockedMode>
    <DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
</PropertyGroup>

<ItemGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'">
    <PackageReference Include="Some.Windows.Nuget" Version="..." />
</ItemGroup>

<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'" >
    <PackageReference Include="Some.Linux.Nuget" Version="..." />
</ItemGroup>

Create lock files:

dotnet restore -p:MyRuntimeIdentifier=linux-x64 --force-evaluate
dotnet restore -p:MyRuntimeIdentifier=win-x64 --force-evaluate

and commit packages.lock.linux-x64.json and packages.lock.win-x64.json files.

Then dotnet restore, dotnet build etc just work regardless if on Linux or Windows. Or use a specific RID:

dotnet restore -p:MyRuntimeIdentifier=linux-x64
dotnet build --no-restore -p:MyRuntimeIdentifier=linux-x64
dotnet publish ./web/web.csproj --no-build --runtime linux-x64

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area:RestoreRepeatableBuild The lock file features Functionality:Restore Partner:CLI-SDK Platform:Xplat Priority:2 Issues for the current backlog. Resolution:BlockedByExternal Progress on this task is blocked by an external issue. When that issue is completed this can proceed Type:Bug
Projects
None yet
Development

No branches or pull requests