Include additional files in build using MSBuild - msbuild

In the past we have used a combination of TeamCity and MsDeploy to deploy our projects. We've recently moved into using Octopus deploy, and TeamCity purely for the Build. This meant changing the build process in TeamCity and removing any references to MsDeploy.
In the past we've used confiiguration such as the below in our project file which included additional files and directories:
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<Target Name="CustomCollectFiles" BeforeTargets="BeforeBuild">
<ItemGroup>
<UCommerceAssemblies Include="$(MSBuildThisFileDirectory)..\..\Resources\UCommerce\ucommerce\*" />
<FilesForPackagingFromProject Include="%(UCommerceAssemblies.Identity)">
<DestinationRelativePath>bin\uCommerce\%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
However this now doesnt seem to get invoked whatsoever. It appears (to me anyhow) that these pipelines were created for use with Publishing from Visual Studio, or using MSDeploy, however we need to just include these files in the directory either before or after the build has taken place. There seems to be tons of references across the web about doing this, however they all refer to using MSDeploy.
Can anyone shed any light on how I can include additional files/directories in the build without using MSDeploy?
Thanks for your time in advance
dotdev

As you are using Octopus for deployment you only need to include additional files (reference assemblies, etc.) into code package sent to Octopus.
To do this one needs to use OctoPack for project packaging. Then add .nuspec file into the project, for example this line in .nuspec will add all files from some different location into the .nupkg package under "bin\additional" and will be deployed correctly by octopus.
 
Nuspec docs
Similar solution is discuses here.

Related

MSBuild wildcard matching of files for deployment

I am hoping to be able to use MSBuild to capture a subtree of files produced during the build of a project using Microsoft.NET.Sdk.Web and include them in deployment. So far, I have found that if I simply create the files inside the project folder before deployment, then it works but only for certain filetypes. DLLs, for instance, are excluded, presumably assumed to be non-content items. I have been poking around how the deployment stuff works, and have found the <ResolvedFileToPublish> element that I can put into <ItemGroup>, but I haven't figured out how it might be possible to employ this with wildcards. Specifically, I have a post-build step that places files into a folder deployment within the project, and I want all files in that subtree to be included in the package that is produced by /p:DeployOnBuild=true. How can I tack my files onto the deployment stage so that they're included in the ZIP even if they don't look like content items?
I have found a solution, in the form of adding a new <Task> set to run immediately after the internal tasks which collect files for publishing. This is not suitable for a long-term solution, since it ties to internal state, but this is a temporary fix and as such I think it's alright.
By adding this to the .csproj:
<Target Name="__CopyDeploymentToPublish" AfterTargets="_CopyResolvedFilesToPublishAlways">
<Exec Command="PowerShell.exe -Version 3.0 -ExecutionPolicy Unrestricted $(SolutionDir)deploy_webapp.ps1 -Source $(SolutionDir)src\IQ.Auth.OAuth2.Web -Target $(PublishDir)" />
</Target>
...my PowerShell script runs right after the standard deployment logic aggregates the files it intends to package up. I can at that point do whatever I want to the files and the way they're left is what'll end up in the ZIP file.

Modify files after deployment package is created

