问题 将.sln替换为MSBuild并将包含的项目包装到目标中


我想创建一个MSBuild项目,该项目反映解决方案中的项目依赖项,并将VS项目包装在可重用目标中。

我喜欢解决的问题是在BizTalk应用程序中svn导出,构建和部署特定程序集(及其依赖项)。

我的问题是:如何为svn导出,构建和部署可重用的目标制作目标,并在为不同的依赖项构建它们时重用已包装的项目?

我知道只需构建解决方案并仅部署所需的程序集会更简单,但我希望尽可能多地重用目标。

零件

我想部署的项目

<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <ExportRoot Condition="'$(Export)'==''">Export</ExportRoot>
    </PropertyGroup>

    <Target Name="Clean_Export">
        <RemoveDir Directories="$(ExportRoot)\My.Project.Dir" />
    </Target>

    <Target Name="Export_MyProject">
        <Exec Command="svn export svn://xxx/trunk/Biztalk2009/MyProject.btproj --force" WorkingDirectory="$(ExportRoot)" />
    </Target>

    <Target Name="Build_MyProject" DependsOnTargets="Export_MyProject">
        <MSBuild Projects="$(ExportRoot)\My.Project.Dir\MyProject.btproj" Targets="Build" Properties="Configuration=Release"></MSBuild>
    </Target>

    <Target Name="Deploy_MyProject" DependsOnTargets="Build_MyProject">
        <Exec Command="BTSTask AddResource -ApplicationName:CORE -Source:MyProject.dll" />
    </Target>
</Project>

它所依赖的项目看起来几乎就像这样(其他.btproj和.csproj)。


7552
2018-03-18 17:50


起源



答案:


哇,这是一个论坛帖子的加载问题。我写了大约20页关于在我的文件中创建可重用的.targets文件 ,但我会带你从这里开始基础知识。我相信创建可重用构建脚本(即.targets文件)的关键是三个要素:

  • 放置行为(即目标)分成文件
  • 放置数据(即属性和项目,这些被称为.proj文件)进入自己的文件
  • 可扩展性
  • .targets文件应该验证假设

我们的想法是,您希望将所有目标放在单独的文件中,然后这些文件将由驱动构建过程的文件导入。这些是包含数据的文件。由于您导入了.targets文件,因此您可以获得所有目标,就像它们已经内联定义一样。 .proj和.targets文件之间将存在静默契约。该合同在两个使用的属性和项目中定义。这是需要验证的。

这里的想法并不新鲜。此模式后跟.csproj(以及Visual Studio生成的其他项目)。如果您查看.csproj文件,您将找不到单个目标,只有属性和项目。然后在文件的底部导入Microsoft.csharp.targets(可能因项目类型而异)。这个项目文件(以及它进口的其他人)包含实际执行构建的所有目标。

所以它的布局如下:

  • SharedBuild.targets
  • MyProduct.proj

哪里 MyProdcut.proj 可能看起来像:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This uses a .targets file to off load performing the build -->
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath>
  </PropertyGroup>

  <ItemGroup>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
  </ItemGroup>

  <Import Project="SharedBuild.targets"/>
</Project>

SharedBuild.targets 可能看起来像:

<Project  DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This represents a re-usable build file -->
  <Target Name="SharedBuild_Validate">
    <!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info
         about this validation pattern
    -->
    <ItemGroup>
      <_RequiredProperties Include ="Configuration">
          <Value>$(Configuration)</Value>
      </_RequiredProperties>    
      <_RequiredProperties Include ="OutputPath">
          <Value>$(OutputPath)</Value>
      </_RequiredProperties>

      <_RequiredItems Include="Projects">
        <RequiredValue>%(Projects.Identity)</RequiredValue>
        <RequiredFilePath>%(Projects.Identity)</RequiredFilePath>
      </_RequiredItems>
    </ItemGroup>

    <!-- Raise an error if any value in _RequiredProperties is missing -->
    <Error Condition="'%(_RequiredProperties.Value)'==''"
           Text="Missing required property [%(_RequiredProperties.Identity)]"/>

    <!-- Raise an error if any value in _RequiredItems is empty -->
    <Error Condition="'%(_RequiredItems.RequiredValue)'==''"
           Text="Missing required item value [%(_RequiredItems.Identity)]" />

    <!-- Validate any file/directory that should exist -->
    <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"
           Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />
  </Target>

  <PropertyGroup>
    <BuildDependsOn>
      SharedBuild_Validate;
      BeforeBuild;
      CoreBuild;
      AfterBuild;
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
  <Target Name="BeforeBuild"/>
  <Target Name="AfterBuild"/>
  <Target Name="CoreBuild">
    <!-- Make sure output folder exists -->
    <PropertyGroup>
      <_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath>
    </PropertyGroup>
    <MakeDir Directories="$(_FullOutputPath)"/>
    <MSBuild Projects="@(Projects)"
             BuildInParallel="true"
             Properties="OutputPath=$(_FullOutputPath)"/>
  </Target>
