I am using following code in 2 our of 3 projects from the solution default target is set to PublishMe
<Target Name="PublishMe" 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>
If I run msbuild it builds and publishes just as expected, the problem is that I can't build it from VS anymore, I am having error of missing projectname.dll
Is there way I can change default build target if it is msbuild doing build?
I tried to /t:ProjectName:target;ProjectName2:target, but it isn't working with custom targets for some reason.
Is there way I can specify param from msbuild console to run PublishMe target and if not present run simple build?
You can usefollowing command
msbuild projectname /t:targetname.
Also please check the below link for more info
Building a solution file using msbuild
Related
I use the _PublishedApplications to generate the structure in the TFS Build Server. After this, I use the <HeatDirectory> in the WiX project to correctly harvest the content of _PublishedApplications folder. But my problem is the order during build.
If I use the <HeatDirectory> inside <Target Name="BeforeBuild"> it doesn't include the binaries copied to the _PublishedApplications, as the harvesting is executed before the publish (file copy).
If I change the target to BeforeCompile the compilation doesn't succeed because there is no file in first place. Here is the code for the WiX project (the relevant part of it):
<ItemGroup>
<Compile Include="Product.wxs" />
<Compile Include="Autogenerated.wxs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SimpleProject\SimpleProject.csproj">
<Name>SimpleProject</Name>
<Project>{GUID}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
</ItemGroup>
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeCompile">
<HeatDirectory OutputFile="Autogenerated.wxs" Directory="$(Sources)"
PreprocessorVariable="var.SimpleProject.TargetDir"
AutogenerateGuids="true" SuppressRegistry="true"
ToolPath="$(WixToolPath)" DirectoryRefId="INSTALLFOLDER"
ComponentGroupName="ComponentGroup_Core"
SuppressRootDirectory="true" />
</Target>
Question
How can I execute the harvesting after the binaries are copied to _PublishedApplications?
I build my solution using the following command:
msbuild SimpleInstaller.sln /p:OutDir=C:\Temp\Output\ /v:diag > C:\Temp\Log.txt
This will output all log to a text file. You can use the MSBuild logger instead.
Then I found when you build a C# project, the target Compile in file Microsoft.Common.targets is invoked. This target has an attribute DependsOnTarget which contains a reference to target BeforeCompile.
I can override this target BeforeCompile in my own C# project, just by adding the following code at the end of it (file .csproj):
...
<Target Name="BeforeCompile">
<!-- custom action. -->
</Target>
</Project>
But the problem is my WiX project cannot override the BeforeCompile target because this target isn't defined for WiX projects. You can check this in the wix2010.targets file. The target Compile only has dependence upon targets PrepareForBuild, ResolveWixExtensionReferences and GenerateCompileWithObjectPath.
My solution was to identify an alternative to BeforeCompile which is the Harvest target. My WiX project (.wixproj) has the following target now:
<Target Name="Harvest">
<HeatDirectory OutputFile="Autogenerated.wxs" Directory="$(Sources)"
PreprocessorVariable="var.SimpleProject.TargetDir"
AutogenerateGuids="true" SuppressRegistry="true"
ToolPath="$(WixToolPath)" DirectoryRefId="INSTALLFOLDER"
ComponentGroupName="ComponentGroup_Core"
SuppressRootDirectory="true" />
</Target>
All this problem occurred because my first project in the solution was the WiX project and only then I added the C# projects. For this reason the BeforeBuild was being executed before everything else.
Another solution to solve this issue is to edit the solution file (.sln) and move the WiX project declaration in the beginning of the solution file to the end of all project declarations (not the end of the solution file). Then the BeforeBuild of the WiX project will be executed after the _PublishedApplications folder is created by the C# project.
This manual edit is required because if you change the Project Build Order you are actually changing the project references (at least in the solution file), but the target BeforeBuild is called anyway before the ResolveProjectReferences which is the responsible for invoking the build of any references.
This is the project declaration that should be after all others:
Project("GUID") = "SimpleInstaller", "SimpleInstaller\SimpleInstaller.wixproj", "GUID"
EndProject
My recommendation is still to use Harvest target as it is independent of any changes in the solution file.
The first thing you need to do is add a project reference from your installer project to your application project. This will force the application to build and be available for your installer project.
You can verify the build sequence of the projects if you right click your solution under VisualStudio and click the "Project Build Order...", if you need to change the order you need to configure the Project dependencies.
Then do something like this in your installer project:
<PropertyGroup>
<RootDir>{PATH TO _PublishedApplications FOLDER}</RootDir>
<HarvestDirectoryNoLogo>true</HarvestDirectoryNoLogo>
<HarvestDirectorySuppressAllWarnings>false</HarvestDirectorySuppressAllWarnings>
<HarvestDirectoryTreatWarningsAsErrors>false</HarvestDirectoryTreatWarningsAsErrors>
<HarvestDirectoryTreatSpecificWarningsAsErrors>
</HarvestDirectoryTreatSpecificWarningsAsErrors>
<HarvestDirectoryVerboseOutput>false</HarvestDirectoryVerboseOutput>
<HarvestDirectoryAutogenerateGuids>false</HarvestDirectoryAutogenerateGuids>
<HarvestDirectoryGenerateGuidsNow>true</HarvestDirectoryGenerateGuidsNow>
<HarvestDirectorySuppressFragments>true</HarvestDirectorySuppressFragments>
<HarvestDirectorySuppressUniqueIds>false</HarvestDirectorySuppressUniqueIds>
</PropertyGroup>
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeBuild">
<ItemGroup>
<HarvestDirectory Include="$(RootDir)">
<Transforms>
</Transforms>
<ComponentGroupName>MyComponent</ComponentGroupName>
<DirectoryRefId>WebSiteRoot</DirectoryRefId>
<PreprocessorVariable>var.RootDir</PreprocessorVariable>
<SuppressCom>false</SuppressCom>
<SuppressRegistry>false</SuppressRegistry>
<SuppressRootDirectory>true</SuppressRootDirectory>
<KeepEmptyDirectories>true</KeepEmptyDirectories>
</HarvestDirectory>
</ItemGroup>
<HeatDirectory
NoLogo="$(HarvestDirectoryNoLogo)"
SuppressAllWarnings="$(HarvestDirectorySuppressAllWarnings)"
SuppressSpecificWarnings="$(HarvestDirectorySuppressSpecificWarnings)"
ToolPath="$(WixToolPath)"
TreatWarningsAsErrors="$(HarvestDirectoryTreatWarningsAsErrors)"
TreatSpecificWarningsAsErrors="$(HarvestDirectoryTreatSpecificWarningsAsErrors)"
VerboseOutput="$(HarvestDirectoryVerboseOutput)"
AutogenerateGuids="$(HarvestDirectoryAutogenerateGuids)"
GenerateGuidsNow="$(HarvestDirectoryGenerateGuidsNow)"
OutputFile="$(IntermediateOutputPath)_%(HarvestDirectory.Filename)_dir.wxs"
SuppressFragments="$(HarvestDirectorySuppressFragments)"
SuppressUniqueIds="$(HarvestDirectorySuppressUniqueIds)"
Transforms="%(HarvestDirectory.Transforms)"
Directory="#(HarvestDirectory)"
ComponentGroupName="%(HarvestDirectory.ComponentGroupName)"
DirectoryRefId="%(HarvestDirectory.DirectoryRefId)"
KeepEmptyDirectories="%(HarvestDirectory.KeepEmptyDirectories)"
PreprocessorVariable="%(HarvestDirectory.PreprocessorVariable)"
SuppressCom="%(HarvestDirectory.SuppressCom)"
SuppressRootDirectory="%(HarvestDirectory.SuppressRootDirectory)"
SuppressRegistry="%(HarvestDirectory.SuppressRegistry)" />
</Target>
</Project>
I generated this code based on the documentation of the HeatDirectory Task and I use it in real projects.
I want to run an MSBuild Task (which signs an executable/dll) but only when the output exe/dll has changed. If none of the source files have changed causing a recompile of the exe/dll then I don't want the task to run.
Despite spending several hours trying different things out I cannot work out how to get my target task to only run if the project has been compiled where the output files have changed (in other words the CoreCompile target was not skipped I think).
You can just do this:
<PropertyGroup>
<TargetsTriggeredByCompilation>DoStuffWithNewlyCompiledAssembly</TargetsTriggeredByCompilation>
</PropertyGroup>
This works because someone smart at Microsoft added the following line at the end of the CoreCompile target in Microsoft.[CSharp|VisualBasic][.Core].targets (the file name depends on the language and MSBuild/Visual Studio version).
<CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>
So if you specify a target name in the TargetsTriggeredByCompilation property, your target will run if CoreCompile runs-- and your target will not run if CoreCompile is skipped (e.g. because the output assembly is already up-to-date with respect to the code).
Should be the same as this answer, using the TargetOutputs parameter::
<MSBuild Projects="File.sln" >
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Message Text="Assemblies built: #(AssembliesBuiltByChildProjects)" /> <!-- just for debug -->
<CallTarget Targets="SignExe" Condition="'#(AssembliesBuiltByChildProjects)'!=''" />
I created a custom common target "RealClean" which remove every files in the output and "intermediate output" directory. I put it in the Microsoft.Common.targets file.
When I run MsBuild on my csproj everything is fine.
But when I run MsBuild on my sln (which just references a list of csproj) I have the following error
error MSB4057: The target "RealClean" does not exist in the project.
Here is the command line I enter to run MsBuild
C:\Windows\Microsoft .NET\Framework\v3.5\MsBuild.exe /p:Configuration="Release";OutputPath="..\..\MSBuild.Referentiel.net35";nowarn="1591,1573" /t:RealClean mySolution.sln
Any hint?
I had the same issue but didn't want to modify things outside of the source tree in order to get this to work. Adding files to C:\Program Files... means that you have to do this manually on every dev machine to get the same behavior.
I did three things:
1) Created a Custom targets file which I import into every C# and/or VB/F# project in my solution by adding the following to each proj file:
<!-- Rest of project file -->
<PropertyGroup Condition="'$(SolutionDir)' == '' or '$(SolutionDir)' == '*undefined*'">
<!-- Relative path to containing solution folder -->
<SolutionDir>..\</SolutionDir>
</PropertyGroup>
<Import Project="$(SolutionDir)CommonSettings.targets" />
2) Added a clean target which gets called after the real Clean (using the AfterTargets attribute from MSBuild 4.0):
<Target Name="CleanCs" AfterTargets="Clean">
<Message Text="Deep cleaning C# project..." />
<CreateItem Include="$(OutDir)**\*.*; $(ProjectDir)\obj\**\*.*; $(IntermediateOutputPath)**\*.*"
Exclude="**\bin\**\*.vshost.exe; $(IntermediateOutputPath)**\*.log">
<Output TaskParameter="Include" ItemName="AfterClean_FilesToDelete"/>
</CreateItem>
<Delete Files="#(AfterClean_FilesToDelete)" />
<CreateItem Include="$(ProjectDir)\obj\" >
<Output TaskParameter="Include" ItemName="AfterClean_DirectoriesToDelete" />
</CreateItem>
<CreateItem Include ="$(ProjectDir)\bin\" Condition="'$(TargetExt)' != '.exe'" >
<Output TaskParameter="Include" ItemName="AfterClean_DirectoriesToDelete"/>
</CreateItem>
<RemoveDir ContinueOnError="true" Directories="#(AfterClean_DirectoriesToDelete)" />
</Target>
3) In my continuous integration MSBuild project I check and make sure that all proj files have #1:
<ItemGroup>
<!-- Exclude viewer acceptance tests as they must compile as x86 -->
<CheckProjects_CsProjects Include="**\*.csproj" />
</ItemGroup>
<Target Name="CheckProjects">
<!--
Look for C# projects that don't import CommonSettingsCs.targets
-->
<XmlRead XPath="//n:Project[count(n:Import[#Project[contains(string(), 'CommonSettingsCs.targets')]]) = 0]/n:PropertyGroup/n:AssemblyName/text() "
XmlFileName="%(CheckProjects_CsProjects.Identity)"
Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
Prefix="n" >
<Output TaskParameter="Value" ItemName="CheckProjects_CsMissingImports"/>
</XmlRead>
<Error Text="Project missing CommonSettingsCs.targets: %(CheckProjects_CsMissingImports.Identity)"
Condition="'%(CheckProjects_CsMissingImports.Identity)' != ''" />
</Target>
This prevents developers from forgetting to add #1. You could create your own project template to ensure that al new projects have this by default.
The advantage to this approach is setting up a new source tree enlistment doesn't involve anything more than getting the current source tree. The downside is that you have to edit the project files once when you create them.
To work on solution file, MSBuild creates a temporary MSBuild project file containing only some targets like Build and Clean. So you can't call your custom target on a solution file.
Madgnome is probably right. But I wanted to add that you should not be editing the Microsoft.common.targets files. If you do so you risk having a different build process on that machine versus what everybody else has. In your case you could have created a new MSBuild file with just the RealClean target and placed it at
C:\Program Files (x86)\MSBuild\v4.0\Custom.After.Microsoft.Common.targets
or for 32 bit
C:\Program Files\MSBuild\v4.0\Custom.After.Microsoft.Common.targets
and essentially that would be the same as putting that file inside of Microsoft.Common.targets, except you don't have to modify that file.
I'm trying to get Team City to build my .NET solution and run my nUnit tests.
I know I can modify the individual projects and tell them always run the unit tests. I don't want the unit tests to run when I click "build" in visual studio, but I do want the unit tests to run when Team City kicks off a msbuild task.
I tried "msbuild solutionname.sln" and gave team city the targets of "BUILD" and my custom build tag of "TEST". However, msbuild can't find any specified target when invoked against a sln solution. So, I ran msbuild to convert my solution into a project which has a target like this:
<Target Name="Build">
<MSBuild Projects="#(BuildLevel0)" >
</Target>
I naively thought I could write a new task like this:
<Target Name="BuildAndTest">
<CallTarget Targets="Build"/> <!-- This builds everything in solution -->
<CallTarget Targets="Test"/> <!-- DOES NOT WORK. This target exists in project that gets built by this solution -->
</Target>
The nunit target looks like this:
<Target Name="Test" DependsOnTargets="Build" Condition=" '$(Configuration)' == 'Release'">
<NUnit Assemblies="$(OutputPath)\Tsa.BaseTest.dll" ContinueOnError="false" ToolPath="C:\Program Files\NUnit 2.5.2\bin\net-2.0\" DisableShadowCopy="true" OutputXmlFile="$(OutputPath)\nunit-results.xml" />
</Target>
As you can see, it references OutputPath, which only the project knows--the solution doesn't have reference to $OutputPath, else I'd just put all the test targets into the "solution project".
Any suggestions on how I can get this to work?
I think you're making this a lot harder than it needs to be. TeamCity has built-in support for running NUnit unit tests after the build - you don't need to modify the MSBuild file at all. Just set up your Build Configuration (I think it's under Runner) to specify the version of NUnit and which assemblies are test assemblies.
NOTE: I checked and we have this under Runner: sln2008 (section NUnit Test Settings) in TeamCity Enterprise Version 4.5.4, but I don't see anything on the JetBrains site that states that it's specific to Enterprise. It may require a version upgrade, though. See TeamCity Testing Frameworks.
This is what finally worked. It is ignored by visual studio, msbuild will run this section correctly, and team city will as well, although it replaces the Target with its own an runtime (according to the build log).
TeamCity will "automatically" run nunit tests and display the results, only in the sense that it will do so after manually editing the msbuild file, doing numerous manual teaks and telling TeamCity where each assembly is and where each output file is.
<Project (snip) DefaultTargets="BuildAndTest" (snip)>
<Target Name="BuildAndTest">
<CallTarget Targets="Build" />
<CallTarget Targets="TestBase" />
</Target>
<Target Name="TestBase" DependsOnTargets="Build">
<NUnit Assemblies="Tsa.BaseTest\bin\RELEASE\Tsa.BaseTest.dll" ContinueOnError="false" ToolPath="C:\Program Files\NUnit 2.5.2\bin\net-2.0\" DisableShadowCopy="true" OutputXmlFile="$(SolutionDir)\Tsa.BaseTest\bin\RELEASE\nunit-results.xml" />
</Target>
</Target>
</Project>
I'm creating a custom ITask for MSBuild which uploads the output files of my build. I'm using a web deployment project to publish my app and hooking in to the AfterBuild target to do my custom work.
If I add a file to my web application, the first time I do a build, my custom task is not recognizing the recently added file. In order for that file to show up in my array of ITaskItems, I have to first do a build with my 'AfterBuild' target removed and then start the build again with my 'AfterBuild' target in place.
Here is what my build file looks like:
<ItemGroup>
<PublishContent Include="$(OutputPath)\**" />
</ItemGroup>
<Target Name="AfterBuild">
<UploadTask FilesToPublish="#(PublishContent)" />
</Target>
The list in #(PublishContent) seems to be initialized at the beginning of the build instead of reflecting any changes that might have taken place by the build process itself.
Any ideas?
Thanks
Your ItemGroup is going to get evaluated when the project file first loads (when you first open Visual Studio or you 'unload' and 'reload' the project in Solution Explorer).
What you probably need is to create an ItemGroup as a task in your 'AfterBuild' target. Example:
<CreateItem Include="$(OutputPath)\**">
<Output TaskParameter="Include" ItemName="OutputFiles"/>
</CreateItem>
followed by:
<Target Name="AfterBuild">
<UploadTask FilesToPublish="#(OutputFiles)" />
</Target>