Visual Studio Post Build Event - xcopy to different relative directory - visual-studio

Essentially, what I want to do is use a wildcard for a directory.
The post build event is on a PageComponents project:
PageComponents
WidgetTemplates
WidgetTemplate1
Usercontrol1
WidgetTemplate2
Usercontrol2
And I want to copy all user controls to a Web project, but to a different relative location:
Web
CtrlPresentation
Usercontrol1
Usercontrol2
I tried to wildcard the WidgetTemplates directory:
xcopy "$(ProjectDir)WidgetTemplates\*\*.ascx" "$(SolutionDir)Client.Web\CtrlPresentation" /y /s
But this fails all together. So then I tried the following:
xcopy "$(ProjectDir)WidgetTemplates\*.ascx" "$(SolutionDir)Client.Web\CtrlPresentation" /y /s
But this copies each individual WidgetTemplate folder over as well.
Is there a way to achieve what I'm trying to do?

Try using a Task.
<ItemGroup>
<MySources Include="$(ProjectDir)WidgetTemplates\**\*.ascx" />
</ItemGroup>
<Target Name="CopyOver">
<Copy SourceFiles="#(MySources)" DestinationFiles="#(MySources->'$(SolutionDir)Client.Web\CtrlPresentation\%(FileName)%(Extension)')">
</Target>
and use this target after your Build
<BuildDependsOn>
$(BuildDependsOn);
CopyOver
</BuildDependsOn>

Related

Post-Build event msbuild. Rename file at end of successful build

I don't know MSbuild scripting and don't have time to learn it right now. I need a method at the end of successful build to rename a dacpac file to include the version that is currently being built.
Example: (TfsDropLocation)\filename.dapac to (TfsDropLocation\filename.1.0.0.0.dacpac)
Is there a way to do this?
Is there a way to do this?
The answer is yes. If you don't mind editing the Visual Studio project file, then there is a simple solution that allows you to use a macro which looks like this:#(VersionNumber):
To accomplish this, unload your project. Then at the very end of the project, just before the end-tag, place below scripts:
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
</PropertyGroup>
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<ItemGroup>
<VersionNumber Include="#(Targets->'%(Version)')"/>
</ItemGroup>
</Target>
Now as promised, the assembly version is available to your post build event with this macro. So you add rename the file name by copy task in the post-build event with below command line:
copy /Y "(TfsDropLocation)\filename.dapac" "(TfsDropLocation)\filename.#(VersionNumber).dapac"
If you do not want to keep the previous filename.dapac, you can add a del command in the post-build event:
del "(TfsDropLocation)\filename.dapac"
Note: Do not ignore double quotation marks in the post-build event command line.
Then you can check you output and windows explorer, I used the file dll to test, you can check my test result:

Copy entire directory to output folder maintaining the folder structure?

