Solution1.sln 包含两个项目:
- 项目A.csproj
- 项目B.csproj
ProjectB 有一个名为“Foo”的自定义目标。我想跑:
msbuild Solution1.sln /t:Foo
这将失败,因为 ProjectA 没有定义“Foo”目标。
有没有办法让解决方案忽略缺失的目标?(例如,如果特定项目的目标不存在,则什么都不做)而不修改 SLN 或项目文件?
Solution1.sln 包含两个项目:
ProjectB 有一个名为“Foo”的自定义目标。我想跑:
msbuild Solution1.sln /t:Foo
这将失败,因为 ProjectA 没有定义“Foo”目标。
有没有办法让解决方案忽略缺失的目标?(例如,如果特定项目的目标不存在,则什么都不做)而不修改 SLN 或项目文件?
如果您不想编辑解决方案或项目文件,并且您很高兴它可以从 MSBuild 命令行而不是 Visual Studio 工作,那么有一个由两部分组成的解决方案。
首先,运行时出现的错误:
MSBuild Solution1.sln /t:Foo
不是 ProjectA 不包含 Foo 目标,而是解决方案本身不包含 Foo 目标。正如@Jaykul 建议的那样,设置 MSBuildEmitSolution 环境变量将显示解决方案 metaproj 中包含的默认目标。
使用 metaproj 作为灵感,您可以在解决方案文件(文件名模式很重要)旁边引入一个新文件“before.Solution1.sln.targets”,其内容如下:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<MSBuild Projects="@(ProjectReference)" Targets="Foo" BuildInParallel="True" Properties="CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
MSBuild 元素大多只是从解决方案 metaproj 的 Publish 目标复制而来。调整目标名称和任何其他详细信息以适合您的方案。
有了这个文件,您现在将收到 ProjectA 不包含 Foo 目标的错误。ProjectB 可能会也可能不会构建,具体取决于项目间的依赖关系。
所以,其次,为了解决这个问题,我们需要给每个项目一个空的 Foo 目标,然后在实际上已经包含一个的项目中覆盖它。
我们通过引入另一个文件来做到这一点,例如“EmptyFoo.targets”(名称不重要),如下所示:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo" />
</Project>
然后我们通过运行带有额外属性的 MSBuild 让每个项目自动导入此目标文件,例如:
MSBuild Solution1.sln /t:Foo /p:CustomBeforeMicrosoftCommonTargets=c:\full_path_to\EmptyFoo.targets
或者在第一个目标文件的 MSBuild 元素的 Properties 属性中包含 CustomerBeforeMicrosoftCommonTargets 属性,您可以选择指定相对于 $(SolutionDir) 属性的完整路径。
但是,如果您愿意将 Foo 与任何默认解决方案目标(即 Build、Rebuild、Clean 或 Publish)一起运行,您可以从 MSBuild 中的 Web Publishing Pipeline 如何使用 DeployOnBuild 属性调用在包含不支持发布的其他项目类型的解决方案中的 Web 项目上发布目标。
有关 before.Solution1.sln.targets 文件的更多信息:http: //sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx
您可以按项目名称定位它们,例如 /t:project:target (可能需要引号,我不记得了)。
您可以通过设置环境变量MSBuildEmitSolution = 1
... 找到所有生成的目标,这会导致 msbuild 将它为您的解决方案生成的 temp .metaproj 文件保存到磁盘。该文件中定义了所有这些目标,只需打开它并查看;)
也许不是最好的答案,而是一个合理的破解。
msbuild ProjectA.csproj
msbuild ProjectB.csproj /t:Foo
当 msbuild 构建解决方案 - msbuild 仅将有限的一组目标发送到它的 .metaproj 文件中,并且 afaik - 您无法通过构建 sln 文件构建自定义目标,您必须使用原始 project1.csproj 或自定义构建脚本。
仅供参考:
ContinueOnError
在使用 MSBuildTask 或使用-p:ContinueOnError=ErrorAndContinue
时使用(dotnet) msbuild
它可能在有限的场景中有所帮助:例如,您有一个 .csproj 文件列表,并且只想将元数据附加到特定的项目文件项,那么您可以编写如下内容:
<Target Name="UniqueTargetName" Condition="'$(PackAsExecutable)' == 'Package' Or '$(PackAsExecutable)' == 'Publish'" Outputs="@(_Hello)">
<ItemGroup>
<_Hello Include="$(MSBuildProjectFullPath)" />
</ItemGroup>
</Target>
<Target Name="BuildEachTargetFramework" DependsOnTargets="_GetTargetFrameworksOutput;AssignProjectConfiguration;_SplitProjectReferencesByFileExistence"
Condition="$(ExecutableProjectFullPath) != ''">
<Message Text="[$(MSBuildThisFilename)] Target BuildEachTargetFramework %(_MSBuildProjectReferenceExistent.Identity)" Importance="high" />
<MSBuild
Projects="%(ProjectReferenceWithConfiguration.Identity)"
Targets="UniqueTargetName"
ContinueOnError="true">
<Output TaskParameter="TargetOutputs" ItemName="_Hallo2" />
</MSBuild>
<Message Text="[$(MSBuildThisFilename)] ########### HELLO %(_Hallo2.Identity)" Importance="high" />
</Target>