Home: project.assets.json does not work with shared intermediates

Created on 1 Feb 2017  路  33Comments  路  Source: NuGet/Home

Files that generated into the intermediates, typically always include the project name in the name of the file:

Debug\ConsoleApp146.AssemblyInfo.cs
Debug\ConsoleApp146.csproj.FileListAbsolute.txt
Debug\ConsoleApp146.csprojResolveAssemblyReference.cache
Debug\ConsoleApp146.dll
Debug\ConsoleApp146.pdb
ConsoleApp146.1.0.0.nuspec
ConsoleApp146.csproj.nuget.g.props
ConsoleApp146.csproj.nuget.g.targets

The assets file does not:

project.assets.json

This is going to lead to clashes when projects share intermediate directories, which is pretty common in large projects such as Roslyn.

Restore Backlog 2 DCR

Most helpful comment

I think a best effort should be made to make the path unique in common cases. Consider the trivial case of two projects in the same folder with default output paths. For ages, this simple case worked because everything put the project name in obj files, until assets file came along. So I think it is good to have the error as you suggest, but I still see value in being unique by default up to project name.

All 33 comments

good issue. we think we can change without too much work...but likely too late for rtm.
/cc @jainaashish @emgarten

Agreed, this would be a good change. We've been working towards making this file name/path extensible, but there a few places still expecting project.assets.json.

Yeah, let's fix it later - just make sure you coordinate with us.

$(ProjectAssetsFile) will contain the full path that NuGet writes to. This of course won't exist if the project hasn't been restored yet, so it might be hard to rely on this.

Good to know, we should be respecting that: https://github.com/dotnet/roslyn-project-system/issues/1437

Make note - this also affects two projects in the same directory: https://github.com/dotnet/roslyn-project-system/issues/1528

When I define a different IntermediateOutputPath, nuget files still always end up in $(ProjectDir)\obj\. I am following this MSDN blog post

Example:

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <IntermediateOutputPath>$(SolutionDir)\obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
    <OutputPath>$(SolutionDir)\bin\$(Configuration)\$(AssemblyName)\</OutputPath>
  </PropertyGroup>

All other files get moved correctly, except for these 3 which still appear in $(ProjectDir)\obj\:

App.csproj.nuget.g.props
App.csproj.nuget.g.targets
project.assets.json

~@caleblloyd NuGet reads BaseIntermediateOutputPath~

update by @nkolev92

NuGet now respects MSBuildProjectExtensionsPath.

Please refer to microsoft/msbuild#1603 and NuGet/NuGet.Client#2131 and NuGet/NuGet.Client#2121.

Original comment: https://github.com/NuGet/Home/issues/4463#issuecomment-404863327

Given where the base intermediate path is set, you can't set it without expanding SDK into explicit imports of a props and targets.

I am trying to run a Docker container that has code mounted into it while also developing on the host inside of an IDE. The IDE keeps running Dotnet Restore and conflicting with the Docker container's Dotnet Restore.

I have to do multiple mounts to exclude the bin and obj folders if they are in the default path, so I thought maybe I'd move them using BaseIntermediateOutputPath. But this causes Entity Framework Tools and dotnet test to not work properly.

Is there another solution to this besides for waiting for the ability to name project.assets.json different names on the Host and in Docker? I can run different build configurations on the Host and Docker (e.g. run dotnet run -c Debug on the Host and run dotnet run -c Docker on Docker) and right now the only conflict I'm getting is that Nuget keeps clobbering the other side

Can you expand on the issues you have with EF and dotnet test when you change BaseIntermediateOutputPath with a bug over on http://github.com/roslyn-project-system? I'll move them to the appropriate repros if it's a bug in those tools.

Yes, I will do that in a few days. Thanks!

@davkean Does this mean that having a shared intermediate folder is supported once these issues are fixed?

@jdasilva These are two issues that I found scouting it - but I've not confirmed that it's fully supported yet. Sounds like @caleblloyd was running into issues that I'd like to capture

Thanks, I was thinking about a comment like this https://github.com/dotnet/sdk/issues/760#issuecomment-276259786. I've been using a common intermediate folder for a couple of projects since at least VS2013 and this still works under VS2017 with the old project system. I'd love to move to the new one, so I really meant "supported" in the sense of if something blocks this will it be considered a bug? You seem to be doing that already which is great and I just want to make sure I'm on the same page.

