65.9K
CodeProject 正在变化。 阅读更多。
Home

无需解决方案即可编译多个项目

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (6投票s)

2007年6月24日

4分钟阅读

viewsIcon

48316

downloadIcon

426

一篇关于创建构建过程以在不使用解决方案文件的情况下编译多个 .NET 项目的文章。

引言

自动化构建过程非常重要。自动化的构建过程可以及时发现新问题,这意味着需要查看的代码量会大大减少,从而更容易找出问题所在(在我的博客 Making the Build 上阅读更多)。但是,构建过程的建立可能很耗时,而且如果不够自动化,它本身也可能成为错误的来源。

那么,当您的应用程序中项目过多,无法合理地放入一个解决方案时,您该怎么办?如何运行您的自动化构建?能够指向一个目录并根据项目中已定义的依赖关系按正确的顺序编译所有项目,岂不是很棒?

本文附带的源代码可以做到这一点!只需给它一个根目录或文本文件,它就会构建一个 MSBuild 项目,您可以使用该项目按正确的顺序编译您的项目。

市面上有许多不同的构建系统(我们在工作中使用的 FinalBuilder),因此附带的代码是一个简单的可执行文件,它将生成一个 MSBuild 文件,然后您可以将该文件传递给 MSBuild 来编译您的项目。如果您有一个能够支持自定义活动的构建系统,您应该能够足够轻松地改编代码。

背景

为了使用 MSBuild 项目文件,您可能需要熟悉 MSBuild。MSBuild 是随 Visual Studio 2005 一起引入的,是 Visual Studio 用于构建项目的工具。它是一个可扩展的系统,具有很大的灵活性。然而,它也非常复杂且难以配置。我个人希望有人能围绕 MSBuild 构建一个可视化工具,尽管我对 FinalBuilder 很满意。

如果您想了解更多关于 MSBuild 的信息,可以尝试从 MSBuild 概述 开始。不用担心,本文将解释您运行附带代码生成的 MSBuild 项目所需了解的有关 MSBuild 的所有知识。

Using the Code

代码由两个类和一个模块组成。这两个类是 ProjectListProjectProjectList 本质上是一个项目列表。它还管理项目之间的交互,并包含创建 MSBuild 文件的代码。该模块创建 ProjectList,根据命令行初始化它,并调用 ProjectList 来保存 MSBuild 项目文件。

该应用程序只接受两个命令行参数。第一个参数必须是要包含的 VB.NET 或 C# 项目文件所在的目录或文本文件的路径,第二个参数必须是要创建的 MSBuild 项目的写入路径(扩展名应为 .proj)。

如果您选择使用文本文件,该文本文件可以包含任何根目录、vbproj 或 csproj 文件,或者更多的文本文件的组合。只需每行添加一个。您可以通过在行的开头放置一个单引号 (') 来为文本文件添加注释。您也可以包含空行。

使用 ProjectList 非常简单(正如您在 Main() 中应该能够看到的)。要创建 MSBuild 项目,您只需要以下代码

Dim projects As New ProjectList()
projects.LoadProjects("C:\MyProjects")
projects.SaveMSBuildProject("C:\MyProjects.proj")

此代码实例化 ProjectList,从给定的源路径(根目录或文本文件)加载项目,然后保存 MSBuild 项目。

现在,如果您想编译上面创建的 MSBuild 项目文件,您需要打开 Visual Studio 命令提示符(对于 VS 2005,请转到 开始 -> 所有程序 -> Microsoft Visual Studio 2005 -> Visual Studio Tools -> Visual Studio 2005 Command Prompt)。然后您可以输入

MSBuild C:\MyProjects.proj

项目依赖关系严格基于项目名称。以下是一个 .vbproj 文件中引用的示例(非系统程序集看起来类似,尽管它们可能包含更多信息)

<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Deployment" />
    <Reference Include="System.Xml" />
</ItemGroup>

项目依赖关系引擎基本上从项目文件中获取引用列表,然后,一旦 ProjectList 完全加载,它就会遍历每个引用,并检查 ProjectList 中是否有匹配的项目。要查看此代码,请查找 ProjectList.InitializeReferencesProject.InitializeReferences 方法(大部分工作是在 Project.InitializeReferences 中完成的 - 代码如下)。

Public Sub InitializeReferences(ByVal projects() As Project)

    mProjectReferences = New List(Of Project)

    For Each refAsmName As String In mAllProjectReferences
        ' we only consider references for projects in our list
        ' not to external references (such as System.dll)
        For Each proj As Project In projects
            ' find the referenced project based on the assembly name
            If proj.AssemblyName.Equals(refAsmName, _
        StringComparison.InvariantCultureIgnoreCase) Then
                ' this reference is in our project list, so we add it
                ' to this projects reference list
                mProjectReferences.Add(proj)

                ' exits the inner loop, but still goes through the rest of
                ' the references
                Exit For
            End If
        Next
    Next
End Sub

需要注意的一点是循环引用。如果两个项目相互引用,它们将无法正确编译。ProjectList 将忽略循环引用(将显示命令行消息)。这意味着编译很可能会失败(我们继续是因为有非零的可能性它会成功,所以我们不妨尝试一下)。查看 ProjectList.SortByReferenceProjectList.AddProject 来了解项目是如何排序的。

Private Sub SortByReference()
    Dim sortedProjects As New List(Of Project)

    For Each proj As Project In Me
        AddProject(proj, sortedProjects, New Stack(Of Project))
    Next

    ' clear the list and add the projects back in their sorted order
    Me.Clear()
    Me.AddRange(sortedProjects)
End Sub

Private Sub AddProject( _
    ByVal proj As Project, _
    ByVal projects As List(Of Project), _
    ByVal refStack As Stack(Of Project))

    If projects.Contains(proj) Then Return

    refStack.Push(proj)

    For Each ref As Project In proj.ProjectReferences

        If refStack.Contains(ref) Then
            ' circular reference found
            ' circular references cannot be compiled in a clean
            ' 'environment (when no assemblies are built)
            Dim circRefList As New List(Of String)
            For Each circRef As Project In refStack
                circRefList.Insert(0, circRef.Name)
            Next
            circRefList.Add(ref.Name)
            Console.WriteLine("Circular reference found. " _
                & String.Join(" -> ", circRefList.ToArray()))
            ' This has to be treated as a non-reference otherwise
            ' we will get into an infinite loop
            Continue For
        End If

        AddProject(ref, projects, refStack)
    Next

    refStack.Pop()

    projects.Add(proj)
End Sub

我计划在未来几周内写另一篇博文,其中包含更多关于创建构建的建议,所以如果您有兴趣,请订阅我的博客。您可以在 Brian Online 上找到它。

历史

  • 2007 年 6 月 23 日 - 创建文章
© . All rights reserved.