Just like you can have Directory.Build.props and Directory.Build.targets to customize your projects' build, you can also use Directory.Solution.props
and Directory.Solution.targets
to customize your solutions (command-line) builds. Just like the original (older?) mecanisms, Visual Studio will not load those customizations either , however.
In order to inspect how and where they are included in the build, it's useful to set use the troubleshooting technique of setting the envvar MSBUILDEMITSOLUTION=1
and run a build. You can inspect the .metaproj MSBuild project generated from the solution, where you will see the imported projects.
For a Directory.Solution.props with:
Copy <Project>
<PropertyGroup>
<SolutionPropsProp>from-solution.props</SolutionPropsProp>
</PropertyGroup>
</Project>
And a Directory.Solution.targets with:
Copy <Project>
<PropertyGroup>
<SolutionTargetsProp>from-solution.targets</SolutionTargetsProp>
</PropertyGroup>
<Target Name="CustomSolutionBuild">
</Target>
</Project>
You will see a .metaproj similar to:
Copy <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="ValidateSolutionConfiguration;ValidateToolsVersions;ValidateProjects" DefaultTargets="Build">
<PropertyGroup>
<RoslynTargetsPath>C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\Roslyn</RoslynTargetsPath>
<_DirectorySolutionPropsFile>Directory.Solution.props</_DirectorySolutionPropsFile>
<_DirectorySolutionPropsBasePath>C:\Code\kzu\moq</_DirectorySolutionPropsBasePath>
<DirectorySolutionPropsPath>C:\Code\kzu\moq\Directory.Solution.props</DirectorySolutionPropsPath>
<SolutionPropsProp>from-solution.props</SolutionPropsProp>
<Configuration>Debug</Configuration>
<Platform>Any CPU</Platform>
...
<_DirectorySolutionTargetsFile>Directory.Solution.targets</_DirectorySolutionTargetsFile>
<_DirectorySolutionTargetsBasePath>C:\Code\kzu\moq</_DirectorySolutionTargetsBasePath>
<DirectorySolutionTargetsPath>C:\Code\kzu\moq\Directory.Solution.targets</DirectorySolutionTargetsPath>
<SolutionTargetsProp>from-solution.targets</SolutionTargetsProp>
</PropertyGroup>
...
<Target Name="_IsProjectRestoreSupported" Returns="@(_ValidProjectsForRestore)" />
<Target Name="CustomSolutionBuild" />
<Target Name="Build" Outputs="@(CollectedBuildOutput)">
...
</Project>
Notice how the targets aren't imported but rather embedded in specific places (top of property group for .props-declared properties, bottom of property group for .targets-declared properties, and before Build target for targets). If you have properties, they are actually even evaluated before being embedded in the file, i.e.:
Copy <SolutionNow>$([System.DateTime]::Now)</SolutionNow>
is embedded in the .metaproj as the actually evaluated value, such as:
Copy <SolutionNow>10/21/2020 4:36:30 AM</SolutionNow>