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?
Related
I'm developing a solution containg multiple web projects that share static content like javascript files.
I've done some research and it seems that proper way of handling it is to add shared files as solution items and then add them as links to web projects:
Then web projects should implement copying these files to output directory (I set them to Content beforehand):
<Project Sdk="Microsoft.NET.Sdk.Web">
...
<Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
<Copy SourceFiles="%(Content.Identity)"
DestinationFiles="%(Content.Link)"
OverwriteReadOnlyFiles="true"
ContinueOnError="true"
Condition="'%(Content.Link)' != ''" />
</Target>
</Project>
Unfortunetely it doesn't work. I tried installing the MSBuild.WebApplication.CopyContentLinkedFiles NuGet package, but it didn't help.
I tried setting Copy to output directory property to Always which make them present in the build:
However scripts were still missing after launching the app from VS:
How can I make this work? I'm looking for a simplest solution possible.
Your setup potentially will work for publish scenarios but for development environment it won't - the wwwroot folder from the project root (i.e. WebApp1\wwwroot and WebApp2\wwwroot) is used to serve the static files, so you need to copy the shared file there on build (possibly exclude it from git also for convenience).
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>
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>
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.
For example after I build the Release version I want it name changed to (for example) MyApp-1.2.exe. After I build the next version I want the exe to be named MyApp-1.3.exe. I still want to be able to run the MyApp-1.2.exe.
I would rather not use any external tool(I know there are nAnt and nMaven) and do it in VS. IF its not possible than I bet nAnt is the better option for me.
You could do it also via MSBuild with the help of the GetAssemblyIdentity task:
<GetAssemblyIdentity
AssemblyFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe">
<Output
TaskParameter="Assemblies"
ItemName="AssemblyIdentities"/>
</GetAssemblyIdentity>
And then rename your .exe file:
<Copy
SourceFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe"
DestinationFiles="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp-(AssemblyIdentities.Version).exe"></Copy>
<Delete
Files="$(MSBuildProjectDirectory)\src\MyApp\bin\MyApp.exe"></Delete>
If you really want to do it in VS, you can (again) do it using the Post-build Command function (from Project properties -> Build Events).
Running a script or batch file that will copy the file and then rename it. I used to do it based on current date (not version) a few years back (not even sure why).
You can do virtually anything in the build events if you can create batch files or scripts.
Using a build system is IMHO preferable to this approach.