Visual Studio not Copying Directory of Linked Files - c#

A common practice when staging unmanaged libraries or resources is to add those resources as links to the project and set them to copy to the output directory.
As of Visual Studio 2013, this is handled quite well and even if they are referenced Library Foo which is then consumed by Application Bar, they will end up in Bar's output directory.
The same doesn't appear to be true for directories of files though.
In this case, my application appears to only copy this directory of files to the output directory on rebuild only. Then inevitably I'll do a couple build actions and I'll notice the Libs directory is empty again. Then do a Rebuild and cef.pak is back.
My solution to this was to employ a Build Step to copy the Libs directory manually to the output directory. Though I cannot find the proper macro to generically express that even though this build step is part of Foo, I want the files copied to the Bar's (e.g. StartUp Project in VS terms) output directory.
Update
Thank you everyone for the great answers and testing this. I should clarify that I still see this issue when there is an extra level of library in between. That is to say Application Bar referencing library Foo which references library Other which is the one w/ these linked files. In that case, when Other's linked files are set to copy to output directory, they seem to only make it on rebuilds. My solution to this is less than ideal which is to have Foo reference Other directly.

unless you rebuild/clean solution, VS does remove files from output folder.
so i believe you forgot to say that your program(or third party) does so.
First: this is an example for similar/exact bug.
download the solution and do the following steps:
rebuild the solution
open the output folder of Bar
the file cef.pak will be exist in Libs
press F5 -> execute Bar -> everything alright
execute again with F5 -> you will receive an file not found exception
if you repeat steps 1-5 you will receive the same behavior
The issue exist only if there is no changes in Bar and something(the exe / other process/etc) has deleted the file.
Second: I've already faced this issue.
when one of my team member reported this issue, then the answer he received was a kind of "it's not a bug it's a feature"....
As i know, there is no "magic button" to solve for this problem.
But there are several workarounds:
(as you did) adding pre/post build events. I recommend you to do this in new build configuration.
change the build output folder of Foo to Bar output folder.
add in your code a path replacement with if debug:
#if DEBUG
cefFilePath = <build output of foo>
#endif
put the file as embedded resource then extract it.
create an extension\external program to deal with this problem. each project the file keep the information about those file:
<ItemGroup>
<None Include="Libs\cef.pak">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
the application/plugin will do the job for you.
I can offer more solutions/workarounds, but I believe that you did the right thing -> build event.
edit:
I updated my example in the link. now the solution contains 3 workarounds example.

Tried doing same with VS2013. It worked quite well, everytime I change something in Bar's File, Run Foo (Startup Project), the latest file is copied there. I am just writing steps I followed, may be I am missing something to replicate or you missed a step. Please have a look:
Create two Projects Foo and Bar
Created a directory Libs in Bar
Added a text file "TextFile1.txt" in Libs
Right Click -> Properties of the file -> Copy to Output Directory = Copy Always
Added reference of Bar from Foo Project
Started reading the TextFile's text from Foo's Main method. Reading file from Bin\Debug\Libs directory.
Every time I press F5 I see the latest file get copied there.

Edit "Foo.csproj" (make sure the project is closed in VS) and add this add the bottom, in place of a commented out example that is already there in every new project file (use your own paths):
<Target Name="AfterBuild">
<Copy SourceFiles="Libs\cef.pak;Libs\file2.ext" DestinationFolder="..\..\Bar Solution\Bar Project\output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>
Edit "Bar.csproj" as well, just in case changing those files doesn't trigger Foo build:
<Target Name="BeforeBuild">
<Copy SourceFiles="..\..\Foo Solution\Foo Project\Libs\cef.pak;..\..\Foo Solution\Foo Project\Libs\file2.ext" DestinationFolder="output" SkipUnchangedFiles="True" OverwriteReadOnlyFiles="True" />
</Target>

Related

How do I include .deps and .runtimeconfig files as project dependencies?