I want a specific directory to be copied to output folder ("bin") on every build. I think it can be handled via post build scripts. But I'm not sure how to copy a directory itself. I know how to handle specific files.
For eg, this works for a file:
In
Project > Properties > Build Events> Post Build
COPY "$(SolutionDir)Resources\Release Notes.pdf" "$(TargetDir)"
But suppose I have a directory Template, now I need everything under Template to come to bin folder upon successful build maintaining the folder structure.
I tried this:
COPY "$(SolutionDir)Resources\Template\" "$(TargetDir)"
Only the files in Template directory gets copied this way and not the sub directories and the files inside Template folder. I want the folder Template itself to come inside my output bin folder. In other words, bin should look like:
bin > Template > abc.xxx
xxx.yyy
Subdirectory1 > asd.qwe
zxc.qwe
Subdirectory2 > ...
This could be a duplicate, but I couldn't find a relevant thread. Thanks.
I just added this to my *.csproj file (right click Edit Project File)
<ItemGroup>
<Content Include="MYCUSTOMFOLDER\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
I think for this the directory needs to be on same hierarchy level as *.csproj file or bellow that.
This worked for me. /S is the key which copies everything recursively.
XCOPY "$(SolutionDir)Resources\Template" "$(TargetDir)\Template\" /S
Since I wanted files to be overwritten every time without a prompt, I added a /Y switch as well.
XCOPY "$(SolutionDir)Resources\Template" "$(TargetDir)\Template\" /S /Y
Try XCOPY instead of COPY; e.g.
XCOPY "$(SolutionDir)Resources\Template\" "$(TargetDir)\Template" /s /i /y
More info on XCOPY here...
http://www.computerhope.com/xcopyhlp.htm
Here's an additional solution working on Visual Studio 2019 as of the date of this post. This will copy the folder structure recursively and all files within. Tested on a C++ .vcxproj in a multi-project solution.
First, start by editing your [ .proj / .vcxproj / .csproj ] file. Once open, find your project scoped tag. If you already have ItemGroups within, then paste the code below directly after the existing ones. Otherwise, add it in before the PropertyGroup tags. Then modify the Include & Link parameters for the folder structure you wish to copy to the output path.
<ItemGroup>
<Content Include="..\Assets\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<DeploymentContent>true</DeploymentContent>
<Link>Assets\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>
Note: If you have multiple top level folders, like JS, IMG, BIN, etc., then create a new entry for each one.
The solution by CodingYourLife almost worked for me, but I found out that PreserveNewest was not being respected. I found a solution on the Visual Studio forums that works correctly. My .CSPROJ now looks like this:
<Content Include="assets\**">
<Link>assets\%(RecursiveDir)\%(Filename)%(Extension)</Link>
<TargetPath>assets\%(RecursiveDir)\%(Filename)%(Extension)</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Note: This solution requires Visual Studio 16.10 or newer.
I have a working solution of this question:
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<ItemGroup>
<CommonFont Include="..\common\src\Web\wwwroot\css\fonts\*.*" />
</ItemGroup>
<Copy SourceFiles="#(CommonFont)" DestinationFolder="wwwroot\css\fonts" SkipUnchangedFiles="true" />
</Target>
This is the only solution that worked for me (VS2022, .Net Framework):
<ItemGroup>
<ContentWithTargetPath Include="..\..\..\Libraries\Core\Business\Vodovoz.Reports\Reports\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>Reports\%(RecursiveDir)\%(Filename)%(Extension)</TargetPath>
</ContentWithTargetPath>
</ItemGroup>

Web.config transformation: Unrecognized attribute 'xmlns:xdt'. Note that attribute names are case-sensitive

