What the question should have been:
Here is the content of my ModuleCompilation.targets file :
<ItemGroup>
<ConfigFilesToMove Include="bin\$(Configuration)\*.config"/>
</ItemGroup>
<Target Name="AfterBuild">
<Message Text="Message for AfterBuild" />
<Move SourceFiles="#(ConfigFilesToMove)" DestinationFolder="bin\$(Configuration)\Configuration\" />
</Target>
The Message is written to the console but the files are not moved, how come ?
Original question (you can ignore):
I have included an Import in my csproj file to import a task from another file :
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\ModuleCompilation.targets" />
Now, here is the content of my ModuleCompilation.targets file :
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="AfterBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BaseFiles Include="bin\$(Configuration)\Libraries\ModuleBase*"/>
<ConfigFilesToMove Include="bin\$(Configuration)\*.config"/>
<OtherFilesToMove Include="bin\$(Configuration)\*" Exclude="bin\$(Configuration)\$(MSBuildProjectName)*" />
</ItemGroup>
<Target Name="AfterBuild">
<Message Text="AfterBuild" />
<Move SourceFiles="#(ConfigFilesToMove)" DestinationFolder="bin\$(Configuration)\Configuration\" />
<Move SourceFiles="#(OtherFilesToMove)" DestinationFolder="bin\$(Configuration)\Libraries\" />
<Message Text="bin\$(Configuration)\Libraries\ModuleBase*" />
<Delete Files="#(BaseFiles)" />
</Target>
</Project>
When I build or rebuild, I do have the "AfterBuild" message in my console, and the files are all indeed generated, but they haven't moved from the Debug folder to where I want them to be.
If I rebuild my projet, the files are moved, I guess because there already were present, but I want to files to be generated and then to be moved, I don't want to have them to be already present !
I must be missing something really obvious, can you help me please ?
Edit:
Ok so I've made some tests and this works :
<ConfigFilesToMove Include="bin\Debug\App.config"/>
While this doesn't:
<ConfigFilesToMove Include="bin\Debug\*.config"/>
Does anyone knowns why ?
Edit 2:
This is really weird, here is my code :
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="$(OutputPath)*.config"/>
<Pouet Include="bin\Debug\*.dll"/>
</ItemGroup>
<Target Name="AfterBuild">
<Message Text="#(Pouet)" />
<Message Text="pouetss" Condition="Exists(#(Pouet))"/>
<Message Text="nooo" Condition="!Exists(#(Pouet))"/>
<MakeDir Directories="$(OutputPath)Configuration\" />
<Copy SourceFiles="#(Pouet)" DestinationFolder="$(OutputPath)Configuration\" />
</Target>
</Project>
Here is what the log from MSBuild says :
1>Target "AfterBuild: (TargetId:75)" in file "C:\Tests\ModuleTest\Modules\ModuleCompilation.targets" from project "C:\Tests\ModuleTest\Modules\FirstModule\FirstModule.csproj" (target "Build" depends on it):
1>Task "Message" (TaskId:47)
1>Done executing task "Message". (TaskId:47)
1>Task "Message" skipped, due to false condition; (Exists(#(Pouet))) was evaluated as (Exists()).
1>Task "Message" (TaskId:48)
1> Task Parameter:Text=nooo (TaskId:48)
1> nooo (TaskId:48)
1>Done executing task "Message". (TaskId:48)
1>Task "MakeDir" (TaskId:49)
1> Task Parameter:Directories=bin\Debug\Configuration\ (TaskId:49)
1> Creating directory "bin\Debug\Configuration\". (TaskId:49)
1>Done executing task "MakeDir". (TaskId:49)
1>Task "Copy" (TaskId:50)
1> Task Parameter:DestinationFolder=bin\Debug\Configuration\ (TaskId:50)
1>Done executing task "Copy". (TaskId:50)
1>Done building target "AfterBuild" in project "FirstModule.csproj".: (TargetId:75)
So MSBuild ignores every value that contains a wildcard ?
Problem :
ItemGroup created outside of a Target are resolved before any Target is run, so you can't target files that a Target will create
Solution :
I changed my code to :
<Target Name="AfterBuild">
<ItemGroup>
<ConfigFilesToMove Include="bin\$(Configuration)\*.config"/>
</ItemGroup>
<Message Text="Message for AfterBuild" />
<Move SourceFiles="#(ConfigFilesToMove)" DestinationFolder="bin\$(Configuration)\Configuration\" />
</Target>
And now my files are detected because the ItemGroup is resolved after the files are created.
Related
I have two files that I want to configure by environment: App.config and ApplicationInsights.config. I have created the files App.Debug.config and ApplicationINsights.Debug.config and added the following tasks to the csproj file:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
<TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
<ItemGroup>
<AppConfigWithTargetPath Remove="app.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('ApplicationInsights.$(Configuration).config')">
<Message Text="Transforming app insights config file to $(OutputPath)\ApplicationInsights.config" Importance="high" />
<TransformXml Source="ApplicationInsights.config" Transform="ApplicationInsights.$(Configuration).config" Destination="$(OutputPath)\ApplicationInsights.config" />
</Target>
Both tasks work when they are the only task in the file, but when both are included only the second transform is executed. I have tried giving the tasks different Names, but to no avail. What can I do to get both tasks to run?
You have to give the two tasks different Names and then hook into the existing AfterCompile target:
<Target Name="SomeUniqueName1" AfterTargets="AfterCompile" …>
…
</Target>
<Target Name="SomeUniqueName2" AfterTargets="AfterCompile" …>
…
</Target>
The <UsingTask> only needs to be there once to define the imported TransformXml task.
I'm using a msbuild file, TeamCity and Web Deploy to deploy my siteand everything works just fine, for the files included in the Visual Studio csproj file. In addition to these files I want to publish a couple of more files such as license files etc depending on environment.
This is my build file DeployToTest.proj:
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<ItemGroup>
<LicenseSourceFiles Include="License.config"/>
<RobotSourceFile Include="robots.txt" />
</ItemGroup>
<Target Name="Build">
<Message Text="Starting build" />
<MSBuild Projects="..\..\WebApp.sln" Properties="Configuration=Test" ContinueOnError="false" />
<Message Text="##teamcity[buildNumber '$(FullVersion)']"/>
<Message Text="Build successful" />
</Target>
<Target Name="Deploy" DependsOnTargets="Build">
<Copy SourceFiles="#(LicenseSourceFiles)" DestinationFolder="..\..\wwroot"></Copy>
<Copy SourceFiles="#(RobotSourceFile)" DestinationFolder="..\..\wwwroot"></Copy>
<Message Text="Started deploying to test" />
<Exec Command="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe ..\..\wwwroot\WebApp.csproj /property:Configuration=Test /t:MsDeployPublish /p:MsDeployServiceUrl=99.99.99.99;DeployIisAppPath=MySite;username=user;password=pass;allowuntrustedcertificate=true" />
<Message Text="Finished deploying to test" />
</Target>
</Project>
As you can see I tried to copy the license.config and robots.txt without any luck.
This .proj file is selected as the 'Build file path' in TeamCity.
Any suggestions on how I can accomplish this?
To solve this problem it may be worth executing the build script with the verbosity set to the 'detailed' or 'diagnostic' level. That should tell you exactly why the copy step fails.
However one of the most likely problems could be the fact that the script is using relative file paths, which depend on the working directory being set to the correct value. For build scripts I prefer use absolute paths to prevent any file path problems.
To get the absolute path you can use the MSBuildProjectDirectory property. The value of this property points to the path of the directory containing the currently executing MsBuild script. With that you can change your MsBuild script like this:
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<BaseDir>$(MSBuildProjectDirectory)</BaseDir>
</PropertyGroup>
<ItemGroup>
<LicenseSourceFiles Include="$(BaseDir)\License.config"/>
<RobotSourceFile Include="$(BaseDir)\robots.txt" />
</ItemGroup>
<Target Name="Build">
<Message Text="Starting build" />
<MSBuild Projects="$(BaseDir)\..\..\WebApp.sln" Properties="Configuration=Test" ContinueOnError="false" />
<Message Text="##teamcity[buildNumber '$(FullVersion)']"/>
<Message Text="Build successful" />
</Target>
<Target Name="Deploy" DependsOnTargets="Build">
<Copy SourceFiles="#(LicenseSourceFiles)" DestinationFolder="$(BaseDir)\..\..\wwroot"></Copy>
<Copy SourceFiles="#(RobotSourceFile)" DestinationFolder="$(BaseDir)\..\..\wwwroot"></Copy>
<Message Text="Started deploying to test" />
<Exec Command="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe ..\..\wwwroot\WebApp.csproj /property:Configuration=Test /t:MsDeployPublish /p:MsDeployServiceUrl=99.99.99.99;DeployIisAppPath=MySite;username=user;password=pass;allowuntrustedcertificate=true" />
<Message Text="Finished deploying to test" />
</Target>
</Project>
Now this should fix the problem if there is indeed a problem with the relative file paths.
Solution was to change settings for the web project in Visual Studio. Under Package/Publish Web i set 'Items to deploy' to 'All files in this project folder'. I then added a filter to remove all .cs files and other unwanted files.
I've got the following xml file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="DeployPrototype" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<dotCover>..\..\..\plugins\dotCover\bin\dotCover.exe</dotCover>
</PropertyGroup>
<ItemGroup>
<SourceFiles Include="..\Prototype\Site\Site\Bin\TestServer\Default.html;..\Prototype\Site\Site\Bin\TestServer\Site.xap"/>
<DestinationFolder Include="C:\inetpub\wwwroot\ProjectName\Prototype"/>
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="../ProjectName.Web.sln" Properties="Configuration=testserver" />
<Message Text="Building ProjectName solution" Importance="high" />
</Target>
<Target Name="TeamCity" DependsOnTargets="Build">
<Message Text="Before executing MSpec command" Importance="low" />
<Exec Command="..\packages\Machine.Specifications.0.4.10.0\tools\mspec-clr4.exe ..\Hosts\ProjectName.Hosts.Web.Specs\bin\ProjectName.Hosts.Web.Specs.dll --teamcity" />
<Message Text="Ran MSpec" Importance="low" />
<Exec Command="$(dotCover) c TestServerBuildAndRunTestsOnly_DotCover.xml" />
<Message Text="##teamcity[importData type='dotNetCoverage' tool='dotcover' path='build\coverage.xml']" Importance="high" />
</Target>
<Target Name="DeployPrototype" DependsOnTargets="TeamCity">
<Message Text="Before executing copy, source files are: #(MySourceFiles) and destination folder is: #(DestinationFolder)" Importance="low" />
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="#(DestinationFolder)"
/>
<Message Text="Atter executing copy" Importance="low" />
</Target>
</Project>
Everything in this script works apart from the copying of the files. The messages I've put put in the copying section don't appear in the full log in TeamCity. In the configuration settings of the latter, I've put "DeployPrototype" as my target.
Why is the copying operation not happening?
For a given problem involving MSBuild not working under TeamCity, the answer almost always involves adding /v:d (Every step carried out and information about skipped steps) or /v:diag (Detailed plus dumps of ItemGroups etc. for diagnostic purposes) to the MSBuild args and the TeamCity-managed build output will have the answer.
After looking around I can't find a simple answer to this problem.
I am trying to create an MSBuild file to allow me to easily use SpecFlow and NUnit within Visual Studio 2010 express.
The file below is not complete this is just a proof of concept and it needs to be made more generic.
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDependsOn>
BuildSolution;
SpecFlow;
BuildProject;
NUnit;
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<Solution>C:\Users\Craig\Documents\My Dropbox\Cells\Cells.sln</Solution>
<CSProject>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\Configuration.csproj</CSProject>
<DLL>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\bin\Debug\Configuration.dll</DLL>
<CSFile>C:\Users\Craig\Documents\My Dropbox\Cells\Configuration\SpecFlowFeature1.feature.cs</CSFile>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)">
<Message Text="Build Started" Importance="high" />
<Message Text="Build Ended" Importance="high" />
</Target>
<Target Name="BuildSolution">
<Message Text="BuildSolution Started" Importance="high" />
<MSBuild Projects="$(Solution)" Properties="Configuration=Debug" />
<Message Text="BuildSolution Ended" Importance="high" />
</Target>
<Target Name="SpecFlow">
<Message Text="SpecFlow Started" Importance="high" />
<Exec Command='SpecFlow generateall "$(CSProject)"' />
<Message Text="SpecFlow Ended" Importance="high" />
</Target>
<Target Name="BuildProject">
<Message Text="BuildProject Started" Importance="high" />
<MSBuild Projects="$(CSProject)" Properties="Configuration=Debug" />
<Message Text="BuildProject Ended" Importance="high" />
</Target>
<Target Name="NUnit">
<Message Text="NUnit Started" Importance="high" />
<Exec Command='NUnit /run "$(DLL)"' />
<Message Text="NUnit Ended" Importance="high" />
</Target>
</Project>
The SpecFlow Task looks in the .csproj file and creates a SpecFlowFeature1.feature.cs.
I need to include this file when building the .csproj so that NUnit can use it.
I know I could modify (either directly or on a copy) the .csproj file to include the generated file but I'd prefer to avoid this.
My question is: Is there a way to use the MSBuild Task to build the project file and tell it to include an additional file to include in the build?
Thank you.
I found no way of doing it without editing the project file.
So I made an MSBuild file to:
Copy the project files
Run the copies through SpecFlow
Add the new .cs files to the copied projects
Compile the projects
Debug Run each of the compiled DLLs through NUnit
Clean up - Delete the copied projects
I've blogged about how to use it here:
http://learntdd.wordpress.com/2010/06/10/using-specflow-and-nunit-on-visual-studio-2010-express/
(It's version 1, I'd like to improve the script)
I couldn't think of any way to achieve without any modification to the .csproj file.
The approach I'd suggest would look like this.
In your .csproj you Import a container target file
...
<Import Project="SpecFlow.target" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
...
just above the CSharp.targets.
Specflow.targets would look like this
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="#(Compile)" />
</ItemGroup>
</Project>
so it doesn't harm while building the project from VS.
You could then use the Output of your SpecFlow Exec and add it to the SpecFlow.targets file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="#(Compile)" />
<Compile Include="SpecFlowFeature1.feature.cs" />
</ItemGroup>
</Project>
...
and clean SpecFlow.targets after building your .csproj.
All
i am trying to automatically update the assembly information of a project using AssemblyInfo task, before build however the target appears to do nothing (no failure/error) just no update/creation
Below is the build.proj file I am using (obviously some contents altered)
Can anyone help?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.targets"/>
<PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<PropertyGroup>
<BuildDir>C:\svn\Infrastructure</BuildDir>
</PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="Infrastructure.sln"/>
</ItemGroup>
<Target Name="Build" DependsOnTargets="ChangeDataAccessAssemblyInfo">
<RemoveDir Directories="$(BuildDir)\Builds" Condition="Exists('$(BuildDir)\Builds')" />
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=Debug" Targets="Rebuild" />
</Target>
<ItemGroup>
<TestAssemblies Include="Build\Logging\Logging.UnitTests.dll" />
</ItemGroup>
<!--<UsingTask TaskName="NUnit" AssemblyFile="$(teamcity_dotnet_nunitlauncher_msbuild_task)" />
<Target Name="Test" DependsOnTargets="Build">
<NUnit NUnitVersion="NUnit-2.4.6" Assemblies="#(TestAssemblies)" />
</Target>-->
<Target Name="ChangeDataAccessAssemblyInfo" >
<Message Text="Writing ChangeDataAccessAssemblyInfo file for 1"/>
<Message Text="Will update $(BuildDir)\DataAccess\My Project\AssemblyInfo.vb" />
<AssemblyInfo CodeLanguage="VB"
OutputFile="$(BuildDir)\DataAccess\My Project\AssemblyInfo_new.vb"
AssemblyTitle="Data Access Layer"
AssemblyDescription="Message1"
AssemblyCompany="http://somewebiste"
AssemblyProduct="the project"
AssemblyCopyright="Copyright notice"
ComVisible="true"
CLSCompliant="true"
Guid="hjhjhkoi-9898989"
AssemblyVersion="$(Major).$(Minor).1.1"
AssemblyFileVersion="$(Major).$(Minor).5.7"
Condition="$(Revision) != '0' "
ContinueOnError="false" />
<Message Text="Updated Assembly File Info"
ContinueOnError="false"/>
</Target>
</Project>
I think you are missing the specification of the AssemblyInfoFiles attribute on your AssemblyInfo task. Here's how it looks on a project I'm working on...
<Target Name="AfterGet">
<Message Text="In After Get"/>
<CreateItem Include="$(SolutionRoot)\Source\SomeProject\My Project\AssemblyInfo.vb">
<Output ItemName="AssemblyInfoFiles" TaskParameter="Include"/>
</CreateItem>
<Attrib Files="#(AssemblyInfoFiles)"
ReadOnly="false"/>
<AssemblyInfo AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyDescription="$(LabelName)">
</AssemblyInfo>
</Target>
What we're doing is first using to create a property that contains the name of the file we'll be updating. We have to do this via createItem because when we start the build the file doesn't exist (and that is when MSBuild evaluates the and definitions in your build file.
We then take the readonly bit off the file.
Finally we invoke the AssemblyInfo task passing it the file(s) to update and a custom assembly name that we want to give it (in this case we put the TFS build label into the Assembly Description field so that we can easily tell which team build the assembly came from.