8

My solution consists of:

Client - startup project, UI layer. Depends on: App

App - library, application layer, assembler. Depends on: Lib1, ...

Lib1 - library, business logic layer. Needs a specific file to work properly: ThirdParty.dll

I've added ThirdParty.dll to the Lib1 project (Add > Existing Item... > Add) and set Copy to Output Directory property of dll file to Copy Always. Now the dll file is copied to the Lib1 output and to the App output, but not to the Client output where I need it to be.

What is the right (simple? obvious?) way to copy ThirdParty.dll to the output of Client on each solution build?

UPD ThirdParty.dll is not a reference. Actually, that's another reference dependence. My question is applied to any file that needs to be in the folder of running application.

Recorded video to be sure I'm doing it right: http://youtu.be/QwS2tOIc5yQ

astef
  • 8,575
  • 4
  • 56
  • 95

3 Answers3

11

Add Existing Item as Link:

I had a similar issue in VS2010 and I kinda ended up adding the file as Link and updating its property to Copy Always.

In your case, in CLIENT project, add ThirdParty.dll as Link (Add > Existing Item > Add as Link) and set Copy to Output Directory property of dll file to Copy Always. Note: It would copy the folder hierarchy from Project Node.

Just for Reference: I was actually using an open source LibGit2Sharp which required a dll (libGit2.dll) to be available in the output directory. Therefore, in the UI layer, which had added application layer containing LibGit2Sharp.dll as reference; I had to add libGit2.dll as a Link + Copy Always. This was recommended solution.

Post Build:

The other option could be to write a post build scripts for CLIENT

To know how the Copy on MSBuild works, you could refer to Microsoft.Common.targets file (should be available @ C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets in your system)

<Target
        Name="CopyFilesToOutputDirectory"
        DependsOnTargets="
            ComputeIntermediateSatelliteAssemblies;
            _CopyFilesMarkedCopyLocal;
            _CopySourceItemsToOutputDirectory;
            _CopyAppConfigFile;
            _CopyManifestFiles;
            _CheckForCompileOutputs;
            _SGenCheckForOutputs">
jacob aloysious
  • 2,547
  • 15
  • 16
  • 1
    @astef Installing Visual Studio 2012 which doesn't have this bug is a bit better =) – Ark-kun Jun 05 '13 at 21:20
  • @Jacob If you have a good understanding of how the standard MSBuild targets are interleaved in a Visual Studio build, maybe you can look at this case and try to find out why VS2012 and VS2010 behaviors diverge when they build the same project. See my answer for the relevant parts of the log: http://stackoverflow.com/a/16848316/1497385 – Ark-kun Jun 05 '13 at 21:23
  • @Ark-kun: As I see, VS2010 uses MSBuild of .Net4.0 and VS2012 uses 4.5. I haven't been lucky to get a chance to work on VS2012, will surely check that out.. – jacob aloysious Jun 06 '13 at 03:04
  • There is only one MSBuild.exe with .Net 4.0 and .Net 4.5 - `C:\windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe`. There is no c:\Windows\Microsoft.NET\Framework\v4.5.*\. Also VS 2012 uses `C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets` - the same one as VS 2010. – Ark-kun Jun 06 '13 at 17:28
4

It's easy and it's strange that it didn't work for you. I've just checked and this works:

  1. Add ThirdParty.dll to the Lib project.
  2. The Build Action should be set to Content (the default value) !!!
  3. Set Copy to Output Directory to Copy if newer or Copy always
  4. Reference Lib from App
  5. Reference App from Client

This works in Visual Studio 2012, but doesn't seem to work in Visual Studio 2010.

VS2010:

Target "AssignTargetPaths" skipped. Previously built successfully.
Target "_SplitProjectReferencesByFileExistence" in file "C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets" from project "Test.ContentFiles\App\App.csproj" (target "GetCopyToOutputDirectoryItems" depends on it):
Task "ResolveNonMSBuildProjectOutput" skipped, due to false condition; ('$(BuildingInsideVisualStudio)'=='true' and '@(ProjectReferenceWithConfiguration)'!='') was evaluated as ('true'=='true' and ''!='').
Done building target "_SplitProjectReferencesByFileExistence" in project "App.csproj".
Target "GetCopyToOutputDirectoryXamlAppDefs" in file "C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Xaml.targets" from project "Test.ContentFiles\App\App.csproj" (target "GetCopyToOutputDirectoryItems" depends on it):
Task "AssignTargetPath"
Done executing task "AssignTargetPath".
Done building target "GetCopyToOutputDirectoryXamlAppDefs" in project "App.csproj".
Target "GetCopyToOutputDirectoryItems" in file "C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets" from project "C:\Users\Ark-kum\Documents\visual studio 2010\Projects\Test.ContentFiles\App\App.csproj" (entry point):
Task "MSBuild" skipped, due to false condition; ('@(_MSBuildProjectReferenceExistent)' != '' and '$(_GetChildProjectCopyToOutputDirectoryItems)' == 'true' and '%(_MSBuildProjectReferenceExistent.Private)' != 'false' and '$(UseCommonOutputDirectory)' != 'true') was evaluated as ('' != '' and 'true' == 'true' and '' != 'false' and 'false' != 'true').
Task "AssignTargetPath"
Done executing task "AssignTargetPath".
Done building target "GetCopyToOutputDirectoryItems" in project "App.csproj".
Done executing task "MSBuild".
Task "AssignTargetPath"
Done executing task "AssignTargetPath".
Done building target "GetCopyToOutputDirectoryItems" in project "Client.csproj".
Target "_CopyOutOfDateSourceItemsToOutputDirectory" in file "C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets" from project "Test.ContentFiles\Client\Client.csproj" (target "_CopySourceItemsToOutputDirectory" depends on it):
Building target "_CopyOutOfDateSourceItemsToOutputDirectory" completely.
Output file "bin\Debug\FileList.App.txt" does not exist.
Task "Copy"
Copying file from "Test.ContentFiles\App\FileList.App.txt" to "bin\Debug\FileList.App.txt".

