10

Solution1.sln 包含两个项目:

  • 项目A.csproj
  • 项目B.csproj

ProjectB 有一个名为“Foo”的自定义目标。我想跑:

msbuild Solution1.sln /t:Foo

这将失败,因为 ProjectA 没有定义“Foo”目标。

有没有办法让解决方案忽略缺失的目标?(例如,如果特定项目的目标不存在,则什么都不做)而不修改 SLN 或项目文件?

4

5 回答 5

10

如果您不想编辑解决方案或项目文件,并且您很高兴它可以从 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

于 2013-04-11T11:20:14.570 回答
2

您可以按项目名称定位它们,例如 /t:project:target (可能需要引号,我不记得了)。

您可以通过设置环境变量MSBuildEmitSolution = 1... 找到所有生成的目标,这会导致 msbuild 将它为您的解决方案生成的 temp .metaproj 文件保存到磁盘。该文件中定义了所有这些目标,只需打开它并查看;)

于 2013-04-08T00:28:30.907 回答
1

也许不是最好的答案,而是一个合理的破解。

msbuild ProjectA.csproj
msbuild ProjectB.csproj /t:Foo
于 2013-04-07T23:36:13.883 回答
1

当 msbuild 构建解决方案 - msbuild 仅将有限的一组目标发送到它的 .metaproj 文件中,并且 afaik - 您无法通过构建 sln 文件构建自定义目标,您必须使用原始 project1.csproj 或自定义构建脚本。

于 2013-04-08T14:32:12.513 回答
0

仅供参考:

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>
于 2020-08-02T05:32:44.850 回答