在Delphi中设置一个大型软件系统

19
我们有一个大约16年历史的软件包,它几乎经历了所有Delphi版本(除了 .NET 版本)。多年来,交叉引用和附加包(如第三方库)的正确设置变得非常混乱。我想知道在处理像这样的大型项目(和项目组)时是否存在某些标准实践。
当前的设置是这样的:这是一个多应用程序系统,涉及12个可执行项目(以及几个 DLL 和服务项目)。我们还将这些项目存储在SourceSafe中,并且不同的开发人员在不同的计算机上共同开发。 所有这些项目都被倾泻到一个中央文件夹中。 "Root" 文件夹包含主要的 EXE 项目(以及大约20个包含单位和窗体的文件夹),并且几乎看起来像是无尽的文件夹和文件层次结构。仅此一个项目就涉及50万行代码。
然后所有其他的应用程序并没有与主要项目正确分离开来。每个项目都有自己的文件夹,位于主项目的根目录中。
我的两个主要关注点是:
1.如何正确设置DCU文件,使它们不会与项目混合?DCU 不应该放置在 SourceSafe 中(或任何类似的文件中),编译自项目的任何文件也不应该如此。Visual SourceSafe 在未检出时使文件只读,而在此情况下无法对 DCU 文件(以及 EXE 文件等)进行写入。因此,如何将任何此类文件正确分离到远程位置,以避免与源代码混合?
2.如何正确设置软件包和库?我们有以下内容:
- QuickReports 5.05 - NativeJpg 库 V302 - - 另一个匿名报告库 - 我们自己的组件包,需要 QuickReports、NativeJpg 和其他匿名库
这4个库存储在每台计算机的完全不同位置,并且需要一些集中化。设置每个新开发人员的计算机的最大痛苦是从主开发人员的计算机中找到它们,并将它们复制到每台其他计算机的相同位置(并确保库路径正确等)。
我们还需要在同一台计算机上为 Delphi 的不同版本保持完全分离的环境。这意味着每台计算机上都需要项目的副本、软件包和库的副本、SourceSafe 中的项目、软件包和库的副本等。每台计算机都需要具有相同的设置。我们已经利用环境变量来指导我们的项目在哪里查找某些项目文件(和库)。
另一个新问题:XE2 引入了64位能力。我们暂时不打算进行64位编译,但将来肯定会。我应该如何在所有这些项目中区分32位和64位?
我真正想要的是一个关于如何优化这样的环境并使其组织良好的教程的参考。我不希望有人花时间回答所有这些问题。这些项目已经超过15年了,来自世界各地的200多名开发人员曾经参与过其中,并且在项目之间有很多交叉引用。例如,一个项目可能使用另一个项目的单位,反之亦然。我个人不喜欢这个概念,但一开始也没有设计它。我被赋予任务,即组织这个系统并彻底记录如何在新开发人员在我们的项目上工作时设置 Delphi。当我

15
第一步是获取一个真正的版本控制系统。Sourcesafe不适合此目的。 - David Heffernan
3
Delphi没有版本控制系统。Subversion是一个不错的选择。 - David Heffernan
5
@warren subversion仍然在它的领域表现出色。分布式版本控制系统也有其缺点。 - David Heffernan
9
没有什么版本控制系统可以替代一个好的流程。这里的问题不在于SourceSafe(至少它有良好的图形用户界面,而大多数其他版本控制系统却没有,简化了使用)。你甚至可以用最时髦的版本控制系统来替换它,但如果没有清晰的管理环境的思路,仍然会遇到问题。向问题投入更多需要掌握的工具通常并不是解决问题的最佳途径。首先需要清楚地理解问题,然后才能选择哪些工具能够更有帮助。 - user160694
5
“behind the times”这个词是指“过时的”,并不等同于“不时髦”。 - davea
显示剩余12条评论
7个回答

12

DCU文件的位置

关于编译过程产生的DCU文件,你应该在每个项目文件中指定一个DCU输出目录。在最新版本的Delphi中,使用默认值.\$(Platform)\$(Config)就可以了。这会在项目目录下创建子文件夹,如: Win32\DEBUG 或者 Win64\RELEASE

