【问题标题】:Using MSBuild, '$(SolutionDir)' resolves incorrectly to C:\ when running 'msbuild /t:restore'使用 MSBuild,运行“msbuild /t:restore”时,“$(SolutionDir)”错误地解析为 C:\
【发布时间】:2017-10-02 13:26:47
【问题描述】:

我有 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”(恢复目标)(1)-> "C:\Users\phelan\workspace\bar\foo\foo.csproj" (_IsProjectRestoreSupported 目标) (11) -> C:\Users\phelan\workspace\bar\foo\foo.csproj(7,3): 错误 MSB4019:导入的项目 “C:\WpfNetStandardSample\CustomProjectSystem\CustomProject.props”原为 未找到。确认声明中的路径是 正确,并且该文件存在于磁盘上。

主要问题

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 存储库

https://github.com/bradphelan/msbuildbug

【问题讨论】:

标签: .net msbuild continuous-integration visual-studio-2017 .net-standard-2.0


【解决方案1】:

发生这种情况是因为对于解决方案的 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 创建的 &lt;MSBuild&gt; 项时,它们不会设置这些属性,因此在评估期间它们在项目中不可用。

作为一种解决方法,我建议使用 MSBuild 15 的 Directory.Build.props 逻辑,该逻辑会自动将具有此名称的文件导入使用公共属性/目标的所有项目(几乎所有项目类型)。

在此文件中,放置在解决方案目录或它适用的项目上方的任何目录中,您可以设置要在项目中使用的变量 (ExtPath) 或直接设置所需的公共属性:

<Project>
  <PropertyGroup>
    <ExtPath>$(MSBuildThisFileDirectory)</ExtPath>
    <VersionPrefix>1.2.3</VersionPrefix>
  </PropertyGroup>
  <Import Project="$(ExtPath)Some\other.props" />
</Project>

【讨论】:

    【解决方案2】:

    以下是解决方法,而不是解决方案。使用相对路径:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup Label="Globals">
                <ExtPath>..\WpfNetStandardSample\CustomProjectSystem\</ExtPath>
      </PropertyGroup>
    
      <Import Project="$(ExtPath)CustomProject.props" />
    
    </Project>
    

    【讨论】:

    • 我建议在定义 &lt;ExtPath&gt;$(MSBuildThisFileDirectory)&lt;/…&gt; 的 sln 目录中创建一个 Directory.Build.props 文件,这样您就不必在每个项目中都设置它。
    • 是的,这是 msbuild 15 的一个特性(通用道具/目标)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    • 2018-09-02
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多