</Project>

别看太多了 SharedBuild_Validate 目标了。我把它放在那里是为了完整,但不要专注于它。您可以在我的博客上找到更多相关信息 http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx

需要注意的重要部分是可扩展性点。即使这是一个非常基本的文件,它也包含可重用的.targets文件的所有组件。您可以通过传入要构建的不同属性和项来自定义它的行为。您可以通过覆盖目标来扩展它的行为(BeforeBuildAfterBuild 甚至 CoreBuild并且您可以将自己的目标注入到构建中:

<Project ...>
   ...
  <Import Project="SharedBuild.targets"/>
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      CustomAfterBuild
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="CustomAfterBuild">
    <!-- Insert stuff here -->
  </Target>
</Project>

在您的情况下,我将创建一个使用所需属性的SvnExport.targets文件:

  • SvnExportRoot
  • SvnUrl
  • SvnWorkingDirectory 您将使用这些属性执行导出。

然后为Biztalk构建和部署创建另一个。如有必要,您可以将其拆分为2。

然后在.proj文件中导入两个并设置目标以按正确的顺序构建,然后关闭。

这只是创建可重复使用的构建元素的开始,但这应该让车轮转向你的脑袋。我要将所有这些发布给我 博客 以及所有文件的下载链接。

更新:

发表于博客 http://sedodream.com/2010/03/19/ReplacingSolutionFilesWithMSBuildFiles.aspx


16
2018-03-19 03:53



感谢您对可重复使用的目标的全面入门!这将给我一个很好的起点。我认为我尝试的问题是我试图混合驱动构建的.proj和可重用的.targets。 - Filburt
是否有任何“简单”方法强制所有包含的.proj文件中的<Import Project =“SharedBuild.targets”/>元素?我的意思是,如果您包含像<Projects Include =“src \ .. * .proj”/>这样的项目,您可能会包含未与自定义目标连接的项目。 OH和@filburt,你应该看看Sayed的书,值得每一分钱! :) - JohannesH
@JohannesH我会说这是一个简单的方法,但收集评论有点太多了。也许退房 我对Good-practices的回答:如何重用.csproj和.sln文件来为CI创建MSBuild脚本?  - “项目”部分。基本上只需在最外层范围内包含全局导入并添加到此集合中。 - Filburt
@JohannesH ......是的,Sayed摇滚!从我上面链接的答案中你会看到我拼凑了一个很好的解决方案,它驱动我的BizTalk构建一个部署过程。 - Filburt
谢谢你们,我很高兴我的书对你有所帮助。 - Sayed Ibrahim Hashimi


答案:


哇,这是一个论坛帖子的加载问题。我写了大约20页关于在我的文件中创建可重用的.targets文件 ,但我会带你从这里开始基础知识。我相信创建可重用构建脚本(即.targets文件)的关键是三个要素:

  • 放置行为(即目标)分成文件
  • 放置数据(即属性和项目,这些被称为.proj文件)进入自己的文件
  • 可扩展性
  • .targets文件应该验证假设

我们的想法是,您希望将所有目标放在单独的文件中,然后这些文件将由驱动构建过程的文件导入。这些是包含数据的文件。由于您导入了.targets文件,因此您可以获得所有目标,就像它们已经内联定义一样。 .proj和.targets文件之间将存在静默契约。该合同在两个使用的属性和项目中定义。这是需要验证的。

这里的想法并不新鲜。此模式后跟.csproj(以及Visual Studio生成的其他项目)。如果您查看.csproj文件,您将找不到单个目标,只有属性和项目。然后在文件的底部导入Microsoft.csharp.targets(可能因项目类型而异)。这个项目文件(以及它进口的其他人)包含实际执行构建的所有目标。

所以它的布局如下:

  • SharedBuild.targets
  • MyProduct.proj

哪里 MyProdcut.proj 可能看起来像:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This uses a .targets file to off load performing the build -->
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath>
  </PropertyGroup>

  <ItemGroup>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
  </ItemGroup>

  <Import Project="SharedBuild.targets"/>
