how to write inline code in <Target Name="AfterBuild"> in VisualStudio.csproj - visual-studio

if $(ConfigurationName) doesn't work in <AfterBuild>
<Target Name="AfterBuild">
if $(ConfigurationName) == Release (
<Exec Command="grunt karma:unit --no-color > grunt-karma-output.txt" IgnoreExitCode="true" />
<Exec Command="type grunt-karma-output.txt" CustomErrorRegularExpression=".*mPhantomJS.*FAILED" IgnoreExitCode="false" />
)
</Target Name="AfterBuild">
if $(ConfigurationName) WORKS in <PostBuildEvent>
<PostBuildEvent>
if $(ConfigurationName) == Release (
<Exec Command="grunt karma:unit --no-color > grunt-karma-output.txt" IgnoreExitCode="true" />
<Exec Command="type grunt-karma-output.txt" CustomErrorRegularExpression=".*mPhantomJS.*FAILED" IgnoreExitCode="false" />
)
</PostBuildEvent>
Could anyone suggest how to check if the build is in release mode in AfterBuild?

Use a Condition on the target:
<Target Name="AfterBuild" Condition="$(Configuration)==Release">
<Exec Command="echo AfterBuild"/>
</Target>
Btw this also works the same way for the PostBuildEvent (and the code you posted definitely does not work).
<PropertyGroup Condition="$(Configuration)==Release">
<PostBuildEvent>echo PostBuild</PostBuildEvent>
</PropertyGroup>

Related

Is there an indexer available in MSBuild ItemGroups?

I'm using this stack overflow answer to return the built assembly's #(Version) as an ItemGroup for use in the post-build event.
However, I want to have Major and Minor separately, as I only want vMajor.Minor in the post build. To do this, I'd like to index inside the Include attribute.
The following works, but will break if the Major or Minor numbers go over 9 (single digits only):
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<ItemGroup>
<Version Include="#(Targets->'%(Version)')" />
<Major Include="#(Version->Substring(0,1))" />
<Minor Include="#(Version->Substring(2,1))" />
</ItemGroup>
</Target>
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
<PostBuildEvent>
if $(ConfigurationName) == Release copy /y $(TargetPath) $(SolutionDir)..\Builds\$(TargetName)_v#(Major).#(Minor)$(TargetExt)
</PostBuildEvent>
</PropertyGroup>
What I'm looking for is something more along the lines of:
<Major Include="#(Version->Split('.')[0])" />
<Minor Include="#(Version->Split('.')[1])" />
or, only if an indexer is really not possible,
<Major Include="#(Targets->Version->Major)" />
<Minor Include="#(Targets->Version->Minor)" />
<Major Include="#(Targets->'%(Version)'->'%(Major)')" />
<Minor Include="#(Targets->'%(Version)'->'%(Minor)')" />
<Major Include="#(Targets->'%(Version.Major)')" />
<Minor Include="#(Targets->'%(Version.Minor)')" />
What options, if any, do I have to index an array or otherwise pick apart a version number like 1.0.2838.24877 into 1 and 0?
Edit
This now works, by first using a <PropertyGroup> I can utilitize Split('.')[n] like in this post, but the $(Major) Properties ($()) are not available in the Post-Build event (why not? Am I missing something?). This would be more elegant if I could just directly use $(Major) in the post-build.
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<PropertyGroup>
<Version>#(Targets->'%(Version)')</Version>
<Major>$(Version.Split('.')[0])</Major>
<Minor>$(Version.Split('.')[1])</Minor>
<Build>$(Version.Split('.')[2])</Build>
<Revision>$(Version.Split('.')[3])</Revision>
</PropertyGroup>
<ItemGroup>
<Version Include="$(Version)" />
<Major Include="$(Major)" />
<Minor Include="$(Minor)" />
<Build Include="$(Build)" />
<Revision Include="$(Revision)" />
</ItemGroup>
<Message Text="Version: #(Version)" />
<Message Text="Major.Minor.Build.Revision: #(Major).#(Minor).#(Build).#(Revision)" />
</Target>
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
<PostBuildEvent>
if $(ConfigurationName) == Release copy /y $(TargetPath) $(SolutionDir)..\Builds\$(TargetName)_v#(Major).#(Minor)$(TargetExt)
</PostBuildEvent>
</PropertyGroup>
Why are ItemGroups #() available and PropertyGroups $() not?
This is the best I've come to, with little understanding of what else would be available or how to use indexers within ItemGroups.
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<PropertyGroup>
<Version>#(Targets->'%(Version)')</Version>
</PropertyGroup>
<ItemGroup>
<Version Include="$(Version)" />
<Major Include="$(Version.Split('.')[0])" />
<Minor Include="$(Version.Split('.')[1])" />
<Build Include="$(Version.Split('.')[2])" />
<Revision Include="$(Version.Split('.')[3])" />
</ItemGroup>
<Message Text="Version: #(Version)" />
<Message Text="Major.Minor.Build.Revision: #(Major).#(Minor).#(Build).#(Revision)" />
</Target>
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
<PostBuildEvent>
if $(ConfigurationName) == Release copy /y $(TargetPath) $(SolutionDir)..\Builds\$(TargetName)_v#(Major).#(Minor)$(TargetExt)
</PostBuildEvent>
</PropertyGroup>
I would like to be able to accomplish this without the <PropertyGroup> section, if possible.

