使用 MSBuild,“$(SolutionDir)”在 运行 'msbuild /t:restore' 时错误地解析为 C:\
Using MSBuild, '$(SolutionDir)' resolves incorrectly to C:\ when running 'msbuild /t:restore'
我有foo.csproj,
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<ExtPath>$(SolutionDir)\WpfNetStandardSample\CustomProjectSystem\</ExtPath>
</PropertyGroup>
<Import Project="$(ExtPath)CustomProject.props" />
</Project>
引用自解决方案文件 bar.sln.
为了从持续集成服务器恢复我的 NuGet 包,我 运行
msbuild /t:restore bar.sln
我得到错误:
"C:\Users\phelan\workspace\bar\bar.sln" (restore target) (1) ->
"C:\Users\phelan\workspace\bar\foo\foo.csproj"
(_IsProjectRestoreSupported target) (11) ->
C:\Users\phelan\workspace\bar\foo\foo.csproj(7,3): error
MSB4019: The imported project
"C:\WpfNetStandardSample\CustomProjectSystem\CustomProject.props" was
not found. Confirm that the path in the declaration is
correct, and that the file exists on disk.
主要问题
SolutionDir 如何以及为什么解析为 C:\ 而不是解决方案文件所在的目录?
备注
- 整个解决方案在 Visual Studio.
内部构建
- 运行
msbuild foo.sln
构建整个解决方案(假设 NuGet 包已经恢复)
- MSBuild 版本为 15.3.409.57025
- 解决方案是混合项目,一些使用 packages.config,一些使用 PackageReference。使用 PackageReference 的一些项目是 dotnetstandard 库,一些是 net461 库。
- 似乎有多种关于如何恢复 NuGet 包的建议
dotnet restore
nuget restore
msbuild /t:restore
dotnet msbuild /t:restore foo.sln
我的混合设置类型的正确流程是什么?
GitHub 用于重现错误的存储库
以下是解决方法,不是解决方案。使用相对路径:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<ExtPath>..\WpfNetStandardSample\CustomProjectSystem\</ExtPath>
</PropertyGroup>
<Import Project="$(ExtPath)CustomProject.props" />
</Project>
发生这种情况是因为对于解决方案的 Restore
目标,SolutionDir
未传递给生成的项目引用。
当运行:
时你可以看到这个
MSBuildEmitSolution=1 dotnet msbuild /t:Restore
对于 foo.sln
这将生成一个 foo.sln.metaproj
文件,其中包含解决方案格式的实际 MSBuild 解释,以及添加的扩展名(例如我们的例子中的 15.0/SolutionFile/ImportAfter/Microsoft.NuGet.ImportAfter.targets
).
生成的 Build
目标包含以下内容:
<Target Name="Build" Outputs="@(CollectedBuildOutput)">
<MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)">
<Output TaskParameter="TargetOutputs" ItemName="CollectedBuildOutput" />
</MSBuild>
</Target>
您可以看到这明确设置了 SolutionDir
和其他与解决方案相关的属性。这同样适用于生成到解决方案中的许多其他目标。
然而,NuGet 目标不会这样做(技术原因是它们为项目和解决方案共享相同的 MSBuild 代码)。检查由 NuGet 创建的 <MSBuild>
项时,它们不会设置这些属性,因此在评估期间它们在项目中不可用。
作为解决方法,我建议使用 MSBuild 15 的 Directory.Build.props
逻辑,该逻辑会自动将具有此名称的文件导入所有使用通用 properties/targets 的项目(几乎所有项目类型)。
在这个文件中,放置在解决方案目录或它适用的项目之上的任何目录,您可以设置一个变量以在项目中使用(ExtPath
)或直接设置所需的公共属性:
<Project>
<PropertyGroup>
<ExtPath>$(MSBuildThisFileDirectory)</ExtPath>
<VersionPrefix>1.2.3</VersionPrefix>
</PropertyGroup>
<Import Project="$(ExtPath)Some\other.props" />
</Project>
我有foo.csproj,
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<ExtPath>$(SolutionDir)\WpfNetStandardSample\CustomProjectSystem\</ExtPath>
</PropertyGroup>
<Import Project="$(ExtPath)CustomProject.props" />
</Project>
引用自解决方案文件 bar.sln.
为了从持续集成服务器恢复我的 NuGet 包,我 运行
msbuild /t:restore bar.sln
我得到错误:
"C:\Users\phelan\workspace\bar\bar.sln" (restore target) (1) -> "C:\Users\phelan\workspace\bar\foo\foo.csproj" (_IsProjectRestoreSupported target) (11) -> C:\Users\phelan\workspace\bar\foo\foo.csproj(7,3): error MSB4019: The imported project "C:\WpfNetStandardSample\CustomProjectSystem\CustomProject.props" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.
主要问题
SolutionDir 如何以及为什么解析为 C:\ 而不是解决方案文件所在的目录?
备注
- 整个解决方案在 Visual Studio. 内部构建
- 运行
msbuild foo.sln
构建整个解决方案(假设 NuGet 包已经恢复) - MSBuild 版本为 15.3.409.57025
- 解决方案是混合项目,一些使用 packages.config,一些使用 PackageReference。使用 PackageReference 的一些项目是 dotnetstandard 库,一些是 net461 库。
- 似乎有多种关于如何恢复 NuGet 包的建议
dotnet restore
nuget restore
msbuild /t:restore
dotnet msbuild /t:restore foo.sln
我的混合设置类型的正确流程是什么?
GitHub 用于重现错误的存储库
以下是解决方法,不是解决方案。使用相对路径:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Globals">
<ExtPath>..\WpfNetStandardSample\CustomProjectSystem\</ExtPath>
</PropertyGroup>
<Import Project="$(ExtPath)CustomProject.props" />
</Project>
发生这种情况是因为对于解决方案的 Restore
目标,SolutionDir
未传递给生成的项目引用。
当运行:
时你可以看到这个MSBuildEmitSolution=1 dotnet msbuild /t:Restore
对于 foo.sln
这将生成一个 foo.sln.metaproj
文件,其中包含解决方案格式的实际 MSBuild 解释,以及添加的扩展名(例如我们的例子中的 15.0/SolutionFile/ImportAfter/Microsoft.NuGet.ImportAfter.targets
).
生成的 Build
目标包含以下内容:
<Target Name="Build" Outputs="@(CollectedBuildOutput)">
<MSBuild Projects="@(ProjectReference)" BuildInParallel="True" Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)">
<Output TaskParameter="TargetOutputs" ItemName="CollectedBuildOutput" />
</MSBuild>
</Target>
您可以看到这明确设置了 SolutionDir
和其他与解决方案相关的属性。这同样适用于生成到解决方案中的许多其他目标。
然而,NuGet 目标不会这样做(技术原因是它们为项目和解决方案共享相同的 MSBuild 代码)。检查由 NuGet 创建的 <MSBuild>
项时,它们不会设置这些属性,因此在评估期间它们在项目中不可用。
作为解决方法,我建议使用 MSBuild 15 的 Directory.Build.props
逻辑,该逻辑会自动将具有此名称的文件导入所有使用通用 properties/targets 的项目(几乎所有项目类型)。
在这个文件中,放置在解决方案目录或它适用的项目之上的任何目录,您可以设置一个变量以在项目中使用(ExtPath
)或直接设置所需的公共属性:
<Project>
<PropertyGroup>
<ExtPath>$(MSBuildThisFileDirectory)</ExtPath>
<VersionPrefix>1.2.3</VersionPrefix>
</PropertyGroup>
<Import Project="$(ExtPath)Some\other.props" />
</Project>