如果使用选项集来设置项目文件,则可以从少量的选项文件中控制此设置(以及所有其他设置)。

第三方代码的位置

应始终将第三方库用作代码。如果供应商要求以代码形式接收库,则支付费用。一旦完成,只需将源代码包含到您的版本控制系统(VCS)中,并将其与自己的代码基本相同对待。我说“基本相同”,因为您应该避免修改它。

一旦将所有代码放入VCS中,您就可以通过单个checkout操作将整个源代码放入新计算机中。

项目组织

我个人非常反感使用编译器搜索路径。我不使用它们,而是在 .dpr 文件中包含所需的每个单元。

如果您使用搜索路径,则无法处理变体项目。例如,假设客户发现您2年前发布的软件中存在一个漏洞。您希望通过升级旧版本软件来解决该漏洞。要求他们升级到最新版本可能不可行。也许他们没有支付升级费用。也许完整的升级包含他们现在不想处理的破坏性更改。所有仍在使用Delphi 7的开发人员都是一个完美的例子。

现在,已经说明了场景,那么您如何为这个两年前的项目创建一个构建环境呢?如果使用搜索路径,它们将指向今天的库。您将不得不更改搜索路径或将旧的库复制到今天的库之上。

通过不使用搜索路径并将所有源代码包含在版本控制系统中,可以轻松解决整个头痛问题。

您应该的目标是能够检出程序的任何历史版本并立即进行构建。您应该能够充满信心地这样做,以确保构建的软件与发布该版本时构建的软件完全相同。这还需要您拥有构建自动化,但我无法想象您会在这样的规模的项目中缺乏这方面的支持。


1
关于第三方源代码控制的最佳实践:在Subversion文档中的供应商分支 - mjn
+1 不喜欢编译器搜索路径。我还把所有单位包含在 dpr 文件中,仅将搜索路径用于第三方库。 - Marjan Venema
我同意关于编译器搜索路径的观点,但这可能是一个非常困难的更改。我考虑过制作一个实用程序来生成完整的 DPR 文件,并消除对库路径的需求... 你有没有已经写好的工具? - Warren P
@WarrenP 不需要,我们只需手动将源代码文件添加到 .dpr 文件中。 我们只有少数几个项目,并且一旦项目设置完成,就很少更改。 - David Heffernan
@WarrenP:Gexperts的备份工程实用程序有一个选项,可以在库路径中定位所有包含{$I}文件 - 这样它们的源代码就可以成为起点。 - Fabricio Araujo
关于搜索路径的观点并不完全正确。在搜索路径中使用硬编码的全局文件夹(例如“c:\3rdparty\awesomelib\src”)确实会导致运行不同版本的问题。使用相对路径(例如“....\3rdparty\awesomelib\src”)可以避免这个问题。您可以通过这种方式维护具有不同库版本的不同分支。 - awmross

11

我将着重讨论文件夹组织。这是来自一个包含50个以上exe和dll以及大量第三方库的软件套件,所以我想我知道你在哪里...

我们使用Perforce作为源代码控制系统,因此我的默认工作区根文件夹名为Perforce,但我还设置了几个其他工作区,它们分别位于Perforce2、Perforce3等文件夹中。

一般的文件夹设置(从工作区根文件夹开始)

General
  Components
    Delphi
      Indy
        Indy9
        Indy10
      MadCollection
        v2.5.8.0
        v2.6.0.0
  Plugins
Releases
  Released
  ... a folder for each release we publish ... (and equal to a branch in Perforce)
Work
  Acceptance
  Sub1
  Sub2

我的IDE环境库路径为空(甚至没有BDE标准路径)。这确保了项目的路径声明所有所需的路径,并且项目不依赖于特定机器的IDE设置。

我们在IDE中设置了环境变量(例如MRJ),指向“General\ Components \ Delphi”,因此在项目选项中,我们将组件的路径声明为 $(MRJ)\ MadCollection \ 2.6.0.0

