msbuild: How to keep referenced Assembly out of Build Directory? Private not working
Steps to reproduce
I’m trying to build a project that has a Project Dependency to another project where the final output folder should not contain the original assembly and its depedencies. Essentially I need to use Copy Local: No behavior. This works for assembly references, but not for project references.
The options show in Visual Studio and in the project, but they seem to be ignored when the project is built.
Here’s what the relevant project section I’m using looks like:
<ItemGroup>
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<ProjectReference Include="..\..\MarkdownMonster\MarkdownMonster.csproj">
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</ProjectReference>
</ItemGroup>
The project above is the main project of the application, and this project is an addin that runs in the context of that host application.
The project compiles fine, but the output folder of the addin project still gets all the host project references AND all of its dependencies. IOW, it pulls in the entire host application.

I’ve been working around this by using an explicit assembly reference instead of a project reference like this:
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0'">
<Reference Include="$(SolutionDir)MarkdownMonster\bin\$(Configuration)\$(TargetFramework)\win-x86\MarkdownMonster.dll">
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net462'">
<Reference Include="$(SolutionDir)MarkdownMonster\bin\$(Configuration)\$(TargetFramework)\win-x86\MarkdownMonster.exe">
<Private>false</Private>
</Reference>
</ItemGroup>
This works as expected, but this has build order problems where often compilation fails if the main project has not been compiled (even if explicitly setting the project dependencies in the solution file).
Other
This project is a multi-targeted WPF project to build both .NET Core 3.0 and for 4.6.2 and both the 3.0 and 4.6.2 build do the same thing.
This used to work just by Copy Local: No on the assembly reference added by a project reference in old style .csproj projects.
Expected behavior
I’d like to see a Project Reference behave the same way as the Assembly Reference with <Private>false</Private> providing a compilation reference, but not actually copying the output into the build target folder.
Actual behavior
Files are copied local even Copy Local: No
Environment data
OS info: Windows 10 dotnet core SDK 3.0 Preview 5 .NET Core 3.0 WPF Project
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 22
- Comments: 23 (2 by maintainers)
Commits related to this issue
- Initializing repro for https://github.com/dotnet/msbuild/issues/4371 — committed to rainersigwald/demo-msbuild-4371 by rainersigwald 2 years ago
Any progress on that? We’re struggling with this issue as well. Always trashes our modules folder which should only contain the module libraries as their dependencies are resolved at runtime anyways. Can’t believe that something so simple still hasn’t made it into the toolchain.
As a follow up - the solution appears to be in this declaration to reference the main project:
It also works with
<IncludeAssets>compile</IncludeAssets>.This generates only the local project main assembly plus any direct project references, but excludes any incoming dependencies from the main
MarkdownMonsterproject.I’ve updated and somewhat cleaned up my old post with more detailed information on how to best reference resources from a typical addin style project here:
.NET Core SDK Projects: Controlling Output Folders and Dependencies Output
i am heading the same issue atm and its really frustrating that a workflow that is even documented here (https://docs.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support#simple-plugin-with-no-dependencies) is not working cause of that issue
How is this still not fixed? It was working fine with the old csproj format.
I’ve taken to explicitly referencing the project’s assembly instead of the project in this case.
Using
<Private>false</Private>keeps the dependencies from being copied over.None of the other things suggested (
<PrivateAssets>) in the posts above seem to work for me for using a project reference.I fail to see why a project reference should not work the same as a another reference using the same settings. At the end of the day the end result that is expected is the same and you should be able to use a direct reference, Nuget reference or project reference pretty much interchangeably.
I have found a workaround for this by adding DisableTransitiveProjectReferences to the project settings.
I found answer here You need to set PrivateAssets=all to MarkdownMonster.csproj reference
I have a
<ProjectReference>that passes a Transitive ProjectReferences. Both the project A, and its dependency project B require a third project C to build, and A references C through B. I wish to exclude both B and C from A’s build output. In A I can<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>, add C’s<ProjectReference>and add<Private>False</Private>to it, but I was wondering if there is another (shorter) way to do it. And no, doing it in an<ItemDefinitionGroup>block is undesirable to me, since I want this to apply only to B and C, and not to other<ProjectReference>s.If desired, there is a further trick to this step. If you have many .dlls that need this treatment, and they are all below a singular file system node (subtree) such that there is a prevailing
Directory.Build.Propsfile, then you don’t have to manually clutter-up every.csprojfile all over the place. That job is actually far worse that it seems at first, because for each of those sub-modules, you have to go in and expand each and every one of the<ProjectReference>it makes, within them all, in turn.Instead, you can automatically apply the behavior to any/all of the (sub-suib-)
<ProjectReference>“mentions” that are made by any of the respective (subsumed) submodules by doing the following:In an appropriately prevailing Directory.Build.Props:
Intuitively enough, there’s an extra level of nesting, which is the magic here: the
<ItemDefinitionGroup>element (note: not<ItemGroup>that you’re familiar with), deploys an abstracted operation, like a “macro”, over any/all number of scoped<ProjectReference>elements that may later follow.As with the theory of this solution in general, we only want to affect
.dllbuilds, because this is the “sub-sub-module” proliferation that becomes so useless and problematic. The common-sense rule-of-thumb (indeed, the TL;DR; of the whole issue here IMHO) is that, since a sub-module itself can never be a primary runtime host, it should never receive or privately posess any binaries of any (putative) referents (beyond itself), as a result of–or during the course of–its being built. This is called “nipping the (.dllproliferation) problem in the bud.”Only the final executable host in a modular system should have to worry about and implement some policy for gathering the disparate parts needed for actual, runtime use, That policy should not be “regularly freeze the hard disk by wastefully reducing-down a gigatic union of 80% binary images which are byte-duplicates, since every sub-module has pointlessly hoarded duplicate copies for itself.”
Who cares about tracking overlapping usage between adjacent or unrelated plug-ins? It’s a non-problem that can’t affect anything; leave it to the host-build where it’s comically trivial to just de-duplicate the entire set of all comers, for once and only. In fact, if the method described here is followed assiduously, there’s not even any de-duping during the host build–it can just copy each sub-module’s \bin directory into its own, and ideally there will be zero excess-copying and no .DLL file will ever be overwritten. I’ve done it this way for many years and the builds are obviously way faster, not to mention more reliable due to reduced overwriting collisions in general.
A similar syntax works for subduing all
<PackageReference>entries that may happen to be referenced by any/all of the submodules as well:Oh, and
<Reference>works similar to<ProjectReference>, but indeed it, and in fact all three of the reference-tag-types I’ve mentioned are treated independently by the<ItemDefinitionGroup>handing, so you can (or must) deploy any combination of them according to your needs.@80O The syntax you want is this:
This works, but it shouldn’t be this hard.
There’s no reason we shouldn’t be able to reference the
.csprojas a<ProjectReference>and it should be able to figure out to reference the main assembly in the correct folder without all this MsBuild stuff.Especially since none of the tooling (Visual Studio, Rider etc.) will set this up correctly if you set Copy Local to false.
The no Copy Local is sort of an edge case - typically for Addins or pluggable components - but this should still work and it seems to me this syntax should just work:
You have to apply the
<PrivateAssets>all</PrivateAssets>to the parent of your dependency, since the copy to output trigger was earlier in dependency tree. This works well. Alternative solution can be to add the dependency again and force it to not copy. Another solution would be to addPrivateAssetstoDirectory.Build.propsas follows:Source of this information is https://curia.me/net-core-transitive-references-and-how-to-block-them/