Recent libgit2sharp Nuget uses a new Nuget feature that allows you to include a piece of a build script in your NuGet. The purpose it to copy a native dll to a subfolder of the bin folder, like that:
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\..\..\lib\net40\NativeBinaries\amd64\git2-e0902fb.dll">
<Link>NativeBinaries\amd64\git2-e0902fb.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>...
Now, it was all nice and beautiful locally, but when I deployed it to AppHarbor, the native dlls appeared in the /bin folder (in addition to the target subfolder), which caused my app to fail.
The problem lies in the _CopyWebApplicationLegacy target, which does not execute locally (it's run only if you have a non-default output dir), thus I don't have this problem on my dev machine. Namely, it executes the following piece of code:
<!-- Copy items that have been marked to be copied to the bin folder -->
<Copy SourceFiles="#(_SourceItemsToCopyToOutputDirectory)"
DestinationFolder="$(WebProjectOutputDir)\bin"
SkipUnchangedFiles="true"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
You can see that the target folder is always /bin -- I believe it's a bug in the Microsoft.WebApplication.targets file (I can't control it on the target machine).
Is there a simple fix, or should I revert to a script in the PostBuild event (which I'll have to update with each new version)?
As mentioned here: https://github.com/libgit2/libgit2sharp/issues/1089#issuecomment-111000722
the way AppHarbor is building your project, it's triggering the old _CopyWebApplicationLegacy target, and that is basically broken. It messes up all files that are using the Copy to Output Directory property by putting them directly into the output directory instead of respecting the relative folder structure. It also doesn't run any web.config transforms you may have.
You can make your project use the newer _CopyWebApplication target instead by adding the following to your project file:
<UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication>
<PipelineDependsOnBuild>False</PipelineDependsOnBuild>
The thing I'm not sure about is if AppHarbor has some reason why they wouldn't want you to use the newer copy target instead of the old broken one.
Related
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.
I have a solution file (MySolution.sln) with a single project in it (MyProject.vcxproj). I would like to execute a custom target (MyCustomTarget) on my project through the solution. It would look something like this:
msbuild MySolution.sln /t:MyCustomTarget
When I execute the command, I'll get an error message:
MySolution.sln.metaproj : error MSB4057: The target "MyCustomTarget" does not exist in the project. [MySolution.sln]
You can replace MyCustomTarget with any standard targets from Microsoft.Cpp.Win32.targets (e.g.: ClCompile, Link) or any other target of your choice you include from .targets files in MyProject.vcxproj. None of them would work.
When the environment variable msbuildemitsolution is set to 1, I can inspect the generated MySolution.sln.metaproj file. At the bottom 4 targets are specified: Build, Rebuild, Clean, and Publish. Using these targets instead of MyCustomTarget, the project builds ok. Also, if I specify the project file instead of the solution file, it builds too:
msbuild MyProject.vcxproj /t:MyCustomTarget
But using this format, I will lose the OutDir property, manually have to set the Configuration and Platform, so I just lose the benefits of having a solution file.
Is there any way I can use my custom target with the solution file I originally intended?
As far as I understand the problem is that msbuild generates this intermediate project file (mysolution.sln.metproj) but that will won have the imports from MyProject.vcxproj, including the .targets files. No wonder MyCustomTarget is not recognized.
My current workaround is using the project file with msbuild and trying not to miss anything from the solution file:
msbuild MyProject.vcxproj /t:MyCustomTarget /p:Configuration=MyConfig;Platform=MyPlatform;OutDir=MySolution\Platform_MyConfig\
But this is not a proper solution, inflexible, prone to error and does not automatically adapt changes in the solution file.
MSBuild 15 now raises custom targets automatically into the solution metaproj so your initial approach of running the target directly on the solution is now supposed to work.
I think you already answered your question. The answer is NO. There is no target called "MyCustomTarget" inside the .sln.metaproj file, so MSBuild gives you that error message.
Now, to resolve your problem with passing extra parameters on command line. Passing platform and configuration won't be required, if you set defaults in your .vcxproj file. Add this somewhere in your project file, before any of the standard target files are imported:
<Platform Condition="'$(Platform)'==''">MyPlatform</Platform>
<Configuration Condition="'$(Configuration)'==''">MyConfiguration</Configuration>
Configuring OutDir, which is shared across all projects in solution can be done like this. I will assume your solution is structured so that .sln file is in root folder, and all projects are in sub-folders (arbitrarily deep) under the root, or in the same folder as the solution. If this is not the case, you will have to tweak the code a little to adjust to your situation.
Right after you defined Platform and Configuration in your project, add this property group:
<PropertyGroup>
<RootFolder>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),MySolutionName.sln))</RootFolder>
<OutDir Condition="'$(OutDir)'==''">$(RootFolder)\$(Platform)_$(Configuration)</OutDir>
</PropertyGroup>
The code above follows your convention of setting OutDir to MySolution\MyPlatform_MyConfiguration.
The downside of all this approach is that you have to manually modify all projects in your solution. However it will give you lots of flexibility in the future. For example, any common settings shared across all projects, could be extracted into single .props file that you can <Import> into every project, so changes to configuration could be done in one place.
In order to use the custom target that exists in your project file while building using the solution file, use the following format:
msbuild MySolution.sln /t:MyProject:MyCustomTarget
Note that if the project is in a sub folder (solution folder) you need to add the folder name:
msbuild MySolution.sln /t:src\MyProject:MyCustomTarget
and if the project name contains spaces or dots they are replaced with underscores.
I have an automated build process set up to run from a build definition in TFS, which publishes a web application and generates/executes a database project script successfully via publish profiles that are passed as msbuild arguments in the build process definition. Everything is now running as expected except that several unnecessary files are being deployed to the target folder, including the .csproj file, all of the config transforms, and the properties folder which contains all of my publish profiles.
This is strange because 1. It's definitely not including ALL files/folders and mostly appears to be including ones used by the publish profile like transforms, while applying the transform correctly and excluding any explicitly excluded file (as defined in the pubxml), and 2. The process works perfectly if I do it by publishing from the project in Visual Studio 2013. I have the profile configured to only include files needed by the application, and I've confirmed in the csproj file that this property is there.
I tried excluding the properties folder from deployment in the pubxml file, but this causes the build to crash because it can't find the assembly file. What I've gathered is that the process is keeping all files it needs to complete the build, and dropping all of those files in my destination folder. FWIW, I'm using the "file system" method and I'm not sure yet if web deploy will make a difference. I haven't been able yet to connect to the target server with web deploy, but that's a separate problem to solve. Is there something in the build that I can configure so that my destination folder has only the files it needs to run the application, and not the files needed to BUILD the application?
FYI I also have not been using a drop folder, I'm not sure if that makes a difference or not but that might be the only thing I haven't tested as it doesn't seem necessary since I'm using a publishprofile and don't want to use the default tfs build configuration.
I found a solution that works well enough, after reading this: http://www.asp.net/web-forms/tutorials/deployment/advanced-enterprise-web-deployment/excluding-files-and-folders-from-deployment
This was a little uglier solution than I wanted, since it requires hard-coding the names of excluded files, but it does the trick and only requires identifying the files and folders in one location instead of altering a publish profile for each target environment. I created a wpp.targets file and used the ExcludeFromPackageFolders and ExcludeFromPackageFiles elements to identify the extra files. Ironically, if I don't also name the wpp.targets file in the exclude element, THAT file is included in my package. It's possible MSDeploy doesn't have the same issues with TFS as filesystem, but after spending half a day trying to work through a different set of issues and permissions workarounds, we decided that file system is a cleaner publishing method.
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.
Elsewhere on the Web, you can find recommendations on using something like this to simulate the Publish feature in the VS 2005-2008 IDE from a command-line (I hope I did not goof up the syntax!):
msbuild /t:ResolveReferences;_CopyWebApplication /p:BuildingProject=true;OutDir=C:\inetpub\wwwroot\ blah.csproj
Now, it looks like the .dll's copy fine. However, there are certain configuration files and template files that are copied to the bin folder which are needed for the app to work. For example, an NHibernate configuration file shows up in blah.csproj as:
<None Include="blah.cfg.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
While using Publish from within the IDE copies this file as it should, the aforementioned _CopyWebApplication target does not. I need this file to be copied in the build script. Is this desired behavior for _CopyWebApplication? Any recommendations on how to fix this?
EDIT 4/21/2010:
Let me clarify that we are limited (for now) to VS 2005 and VS 2008 projects, and that our build scripts are written for MSBuild 3.x. We are not yet ready to move to VS 2010.
Let me also specify that we are looking for a solution available from within a command line so that we can automate a Publish-like command along with custom build options, and possibly automate deployments down the road.
This is just a workaround.
In the build script for publishing Web sites, after running MSBuild on the Web project itself to publish it (Targets="ResolveReferences;_CopyWebApplication"), I added a copy operation:
<Copy SourceFiles="#(ProjectBinFiles)" DestinationFolder="$(StageBin)\%(ProjectBinFiles.RecursiveDir)" />
where ProjectBinFiles is an Item representing the files in the bin directory in the source directory, and StageBin is a Property representing the bin folder in the published site's directory. So far, it seems to work.
I was having a similar issue as well. I think the answer is using MSDeploy. Investigating it now, but may provide functionality required...
I had this same issue in VS 2012 and eventually fixed it by doing the following to any files which needed to be copied:
Set the Copy to Output file property to Copy if newer
Set the Build Action file property to Content (or Compile if the file needs to be compiled)