If you are thinking of something like PowerShell Profiles, there is no equivalent feature in MSBuild. But there are features that can be used to meet your need.
.user File
As a peer of the project file, e.g. foo.csproj
, create a .user file that matches the project name, e.g. foo.csproj.user
. This file is expressly meant for local customization and should be excluded from source control. (See ".user file".)
It is, however, per-project. You would need to create and maintain a separate .user file for each project.
Directory.Build.props and Directory.Build.targets
For both Directory.Build.props
and Directory.Build.targets
, MSBuild will always walk up the chain of parent directories and import the file if it is found.
You could create a Directory.Build.props
and/or Directory.Build.targets
in C:\Users\MyUserName
. These files will be found and used by all proejcts in sub-directories of C:\Users\MyUserName
-- unless another file is found first.
It is a good idea to implement "multi-level merging". Essentially customize every Directory.Build.props
and Directory.Build.targets
file to continue walking up the directories and chain import. This is nice because sets of projects can share customizations.
The documentation gives the following example for a Directory.Build.props
:
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
This can be generalized to work for both Directory.Build.props
and Directory.Build.targets
by using the $(MSBuildThisFile)
property instead of hard-coding the file name.
The Import
will throw an error if the Project
attribute is empty, i.e. there will be an error when no file is found above.
Often a file is created in the top of the code tree that doesn't look above. That stops the import chain and doesn't throw an error but prevents using a 'global' file that is outside of the code tree.
However, the Import
can have a condition so boilerplate for the Directory.Build.props
and Directory.Build.targets
files can be:
<Project>
<PropertyGroup>
<ParentFile>$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)', '$(MSBuildThisFileDirectory)../'))</ParentFile>
</PropertyGroup>
<Import Project="$(ParentFile)" Condition="$(ParentFile) != '' and Exists($(ParentFile))" />
...
To keep things consistent and sane I recommend to always import the "parent file" at the top of the "child file".
Given a project directory path of A\B\C
with a Directory.Build.targets
file in each directory:
A\B\C\Directory.Build.targets
will be found first and will import A\B\Directory.Build.targets
.
A\B\Directory.Build.targets
will import A\Directory.Build.targets
.
A\Directory.Build.targets
will find no file to import.
The order of the content of the files in the project after import will be:
A\Directory.Build.targets
A\B\Directory.Build.targets
A\B\C\Directory.Build.targets
A file lower in the code tree (and closer to the project file) can override customizations from higher in the code tree. This is a good general pattern even if it seems counter to your needs.
But you should also follow the "MSBuild best practices" and test properties before setting them.
If the following is used in A\B\C\Directory.Build.targets
and BaseOutputPath
has already been defined in A\Directory.Build.targets
, the value will not be overridden.
<PropertyGroup>
<BaseOutputPath Condition="'$(BaseOutputPath)' == ''">$(USERPROFILE)</BaseOutputPath>
</PropertyGroup>
I know this probably feels a bit DIY but it is very flexible, extensible, and well supported by MSBuild.
Directory.Build.prop
s, to set the appropriate MSBuild properties in one place, to apply to all the projects in the solution.Directory.Build.props
file in the install directory underC:\Program Files
. It doesn't even make sense because theC:\Program Files
directory has restricted access. That answer should be downvoted.Directory.Build.props
andDirectory.Build.targets
files are searched for by walking up through the parent directories. If the files are already in use in the code tree, your local file may not be imported.