MSBuild:忽略不存在的目标

时间:2013-04-07 23:31:16

标签: msbuild

Solution1.sln包含两个项目:

  • ProjectA.csproj
  • ProjectB.csproj

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

msbuild Solution1.sln / t:Foo

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

有没有办法让解决方案忽略丢失的目标? (例如,如果特定项目不存在目标,则不执行任何操作)而不修改SLN或项目文件?

5 个答案:

答案 0 :(得分:8)

如果您不想编辑解决方案或项目文件,可以使用两部分解决方案,并且您很高兴它可以从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与任何默认解决方案目标(即构建,重建,清理或发布)一起运行,您可以从MSBuild中的Web发布管道如何使用DeployOnBuild属性中获得一些灵感。在包含不支持发布的其他项目类型的解决方案中调用Web项目的发布目标。


有关before.Solution1.sln.targets文件的更多信息: http://sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx

答案 1 :(得分:2)

您可以按项目名称定位,例如/ t:project:target(可能需要引号,我无法记住)。

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

答案 2 :(得分:1)

也许不是最好的答案,而是合理的黑客攻击。

msbuild ProjectA.csproj
msbuild ProjectB.csproj /t:Foo

答案 3 :(得分:1)

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

答案 4 :(得分:0)

仅供参考:

使用MSBuildTask时使用ContinueOnError或使用-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>