VS2012:

Target "GetCopyToOutputDirectoryItems" skipped. Previously built successfully.
Done executing task "MSBuild".
Task "AssignTargetPath"
Done executing task "AssignTargetPath".
Done building target "GetCopyToOutputDirectoryItems" in project "Client.csproj".
Target "_CopyOutOfDateSourceItemsToOutputDirectory" in file "C:\windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets" from project "Test.ContentFiles\Client\Client.csproj" (target "_CopySourceItemsToOutputDirectory" depends on it):
Building target "_CopyOutOfDateSourceItemsToOutputDirectory" completely.
Output file "bin\Debug\FileList.Lib.txt" does not exist.
Output file "bin\Debug\FileList.App.txt" does not exist.
Task "Copy"
Copying file from "Test.ContentFiles\Lib\FileList.Lib.txt" to "bin\Debug\FileList.Lib.txt".
Copying file from "Test.ContentFiles\App\FileList.App.txt" to "bin\Debug\FileList.App.txt".

Quote from the .target files:

============================================================
                                    GetCopyToOutputDirectoryItems

Get all project items that may need to be transferred to the output directory.
This includes baggage items from transitively referenced projects. It would appear
that this target computes full transitive closure of content items for all referenced
projects; however that is not the case. It only collects the content items from its
immediate children and not children of children. The reason this happens is that
the ProjectReferenceWithConfiguration list that is consumed by _SplitProjectReferencesByFileExistence
is only populated in the current project and is empty in the children. The empty list
causes _MSBuildProjectReferenceExistent to be empty and terminates the recursion.
============================================================
Ark-kun
  • 6,358
  • 2
  • 34
  • 70
  • Are you sure that after building solution `ThirdParty.dll` is copied to the `Client` output? I see it in `App` project output only. – astef May 31 '13 at 06:25
  • Yes, I'm sure. I've specifically checked it. (I've used an image though, not .dll file.). Are you sure the `Build Action` for `ThirdParty.dll` is set to `Content`? I can zip and share my solution (VS 2012) when I get home. – Ark-kun May 31 '13 at 15:22
  • Yes, `Build Action` is `Content`. Im using VS2010 - that could be it's bug? Look at my video, everything is very simple: http://youtu.be/QwS2tOIc5yQ – astef May 31 '13 at 16:02
  • You're complicating things a bit with this style of debugging. Remove the file Just go to the `Client/bin/Debug` with Explorer. Right-Click on `Client` and select `Build`, then switch to Explorer and look at the new files. – Ark-kun May 31 '13 at 22:15
  • I've confirmed that this works in VS2012, but doesn't work in VS2010. It's a bit strange and it's hard to tell which version is buggy. Neither version really supports the "baggage items from transitively referenced projects", but somehow VS2012 does this. VS2012 skips some build steps that "were previously build successfully" or because the `BuildingProject` property somehow strangely becomes false. VS2010 executes more targets and somehow loses the grandchild content files in the process. – Ark-kun May 31 '13 at 22:21
  • +1 I didn't think this worked because I had only been using VS10. It works as expected in VS12/13. – Ternary Oct 09 '14 at 14:22
1

You could use(in visual studio) Lib1 project Properties -> Build Events -> Post-build event command line.

In the "Post-build event command line" edit box type copy commands that will copy required files to the Client output folder. In that editbox you can use various VS macros (Click "Edit Post Build" -> Macros) Like this:

copy path\to\ThirdParty.dll  $(SolutionDir)Client\bin\Debug

Perhaps, you will need to "Build"->"Rebuild Solution" at first time.

Vlad I.
  • 425
  • 2
  • 7
  • Bad thing is that `Lib1` becomes dependant on `Client` project. I think that is wrong the same as making `Client` dependant on all this third-party files. If choosing between all bad solutions I'd prefer adding all files to `Client` (Add => Existing Item...) – astef May 30 '13 at 13:04
  • In any case Client depends from Lib1(via App). if you don't need dependencies from any projects and build actions then simply copy manually all your third party resources to output folder at first time. Or use post build step on Client that will copy all stuff to Client's directory. – Vlad I. May 30 '13 at 13:23