3

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....

Thomas Oatman
  • 301
  • 2
  • 9

2 Answers2

5

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.

Thomas Oatman
  • 301
  • 2
  • 9
  • I am glad you have got your solution and thanks for your sharing, I would appreciate it if you mark them as answer and this will be beneficial to other community. – Barrnet Chou Feb 18 '21 at 06:49
  • Hi @Barrnet-Chou Mark what as answer? It won't let me mark my own as answer, and the one below here was not exactly what I was looking for. – Thomas Oatman Feb 20 '21 at 17:25
1

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>
  <!-- ... -->
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • Hi dxiv. Thank you. I have thought about such an approach -- but doesn't this still mean someone has to edit new project files? I want to avoid mistakes for my developers in the future; editing the vcxproj will likely be forgotten. – Thomas Oatman Feb 06 '21 at 15:08
  • @ThomasOatman To change the default settings for *future* projects, you'll need to either use [project templates](https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-project-templates?view=vs-2019) (and make sure everyone else who may add projects to the solution does), or modify the installation defaults in one of Visual Studio's `.props` files that is included by default in new projects. I am not aware of a (builtin, automatic) way to enforce at the solution level that all child projects *must* inherit certain custom settings. – dxiv Feb 06 '21 at 18:32
  • 1
    Directory.Build.props and Directory.Build.targets seem to be just that. They are searched for in the directory tree by the VS .prop files: [link]https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019 I believe the real question, is when does $(ProjectName) get defined such that I can place the new macros *after* that... – Thomas Oatman Feb 06 '21 at 19:14
  • @ThomasOatman YMMV but when relying on the automatic inclusion you don't control the position where it gets inserted in the `.vcxproj`, which is why I prefer to add the reference(s) explicitly. Some settings must go into separate `.props` files included at different points in `.vcxproj` in order for the settings to be applied correctly (e.g. `ProjectConfigurations` vs. other `UserMacros`). – dxiv Feb 06 '21 at 19:23