</Project>

SharedBuild.targets 可能看起来像:

<Project  DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This represents a re-usable build file -->
  <Target Name="SharedBuild_Validate">
    <!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info
         about this validation pattern
    -->
    <ItemGroup>
      <_RequiredProperties Include ="Configuration">
          <Value>$(Configuration)</Value>
      </_RequiredProperties>    
      <_RequiredProperties Include ="OutputPath">
          <Value>$(OutputPath)</Value>
      </_RequiredProperties>

      <_RequiredItems Include="Projects">
        <RequiredValue>%(Projects.Identity)</RequiredValue>
        <RequiredFilePath>%(Projects.Identity)</RequiredFilePath>
      </_RequiredItems>
    </ItemGroup>

    <!-- Raise an error if any value in _RequiredProperties is missing -->
    <Error Condition="'%(_RequiredProperties.Value)'==''"
           Text="Missing required property [%(_RequiredProperties.Identity)]"/>

    <!-- Raise an error if any value in _RequiredItems is empty -->
    <Error Condition="'%(_RequiredItems.RequiredValue)'==''"
           Text="Missing required item value [%(_RequiredItems.Identity)]" />

    <!-- Validate any file/directory that should exist -->
    <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"
           Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />
  </Target>

  <PropertyGroup>
    <BuildDependsOn>
      SharedBuild_Validate;
      BeforeBuild;
      CoreBuild;
      AfterBuild;
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
  <Target Name="BeforeBuild"/>
  <Target Name="AfterBuild"/>
  <Target Name="CoreBuild">
    <!-- Make sure output folder exists -->
    <PropertyGroup>
      <_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath>
    </PropertyGroup>
    <MakeDir Directories="$(_FullOutputPath)"/>
    <MSBuild Projects="@(Projects)"
             BuildInParallel="true"
             Properties="OutputPath=$(_FullOutputPath)"/>
  </Target>
</Project>

别看太多了 SharedBuild_Validate 目标了。我把它放在那里是为了完整,但不要专注于它。您可以在我的博客上找到更多相关信息 http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx

需要注意的重要部分是可扩展性点。即使这是一个非常基本的文件,它也包含可重用的.targets文件的所有组件。您可以通过传入要构建的不同属性和项来自定义它的行为。您可以通过覆盖目标来扩展它的行为(BeforeBuildAfterBuild 甚至 CoreBuild并且您可以将自己的目标注入到构建中:

<Project ...>
   ...
  <Import Project="SharedBuild.targets"/>
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      CustomAfterBuild
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="CustomAfterBuild">
    <!-- Insert stuff here -->
  </Target>
</Project>

在您的情况下,我将创建一个使用所需属性的SvnExport.targets文件:

  • SvnExportRoot
  • SvnUrl
  • SvnWorkingDirectory 您将使用这些属性执行导出。

然后为Biztalk构建和部署创建另一个。如有必要,您可以将其拆分为2。

然后在.proj文件中导入两个并设置目标以按正确的顺序构建,然后关闭。

这只是创建可重复使用的构建元素的开始,但这应该让车轮转向你的脑袋。我要将所有这些发布给我 博客 以及所有文件的下载链接。

更新:

发表于博客 http://sedodream.com/2010/03/19/ReplacingSolutionFilesWithMSBuildFiles.aspx


16
2018-03-19 03:53



感谢您对可重复使用的目标的全面入门!这将给我一个很好的起点。我认为我尝试的问题是我试图混合驱动构建的.proj和可重用的.targets。 - Filburt
是否有任何“简单”方法强制所有包含的.proj文件中的<Import Project =“SharedBuild.targets”/>元素?我的意思是,如果您包含像<Projects Include =“src \ .. * .proj”/>这样的项目,您可能会包含未与自定义目标连接的项目。 OH和@filburt,你应该看看Sayed的书,值得每一分钱! :) - JohannesH
@JohannesH我会说这是一个简单的方法,但收集评论有点太多了。也许退房 我对Good-practices的回答:如何重用.csproj和.sln文件来为CI创建MSBuild脚本?  - “项目”部分。基本上只需在最外层范围内包含全局导入并添加到此集合中。 - Filburt
@JohannesH ......是的,Sayed摇滚!从我上面链接的答案中你会看到我拼凑了一个很好的解决方案,它驱动我的BizTalk构建一个部署过程。 - Filburt
谢谢你们,我很高兴我的书对你有所帮助。 - Sayed Ibrahim Hashimi