General包含我们项目使用的IDE插件和组件。我们将所有版本都保存在源代码控制中。这样,当我需要切换回旧版本以跟踪问题时,我可以轻松获取并构建它,因为它的库路径仍然指向此特定版本所需的组件版本。

特定工作分支(Acceptance或其子分支之一)中文件夹的组织结构遵循此模式:

General          
  Includes
  MainComponent1
    Project1
    Project2
    Shared
  MainComponent2  
    Project3
    Project4
    Shared
  Shared
Windows          
  SoftwareSuite  
    Scripts
      Tools
  MainComponent1
    Project1
      Dcus
    Project2
      Dcus
  MainComponent2  
  Tools
    Tool1
      Dcus
    Tool2
      Dcus

General文件夹包含所有平台无关的源文件,Windows文件夹包含所有Windows特定的文件。每个组件可以包含多个项目,并且都有一个共享文件夹用于在这些项目之间共享源代码。直接位于General下面的共享文件夹包含所有项目共享的源代码。Windows文件夹也是以类似的方式设置的。

请注意每个项目都有自己的dcus文件夹。这是在项目选项中配置的。由于路径可以输入为.dcus,我们(至少我)将其设置为任何新项目的默认值。每个项目将其dcus发送到一个唯一的文件夹可以确保两件事情:

  • 通过在版本控制软件中设置过滤器,轻松将dcu排除在版本控制之外。
  • 更重要的是它确保项目的编译/构建永远不会干扰另一个项目的编译/构建。 我可以安全地更改设置和构建,知道我不会被来自先前构建的其他项目留下的dcu所困扰。

+1 这已经为我提供了足够的信息和灵感,让我能够为此撰写一篇20页的教程。我已经写了10页有关为我们软件源设置新环境的教程。多年来,我们几乎一直使用Windows+SQL,最近才开始探索新平台,如64位、Mac、手机等。 - Jerry Dodge
这让我想起来 - 我迫不及待地等待FireMonkey的成熟和完善。现在它仍然很混乱 - 仍然是一个正在工作中的艺术品。 - Jerry Dodge

5
我建议采用以下做法:
  1. 保持库路径简单,确保库路径中的所有内容都是Delphi附带的文件夹或位于d:\Components\文件夹下的DCU二进制文件(库)文件夹。

  2. 使用现代化的版本控制工具。我推荐Mercurial优于其他工具。Source Safe太烂了,请停止使用。

  3. 备份您的环境(导出注册表键等),并以标准化的方式将其恢复到其他开发人员的电脑上。您可以保留一些 .reg 和 .cmd(批处理)文件来自动设置新系统。您可以将这些脚本放在组件仓库中进行版本控制。


1
@serg 这是关于 SVN 的评论还是对 DVCS 的偏好? - David Heffernan
1
@david 在我发现 Mercurial 之后,我认为没有使用 subversion 的理由。Mercurial 在我看来在每个方面都更好。 - kludg
2
@Serg 这完全取决于你的团队采用的流程。对于我的团队来说,我们更适合集中式而不是分散式。但显然其他团队会有不同的流程。 - David Heffernan
1
Mercurial的一个很大的劣势是缺乏锁定功能,尽管这主要是二进制文件(文档等)而不是源代码的问题。 - GolezTrol
3
@Serg:这就是为什么我们在家办公时远程登录到工作电脑。这样做不仅可以避免版本控制方面的麻烦,而且还能够减少很多其他方面的麻烦。 - Marjan Venema
显示剩余9条评论

3

在我们之前主要讨论的范围之外,我建议:

  • 单元测试 - 例如使用 DUnit
  • 持续集成。只是为了确保所有这些项目可以在另一台机器上编译,并且测试正常。

所以这与项目组织和版本控制策略密切相关。


好的观点,即使它们不是被要求的。但是添加持续集成可能会导致希望将您的工作站环境状态持久化在版本控制中,这样您就可以更轻松地设置服务器CI构建环境。 - Warren P