Visual Studio Post build command line Deployment

In Visual Studio am creating a post-build event for Deploying using
md "$(SolutionDir)Deploy\bin"
which created the bin folder inside Deploy folder, inside my Solution.
How do I point this to the folder in some remote machine (where I have the web server)?
$(SolutionDir) to some other folder on a remote machine?
It may look simple to you. :) This is the first time am trying this stuff.
Thanks
The easiest way is to replace $(SolutionDir) with \\server\share
Just as an alternative, I like to keep my .sln and .csproj files "clean".
Then use a second (mini) .msbuild ( which is just a .xml file) to build the .sln, and then do these copy type events as a second action.
Here is a basic example:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapper">
<PropertyGroup>
<WorkingCheckout>.</WorkingCheckout>
<WorkingOutputs>m:\working\outputs</WorkingOutputs>
</PropertyGroup>
<Target Name="AllTargetsWrapper">
<CallTarget Targets="Clean" />
<CallTarget Targets="Build" />
<CallTarget Targets="CopyItUp" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(WorkingOutputs)" />
<MakeDir Directories="$(WorkingOutputs)" />
<Message Text="Cleaning done" />
</Target>
<Target Name="Build">
<MSBuild Projects="$(WorkingCheckout)\MySolution.sln" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="TargetOutputsItemName"/>
</MSBuild>
<Message Text="Build completed" />
</Target>
<!-- -->
<Target Name="CopyItUp" >
<ItemGroup>
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.txt" />
<MyExcludeFiles Include="$(WorkingCheckout)\**\SuperSecretStuff.doc" />
</ItemGroup>
<ItemGroup>
<MyIncludeFiles Include="$(WorkingCheckout)\MyCsProject\bin\$(Configuration)\**\*.*" Exclude="#(MyExcludeFiles)"/>
</ItemGroup>
<Copy
SourceFiles="#(MyIncludeFiles)"
DestinationFiles="#(MyIncludeFiles->'$(WorkingOutputs)\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</Target>
</Project>

If Statement in Visual Studio Project

I have this in my Visual Studio Project
<Target Name="BeforeBuild">
<Message Text="Compiling TypeScript files" />
<Message Text="Executing tsc$(TypeScriptSourceMap) #(TypeScriptCompile ->'"%(fullpath)"', ' ')" />
<Exec Command="tsc$(TypeScriptSourceMap) #(TypeScriptCompile ->'"%(fullpath)"', ' ')" IgnoreExitCode="true" />
</Target>
I want to execute the stuff within the <Target Name="BeforeBuild"></Target> if the Configuration is Debug and the Platform is AnyCPU. Is this possible and if so how is it done?
Try the following
<Target Name="BeforeBuild" Condition="'$(Configuration)' == 'Debug' And '$(Platform)' == 'AnyCPU'">
Slightly more fancy version
<Target Name="BeforeBuild" Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">

