c# copying files to bin directory - c#

I'm able to have files put into the bin directory using the properties "copy if newer".
The problem I have is that I require a large number of dll files to be put along side my application. This means that my project becomes littered with a large number of files.
I'd like to put my files into an assets folder in my solution but have the files present in the bin directory on build. I have tried using post build events but I keep getting windows error codes (even when it's successful).
Is there an alternative way to have the external dlls accessible to my application?

If you unload the project file you can edit the .csproj file.
Near the end you'll find:
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
-->
Lets modify that.
First define which Items the AfterBuild task is going to copy. Notice that you only have to make sure these files exist on your disk in a folder (you say it is called assets) and are under source control. You don't need to include any of those files in your solution or project.
<ItemGroup>
<!-- Include relative to this project file, so ..\assets would bring you to the solution folder
Take all files in the assets folder and subfolders, except *.txt files
-->
<Asset Include="assets\**" Exclude="*.txt">
</Asset>
<!-- take all *.txt files -->
<TextAsset Include="assets\**\*.txt">
<!-- meta data -->
<SubPath>TextFiles</SubPath>
</TextAsset>
</ItemGroup>
Now you'll have two Item collection, one called Asset and one called TextAsset. Those items can be consumed in build Tasks. We are going to use the Copy task. I've commented the build script to explain what happens.
<!-- this does what the name suggests-->
<Target Name="AfterBuild">
<!-- log -->
<Message Importance="high" Text="Start Copying assets"/>
<!-- copy Asset files to one folder (flattens) -->
<Copy SourceFiles="#(Asset)"
DestinationFolder="$(OutputPath)" />
<!-- copy TextAsset files to a subpath, keep folder structure-->
<Copy SourceFiles="#(TextAsset)"
DestinationFiles="#(TextAsset->'$(OutputPath)%(SubPath)\%(RecursiveDir)%(Filename)%(Extension)')" />
<!-- done logging -->
<Message Importance="high" Text="Copied assets"/>
</Target>
Notice that I used the property $(OutputPath) which is one of the well known properties. A similar list exists for item meta data.
The changes will not affect the operation of visual studio. Your changes will be preserved when adding or removing regular project items and/or project settings. As you'll keep this file in source control, also on your buildserver the same targets will be run, doing the same copy.
I prefer to test these build targets from the commandline, specifying only the target I'm interested in, like so:
msbuild Application2.csproj /t:AfterBuild
That gives you a much quicker round-trip time instead of doing a full build.

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.

How to copy Content linked files to project directory without causing duplicates

I am trying to reference some javascript files in my project that are coming from a nuget package. So i added these files as a Content Link reference.
<Content Include="$(NuGetPath_jQuery)\Content\Scripts\**\*.*">
<Link>Scripts\%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
But that I can F5 debug the web project, I need these files to be a part of the project folder structure. I am also using these files in a ScriptBundle so the physical directory structure needs to be present for locally debugging. As a result, I ended up adding the following build ttask that copies the content linked files as per Matt Perdeck's blog which is referenced in a lot of other threads around content linking as per below.
<Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
<Copy SourceFiles="%(Content.Identity)"
DestinationFiles="%(Content.Link)"
SkipUnchangedFiles='true'
OverwriteReadOnlyFiles='true'
Condition="'%(Content.Link)' != ''" />
</Target>
Now VS is happy while F5 debugging. But complains on loading the project the next time about not being able to add the content links because the files already exist. Which were copied in the previous build. Am I missing some secret sauce to tell VS to ignore these warnings? I noticed that msbuild does not throw any such warning. It is only VS that is complaining. I see warnin like below :
Warning The file 'C:\Users\pkorhale.nuget\packages\jquery\3.3.1\Content\Scripts\jquery-3.3.1.slim.min.js' could not be added to the project. Cannot add a link to the file jquery-3.3.1.slim.min.js. There is already a file of the same name in this folder.
Turned out, that I needed to turn off the visibility for this content. Anyways the file gets copied over physically so we end up seeing it in the solution explorer after the first build. Which was good enough for me.
<Content Include="$(NuGetPath_jQuery)\Content\Scripts\**\*.*">
<Link>Scripts\%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</Content>

How to include dynamically generated files in Visual Studio's publish profile