Visual Studio creates two files along with the .exe for my project that are required to run the exe: a deps.json and a runtimeconfig.json. A second project in my solution has the first project as a project reference, but those two files aren't being copied to my second project's output directory. How can I tell Visual Studio that it should copy these files into the output directory of the second project, because the referenced project depends on them?
Output directory of my first project:
Foo.exe
Foo.deps.json
Foo.runtimeconfig.json
Output directory of my second project:
Bar.exe
Foo.exe
Should contain deps and runtimeconfig files, but does not
The solution I found is to manually edit my .csproj file to add the following target:
<Target Name="AddRuntimeDependenciesToContent"
Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'"
BeforeTargets="GetCopyToOutputDirectoryItems"
DependsOnTargets="GenerateBuildDependencyFile;
GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)"
Condition="'$(GenerateDependencyFile)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectDepsFileName)" />
<ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)"
Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'"
CopyToOutputDirectory="PreserveNewest"
TargetPath="$(ProjectRuntimeConfigFileName)" />
</ItemGroup>
</Target>
This solution came from https://github.com/dotnet/sdk/issues/1675#issuecomment-658779827.
There were other somewhat similar solutions posted in that thread, but this is the only one that worked for me. The others would either not consistently copy the files to my second project, or cause the first project to fail to build due to attempting to access a file that didn't yet exist. The key difference with this one is the inclusion of the correct "BeforeTargets" property (and possibly also "DependsOnTargets"), controlling at which point in the build process the files are included.

msbuild cannot copy manifest because it was not found

We have a c# framework (not .core) solution with several projects in it. It is build by TFS. Sometimes (not all the times) I got a build error:
error MSB3030: Could not copy the file
"c:\BuildAgent_work\27\b\Console.Admin\Product.Admin.exe.manifest"
because it was not found.
[c:\BuildAgent_work\27\s\Console.Admin\Console.Admin.csproj]
The app.manifest file is added to the project Properties folder, and I checked it exists on buildagent source folder. I checked it is not exists on the binaries folder. I don't know why it is not copied there during the build.
In fact I don't know if I need this manifest thing at all. I think I don't. This whole thing was added to the project by one of my collegaue for a reason is unknown for me. Is it required to create publish package for web projects? In this case why it is required for a console project? For what reason is it added?
The msbuild parameters (for building the solution) are the following, and the [x] Clean option is checked in the TFS build solution step.
/p:OutDir=$(Build.BinariesDirectory)
/p:GenerateProjectSpecificOutputFolder=true
/p:DeployOnBuild=true /
p:PackageAsSingleFile=true
/p:GenerateDocumentation=true
/p:DisableAllVSGeneratedMSDeployParameter=true
/t:Clean,Build,Publish
/p:RunCodeAnalysis=$(CodeAnalysis.Run);CodeAnalysisRuleSet=$(CodeAnalysis.RuleSet).ruleset;CodeAnalysisIgnoreGeneratedCode=true
/p:IncludeAppPool=true
/p:PrecompileBeforePublish=true;EnableUpdateable=false
An application manifest is an XML file that describes and identifies the shared and private side-by-side assemblies that an application should bind to at run time. These should be the same assembly versions that were used to test the application. Application manifests may also describe metadata for files that are private to the application. You can refer to this document.
So, you first need to check in csproj for any additional operations on the manifest. When the MSBuild creates the publish files it copies files base on the build definition , you can try to edit the .csproj file as bellow which copied the relevant files. app.manifest is your file path .
<ItemGroup>
<None Update="app.manifest">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

Roslyn bin folder generated at c:\bin\roslyn

