【问题标题】:Creating an "in-memory .sln" when building multiple .csproj files using MSBuild使用 MSBuild 构建多个 .csproj 文件时创建“内存中 .sln”
【发布时间】:2012-12-30 07:51:19
【问题描述】:

我们有一个使用多个 .sln 文件的代码项目,每个文件都包含项目(一些项目由不同的解决方案共享,即存在于多个解决方案中)。

我们的问题是,当使用项目引用时,实际项目也必须驻留在同一个解决方案中。

所有这些项目都是在构建服务器上使用 MSBuild 构建的。

我想知道是否可以创建一个 MSBuild 脚本,以某种方式将所有项目“导入”到单个 MSBuild 项目中,这样就好像所有项目实际上位于同一个 Visual Studio .sln 下一样文件?

例如,我想要类似于以下脚本的内容:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <!-- Builds all *.sln files under this repository. -->
    <ItemGroup>
        <SolutionFiles Include="**/*.sln" />
    </ItemGroup>

    <Target Name="Build">
        <MSBuild Projects="@(SolutionFiles)" Targets="Rebuild" />
    </Target>

</Project>

这是否意味着 MSBuild 将构建所有项目的内存中“主项目”,从而克服项目引用问题?

【问题讨论】:

  • 如何从命令行、脚本或代码手动运行 MSBuild?
  • 从命令行运行。

标签: .net visual-studio msbuild continuous-integration


【解决方案1】:

为什么不直接使用上面的语法来构建 csprojs?

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<!-- Builds all *.*proj files under this repository. -->
<ItemGroup>
    <!--SolutionFiles Include="**/*.sln" /-->
    <ProjectFiles Include="**/*.*proj"/>

</ItemGroup>

<Target Name="Build">
    <MSBuild Projects="@(ProjectFiles)" Targets="Rebuild" />
</Target>

</Project>

默认情况下,即使您在命令行模式下没有 sln 文件,MSBuild 也会构建项目引用。如果您想省略项目引用(即您正在按顺序连续构建事物),您可以使用 /p:BuildProjectReferences=false...

【讨论】:

  • 不确定我是否理解您的建议。使用该语法(包括 PrjectFiles)会发生什么?
  • 假设我包含所有项目文件(100 个不同的 .csproj 文件),MSBuild 是否会创建某种内部图,知道首先构建哪个以及构建哪个作为项目引用等?
  • 我上面的示例显然非常初级,但对于大型源代码树,Microsoft 建议使用 Traversal 项目文件(如您的)来管理构建包含。
  • 查看msdn.microsoft.com/en-us/subscriptions/dd483291.aspx 了解更多信息。至于您关于 MSBuild 确定项目排序图的问题,不,它不会那样做。但它会自动构建项目引用,因此您只需添加主输出 csproj 文件并让“BuildProjectReferences”功能自动构建依赖项。
  • 酷,文档从来没有提到过这个功能。我会试试的!
【解决方案2】:

我做过类似的事情,只是在同一个文件夹中有多个 .sln 文件,所有这些文件都引用了部分或全部项目。就我而言,我为每种构建风格(CI、nightly、release 等)都有 .sln 文件。

因此,例如,您可能在total中有以下项目:

  • 网站
  • 网站单元测试
  • 通用库
  • 常用库单元测试
  • 数据访问库
  • 数据访问库单元测试
  • 网站验收测试(例如 Selenium webdriver 测试)
  • 压力测试

您可以有以下解决方案:

  1. CI 解决方案(在提交时运行)。这将引用单元测试项目及其目标

  2. 夜间解决方案(每晚运行)。这将引用压力和验收测试项目

  3. 发布解决方案(按需运行发布)。这将引用网站、通用和数据访问项目(即没有测试项目)

这只是一个简单的示例,但希望对您有所帮助。设置此解决方案/项目结构后,我不会看到太多要求对其进行更改,但如果添加了新项目,那么只需添加到相关解决方案中即可。

【讨论】:

  • 所以你手动创建 .sln 文件?这可以自动完成吗?
  • 手动,因为这些也保存在源代码管理中。例如,CI 解决方案包括测试项目,而发布解决方案不包括。只需几分钟即可创建其他解决方案
  • 我不确定我是否完全理解您的建议。你能详细说明一下吗?如果我需要手动创建 sln,我如何保护自己不必更新每次添加到“主”解决方案文件的新项目?
  • 谢谢。这仍然迫使我做两次工作(将项目添加到解决方案 1,然后将其添加到所有项目的解决方案中)。我正在寻找某种方法,我可以动态地构建一个“主项目”,其中包含要构建的所有 .csproj。不确定这是否可能。
【解决方案3】:

您可以编写小型控制台应用程序,该应用程序将找到所有解决方案文件并构建它们。它甚至会选择正确的 MSBuild 版本:

创建 c# 控制台应用程序

添加对 Microsoft.Build 的引用并使用以下代码

  static void Main(string[] args)
  {
     string[] files = Directory.GetFiles(args[0], "*.sln", SearchOption.AllDirectories);

     foreach (string path in files)
     {
        RunMsBuild(path);
     }
  }

  public static void RunMsBuild(string SrcDir)
  {
     ProjectCollection pc = new ProjectCollection();
     Dictionary<string, string> GlobalProperty = new Dictionary<string, string>();

     //GlobalProperty.Add("Configuration", "Debug");
     //GlobalProperty.Add("Platform", "x86");
     //GlobalProperty.Add("OutputPath", "c:\\src");

     BuildRequestData buidlRequest = new BuildRequestData(SrcDir, GlobalProperty, null, new string[] { "Build" }, null);
     BuildResult buildResult = BuildManager.DefaultBuildManager.Build(new BuildParameters(pc), buidlRequest);
     if (buildResult["Build"].ResultCode == TargetResultCode.Success)
     {
        Console.WriteLine("Build Success");
     }
     else
     {
        Console.WriteLine("Build Fail");
     }
  }

