I have a project with some nuget packages referenced.
In output folders (bin\Debug
or bin\Release
), all referenced libraries lie next to the executable.
How to specify output folder for libraries?
I want all nuget libraries in bin\Release\Libs
and executable in bin\Release
.
Grace to the zivkan's investigation I found the answer. Traditional project has target
CopyFilesToOutputDirectory
which depends on_CopyFilesMarkedCopyLocal
target. In this last one we have taskCopy
:And here I found metadata
DestinationSubDirectory
which is exactly what I need to change.So finally
First, we need to change csproj file and add these lines:
Second, we need to change
app.config
file to let the assembly know the path to the libraries:That's all. All referenced libraries will be copied into subfolder
lib
I woke up early this morning and decided to have a go at doing it myself. Turned out to be pretty quick, but that may be because of my (unfortunate) experience with looking into MSBuild files. Writing this post took me far longer than writing the target.
From your question, I assume you're using a traditional project, since SDK style projects only create the project's assembly in the bin directory. However, I much prefer SDK style projects because use can quickly and easily use the dotnet cli to create test projects and the csproj is much more easily editable. So, I'll give you my steps to find my solution for SDK style projects, and you need to follow along to do something similar with a traditional project.
So, we want to change where a files are being copied, which means we need to modify some items. Everything in MSBuild runs in a target, so we'll need to know when to run our custom target, what items to modify and probably what metadata of those items to modify. I created a new project, added some NuGet references then ran
dotnet msbuild -t:publish -bl
and opened themsbuild.binlog
file.What metadata to change
Searching for the name of a dll that came from a nuget package, I find a message saying copied from ... to ..., so I click on it to go to the entry, and follow the tree back to the task, which I see is the built-in Copy task. The target path to the task is Publish -> _PublishBuildAlternative -> ComputeAndCopyFilesToPublisDirectory -> CopyFilesToPublishDIrectory -> _CopyResolvedFilesToPublishAlways. Double clicking the copy task I see
So, I can guess I need to modify the
RelativePath
metadata of an_ResolvedFileToPublishAlways
item.What item to change
Side note: MSBuild doesn't have public/private modifies, so instead a convention is generally used. Anything starting with an underscore should be considered to be an implementation detail that could change between releases, so it's better to use things that do not start with an underscore, and the teams who maintain the targets file should try harder not to break compatibility.
So, since
_ResolvedFileToPublishAlways
starts with an underscore, let's find out where it was created. Searching for it takes me to a target where the binlog tells me it was added, in a target called_ComputeResolvedFilesToPublishTypes
, and its definition isSo, I can see that it's simply copying
ResolvedFileToPublish
items to new item names. Looking for where those items are created, it's in a target namedComputeFilesToPublish
, and expanding the tree to see all the items created and their metadata, I'm going to guess the items I want to modify all haveAssetType = runtime
, which is perfect for a condition we're going to need to use.When to run our target
Ideally I would run just before
CopyFilesToPublishDirectory
, however looking at its definition I seeThe problem is that when MSBuild executes a target it runs in this order:
DependsOnTargets
BeforeTargets
AfterTargets
So, while I want to run
BeforeTargets='CopyFilesToPublishDirectory'
, theDependsOnTargets
will run before my target, so I can't do that. So I'll choose to runAfterTargets="ComputeFilesToPublish"
. There are other targets that run in between those, and one sounds like that it might addResolvedFileToPublish
items, but with my current project the target doesn't run because of conditions, so my custom target might not be generic enough to work for all projects.Writing our custom target
So now we know when our target will run, which items it will modify and how we will modify their metadata.
Unfortunately the binlog doesn't show the details about the metadata being modified, which is a real pain in the arse when trying to debug build issues and why some items have unexpected values, but in any case I've now successfully changed the destination of NuGet dependencies, and probably project to project references, to a
lib\
directory.