I've taken over maintenance of a web solution that contains a number of c# projects. On compilation one project creates the Roslyn bin folder under it's output directory bin folder as expected, another however creates the Roslyn bin folder in C:\bin\roslyn. I've searched the project files for any clues as to why this is happening but cannot find any reference to it. In the build log it shows that when the project that creates the Roslyn folder under it's output dir, the files are copied with an absolute destination path (C:\project output dir\bin\Roslyn), whereas in the project that creates it under C:\bin\Roslyn, the files are listed as just copied to \bin\Roslyn. Should a project setting point to the output directory ($(OutputDir) is set). Any pointers would be greatly appreciated.
I know of 3 ways to investigate MSBuild issues:
In general, I found binary logs (binlogs) to be most useful. Create one by passing the -bl, then open the generated file in the MSBuild Structured Log Viewer. It shows very similar data to the build log, but has a good search and shows what happened in a tree view that greatly helps understanding.
What I think will probably be of most use to you for this specific issue is outputting the pre-processed file by running msbuild -pp or dotnet msbuild -pp. This basically finds all MSBuild Import statements, and replaces them with the actual contents of the imported file. I believe that MSBuild always evaluates top-down. So if property1 is defined, then is used to evaluate property2, then later property1 changes, the value of property2 will retain its value from when it was evaluated. Keep in mind that execution of targets cause the target to be evaluated, so it can use a property that was defined further down the file, as long as the lower down target was run first, or the properties or items defined further down are global (not in a target).
Finally, if all else fails, you can try setting the log output to the highest verbosity, diagnostic. Note that msbuild -v:d is detailed verbosity, you need msbuild -v:diag to set diagnostic verbosity. I'm not sure if this actually outputs anything more than what's in the binlog, but I think there might have been one or two times I was desperate and the diag output helped (but I can't remember if I used the binlog in those occasions). Anyway, worth a try if the other two methods above don't help.
Thanks for the pointers, Icepickle is correct in questioning if there is a problem, the solution works but there is just a redundant folder placed off the root drive so it's not ideal. I have sorted the problem by going through the solution build (in Diagnostic Verbosity), and in all the projects that are copying files to the root folder I added the following in their project files. This points to the correct path and will only copy if the files aren't already there.
<Target Name="CopyRoslynCompilerFilesToOutputDirectory" AfterTargets="AfterBuild">
<ItemGroup>
<RoslynFiles Include="$(CscToolPath)\*" />
</ItemGroup>
<MakeDir Directories="$(SolutionDir)$(SolutionName)\bin\roslyn" />
<Copy SourceFiles="#(RoslynFiles)" DestinationFolder="$(SolutionDir)$(SolutionName)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />

Getting Content Files and Primary Output programmatically

For some reason, we have a script that creates batch files to XCOPY our compiled assemblies, config files, and various other files to a network share for our beta testers. We do have an installer, but some don't have the permissions required to run the installer, or they're running over Citrix.
If you vomited all over your desk at the mentions of XCOPY and Citrix, use it as an excuse to go home early. You're welcome.
The code currently has hundreds of lines like:
CreateScripts(basePath, "Client", outputDir, FileType.EXE | FileType.DLL | FileType.XML | FileType.CONFIG);
It used to be worse, with 20 int parameters (one per file type) representing whether or not to copy that file type to the output directory.
These hundreds of lines create upload/download batch files with thousands of XCOPY lines. In our setup projects, we can reference things like "Primary output from Client" and "Content Files from Client". I'd love to be able to do that programmatically from a non-setup project, but I'm at a loss.
Obviously MS does it, either using an API or by parsing the .csproj files. How would I go about doing this? I'm just looking for a way to get a list of files for any of the setup categories, i.e.:
Primary Output
Localized Resources
Content Files
Documentation Files
EDIT:
I have a setup project like Hath suggested, and it's halfway to what I'm looking for. The only problem keeping that from being a perfect solution is that multiple projects depend on the same assemblies being in their own folder, and the setup will only copy the file once.
Example:
Projects Admin, Client, and Server all rely on ExceptionHandler.dll, and Admin and Client both rely on Util.dll, while Server does not. This is what I'm looking for:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
ExceptionHandler.dll
Util.dll
Server
Server.exe
Server.exe.config
ExceptionHandler.dll
Since the referenced assemblies are all the same, what I get is this:
Admin
Admin.exe
Admin.exe.config
ExceptionHandler.dll
Util.dll
Client
Client.exe
Client.exe.config
Server
Server.exe
Server.exe.config
This causes a FileNotFoundException when either Client or Server can't find one of the two DLLs it's expecting.
Is there a setup property I'm missing to make it always copy the output, even if it's duplicated elsewhere in another project's output?
EDIT AGAIN: All referenced DLLs are set to "Copy Local", and always have been. I found a decent article on using NAnt and XSLT to grab the list of files, so that may be a possible solution as well, as neouser99 suggested.
ACCEPTED SOLUTION: I'm pretty much back where I started. All .exe and .dll outputs are put into a "bin" directory in the setup project, loosely packed. The other per-application folders contain shortcuts to the executable in that directory.
The difference now is, I'm going to add a custom action to the installer to use reflection, enumerate the dependencies for each executable output, and copy the .exe and .dll files to the separate directories. Bit of a pain, as I just assumed there was a way to programmatically detect what files would be included via some setup library.
why not use another setup project and just set the 'Package files' setting to As Loose uncompressed files (setup project->properties)? then share the folder.. or something.
edit:
I see, you have 3 folders for your outputs. but the setup project only detects the ExceptionHandler.dll and Util.dll once, so it will just pick the first folder and put it in there.
You could do a setup project for each project - bit annoying maybe..
You could manually add in the dll's to the projects that are missing the assembly's
either by adding in the File by 'add file' or 'add assembly' or 'add project output' if you have those projects in the same solution.. (I doubt that's the case though).
or just dump all of them into one output directory...
Although it's designed as a build tool, you might find NAnt to be extremely useful in what you are talking about. The tasks (build, copy, move, delete, etc.) that you can define allow for very fine-grained file lookups, up to general, full folders. If you also incorporate NAnt into your build process, I think you could find that it helps out in more ways then one.
Another approach that has worked for me in the past is to add the shared resource (Assembly, DLL or project) as a reference to each of the Admin, Server and Client projects. Then open the properties panel for the referenced item in each project and set "Copy Local" to true.
Now when you build the projects, each will have its own instance of the Assembly copied into its output folder.
This should also cause the shared components added in this manner to be replicated in each of the output folders in the setup package.
A completely different approach could be to set them up as symbolic links on the network share. A symbolic link is basically a short-cut where the file-system hides the fact that it is a short-cut, so all other applications actually believes that the file has been copied (http://en.wikipedia.org/wiki/NTFS_symbolic_link).
One advantage of this approach is that the file is updated immediately as the file changes and not only when you build your projects. So when you for instance save one of the config-files with a text-editor the update is applied immediately.
The following MSBuild script part can build your SLN file (you can replace it with .csproj) and will report a list of all projects that were build (Dlls, EXEs).
<MSBuild Projects="MySolution.sln" Targets="Clean; Rebuild" Properties="Configuration=$(BuildMode);">
<Output TaskParameter="TargetOutputs"
ItemName="AssembliesBuilt" />
</MSBuild>
Now, this doesn't really solve your problem, but it gets you a list of everything that was build. You also have copylocal, so you could probably just take AssembiesBuild and copy all DLL and .CONFIG files from there.
Example:
AssembliesBuild = c:\myproj\something1\build.dll
you'd go to c:\myproj\something1\ and simply search for all *.dll and *.config files and include them. You can do this pretty easily with MSBuild or powershell, if you have it installed. To output a XCOPY script from MSBuild, I think you'll need MSBuild contrib projct installed.

How do I automate repetitive tasks post-build?

I run an ASP.NET website solution with a few other projects in it. I've known that MSBuild projects are capable of this, but is it the best way? Are they easy to create? Is nAnt, CruiseControl.NET or any other solution better?
When I build the site (using Web Deployment Projects), can I automate part of the build so that it does not copy certain folders from the project into the Release folder? For instance, I have folders with local search indexes, images and other content part of the folder, but I never need or upload those when deploying the project.
I'm also looking toward this type of solution to automatically increment build and version numbers.
Here's an example of a Web Deployment Project scripting this sort of task in the .wdproj file:
<Target Name="AfterBuild">
<!-- ============================ Script Compression============================ -->
<MakeDir Directories="$(OutputPath)\compressed" />
<Exec Command="java -jar c:\yuicompressor-2.2.5\build\yuicompressor-2.2.5.jar --charset UTF-8 styles.css -o compressed/styles.css" WorkingDirectory="$(OutputPath)" />
<Exec Command="move /Y .\compressed\* .\" WorkingDirectory="$(OutputPath)" />
<RemoveDir Directories="$(OutputPath)\sql" />
<Exec Command="c:\7zip-4.4.2\7za.exe a $(ZipName).zip $(OutputPath)\*" />
</Target>
This would allow you to delete a folder.
(I suspect that if you wanted to not have the folder copy over at all, the solution file would be the place to specify that, though I haven't had to use that.)
MaseBase, you can use Web Deployment Projects to build and package Web Sites. We do that all the time for projects with a web application aspect. After you assign a WDP to a Web Site, you can open up the .wdproj file as plain-text XML file. At the end is a commented section of MSBuild targets that represent the sequence of events that fire during a build process.
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.WebDeployment.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="BeforeMerge">
</Target>
<Target Name="AfterMerge">
</Target>
<Target Name="AfterBuild">
</Target>
-->
You can uncomment the targets you want (e.g. "AfterBuild") and insert the necessary tasks there to carry out your repeated post-build activities.
You can set the Build Action/Copy to Output Directory property on individual files (select the file and hit F4 to open the properties window) to control what happens to them during build, but not for folders. This could probably be automated with a (pre) build task if you don't want to do it manually.
Alternatively, you can exclude these folders from the project (right click and 'exclude from project'); they'll still be there ("show all files" in solution explorer), but they won't be included when building the project.
CruiseControl.NET solves a different problem (continuous integration) ... however, I've had great success with NAnt for specifically what you're asking. There's a learning curve, but once you get proficient you'll wonder how you ever got along w/o it.
In addition to #Fredrik's tip about setting project items to "Copy to Output Directory", you can also specify a post-build action in the project's properties in the Build tab and include CMD commands like copy.exe and move.exe.
We use FinalBuilder to automate a bunch of post build / pre build tasks. There's also a web interface so you can kick off builds (or push websites) by logging in to the web site and clicking a button.
http://www.finalbuilder.com/
Can't you edit the Web Deployment project's MSBuild file for it to do what you want?

Categories