Order of AfterBuild tasks in Visual Studio projects ...?

I have defined several AfterBuild - Tasks in my Visual Studio project with different conditions:
<Target Name="AfterBuild" Condition="'$(Configuration)'=='FinalBuilder'">
<Message Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<Message Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
</Target>
But only the last one is executed if the condition match. If I choose the FinalBuilder-Configuration, the AfterBuild tasks is ignored and not executed. If I change the order of the Targets in the project files (Condition="'$(Configuration)'=='FinalBuilder'" as last one), the AfterBuild for FinalBuilder-Configuration is executed but the one for MvcBuildViews is ignored.
Is the order of the target important? Is only the last AfterBuild task taken into account? Or how can I define different AfterBuild tasks with different Conditions?
Thanks
Konrad
The only second one is executed because it was redefined. See MSDN (Declaring targets in the project file chapter).
You should use only one AfterBuild target in your project file like this:
<Target Name="AfterBuild" >
<Message Condition="'$(MvcBuildViews)'=='true'" Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
<Message Condition="'$(Configuration)'=='FinalBuilder'" Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
EDIT:
Or use CallTarget task:
<Target Name="AfterBuild" >
<CallTarget Condition="'$(MvcBuildViews)'=='true'" Targets="MvcBuildTarget" />
<CallTarget Condition="'$(Configuration)'=='FinalBuilder'" Targets="FinalBuilderTarget" />
</Target>
<Target Name="MvcBuildTarget">
<Message Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
</Target>
<Target Name="FinalBuilderTarget" >
<Message Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
If you really need to run multiple AfterBuild tasks (this may be the case for example if you need different Input and Output sets for each task) you can use DependsOnTarget to simply make AfterBuild depend upon all of them:
<Target Name="AfterBuild1"
Inputs="stuff"
Outputs="stuff">
<Message Text="Running first after build task." Importance="high" />
<Exec Command="stuff" />
</Target>
<Target Name="AfterBuild2"
Inputs="other stuff"
Outputs="other stuff">
<Message Text="Running other after build task." Importance="high" />
<Exec Command="stuff" />
</Target>
<Target Name="AfterBuild" DependsOnTargets="AfterBuild1;AfterBuild2" />
If you need to constrain their order, just make AfterBuild2 depend on AfterBuild1 with DependsOnTargets="AfterBuild1".

How do I copy files to another directory using MSBuild?

I'm trying to copy several files in the $(TargetDir) to another folder (e.g. C:\BinCache), but for whatever reason I cannot get MSBuild to stop complaining.
<Target Name="AfterBuild">
<Copy SourceFiles="$(TargetDir)\*.*"
DestinationFolder="C:\BinCache" />
</Target>
What am I doing wrong here?
EDIT: The solution is to use a CreateItem task. Presumably, Visual Studio 2008 removes this restriction. Thanks Scott!
<Target Name="AfterBuild">
<CreateItem Include="$(TargetDir)\*.*">
<Output TaskParameter="Include" ItemName="SourceFiles" />
</CreateItem>
<Copy SourceFiles="#(SourceFiles)" DestinationFolder="C:\BinCache" />
</Target>
SourceFiles needs to be an Item list
you'll need something like
<Target Name="AfterBuild">
<ItemGroup>
<SourceFiles Include="$(TargetDir)\*.*" />
</ItemGroup>
<Copy SourceFiles="#(SourceFiles)" DestinationFolder="C:\BinCache" />
</Target>
Just noticed you're on 2005, in that case you'll need to use the CreateItem task
Just use a regular pre- or post-build event. VS supports this out of the box.
xcopy "#(TargetDir)*.dll" "\yourdir" /i /f /s /v /y

Resources