MSBuild: Do I need Target Rebuild? - unit-testing

I use a build file to build and test my project.
I have a Compile-Target which has this line "Targets = "Rebuild". Do I really need this line? Using Visual Studio I know that I can clean a Solution and build it again, or I can just rebuild the solution.
In my msbuild-file I delete my main folder BuildArtifacts before creating him again. I used this Tutorial and I don't know why he uses Target=Rebuild?
This is my build file:
<Project ToolsVersion="4.0" DefaultTargets="RunUnitTests" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Falls Eigenschaften nicht gesetzt -> Release & Any CPU als default-->
<PropertyGroup>
<!-- ... -->
</PropertyGroup>
<ItemGroup>
<!-- ... -->
</ItemGroup>
<!-- All the stuff go into my main folder -->
<Target Name="Init" DependsOnTargets="Clean">
<MakeDir Directories="#(BuildArtifacts)" />
</Target>
<!-- delete my main folder -->
<Target Name="Clean">
<RemoveDir Directories="#(BuildArtifactsDir)" />
</Target>
<!-- delete NUnit-Files -->
<Target Name="CleanAfter">
<RemoveDir Directories="#(NunitDir)" />
</Target>
<Target Name="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)"
Targets="Rebuild"
Properties="OutDir=%(BuildArtifactsDir.FullPath);
Configuration=$(Configuration);
Platform=$(BuildPlatform)" />
</Target>
<Target Name="RunUnitTests" DependsOnTargets="Compile">
<Exec Command='"#(NUnitConsole)" "#(UnitTestsDLL)" --result=console-test.xml --work=BuildArtifacts' />
<CallTarget Targets="CleanAfter" />
</Target>
</Project>

This depends on your own needs: do you need the whole solution to be rebuilt or not? Arguably, on a build server, you want to do a complete clean/rebuild after every commit to make sure the codebase is sane. Removing just the output directory (I assume that is what the Clean starget does) doesn't necessarily remove all object files as well since they typically go into the intermediate directory which might not be the same as the output directory.

Related

Setting OutDir and IntDir for all projects

