使用MSBuild构建多个.csproj文件时创建“内存中.sln”

时间:2012-12-30 07:51:19

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

我们有一个使用多个.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将构建所有项目的内存“主项目”,从而克服项目参考问题?

5 个答案:

答案 0 :(得分:1)

我做了类似的事情,只是在同一个文件夹中有多个.sln文件,所有文件都引用了部分或全部项目。就我而言,我为每种构建版本都有.sln文件(CI,每晚,发布等)。

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

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

您可以使用以下解决方案:

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

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

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

  4. 这只是一个简单的例子,但希望有所帮助。一旦设置了这个解决方案/项目结构,我就不会想要更改它,但是如果添加了一个新项目,那么只需添加到相关解决方案就可以快速完成。

答案 1 :(得分: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>

MSBuild将默认构建项目引用,即使您在CommandLine模式下没有sln文件也是如此。如果你想省略项目引用(即你按顺序连续构建东西),你可以使用/ p:BuildProjectReferences = false ...

答案 2 :(得分:0)

您可以编写小型控制台应用程序,它将找到所有解决方案文件并构建它们。它甚至会选择合适的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");
     }
  }

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

答案 3 :(得分:0)

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

这不容易或漂亮。但是下面的#4是如果你真的想要像你要求的那样更加“全面”构建自动化和管理路线,它会如何变得更好。

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

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

3)关于“import .proj”问题:我知道<Import Project="my/proj/path/mine.csproj"/>用于您的目的的类似选项,就像文件的批量包含一样,会对待这些文件就好像它们是该外部项目的一部分并且对该程序集进行了简化。内部通常由microsft使用。以这种方式使用的导入会忽略导入的.proj文件中的大部分内容,并且基本上正在查找它包含的文件,就像外部项目中包含的任何其他文件一样。所以我不认为这会让你更进一步,除非你打算在同一个装配中建立所有这些。如果你这样做,你将不得不使用这个意图来构建包含的.proj文件,并滥用/构造外部以包括所有内容所需的引用等等。

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

由于您可以控制构建,因此您可以控制dll的位置,并可以负责构建顺序。而且......你可以控制参考文献。

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


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

答案 4 :(得分:0)

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

  • 目标使用<MSBuild ...而不是像Microsoft的示例中的<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>