I'm in the process of creating a web deployment package through an automatic build trigger on the server.
The package should take care of everything (including creation of a specific website, apppool, and the latest code) on any server desired.
I extracted manually a deploy package from a configured local IIS site, containing all information needed by MsDeploy to create the site, apppool, etc...
They are present in following files
archive.xml
parameters.xml
systeminfo.xml
The idea is now that I would create automatic a deploy package on the build server, that contains the new compiled code, but with the above xml files in the .zip package.
Right now, I'm building the application, after which I execute a PowerShell script that will manually overwrite the files in the .zip with the ones I have.
However, I know you can extend the Target file (with a .wpp.targets file in your project) to plug into the pipeline and modify things along the way.
Unfortunately I'm getting a little lost with the information I found.
I'd like to:
1) configure the creation of the deployment package to use my existing .xml files.
2) if that's not possible, overwrite the files with my own files after the package creation.
My goal is to have a full executable deploy package after the build is finished, so I won't need to PowerShell script anymore.
Any information that will point me closer to a solution or helps me to understand more clearly msbuild targets and/or webdeploy is very appreciated.
I managed doing this by extending the Package MsBuild target.
Adding a .wpp.targets file in the root of the web project with following content.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSBuild.ExtensionPack.4.0\MSBuild.ExtensionPack.tasks"/>
<PropertyGroup>
<DeployFilesDirectory>$(MSBuildProjectDirectory)\Deploy\</DeployFilesDirectory>
</PropertyGroup>
<PropertyGroup>
<OnAfterPackageUsingManifest>
$(OnAfterPackageUsingManifest);
CopyDeployFiles;
ReplaceSetParametersFile;
<!--ZipDeploymentFiles;-->
</OnAfterPackageUsingManifest>
</PropertyGroup>
<Target Name="CopyDeployFiles">
<Message Text="Copy Deploy Files"></Message>
<ItemGroup>
<Files Include="$(DeployFilesDirectory)*.xml" Exclude="$(DeployFilesDirectory)setParameters.xml"></Files>
</ItemGroup>
<MSBuild.ExtensionPack.Compression.Zip TaskAction="AddFiles"
CompressFiles="#(Files)"
ZipFileName="$(PackageFileName)"
RemoveRoot="$(DeployFilesDirectory)"/>
</Target>
<Target Name="ReplaceSetParametersFile" DependsOnTargets="GenerateSampleDeployScript">
<Message Text="Replace Default SetParameters File"></Message>
<Copy DestinationFiles="$(GenerateSampleParametersValueLocationDefault)"
SourceFiles="$(DeployFilesDirectory)setParameters.xml"></Copy>
</Target>
</Project>
The first target is executed after MsDeploy has created the package and will replace the .xml files within the .zip file. I'm using the MsBuild.ExtensionPack Zip support.
The second target is executed after the build has created the sample .cmd and setParameters files and will overwrite the setParameters.xml with my own as well.
It takes a while to understand the concepts of MsBuild targets etc, but once you understand it becomes indeed very powerful.
Creating the package is now as simple as just launching the MsBuild
msbuild "D:\Projects\MyWebProject.csproj" /T:Package /P:Configuration=Release;Platform="AnyCPU";PackageLocation="D:\DeployPackage\package.zip";PublishProfile=MyProfile
And deploying is the same as before
package.deploy.cmd /Y –setParamFile :myParameterFile.xml
Assuming your paths stay the same, you can achieve this by specifying the existing zip as your -dest:package=package.zip. MSDeploy will automatically overwrite the files inside the zip.

MSBuild - Project-specific targets for solution does not work

I have a solution that has multiple projects in it, including a web application. I want MSBuild to execute "WebPublish" target against the web application project and "default target" for all other projects in the solution.
This MSDN article says that I can do it specifying the command line
msbuild SlnFolders.sln /t:NotInSlnfolder:Rebuild;NewFolder\InSolutionFolder:Clean
But I never could make it work - MSBuild return an error, something like "NotInSlnFolder:Rebuild" target does not exist. It does not matter what target to specify, Build, Rebuild or Clean - it does not work in any case.
How can I achieve my goal of specifying project-specific targets for a solution?
The MSDN documentation does not work. Or have I missed something?
NOTE: This workaround is not officially supported by Microsoft, so there is no guarantee that it will work forever.
Short Answer
In folder with the SLN file, create the file before.{YourSolution}.sln.targets, with the following content: (Replace what in curly brackets to whatever you need.)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="{MyCompany_MyProduct_WebApp:WebPublish}">
<MSBuild
Condition="'%(ProjectReference.Identity)' == '{$(SolutionDir)MyCompany.MyProduct.WebApp\MyCompany.MyProduct.WebApp.csproj}'"
Projects="#(ProjectReference)"
Targets="{WebPublish}"
BuildInParallel="True"
ToolsVersion="4.0"
Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"
SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
After that you can execute the command line:
msbuild {YourSolution}.sln /t:{MyCompany_MyProduct_WebApp:WebPublish}
Long Answer
If you add environment variable MSBUILDEMITSOLUTION, setting its value to 1, MSBuild will not delete temporary files generated for the solution and projects.
This will allow you to find {YourSolution}.sln.metaproj and {YourSolution}.sln.metaproj.tmp files generated in the solution folder, which are just standard MSBuild project files.
For MSBuild 3.5, the generated file is {YourSolution}.sln.cache and is retained regardless of environment variables. Analyzing those files, you will understand low-level details of the process and to see the customization opportunities available.
After executing MSBuild with some project-specific target in the .Metaproj file you will find out that the list of project-specific targets is hardcoded and only standard targets are supported (Build, Rebuild, Clean, Compile, Publish; note: Publish and WebPublish are not the same). MSBuild 3.5 only generates Clean, Rebuild and Publish targets as well as a target with just the project's name that means "Build".
You also can see that NotInSlnfolder:Rebuild is just a name of an autogenerated target. In reality MSBuild does not parse it and does not care about project names and location. Also note that the autogenerated target names specify the project name with solution folders hierarchy if it's in one, e.g. SolFolder\SolSubfolder\ProjectName:Publish.
One more critically important thing you will find: The MSBuild Target Name does not support dots. All dots in project names are replaced with underscores. For example, for a project named MyCompany.MyProduct.Components you will have to specify in the command line:
/t:MyCompany_MyProduct_Components:Rebuild
That's why even standard project-specific target Build didn't work - my project name contained dots.
Analyzing file {YourSolution}.sln.metaproj.tmp, you will find out that at runtime it tries to import targets from file named before.{YourSolution}.sln.targets and after.{YourSolution}.sln.targets, if those files exist. This has a key to the workaround for this MSBuild limitation/bug.
You can open your solution file in text editor and check whether following line is exist or not if not then you can add
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> inside the <Project> tag.
Hope this help you.