I am trying to setup a default location for OutDir and IntDir to be used by all projects in the solution. Such as: <OutDir>$(SolutionDir)bld\$(Platform)\$(ProjectName)\$(Configuration)\</OutDir>
When I set this in the Directory.Build.props file, it looks okay in Visual Studio Properties dialog; but when I build, the $(ProjectName) portion is empty. This tells me that this macro was not available when OutDir was read in Directory.Build.props.
I tried adding this to the Directory.Build.targets file and it appeared to be ignored altogether.
Another goal is to not modify any of the vcxproj files after this initial change. And new projects would inherit these settings automatically.
Is this possible? Perhaps I am placing the setting in the wrong place/order in the file... ?
This is a snippet of the Directory.Build.props:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets">
</ImportGroup>
<!-- Define macros for easy and consistent access to various parts of the tree -->
<PropertyGroup Label="UserMacros">
<GslInclude>$(SolutionDir)packages\Microsoft.Gsl.0.1.2.1\build\native\include</GslInclude>
<mySrcDir>$(SolutionDir)src\</mySrcDir>
<myBldDir>$(SolutionDir)bld\</myBldDir>
<myBldIntDir>$(SolutionDir)bld_int\</myBldIntDir>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup>
<BuildMacro Include="GslInclude"> <Value>$(GslInclude)</Value> </BuildMacro>
<BuildMacro Include="mySrcDir"> <Value>$(mySrcDir)</Value> </BuildMacro>
<BuildMacro Include="myBldDir"> <Value>$(myBldDir)</Value> </BuildMacro>
<BuildMacro Include="myBldIntDir"> <Value>$(myBldIntDir)</Value> </BuildMacro>
</ItemGroup>
<!-- Platform Toolset, Windows SDK -->
<PropertyGroup Label="Globals">
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<!-- Default Compiler settings -->
<ItemDefinitionGroup>
<ClCompile>
<!-- .... -->
</ClCompile>
</ItemDefinitionGroup>
<!-- Default Folders for target output -->
<PropertyGroup>
<OutDir>$(myBldDir)$(Platform)\$(ProjectName)\$(Configuration)\</OutDir>
<IntDir>$(myBldIntDir)$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup>
</Project>
And then of course, I simply remove all <OutDir> and <IntDir> settings in the proj files.
I would also love to be able to place conditions on settings based on the version of Visual Studio. Some of our developers are using VS2017 with older ToolSet; some are using VS2019 with the latest....
Thanks to those who helped with this. I got it working - at least for my main goal of establishing a pre-defined location for build 'output'.
Referencing information from here: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019 and searching around in the VS props files, I find a macro 'MSBuildProjectName' which is set early in the process.
I created a Directory.Build.props file in the root of my build tree with contents like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets">
</ImportGroup>
<!-- Define macros for easy and consistent access to various parts of the tree -->
<PropertyGroup Label="UserMacros">
<GslInclude>$(SolutionDir)packages\Microsoft.Gsl.0.1.2.1\build\native\include</GslInclude>
<myBldDir>$(SolutionDir)bld\</myBldDir>
<myBldIntDir>$(SolutionDir)bld_int\</myBldIntDir>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup>
<BuildMacro Include="GslInclude"> <Value>$(GslInclude)</Value> </BuildMacro>
<BuildMacro Include="myBldDir"> <Value>$(myBldDir)</Value> </BuildMacro>
<BuildMacro Include="myBldIntDir"> <Value>$(myBldIntDir)</Value> </BuildMacro>
</BuildMacro>
<!-- etc .... -->
</ItemGroup>
<!-- Platform Toolset, Windows SDK -->
<PropertyGroup Label="Globals">
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup>
<IntDir>$(myBldIntDir)$(Platform)\$(MSBuildProjectName)\$(Configuration)\</IntDir>
<OutDir>$(myBldDir)$(Platform)\$(MSBuildProjectName)\$(Configuration)\</OutDir>
</PropertyGroup>
<!-- lots more .... -->
</Project>
Settings in this file affect all projects living 'below' it in the folder structure.
If a sub-folder needs a different value, add a Directory.Build.props to that folder which imports the 'parent' props file (it may be several layers above):
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<ImportGroup Label="PropertySheets">
</ImportGroup>
<PropertyGroup>
<IntDir>$(myBldIntDir)$(Platform)\Tests\$(MSBuildProjectName)\$(Configuration)\</IntDir>
<OutDir>$(myBldDir)$(Platform)\Tests\$(MSBuildProjectName)\$(Configuration)\</OutDir>
</PropertyGroup>
</Project>
Settings in the 'child' props file override anything set in its parents.
Once I removed all <OutDir> and <IntDir> elements in the XML project files, the "Output Directory" and "Intermediate Directory" settings show up in regular font (not bold which indicates it is not inheriting) and, of course, when I build, everything goes to the consolidated folder tree; away from the source. And the cool thing: New projects will be populated automatically with these new default settings.
note: #dxiv points out that some settings may be pickier and require more work.
It is possible, and it is in fact the recommended way to share settings between projects. Only caveat is that the scheme is rather sensitive to the section tags and order/placement of inclusions. The following should work for OutDir and IntDir.
Directory.Build.props
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_PropertySheetDisplayName>Directory.Build</_PropertySheetDisplayName>
</PropertyGroup>
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros">
<!-- other user macros -->
<OutDir> ... </OutDir>
<IntDir> ... </IntDir>
</PropertyGroup>
<!-- rest of .props file --->
Project.vcxproj
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!-- include custom .props here, can also use full or relative path -->
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="Directory.Build.props" />
</ImportGroup>
<!-- ... -->

Execute selected ant tasks for a selected set of projects