I'm getting this strange intermitent bug in a MVC 3.0 project
When I build the project sometimes I get the following error message:
Unrecognized attribute 'xmlns:xdt'. Note that attribute names are
case-sensitive.
This is referring to the standard web.config tranformation file (Web.Release.config copied below)
There are no other errors or warnings. This is happening in debug mode and release.
Sometimes it clears if I clean the solution
BEGIN UPDATE
Found the issue. In the MVC Project file (MyProject.csproj) I had set build views to true
<MvcBuildViews>true</MvcBuildViews>
Once put back to false the above error goes away. I'd like to have the view build as it stops alot of stupid view code errors etc and is a performance enhancement (pages are precompiled instead of jit)
Anyone know what this is causing the error? is this a bug?
END UPDATE
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an atrribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
I ran into the very same problem.
You will find lots of banter out there related to MvcBuildViews and various error conditions.
But none seem to mention this particular error.
A quick fix that worked for me was to delete the contents of the "obj" directory for the affected web project, then rebuild.
This is kind of a workaround, but you may add the following line to your pre-build commands:
del $(ProjectDir)obj\* /F /S /Q
Right click your project > Properties > Build Events > Pre-build
This works with Continuous Integration and WebDeploy:
This problem occurs the moment I set
<MvcBuildViews>true</MvcBuildViews>
in my Project file, which I need to do.
After reading and testing everything I found about this problem I have a wokraround, that also works with WebDeploy via MSBuild
MSBUild.exe ... /p:DeployOnBuild=true
You (only) need to delete the TransformWebConfig subfolder in your buildfolder during the pre- AND post-build events. It even works with continuous integration servers which break if no folder exists
Pre-build event command line:
if exist "$(ProjectDir)obj\$(ConfigurationName)\transformwebconfig\" del "$(ProjectDir)obj\$(ConfigurationName)\transformwebconfig\*" /F /S /Q
Post-build event command line:
if exist "$(ProjectDir)obj\$(ConfigurationName)\transformwebconfig\" del "$(ProjectDir)obj\$(ConfigurationName)\transformwebconfig\*" /F /S /Q
This even works fine with Resharper which will sometimes get confused, if you delete the whole obj folder.
Make sure to set the Run the post-build event to always!!
UPDATE:
Replaced debug and release with $(ConfigurationName) and removed resulting duplicate line
I resolve my conflict by doing the same thing that Job said. Removing the attribute
xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"
From the main Web.config and leave it into the Web.debug.config and Web.release.config
There is another workaround from Microsoft Team. See details here.
Just copy-paste this snippet into your .csproj or .vbproj file:
<PropertyGroup>
<_EnableCleanOnBuildForMvcViews Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='' ">true</_EnableCleanOnBuildForMvcViews>
</PropertyGroup>
<Target Name="CleanupForBuildMvcViews" Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='true' and '$(MVCBuildViews)'=='true' " BeforeTargets="MvcBuildViews">
<ItemGroup>
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\Package\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TransformWebConfig\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\CSAutoParameterize\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TempPE\**\*" />
</ItemGroup>
<Delete Files="#(_TempWebConfigToDelete)" />
</Target>
This will automate the process of cleaning 'obj' folder using Build Targets.
just remove the xmlns:xdt attribute from the web.config, but keep it in web.release.config and web.debug.config.
Your transform will still work - and so will your website.
I've found this works better for me:
del "$(ProjectDir)obj\*" /F /Q
del "$(ProjectDir)obj\$(ConfigurationName)\AspnetCompileMerge\*" /F /S /Q
del "$(ProjectDir)obj\$(ConfigurationName)\CSAutoParameterize\*" /F /S /Q
del "$(ProjectDir)obj\$(ConfigurationName)\Package\*" /F /S /Q
del "$(ProjectDir)obj\$(ConfigurationName)\ProfileTransformWebConfig\*" /F /S /Q
del "$(ProjectDir)obj\$(ConfigurationName)\TempPE\*" /F /S /Q
del "$(ProjectDir)obj\$(ConfigurationName)\TransformWebConfig\*" /F /S /Q
otherwise build(s) complain about edmxResourcesToEmbed disappearing.
I have seen this too. Specifically, it had been reproducible when changing between build configurations in Visual Studio.
My workaround previously had been to delete everything in the \obj folder, but after going through my web.config closely, I found it had some erroneous text sitting outside an element (i.e. it was invalid XML).
Seemingly the config transforms was just swallowing up an exception when trying to perform the transformation.
Fixed up my web.config to be valid, and all is working as expected now.
Hope this helps someone
I've also run into this issue. For me it was caused by my having created a new debug configuration called, "DevDebug". I fixed it by making a copy the web.debug.config, named web.DevDebug.config and added it to the project.
Then, when I attempted to run the aspnet compiler, it was satisfied that it could find the right configuration version of the config file to merge in.
I just change below on web.config
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
to
<configuration>
It fixed the problem
Right click on the project, click Publish
Goto Settings -> File Publish Options
Uncheck Precompile during publishing
This will prevent the web.debug.config/web.release.config injecting the file back to the web.config if you are using visual studio publish.

Publish a Web Application from the Command Line