@jdasilva Sorry missed this comment (for some reason NuGet issues go to my personal email).

Yes we'd consider anything blocking multiple projects from building to a common intermediate as a bug - this is the reason that we prefix most things in obj with the project name. This issue is the last one that I know that is blocking building to a common intermediate folder (MSBuild fixed their last one)

Here's another issue that results as a problem of this - https://github.com/dotnet/project-system/issues/1935. Basically, building a new csproj in the same directory as the old csproj, breaks the build because the old csproj picks up the same assets file.

Possible issues in SDK too: dotnet/sdk#1438

Just got this issue where we have two csproj in same folder. One is multi-targetted (netstandard2.0, net461 etc), the other just net461. Can't resolve a reference to a net45 nuget package.

@emgarten If you folks are blocked on dotnet/project-system#1437 to fix this - I'll fix it immediately in 15.8.

@davkean
I'm not sure if that's the only "blocker", but fixing that would certainly go a long way.
//cc @rrelyea

For anyone else finding this and needs a workaround, save the following as Directory.Build.props in your project directory:
.xml <Project> <PropertyGroup> <MSBuildProjectExtensionsPath>obj\$(MSBuildProjectName)\</MSBuildProjectExtensionsPath> </PropertyGroup> </Project>
This will cause project.assets.json to be written to $(ProjectDir)\obj\ProjectFileWithNoExtension\project.assets.json. And works fine with multiple .csproj in the same directory (what I needed).

Comment on this gist if it's not related to the issue: https://gist.github.com/piksel/950646e301e0fd994465b26773170494

Any reason BaseIntermediateOutputPath cannot also override MSBuildProjectExtensionsPath?
(I would guess ordering...)

@lucasgl

NuGet now respects MSBuildProjectExtensionsPath.

Please refer to https://github.com/Microsoft/msbuild/issues/1603 and https://github.com/NuGet/NuGet.Client/pull/2131 and https://github.com/NuGet/NuGet.Client/pull/2121.

MSBuildProjectExtensionsPath defaults to BaseIntermediateOutputPath, the trick you need to set it early enough in the evaluation to avoid these problems. Doing in the Directory.Build.Props does just that.

Glad I found this. I ran into the same issue last week. Is there any ETA on when this will be fixed? Our build server calls msbuild with /p:BaseIntermediateOutputPath=<somefolder> (a "Temp" folder inside the working directory of the build job). All was fine as long as the solution only contained .Net framework targets. Now colleagues have added two(!) .Net Standard projects which breaks the build because, of course, there can only be one project.assets.json in this "Temp" folder.
Is there a temporary workaround available?

@BrightLight I posted a workaround

I'd like to revive this bug and work on a fix.

There is no way to guarantee that restore will result in a successful build unless the MSBuildProjectExtensionsPath is unique for each project. If you try to use a rooted path like obj\$(MSBuildProjectName) you could still have two projects in the tree with the same name. So renaming project.assets.json wouldn't help us there.

The only way to guarantee that the assets from a restore are unique per project is to have a unique path per project. So I suggest that we fail restore if multiple projects share the same directory. Otherwise you get non-deterministic restores since there's no guarantee what order the project.assets.json is written. Not to mention that you get into a state where restore succeeds but your build fails with compiler errors about missing types because package assets are wrong.

Does everyone agree that we should just block the ability for projects in restore to share MSBuildProjectExtensionsPath?

With this solution, could two project files exist in the same directory if they set MSBuildProjectExtensionsPath to different locations?

@SwooshyCueb yes that would work. As long as the MSBuildProjectExtensionsPath is unique, NuGet would be restore just fine. However, MSBuild will give you an error if you build in that directory and don't specify a project to build.

I think a best effort should be made to make the path unique in common cases. Consider the trivial case of two projects in the same folder with default output paths. For ages, this simple case worked because everything put the project name in obj files, until assets file came along. So I think it is good to have the error as you suggest, but I still see value in being unique by default up to project name.

There are no technical blockers anymore afaik.

The concern would be the migration plan.
For better or worse newer NuGet versions are often used with older build tooling, so this change would potentially break that.

This needs to be carefully analyzed to avoid fall outs similar to what we had to fix with https://github.com/dotnet/sdk/pull/14517.

Was this page helpful?
0 / 5 - 0 ratings