I have defined targets in ant for our games, e.g. clean, build-ios, build-android, deploy-ios, deploy-android, etc. Now I would like to define a new set of targets that represent our games, say game1, game2, game3.
My goal is to be able to launch ant with a set of target games and a set of target tasks so that for each selected game, each selected task is executed.
Example pseudo code: Foreach [game1, game3]: clean, build-ios, deploy-ios
How can I achieve this with ant? A requirement would be to define which games and which tasks are selected through targets, not to write them in a file that is altered manually.
The subant task is useful for situations where you have multiple subprojects that share similar structures.
In your main build.xml, define a target that rubs the desired build target on your game subdirectories, along with all of the generalized build targets.
<target name="deploy-all">
<subant target="deploy">
<dirset dir="." includes="game-*" />
</subant>
<echo message="All games deployed." />
</target>
<target name="deploy" depends="deploy-ios,deploy-android">
<echo message="${ant.project.name} build complete." />
</target>
<target name="clean">
<echo message="Cleaning ${ant.project.name}" />
</target>
<target name="build-ios" depends="clean">
<echo message="Building iOS ${ant.project.name}" />
</target>
<target name="build-android" depends="clean">
<echo message="Building Android ${ant.project.name}" />
</target>
<target name="deploy-ios" depends="build-ios">
<echo message="Deploying iOS ${ant.project.name}" />
</target>
<target name="deploy-android" depends="build-android">
<echo message="Deploying Android ${ant.project.name}" />
</target>
Then, in the game-* subdirectories, create a simple build.xml that links back to the common one.
game-1/build.xml:
<project name="game-1" default="build">
<import file="../build.xml" />
<echo message="=== Building Game 1 ===" />
</project>
game-2/build.xml:
<project name="game-2" default="build">
<import file="../build.xml" />
<echo message="=== Building Game 2 ===" />
</project>
Edit: If your build needs to include/exclude certain subprojects based on the user's input or a pre-defined property, you can modify the subant task's nested resource collection to accommodate for this.
<property name="game.includes" value="game-*" />
<property name="game.excludes" value="" />
<subant target="deploy">
<dirset dir="." includes="${game.includes}" excludes="${game.excludes}" />
</subant>
The user can then run a command that optionally passes values for game.includes and/or game.excludes. If these properties are not specified, the values defined above by the property task will be used as defaults.

Is the ClCompile item a task as well?

