I am trying to do a Jenkins-based automated build/deployment of a web application (.NET 4.0). The web application project has several project references, which in turn have binary references third party DLLs.
The problem:
The second-level references (references of project references) are not pulled into the bin folder in the obj\<CONFIGURATION>\Package\PackageTmp\bin folder, used for building deployment packages.
When I build in the visual studio, the second level references are pulled into the regular build output directory.
When building with MSBuild, second level dependencies are not pulled into the regular output directory, nor into the PackageTmp\bin directory.
This is confirmed by MS as a Won't-Fix issue here.
Related questions here, here and here either do not match my problem, or offer solutions that don't work. I've reviewed all answers, not just the accepted ones.
My build command looks like this (using MSBuild 4.0):
MSBuild MySolution.sln /p:Configuration=Integration /p:platform="Any
CPU" /t:Clean,Build /p:DeployOnBuild=true /p:DeployTarget=Package
/p:AutoParameterizationWebConfigConnectionStrings=false
I've tried to manually edit Reference elements in project files, adding <Private>True</Private>, with no success.
I am trying to work around this known issue, so that my second-level dependencies are automatically and correctly pulled into the web publishing temp directory.
My current attempt combines the general approach here (customizing the web publishing pipeline by adding a MyProject.wpp.targets file next to the web project file), combined with some MSBuild code for finding DLLs here. So far this has either produced no results or broken the project file. I am new to custom MSBuild code and find it pretty arcane.
My Question: I am looking for a more complete example that works in my specific case. I think the goal is to intervene in the web publishing pipeline that gathers files for copying to the package temp directory, and adding the second-level dependencies to it.
My custom MyWebProj.wpp.targets looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BRPathFiles Include="$(SolutionDir)..\Common\**\*.dll;$(SolutionDir)**\*.dll" />
<ConfigPathFiles Include="$(SolutionDir)..\Common\**\*.config;$(SolutionDir)**\*.config" />
</ItemGroup>
<Target Name="CopySecondLevelDependencies" BeforeTargets="CopyAllFilesToSingleFolderForPackage">
<RemoveDuplicates Inputs="#(BRPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="BRPaths" />
</RemoveDuplicates>
<RemoveDuplicates Inputs="#(ConfigPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="ConfigPaths" />
</RemoveDuplicates>
<CreateItem Include="%(BRPaths.Identity);%(ConfigPaths.Identity);">
<Output ItemName="FileList" TaskParameter="Include"/>
</CreateItem>
<CreateItem Value="#(BRSearchPath);$(ConfigSearchPath)">
<Output TaskParameter="Value" PropertyName="SecondLevelFiles" />
</CreateItem>
</Target>
<ItemGroup>
<FilesForPackagingFromProject
Include="%(SecondLevelFiles->'$(OutDir)%(FileName)%(Extension)')">
<DestinationRelativePath>$(_PackageTempDir)\bin\%(FileName)%(Extension) </DestinationRelativePath>
<FromTarget>CopySecondLevelDependencies</FromTarget>
<Category>Run</Category>
</FilesForPackagingFromProject>
</ItemGroup>
</Project>
Assuming you have collected all libraries needed at runtime in a folder outside your solution/project, have you tried just using post-build events to copy all these libraries to your main project target directory (bin) and then include that directory in your deployment package using Sayeds method: http://sedodream.com/2010/05/01/WebDeploymentToolMSDeployBuildPackageIncludingExtraFilesOrExcludingSpecificFiles.aspx (also available in this post: How do you include additional files using VS2010 web deployment packages?)?
I have (among others) the following line in my main project's post-build events:
xcopy "$(ProjectDir)..\..\Libraries\*.dll" "$(TargetDir)" /Y /S
In addition to this, I have added the following lines to my .csproj file:
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
PostBuildLibraries;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
</PropertyGroup>
<Target Name="PostBuildLibraries">
<ItemGroup>
<_PostBuildLibraries Include="$(TargetDir)**\*" />
<FilesForPackagingFromProject Include="%(_PostBuildLibraries.Identity)">
<DestinationRelativePath>$(OutDir)%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
Be sure to add these lines after the import of the "Microsoft.WebApplication.targets". Check out the links above for more details.
This makes all the desired libraries available after each build (copied to the project's target directory) and each time I create a deployment package (copied to the obj\<CONFIGURATION>\Package\PackageTmp\bin).
Also, since I'm building my main project, not my solution, I'm using the $(ProjectDir) macro instead of the $(SolutionDir).
Related
Our solution contains ~50 projects. They all import a custom .target file that sets the OutDir variable so that all projects build to a common Binaries folder.
Problem is: MSBuild does not check the OutDir folder for the .dlls but keeps looking inside the OutputPath folder (e.g. bin\Debug). As the OutputPath folder is empty it states that each project is not up-to-date and forces a rebuild. This is not an issue on our TFS build agents but it drastically increases the time between hitting F5 and the application starting on our development machines. Debugging becomes quite a pain.
From the Binaries folder we copy the .dlls to our applications folder structure which we use for generating setups etc. Thus simply dropping the use of OutDir in favor of various OutputPaths is not an option.
Is there any way to tell MSBuild to also check the OutDir folder when looking for existing .dlls?
Following import in csproj files works for me in VS 2015. I added comments about which settings make it fail:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<!-- to distinguish by $(Platform) does not work, a rebuild is triggered since the up-to-date check fails -->
<!-- if IntermediateOutputPath is not set here at all, it does not work either, i.e. it always rebuilds -->
<IntermediateOutputPath>$(SolutionDir)obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
<UseCommonOutputDirectory>False</UseCommonOutputDirectory>
<DisableFastUpToDateCheck>false</DisableFastUpToDateCheck>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Library' ">
<!-- To distinguish by \lib\ does not work, a rebuild is triggered since the up-to-date check fails -->
<!-- <OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\lib\</OutputPath> -->
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(OutputType)' == 'Exe' ">
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(Platform)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
</Project>
The file is included in csproj files just before Import Microsoft.CSharp.targets:
.csproj file:
<!-- position of include is important, OutputType of project must be defined already -->
<Import Project="$(SolutionDir)ComponentBuild.props" Condition="Exists('$(SolutionDir)ComponentBuild.props')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
Also see my own SO question about it:
MSBuild, OutputPath to a lib directory is not honoured
I have some xaps being built by other projects in my solution and I need the xaps to be included in the resulting WSP.
I have a mapped folder Layouts with a sub-folder ClientBin and then in the csproj I have the following:
<ItemGroup>
...
<Folder Include="Layouts\ClientBin\" />
...
<Content Include="Layouts\ClientBin\*.xap" />
</ItemGroup>
...
<Target Name="BeforeLayout">
<ItemGroup>
<XAPFiles Include="..\..\out\$(Configuration)\bin\sl\xap\**\*.xap" />
</ItemGroup>
<Copy SourceFiles="#(XAPFiles)" DestinationFolder="Layouts\ClientBin" OverwriteReadOnlyFiles="true" />
</Target>
When I delete the xaps from the destination folder and open the SP project in VS the package manager shows the layouts folder with nothing in it. And then when I build none of the xaps get packaged in the WSP but the copy operation worked local to the project. If I rebuild nothing changes. If I unload and reload the project then build, the WSP does contain the files I need.
This works for my dev box because I can make sure I'm performing all these steps to keep the package manager happy, but it doesn't work on the team build machine. Are there steps I can take to make sure the package manager grabs those xaps or even other ways to achieve what I'm trying to do?
I created empty placeholders in Layouts\ClientBin for all the xaps to be copied.
This wasn't optimal since it required some manual dependency management, but it works.
The end result is that the package manager is aware of those files existing when you open the project in VS and that they need to be packaged up into the WSP. The copy operation works as before but now the new files are packaged.
I can find plenty of examples of building a single web deployment package from a project file but I want to use the same technique in an automated build environment. When I try to specify the parameter as an msbuild argument I end up with packages created in the /obj directory. I want to specify the output location as a folder into which all project files in solution will be created but this isn't working for some reason?
I just worked this out based on a post from Sayed Ibrahim Hashimi I adpated the approach as outlined below
<PropertyGroup>
<DeployOnBuild Condition=" '$(BuildPackages)'!='' ">$(BuildPackages)</DeployOnBuild>
<DesktopBuildPackageLocation Condition=" '$(BuildPackages)'!='' ">$(DeployPackageLocation)\$(AssemblyName)\$(AssemblyName).zip</DesktopBuildPackageLocation>
</PropertyGroup>
Basically adding the BuildPackages and DeployPackageLocation mean that a root location for deployment packages can be controlled by a solution/global parameter and a build level switch like below
> MSBUILD <SolutionPath>.sln /P:Configuration=Release /P:Platform="Any CPU" /P:BuildPackages=true /P:DeployPackageLocation="<YourOutputLocation>"
There are more details on my blog
In the Visual Studio 2012 Build menu, there's a Publish command. In this you can establish profiles which are saved as .pubxml files in the Properties folder of the Project. I have one such profile that's a simple file copy operation - it just compiles the site and dumps it to a folder.
How can I use msbuild at the command line to publish a compiled web application to a folder?
What I've tried
I've tried the example from this question:
After Publish event in Visual Studio
And the changes and examples given here:
http://www.digitallycreated.net/Blog/59/locally-publishing-a-vs2010-asp.net-web-application-using-msbuild
The latter seems to get closest, but it causes every library Project the .csproj I'm attempting to publish from to throw an error:
Project "MainProj.csproj" (1) is building "ReferencedProj.csproj" (2) on node 1 (default targets).
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(609,5): error : The OutputPath property is not set for project 'ReferencedProj.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Staging' Platform='AnyCPU'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project. [ReferencedProj.csproj]
Done Building Project "ReferencedProj.csproj" (default targets) -- FAILED.
This approach is very similar to what's suggested in this answer:
https://stackoverflow.com/a/7077178/176877
The crucial difference may be that I'm in Visual Studio 2012, rather than VS2008 or 2010.
Old question, but in case this helps anyone, this simple, stripped back approach worked fine for me:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
...
<BuildDependsOn>
$(BuildDependsOn);
PublishOtherProject;
</BuildDependsOn>
</PropertyGroup>
<Target Name="PublishOtherProject">
<MSBuild Projects="pathtomyproject.csproj" Properties="DeployOnBuild=true;PublishProfile=nameOfPublishProfile" />
</Target>
The key line being:
<MSBuild Projects="pathtomyproject.csproj" Properties="DeployOnBuild=true;PublishProfile=nameOfPublishProfile" />
I have a library with a pretty verbose configuration section. I've created an XSD and would like to distribute that with my package so that when a user installs the package, Visual Studio knows about the XSD without the user needing to do anything extra. How do I do this?
You can include any files you want in a nuget package by placing them in the content directory. These will then be installed into the root of your target project when the package is installed. If you're using a nuspec file to build your package you would add the following element under the element.
<files>
<file src="Configuration\MyXsd.xsd" target="content\TargetFolderName" />
</files>
This will create the following file in the target project
\TargetFolderName\MyXsd.xsd
Once the xsd is in the target project visual studio should pick it up automatically for validating your config section.
This has recently become more complicated with SDK style projects and the different ways of referencing nuget packages.
Note the end of this section.
Basically, if a nuget project is referenced by package.config file, the files from the content folder of the nuget package will be copied to the referencing project. If the nuget package is referenced by PackageReference in the project file, the files from the contentFiles folder within the package will be used. It is recommended to include both.
Now if you are using a .nuspec file to configure your nuget package, you can use SynXsiS answer to include the file to both directories:
<files>
<file src="Configuration\MyXsd.xsd" target="content\TargetFolderName" />
<file src="Configuration\MyXsd.xsd" target="contentFiles\any\any\TargetFolderName" />
</files>
However, if you want to configure it in the .csproj file of SDK projects, you have to add the file you want to include in the nuget package into the project file with the following properties:
<ItemGroup>
<None Include="MyXsd.xsd">
<Pack>true</Pack>
<PackagePath>contentFiles\any\any\TargetFolderName;content\TargetFolderName</PackagePath>
</None>
</ItemGroup>
The any\any\ part of the path for the contentFiles specifies for which language (cs, vb, ...) and target framework the file is meant.
Note, that the item not necessarily has to be of type "None" it could also be "Content" and others as described here.
You can find all information for this structure here, general information of the folder structure can be found here and help for the SDK style projects here.