之后,您可以从控制台或脚本运行它,并将搜索解决方案文件的路径作为参数传递

【讨论】:

    【解决方案4】:

    ** 没关系,我错过了下面的答案,项目包含命令行模式**

    这并不容易或漂亮。但是,如果您真的想按照您的要求进行更“全面”的构建自动化和管理路线,那么下面的第 4 点是如何开始变得更好的。

    1) 在 OP 示例中,使用 MSBuild 作为脚本:在您的示例中,这更像是一种自动化,就像使用 shell 或批处理文件一样,而不是真正让您获得任何 MSBuild 项目技巧.请参阅关于 .sln 规则的 #2,以及关于我认为您希望这样做更多的内容的 #3,但它还有其他作用。

    2) 关于“仅记忆”.sln 问题:从根本上说,我相信如果您希望 MSBuild 使用解析基于 .sln 的项目引用所需的 .sln,那么它可以' t 处于“零影响”或“仅内存”状态,并且必须刷新或保存到磁盘。即使您拥有项目系统本身也是如此,因为 MSBuild 将查看文件本身以获取解析引用,并期望它在文件系统中,因为它在单独的进程中运行,并且有自己的想法。因此,如果您想使用 .sln 文件,则必须有实际的 .sln 文件。因此,对没有 .sln 文件的 .sln 方法的希望是正确的。如果您运行 VS 来运行 msbuild,它将生成这些文件,并且需要在 MSBuild 开始之前将它们刷新到磁盘,在那里它们将保持“零影响”状态直到构建......只是为了完全详细和清晰。

    3) 关于“import .proj”问题:我知道的&lt;Import Project="my/proj/path/mine.csproj"/&gt; 用于您的目的的一个类似选项与文件的批量包含完全相同,它将处理这些文件就好像它们是该外部项目的一部分并编译到该程序集一样。经常被 microsft 内部使用。以这种方式使用的导入忽略了导入的 .proj 文件中的大部分内容,并且基本上是在寻找要包含的文件,就像外部项目中包含的任何其他文件一样。所以我认为这不会让你走得更远,除非你打算在同一个程序集中构建所有这些。如果你这样做了,你将不得不以这种意图来构建包含的 .proj 文件,并滥用/构建外部以包含一切所需的引用等等。

    4) 它是参考定义。 有很多关于参考的选项,它们是否被复制,将被假定为放在 GAC 中,或者“只是不要做任何事情,相信我他们将在运行时出现在应用程序域搜索路径中”。您可以关闭大部分 cr*p,然后您唯一的依赖是构建和语言编译器能够解析其类型和 whatnbot 的引用程序集,以便编译您当时尝试编译的依赖程序集.如果您考虑一下,还有很多其他的参考资料不是您自己构建的,而是构建拾取并用于编译的。所以真正的问题可能是,“我如何让我的自动构建处于我编译的依赖项的状态,并且看起来像任何其他参考?”

    由于您可以控制构建,因此您可以控制 dll 的放置,并且可以负责构建顺序。而且...您可以控制引用。

    这是剩下的经典问题:在设计时,如果引用指向 .sln 项目,那么您可以在设计时从外部程序集中查找类型无需构建,如果您更改该项目是不可知的,那么你将不得不编译。您可以在许多方面做一些棘手的事情,包括条件 .proj 文件,这会给开发人员带来负担,并且可能需要手动编辑以使其保持同步,或者通过脚本预先构建 .proj 文件过滤器以修复引用,或制作它们是非 sln 引用并强制开发人员构建依赖项以解析类型,他们必须确保他们使用的环境与构建保持一致。


    所以问题归结为引用是如何定义的,是的,MSBuild 需要的任何杂物都需要写入磁盘以供 MSBuild 运行,无论 VS 本身如何。你必须选择你的毒药,因为问题在于您不能同时定义两种方式的引用。这是一个经典的构建管理问题,你并不孤单。

    【讨论】:

      【解决方案5】:

      我们需要在 TFS (Azure DevOps) 中合并多个 .csproj 文件,但在我们的标准构建/部署中排除其他文件。我最终得到了以下 .proj 文件。重要的是

      • 目标使用&lt;MSBuild ...,而不是像微软示例中的&lt;CSC
      • 我需要为每个构建操作(构建、重建、清理、恢复)添加Targets="..."

      我最终得到了一个看起来像

      的文件
      <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <ItemGroup>
          <ProjectReference Include="one.csproj" />
          <ProjectReference Include="two.csproj" />
          <ProjectReference Include="three.csproj" />
        </ItemGroup>
        <Target Name="Build">
          <MSBuild Projects="@(ProjectReference)" Targets="Build" />
        </Target>
        <Target Name="Clean">
          <MSBuild Projects="@(ProjectReference)" Targets="Clean" />
        </Target>
        <Target Name="Rebuild">
          <MSBuild Projects="@(ProjectReference)" Targets="Rebuild" />
        </Target>
        <Target Name="Restore">
          <MSBuild Projects="@(ProjectReference)" Targets="Restore" />
        </Target>
      </Project>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-09
        • 2013-06-23
        • 2010-09-18
        • 1970-01-01
        • 1970-01-01
        • 2011-05-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多