I've got a series of .NET 4 based web applications (WCF and Web) within the same solution, but need to selectively publish, from the command line.
I've tried various things so far, MSBuild, aspnet_compiler, but nothing, to date has worked.
I need to be able to specify the Project, not the solution, have any transforms run and have the output redirected to a folder...basically mimick the right mouse click 'Publish' option, using the File System.
In addition to all of this, I'd like to leave the projects alone - not adding msbuild files all over the place, as this is a specific build, and not necessarily related to the project.
Stuff I've tried:
Publish ASP.NET MVC 2 application from command line and Web.config transformations
Equivalent msbuild command for Publish from VS2008
Save the following script as publishProject.bat
rem publish passed project
rem params: %configuration% %destDir% %srcDir% %proj%
#echo off
SET DestPath=d:\projects\Publish\%2
SET SrcPath=d:\projects\Src\%3\
SET ProjectName=%4
SET Configuration=%1
RD /S /Q "%DestPath%" rem clear existed directory
:: build project
MSBuild "%SrcPath%%ProjectName%.vbproj" /p:Configuration=%Configuration%
:: deploy project
::/t:TransformWebConfig
MSBuild "%SrcPath%%ProjectName%.vbproj" /target:_CopyWebApplication /property:OutDir=%DestPath%\ /property:WebProjectOutputDir=%DestPath% /p:Configuration=%Configuration%
xcopy "%SrcPath%bin\*.*" "%DestPath%\bin\" /k /y
echo =========================================
echo %SrcPath%%3.vbproj is published
echo =========================================
I call it from another batch file
#echo off
rem VS2010. For VS2008 change %VS100COMNTOOLS% to %VS90COMNTOOLS%
call "%VS100COMNTOOLS%\vsvars32.bat"
SET ex=.\publishProject.bat Release
call %ex% KillerWebApp1 KillerWebApp1\KillerWebApp1 KillerWebApp1
call %ex% KillerWebApp2 KillerWebApp2\KillerWebApp2 KillerWebApp2
call %ex% KillerWebApp3 KillerWebApp3\KillerWebApp3 KillerWebApp3
call %ex% KillerWebApp4 KillerWebApp4\KillerWebApp4 KillerWebApp4
EDIT:
Code above works for most cases but not for all. I.e. we use another asp .net application and link it as virtual folder in IIS. For this situation VS2008 worked fine with code above but VS2010 also copy files from virtual directory while deploying. The following code works properly also in VS2010 (solution was found here)
Add to your project file (*.csproj, *.vbproj)
<Target Name="PublishToFileSystem" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''" Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))" Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)" DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="True" />
</Target>
Change publishProject.bat to:
rem publish passed project
rem params: %configuration% %destDir% %srcDir% %proj%
#echo off
SET DestPath=d:\projects\Publish\%2
SET SrcPath=d:\projects\Src\%3\
SET ProjectName=%4
SET Configuration=%1
:: clear existed directory
RD /S /Q "%DestPath%"
:: build and publish project
MSBuild "%SrcPath%%ProjectName%.vbproj" "/p:Configuration=%Configuration%;AutoParameterizationWebConfigConnectionStrings=False;PublishDestination=%DestPath%" /t:PublishToFileSystem
I know this is an old question, but I just learned something, so I decided I'd share: While it is 100% true that the "_CopyWebApplication" target exists and works, as of .NET 4.0 it has been superseded by the "_WPPCopyWebApplication" target in Microsoft.Web.Publishing.targets, which supports new features like web.config transformation syntax, etc.
Have you checked out WebDeploy?
This should do all the steps you need to have - it can bundle up a web app into a deployable file (a ZIP, basically), and there's an "engine" on the server that can interpret that deployable package and do all the heavy lifting for you.
Also see Scott Hanselman's Web Deployment Made Awesome: If You're Using XCopy, You're Doing It Wrong blog post - very enlightening!
My solution for CCNET with the Web.config transformation:
<tasks>
<msbuild>
<executable>C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
<workingDirectory>E:\VersionesCC\Trunk_4\SBatz\Gertakariak_Orokorrak\GertakariakMS\Web</workingDirectory>
<projectFile>GertakariakMSWeb2.vbproj</projectFile>
<targets>Build</targets>
<timeout>600</timeout>
<logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll</logger>
<buildArgs>
/noconsolelogger /p:Configuration=Release /v:diag
/p:DeployOnBuild=true
/p:AutoParameterizationWebConfigConnectionStrings=false
/p:DeployTarget=Package
/p:_PackageTempDir=E:\Aplicaciones\GertakariakMS2\Web
</buildArgs>
</msbuild>
</tasks>