Our build automatically bundles javascript/css files together, and adds a checksum to the name of the file for easy verification. Because these are auto-generated and the names change, I can't include them into the solution. I've tried looking through the msdn links, but I can't find a full schema for all the possible tags.
Stuff I've found but haven't been able to make sense of:
How to Edit deployment settings in Publish Profile
I've also seen this answer on SO, but I haven't been able to make it work, it tries to put it in the obj folder, instead of the publish folder, and again, I can't find the schema to try and figure out how to redirect it.
Ideally, the final goal is to have the publish profile copy these files that sit under the bundles folder in the project to the bundles folder in the publish directory as specified in the PublishProfile.pubxml file.
Thank you for any help!
I ended up finding someone with a similar issue, who helped me understand what I needed to change in the linked SO answer
I ended up with the following structure, which grabs all css and js files from the root of the project (it looks like you can just invent those JSFile and CSSFile tags, they're just names to be used later), and then sending them to the DestinationRelativePath tag, which needs that %(Filename)%(Extension) bit (otherwise it just tries to create a file called bundles). JSFile.Identity seems to give a list of files.
This is what I ended up with. Notice that CSS has the RecursiveDir part, but it didn't actually do anything, and both local publish and teamcity published everything correctly.
<PropertyGroup>
<CollectFilesFromContentDependsOn>
AddFilesToDeploy;
$(CollectFilesFromContentDependsOn);
</CollectFilesFromContentDependsOn>
</PropertyGroup>
<!--Add files to deploy -->
<Target Name="AddFilesToDeploy">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="CurrentAssembly" />
</GetAssemblyIdentity>
<ItemGroup>
<JsFile Include="bundles\*.js" />
<CssFile Include="bundles\*.css" />
<FilesForPackagingFromProject Include="%(JsFile.Identity)">
<DestinationRelativePath>bundles\%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
<FilesForPackagingFromProject Include="%(CssFile.Identity)">
<DestinationRelativePath>bundles\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>

Remove folder from MSBuild

I'm trying to remove a folder (well, actually I thought it was easier to remove the files inside it) from a build using MSBuild scripts.
I thought the way is removing them from the copy task itself, but what I was thinking it was going to see quite straightforward it's not working (I'm sure because I don't have much idea of this stuff, just read documentation yesterday and today). Here is how I'm trying to remove the folder (or the files inside it) ..App_Data/Email Templates with this space (does the space something to do?).
<ItemGroup>
<SourceRootFiles Include="$(BuildFolder)/**/*.*" Exclude="$(BuildFolder)/**/App_Data/Email Templates/*.*">
</SourceRootFiles>
</ItemGroup>
<Target Name="PrepareBuild" DependsOnTargets="CleanUp">
<Message Text="Preparing the build directory : $(LocalBuild)"></Message>
<MakeDir Directories="$(LocalBuild)" />
<Copy SourceFiles="#(SourceRootFiles)" DestinationFolder="$(LocalBuild)\%(RecursiveDir)">
</Copy>
<Exec Command="FOR /r "$(LocalBuild)" %%f IN (.svn) DO RD /s /q "%%f"" IgnoreExitCode="true" />
</Target>
<Target Name="Build" DependsOnTargets="PrepareBuild">
<MSBuild Projects="$(LocalBuild)\Getting.sln" />
</Target>
Update.
Jenkins is raising this error
:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets(1852,5): error : Copying file App_Data\Email Templates\BuyerRegistrationComplete.htm to obj\Latest\Package\PackageTmp\App_Data\Email Templates\BuyerRegistrationComplete.htm failed. Could not find a part of the path 'App_Data\Email Templates\BuyerRegistrationComplete.htm'. [C:\Builds\Getting\Latest\Build\Web\UI\UI.csproj]
Dont' really know if it's exluding it or not
On githup is a project named MsBuildTasks that contains all kind of custom-actions that you can easily integrate in your project
https://github.com/loresoft/msbuildtasks
From your update tells a "new" story.
In your project-file you reference files in the App_Data folder which WebDeployment wants to copy to deployment. Removing App_Data results in missing files and thus failure.
Either move those files to another location in your project or remove the references to those files.
My suggestion would be to make a separate folder for the templates, App_Data has a different purpose.

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