I am trying to understand the visual cpp project document here (https://learn.microsoft.com/en-us/cpp/build/walkthrough-using-msbuild-to-create-a-visual-cpp-project).
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />
<PropertyGroup>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="main.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Targets" />
</Project>
I gather that ClCompile, is an item as it is nested under the ItemGroup tag, it also seems that the files to be compiled are referred to in the ClCompile tag. On the documentation page for Items, https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-items, it states that Items are inputs into the build system. I dont see a task above which takes these ClCompile items and compiles them, how is compilation being achieved? Is the ClCompile item as task as well?
I dont see a task above which takes these ClCompile items and compiles them, how is compilation being achieved? Is the ClCompile item as task as well?
After search all the .targets and .props files in the VCTargets folder under following path:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\VC\VCTargets
We could find following code snippet in the Microsoft.CppCommon.targets file:
<Target Name="ClCompile"
Condition="'#(ClCompile)' != ''"
DependsOnTargets="SelectClCompile">
<PropertyGroup>
<CLToolArchitecture Condition="'$(CLToolArchitecture)' == ''">$(VCToolArchitecture)</CLToolArchitecture>
<CLDeleteOutputOnExecute Condition="'$(CLDeleteOutputOnExecute)' == ''">true</CLDeleteOutputOnExecute>
</PropertyGroup>
<ItemGroup>
<ClNoDependencies Condition="'#(ClNoDependencies)' == '' and '%(ClInclude.NoDependency)' == 'true'" Include="#(ClInclude)"/>
<ClNoDependencies Condition="'$(NoDependencies)' != ''" Include="$(NoDependencies)" />
</ItemGroup>
...
<OnError Condition="'$(OnXamlPreCompileErrorTarget)' != ''" ExecuteTargets="$(OnXamlPreCompileErrorTarget)" />
</Target>
So ClCompile should be a target, which is used to execute the Visual C++ compiler tool, cl.exe to compile the C/C++ source file. That is reason why it not in the MSBuild-item.
See MSBuild (Visual C++) Overview for some more details.
Yes, CLCompile actually runs the CLTask task class. I suspect (though don't know for sure) they did it that way so they could have both CLCompile and CLInclude without having to write task implementations for each. I am not sure what namespace that is found in or the assembly, unlike tasks for pure .net languages the c++ tasks are not in the .net library docs.

MSBuild RegEx Assembly Version

Trying to create an MSBuild task that outputs my code to a folder. All is working except the Regular Expression. My code:
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(OutDir)$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="TheVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<Pattern>(\d+)\.(\d+)\.(\d+)</Pattern>
<In>%(TheVersion.Version)</In>
<OutVersion>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</OutVersion>
</PropertyGroup>
<ItemGroup>
<OutputFiles Include="$(OutDir)*" Exclude="*.tmp" />
<SolnFiles Include="$(SolutionDir)INDIVIDUAL.txt;$(SolutionDir)LICENSE.txt;$(SolutionDir)README.md" />
</ItemGroup>
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
</Target>
When I run this, I get this error:
The item "D:\Dache\INDIVIDUAL.txt" in item list "SolnFiles" does not define a value for metadata "OutVersion". In order to use this metadata, either qualify it by specifying %(SolnFiles.OutVersion), or ensure that all items in this list define a value for this metadata.
When I try %(SolnFiles.OutVersion) it comes up blank. I'm doing something dumb here, what is it?
Took me a few to figure it out. PropertyGroup variables are referenced as $(Var) while ItemGroup output variables are #() and GetAssemblyIdentity is %() - so I changed:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
to this:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\" SkipUnchangedFiles="true" />
and it worked.

Unable to Run test case through Nant when using log4net

I am working on a dotnet 2008, Nunit 2.6,Nant0.91 application.
I am using log4net for my logging purposes.
When trying to run test cases through Nunit, a log file is generated, but when I try to run it through Nant, I am unable to run my test case.
I am pasting the complete information present in my build file:
<?xml version="1.0" encoding="utf-8"?>
<project name="w2" default="run" basedir=".">
<!--Here we are building DLL -->
<target name="build">
<mkdir dir="Libraries" />
<mkdir dir="Output"/>
<csc target="library" output="Libraries/${project::get-
name()}.dll">
<sources>
<include name="C:\\Users\\rm99699\\Documents\\Visual Studio
2008\\Projects\\w2\\w2\\*.cs" />
<include name="C:\\Users\\rm99699\\Documents\\Visual Studio 2008\
\Projects\\w2\\w2\\app.config.xml"/>
</sources>
<references basedir="Libraries">
<include name="${nant::scan-probing-
paths('nunit.framework.dll')}" />
<include name="${nant::scan-probing-paths('log4net.dll')}" />
<include name="D:\selenium-
dotnet-2.8.0\net35\WebDriver.dll" />
<include name="D:\selenium-
dotnet-2.8.0\net35\WebDriver.Support.dll" />
<include name="D:\selenium-
dotnet-2.8.0\net35\ThoughtWorks.Selenium.Core.dll" />
</references>
</csc>
</target>
<!--Here we are running testcases -->
<target name="run" depends="build">
<nunit2 failonerror="false" >
<formatter type="Xml" usefile="true" extension=".xml"
outputdir="Output"/>
<test assemblyname="C:\\Users\\rm99699\\Documents\\Visual Studio 2008\
\Projects\\w2\\w2\\Libraries\\w2.dll" appconfig="C:\\Users\\rm99699\
\Documents\\Visual Studio 2008\\Projects\\w2\\w2\\app.config">
<references basedir="Libraries">
<include name="${nant::scan-probing-
paths('nunit.framework.dll')}" />
<include name="D:\selenium-dotnet-2.8.0\net35\WebDriver.dll" />
<include name="D:\selenium-
dotnet-2.8.0\net35\WebDriver.Support.dll" />
<include name="D:\selenium-
dotnet-2.8.0\net35\ThoughtWorks.Selenium.Core.dll" />
<include name ="D:\nant-0.91-bin\nant-0.91\bin\lib\common
\2.0\log4net.dll" />
<!-- <include name="${nant::scan-probing-paths('log4net.dll')}" />-->
</references>
</test>
</nunit2>
</target>
</project>
Based on my build file, are there any visible errors that are preventing me from running my test case?
What is the console output? NAnt includes log4net in it's core, and I've seen cases where it causes problems when one's code uses a different version of log4net than NAnt does. You could try using an <exec> task to run the unit tests -- especially since NAnt's <nunit2> task uses a heinously outdated NUnit 2.2.