Copy built assemblies (including PDB, .config and XML comment files) to folder post build

Is there a generic way I can get a post-build event to copy the built assembly, and any .config and any .xml comments files to a folder (usually solution relative) without having to write a post-build event on each project in a solution?
The goal is to have a folder that contains the last successful build of an entire solution.
It would be nice to use the same build solution over multiple solutions too, possibly enabling/ disabling certain projects (so don't copy unit tests etc).
Thanks,
Kieron
You can set common OutputPath to build all projects in Sln in one temp dir and copy required files to the latest build folder. In copy action you can set a filter to copy all dlls without "test" in its name.
msbuild.exe 1.sln /p:Configuration=Release;Platform=AnyCPU;OutputPath=..\latest-temp
There exists more complicated and more flexible solution. You can setup a hook for build process using CustomAfterMicrosoftCommonTargets. See this post for example.
Sample targets file can be like that:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
PublishToLatest
</BuildDependsOn>
</PropertyGroup>
<Target Name="PreparePublishingToLatest">
<PropertyGroup>
<TargetAssembly>$(TargetPath)</TargetAssembly>
<TargetAssemblyPdb>$(TargetDir)$(TargetName).pdb</TargetAssemblyPdb>
<TargetAssemblyXml>$(TargetDir)$(TargetName).xml</TargetAssemblyXml>
<TargetAssemblyConfig>$(TargetDir)$(TargetName).config</TargetAssemblyConfig>
<TargetAssemblyManifest>$(TargetDir)$(TargetName).manifest</TargetAssemblyManifest>
<IsTestAssembly>$(TargetName.ToUpper().Contains("TEST"))</IsTestAssembly>
</PropertyGroup>
<ItemGroup>
<PublishToLatestFiles Include="$(TargetAssembly)" Condition="Exists('$(TargetAssembly)')" />
<PublishToLatestFiles Include="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" />
<PublishToLatestFiles Include="$(TargetAssemblyXml)" Condition="Exists('$(TargetAssemblyXml)')" />
<PublishToLatestFiles Include="$(TargetAssemblyConfig)" Condition="Exists('$(TargetAssemblyConfig)')" />
<PublishToLatestFiles Include="$(TargetAssemblyManifest)" Condition="Exists('$(TargetAssemblyManifest)')" />
</ItemGroup>
</Target>
<Target Name="PublishToLatest"
Condition="Exists('$(LatestDir)') AND '$(IsTestAssembly)' == 'False' AND '#(PublishToLatestFiles)' != ''"
DependsOnTargets="PreparePublishingToLatest">
<Copy SourceFiles="#(PublishToLatestFiles)" DestinationFolder="$(LatestDir)" SkipUnchangedFiles="true" />
</Target>
</Project>
In that targets file you can specify any actions you want.
You can place it here "C:\Program Files\MSBuild\v4.0\Custom.After.Microsoft.Common.targets" or here "C:\Program Files\MSBuild\4.0\Microsoft.Common.targets\ImportAfter\PublishToLatest.targets".
And third variant is to add to every project you want to publish import of custom targets. See How to: Use the Same Target in Multiple Project Files

Resources