2
对于类似的设置,我曾经工作的公司发现以下配置非常有用:
  • 所有第三方库(组件等)都放在固定位置(C:\Delphi\name-version)
  • Delphi项目可以从版本控制中的任何地方(驱动器C:或D:和文件夹名称无关)检出,因为所有项目和脚本都使用相对路径
  • 所有项目都是一个主项目文件夹的子文件夹,因此检出这个文件夹将把Delphi项目和其他相关资源带到工作站,并且版本控制更新很容易完成
  • 我们使用一个构建脚本(编写在Apache Ant中),该脚本位于主文件夹中,并遍历所有文件夹以构建Delphi应用程序并针对开发数据库服务器运行单元和集成测试,以验证所有更改是否有效,然后再提交到源代码控制
  • 构建脚本还可以在每次提交时自动在构建服务器上运行(Hudson CI),以查看是否有错误
关于组件库的注意事项:在可能的情况下,避免使用包安装,最好在运行时创建组件。如果需要快速修复一个五年前的项目版本,卸载/安装十几个包可能会令人沮丧。至少对于非可视化组件而言,运行时创建是一个巨大的时间节省器。
将第三方代码检入源代码控制非常有帮助,例如共享还未作为新官方发布的补丁。最佳实践在Subversion文档中的“供应商分支”一章中有介绍。此外,使用Subversion可以将特定版本(标签)放置在项目目录结构中,这既可以用于第三方库,也可以用于自己的源代码,使得依赖管理和工作站设置更加容易。
附注:Ant构建脚本定义了所有搜索路径,因此它是所有开发人员如何配置IDE、放置第三方库以及使用哪些编译器标志的参考。
再次附注:您的项目听起来很有趣 - 我可以提供合同工作 :)

如果不同的主项目需要使用不同版本的共同子项目,则此方法会失效。 - David Heffernan
@David 确实,需要有单独的子项目文件夹结构。Subversion的svn:externals功能可用于处理这些依赖项,将正确版本直接检出到主项目子文件夹中。我也希望有一个 Delphi 版本的 Apache Maven 以进行依赖管理。 - mjn
1
关于软件包的安装,我们的软件需要QuickReports和它自己的组件套件(大约10个常见组件),这些组件需要QR和更多。我花了两天时间将源代码与组件分离,这些组件与项目进行了交叉引用。是的-这些组件在项目中引用了一堆单元,实际上编译了整个其他项目。 - Jerry Dodge

2

我的团队使用虚拟化技术,回顾过去,这是一个非常明智的决定。我们使用 MacBook Pro 笔记本电脑和 VmWare Fusion,但我相信其他软件包也可以很好地工作,例如 VirtualBox 或 VirtualPC。

当新的开发人员开始工作或旧的安装出现问题时,我们总是感到很好,因为只需要从主镜像中复制一个新的 VM 镜像,设置就与原始设置完全相同。主镜像存储在快速的 USB2 磁盘上。现在随着 Thunderbolt 和 USB3 的推出,复制镜像的速度会更快。只要有足够的内存,现代计算机的性能就不是真正的问题。8 GB 的内存应该足以同时运行 2 个镜像。虚拟化的另一个优点是非常容易尝试假设场景。在没有任何风险干扰真正工作环境的情况下,尝试不同的配置和版本。

顺便说一句,我也认为 SourceSafe 是垃圾... :-)


1

一些提示:

  • 为项目创建一个组项目文件,将属于该项目的所有应用程序放在组项目文件下的各自目录中

  • 您应该能够指定要包含在版本控制系统中的文件类型。确保将 Delphi 设置为以文本格式编写 DFM 文件。

  • 您可以告诉 Delphi 在每个应用程序的“dcu”子目录中输出 DCU(减少视觉混乱)。

  • 第三方工具通常坚持安装在不同的位置,你无法做太多事情。制作一个描述如何设置完整工作环境并保持其最新的文档。

  • 在虚拟机中开发。新开发人员获得 VM 的副本。

  • 维护不同的 Delphi 版本?重新考虑一下,尽量使用一个版本。如果您绝对必须有两个组项目和每个版本的目录结构。[我假设您不会使用两个 Delphi 版本编译相同的应用程序,那是开发者的地狱]

  • Delphi XE2 将输出到不同的 32/64 子目录,这应该不会有问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接