我的项目分布在两个解决方案中,称它们为
Mine.sln
和 Theirs.sln
(它们位于不同的存储库中)。 Theirs
中的项目都是对Mine
的依赖。这两个解决方案目前是按顺序构建的。我想更改此设置,以便将依赖项构建为 Mine
的一部分,从而无需使用两个解决方案(它们也在单独的存储库中)。问题是在 Theirs
中,项目在 nuget 包引用上设置了 HintPath
属性(不幸的是,这是 pre-sdk 项目)。
为了更具体一点:
Mine.sln (14 projects)
+=> MP0
+=> MP1
+=> ...
+=> MP13
Theirs.sln (21 projects)
+=> TP0
+=> TP1
+=> ...
+=> TP20
从
Theirs
到Mine
我添加了大概10个左右,还有MP0 => TP1, TP2
等项目依赖
问题:当
Theirs
被构建为Mine
的依赖项时,那些与HintPath
解决方案相关的Theirs
属性不再正确并且依赖项项目无法构建。有一个处理这个问题的 GitHub 问题,虽然没有真正的解决方案:https://github.com/NuGet/Home/issues/738
在
Theirs
中修改项目并不是一个真正的选择,所以我正在探索做一些事情的可能性,比如在构建过程中重写内存中的那些属性。有一个项目可以逐个项目地执行此操作,我查看了他们使用的目标文件(https://github.com/jstangroome/NuGetReferenceHintPathRewrite/blob/master/content/NuGetReferenceHintPathRewrite.targets ),但它是针对每个项目的,需要在 Theirs
中修改项目,这又不是一个真正的选择。
我的希望是在
Directory.Build.targets
的根部使用 Mine.sln
来为由该解决方案构建的每个项目设置一个在 ResolveAssemblyReferences
之前运行的目标。我的 Directory.Build.targets
文件如下所示:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ReferenceHintPathUpdate" BeforeTargets="BeforeResolveAssemblyReference">
<Message Text="ReferenceHintPathUpdate: Building $(MSBuildProjectName) in $(SolutionDir)" Importance="High" />
</Target>
</Project>
我这里只有
Message
任务来测试构建顺序和依赖项。我期待在终端中看到每个项目名称输出,但我只从 some 项目中获得输出(一些由 Mine
引用,一些由 Theirs
引用,但与所有项目都不一样)。我还不能确定为什么这个目标只针对某些项目运行;似乎没有明显的共同模式。
示例(过滤后的)输出:
ReferenceHintPathUpdate: Building MP0 in C:\Users\Me\MySolution
ReferenceHintPathUpdate: Building MP1 in C:\Users\Me\MySolution
...
ReferenceHintPathUpdate: Building MP7 in C:\Users\Me\MySolution
ReferenceHintPathUpdate: Building TP0 in C:\Users\Me\MySolution
ReferenceHintPathUpdate: Building TP4 in C:\Users\Me\MySolution
我需要做什么才能使
Directory.Build.targets
中定义的目标运行此解决方案构建的每个项目,特别是 来自 Theirs.sln
的依赖项项目?
将项目(或尽可能多的项目)从使用
packages.config
迁移到使用PackageReference
。 PackageReference
可用于遗留风格项目和 SDK 风格项目。如果您有任何.Net Framework ASP.NET 项目,这些项目无法迁移,需要继续使用packages.config
.
随着
PackageReference
没有提示路径。
你是如何解决两个repos的问题的?版本控制系统 git 和
Theirs
是否作为子目录中的子模块被拉入?
我假设您无法映射或移动
TP*
项目目录,以便相对HintPath
工作。 (有些版本控制系统支持“覆盖”存储库,但 git 子模块不能那样工作。)
我假设有些项目不能更改为
PackageReference
要么因为项目是 .Net Framework ASP.NET 要么因为它是 Theirs
.
对于遗留样式和 SDK 样式项目,
Directory.Build.props
和 Directory.Build.targets
文件如果找到会自动导入。 MSBuild 向上搜索父目录以查找文件。请参阅按文件夹自定义构建并注意有关搜索范围的部分。
我发现“链接”
Directory.Build.*
文件非常有用,但必须将其明确添加到每个文件中。请参阅有关多级合并的部分。
如果
Theirs
存储库使用 Directory.Build.targets
文件而没有链接,您的 Directory.Build.targets
文件将不会被导入。当不同的项目似乎有不同的行为时,我希望这是问题所在。
以下内容可以添加到所有
Directory.Build.props
和Directory.Build.targets
文件中。
<Import Project="$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)', '$(MSBuildThisFileDirectory)../'))" Condition="'$([MSBuild]::GetPathOfFileAbove('$(MSBuildThisFile)', '$(MSBuildThisFileDirectory)../'))' != ''" />
这是在
multi-level merging中的例子
Import
的一个变体。
使用
$(MSBuildThisFile)
属性而不是硬编码文件名。
如果找不到文件,
GetPathOfFileAbove
返回一个空字符串。拥有 Condition
很重要,因为当 Import
属性计算为空字符串时,Project
会导致错误。使用Condition
,您无需关心哪个文件在目录结构中最高。这也意味着链条在顶部是可扩展的。
我喜欢在
Import
文件的开头始终有这个 Directory.Build.*
。如果 Import
位于不同文件的不同位置,则链接很难理解和排除故障。同样将导入放在文件的开头意味着当前文件可以使用和/或覆盖导入文件中的内容。
如果你能在
Theirs
中进行一些更改,这可能是最好的:使用 PackageReference
并为 Directory.Build.*
文件添加链接。