Running custom MSBuild tasks as part of the git deployment to Azure WebSites

Problem
As part of my csproj I have a custom MSBuild task that executes the YUICompressor and generates a compiled css and js file.
<PropertyGroup>
<CssOutputFile>$(OutDir)..\Styles\compiled.css</CssOutputFile>
<JavaScriptOutputFile>$(OutDir)..\Scripts\compiled.js</JavaScriptOutputFile>
<BuildDependsOn Condition="'$(Configuration)' != 'Debug'">
$(BuildDependsOn);
CompressorTarget;
</BuildDependsOn>
</PropertyGroup>
This runs fine as part of the git deployment and the file is being generated, however the Azure Web Sites deployment engine will then copy all the output files to another folder. In that process it seems it takes whatever you have in your csproj instead of whatever you have in the folder. That menas that the generated compiled.css and compiled.js won't be copied (because they are not in my csproj)
What Azure does to deploy your project should be exactly the same as if you do the following:
Right click on the project and choose Publish
Change the Publish Method to 'File System'
Enter a path and click Publish
So generally, you'll want to make sure that your build process works such that you get the right file when you do this local publish. If it does, then chances are you'll get the same results when git pushing to Azure.
The workaround I used for now is adding an empty compiled.css and .js file to the csproj and I wanted to write this question in case someone goes through the same thing.
It would be great if someone from MS can comment if there are plans on doing something different for this scenario.
Depending on where you place the compiled scripts, you can use star-includes in your project file:
<ItemGroup>
<Content Include="assets\**\*" />
</ItemGroup>
If Azure uses your project file to determine what gets deployed (which seems somewhat strange to start with), then that should work.

MSBuild doesn't pick up references of the referenced project

I bumped into a strange situation with MSBuild just now. There's a solution which has three projects: LibX, LibY and Exe. Exe references LibX. LibX in its turn references LibY, has some content files, and also references to a third-party library (several pre-built assemblies installed in both GAC and local lib folder). The third-party library is marked as "Copy Local" ("private") and appears in the output of the LibX project, as the LibY's output and LibX's content files do. Now, Exe project's output has LibX project output, content files of the LibX project, LibY project output (coming from LibX), but NO third-party library's assemblies.
Now I worked this around by referencing the third-party library directly in Exe project, but I don't feel this is a "right" solution.
Anyone had this problem before?
There is a difference in behavior when building with MSBuild (i.e. command line, TFS Build and other tools) compared to building with Visual Studio. The secondary references are not included in the references variable sent into MSBuild compile tasks.
There are several extension points provided by MSBuild to change how references are to be resolved. I have successfully used AfterResolveReference to fix this issue for some of my projects - I have posted more info about the background on my blog.
The workaround is to add the following code into you vbproj or csproj files
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencyies-->
<ItemGroup>
<ReferencePath Include="#(ReferenceDependencyPaths)">
</ReferencePath>
</ItemGroup>
</Target>
Microsoft has stated that this is a won't fix on Connect
You can actually go into the Microsoft.CSharp.targets or Microsoft.VisualBasic.targets file (located in the framework directory, usually C:\Windows\Microsoft.NET\Framework\v3.5) and modify the csc or vbc task parameters to include additional reference dependencies. In the file (VB targets, line 166; C# targets, line 164) change:\
References="#(ReferencePath)"
to
References="#(ReferencePath);#(ReferenceDependencyPaths)"
This might cause other issues depending on how complicated things are and it may play tricks with the Visual Studio inproc compiler, but it's the only way to do it in MSBuild that I've found.
josant's answer almost worked for me; I kept getting an error in Visual Studio when I tried that:
A problem occurred while trying to set the "References" parameter for the IDE's in-process compiler. Error HRESULT E_FAIL has been returned from a call to a COM component
The solution to my problem was to put a condition on the ItemGroup, like this:
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencies-->
<ItemGroup Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">
<ReferencePath Include="#(ReferenceDependencyPaths)"></ReferencePath>
</ItemGroup>
</Target>
That caused Visual Studio to ignore the reference change completely, and the build works fine locally and on the build server.
Yes, I've had that problem, too. Though I'd love to say otherwise, I believe you must include all transitive dependencies as references in your build file.
I've combined Alex Yakunin's solution with one that will also copy native dll's.
The AfterResolveReferences method fails if you've got a directed graph not a tree with a "trying to deploy different copies of the dll" error. (cf. How to configure msbuild/MSVC to deploy dependent files of dependent assemblies)