我工作的公司在Delphi中开发了一个系统,它包含许多exe模块,如果涉及源代码,它们在某种程度上是相同的.遗憾的是,没有人关心使用库来共享代码.这意味着每次在代码中都有错误修复所有这些模块共享时,程序员必须分别对所有这些模块进行修正!它总是需要这么多时间......
我决定找到一种方法将共享代码放入库中.我考虑过DLL和BPL.在这种情况下,BPL似乎更方便程序员,更不麻烦,特别是代码仅在我们的软件中使用,并且仅在Delphi中使用.
我将所有exe模块共享的所有代码放入BPL中,一切看起来都很好,但有些事我不明白,如果你向我解释,我将不胜感激.
在将代码划分为BPL之后我的期望是,使用我创建的BPL部署exe文件就足够了.但事实证明他们也需要一个rtl100.bpl和vcl100.bpl.为什么会这样?我想只部署exes和我的BPL.我不想为最终用户提供由Borland和第三方公司提供的一大堆库:).我希望它们在exes中编译,就像之前编译的那样.有可能吗?
到目前为止我做的是:
如果涉及到exe项目:
这就是我所做的一切.exe项目编译正确,但我无法访问BPL的源代码(我无法从我的exe项目导航到该代码),即使所有BPL与其源代码文件一起存储.为什么?这对我来说似乎很奇怪.
我总是倾向于写冗长的描述 - 抱歉:).我将感谢你的帮助.我只需要对我提到的要点进行一些解释:仅使用我的BPL部署exe,我作为一个整体做的正确性,以及无法导航到BPL源代码.非常感谢你提前!
谢谢大家的讨论.有人说我选择的方法并不是一个好主意.我们的软件包含100多个模块(大多数模块类似于不同设备的驱动程序).他们中的大多数共享相同的代码 - 在大多数情况下是类.问题是这些类并不总是放在单独的独立的pas单元中.我的意思是共享代码通常放在包含特定于模块的代码的单元中.这意味着当您修复共享类中的错误时,仅将定义的pas单元复制到所有软件模块并重新编译它们是不够的.不幸的是,您必须将固定的代码片段逐个复制并粘贴到每个模块中,并将其粘贴到适当的单元和类中.这需要花费很多时间,这是我想要消除的,选择正确的方法 - 请帮助我.
我认为使用BPL是一个很好的解决方案,但它有一些缺点,正如你们有些人提到的那样.最糟糕的问题是,如果每个EXE需要几个BPL,我们的技术支持人员将不得不知道哪个EXE需要哪个BPL,然后为最终用户提供适当的文件.只要我们没有软件更新程序,这对我们的技术人员和最终用户来说都是一个很大的优势.他们肯定会迷茫和愤怒: - /.
兼容性问题也可能发生 - 如果一个BPL由许多EXE共享,一个BPL的修改对于一个EXE是好的而对另一个也是坏的 - @Warren P.
那么我应该怎么做才能在这么多项目中更快地修复错误?我想到了以下方法之一.如果您有更好的想法,请告诉我.
只要修改了后面修改的代码,这个解决方案似乎就可以了.但是我们也有具有一般使用功能和程序的通行单元,这通常是不可修改的 - 我们在必要时在那里添加新功能,但是在单个项目中.因此,假设您在100个模块中的一个模块中编写了一个新函数,并将其放入其通用单元中.一两个月之后,您修改了一个不同的模块,您认为您需要在2个月前编写的相同功能.您必须找到该模块(如果您不记得它是哪个模块很困难)并将该功能复制到您的代码中.显然 - 只要每个模块中的一般使用单元分别存储在每个模块中,它们就会完全不同.然后,如果有一个错误修复要做...整个故事重复.
对我来说,这似乎是现在最好的解决方案,但有几个缺点.如果我在BPL中修复错误,每个程序员都必须更新他们计算机上的BPL.如果他们忘了怎么办?但是,我认为这是一个小问题.如果我们负责通知对方有关变化,一切都应该没问题.
请帮我选择一个好的解决方案.我只是因为一种愚蠢的软件开发方法,我不希望公司因错误修正而损失更多的时间和金钱.
非常感谢你.
即使这个问题有一个公认的答案,我也会采取措施.
标题询问如何将项目划分为bpls,但真正的问题似乎是:"在项目之间共享代码的最佳方式是什么?"
有几种方法可以做到这一点:
无论你走哪个方向,你都可能需要重组你的项目.从您的描述中可以看出,每个项目都是相对孤立的.使用复制/粘贴共享代码,这很快就会失去同步并导致大量重复工作.因此,让我们研究一下共享代码的每种技术.
这是最直接的方法.您可以创建共享位置,并将要在项目中重复使用的代码放入此位置.这些单元静态链接到您的项目中,因此您无需担心与主要可执行文件一起部署额外的依赖项.到目前为止,静态链接的单元是最容易进行故障排除和调试的.
编译器需要能够找到您的共享单元.有4种方法可以告诉编译器在哪里查看.
-U
开关或msbuild的/property:UnitSearchPath=
开关设置搜索路径.选项1和2将是最有用的.
就您的SVN存储库而言,您可以使用一些选项来组织项目和共享单元.最简单的方法是将所有项目与共享单元一起放在单个主干下:
Projects
trunk
ProjectA
ProjectB
ProjectC
Library (shared units)
Run Code Online (Sandbox Code Playgroud)
如果出于某种原因无法使用上述结构,您可以尝试以下方法:
ProjectA
trunk
Library (branch of main library)
ProjectB
trunk
Library (branch of main library)
ProjectC
trunk
Library (branch of main library)
Library
trunk (main library)
Run Code Online (Sandbox Code Playgroud)
在此配置中,对每个项目的库文件夹所做的更改不会立即对其他项目可用.每个项目都需要定期与主库项目同步更改.这样做的副作用是打破其他项目的更改将被延迟,直到其他项目同步.你是否认为这是好事还是坏事取决于你.一方面,当它们涉及的代码在开发人员的脑海中仍然是新鲜的时,错误更容易且更便宜.另一方面,如果您不进行单元测试(我强烈建议您这样做)或者代码非常脆弱,或者您只是让开发人员容易进行鲁莽的更改,您可能希望控制这些更改被推送到其他项目的频率.
Dll允许您通过在运行时链接代码来共享代码.它们公开了可以从主可执行文件或另一个dll调用的函数.
虽然dll总是在运行时链接,但您可以决定它们是在应用程序启动时加载还是仅在需要时加载.在启动时加载称为静态加载,在Delphi中使用该external
指令完成.绝大多数包含系统api调用的rtl/vcl类都使用静态加载.动态加载允许延迟加载dll,直到需要它为止.这使用WinAPI函数LoadLibrary和GetProcAddress.对FreeLibrary的相应调用将卸载一个dll.
不幸的是,标准dll限制了可以传递的数据类型.如果需要从非Delphi项目访问dll,则需要限制自己使用c样式数据类型.如果您只使用Delphi项目的dll,那么如果您在dll和使用它的任何项目中使用SharedMem单元,您也可以安全地使用Delphi字符串和动态数组.
您可以安全地在dll中使用对象而没有问题,但是如果您想在dll和应用程序之间传递对象,则需要提取对象的数据并将其作为基本类型传递,并将其重新组合到另一端的对象中.这称为(反)序列化或编组,除了滚动自己之外,还有更简单的方法.
COM(组件对象模型)在Delphi中得到很好的支持,但它有一点学习曲线.使用COM对象非常简单,但如果您不熟悉COM,那么设计一个将需要时间.COM的优势在于它是语言中立的,并且支持大多数针对Windows平台的语言(包括面向.NET框架的语言).
Bpls(也简称为"软件包")是特殊格式的dll,可以更轻松地处理对象.与标准dll一样,它们在运行时链接,可以静态或动态加载.它们比COM dll更容易学习和使用,并且比COM提供更多的无缝集成到您的项目中.包由两部分组成:bpl和dcp.dcp就像编译普通单元文件时生成的dcu文件,除了它包含一大堆单元.使用在bpl中编译的类就像将dcp添加到项目的包列表中一样简单,然后将一个单元添加到项目单元之一的uses子句中.
部署应用程序时,您还需要安装bpl.正如其他人已经指出的那样,如果你使用任何形式,你必须至少包括rtl包,并且很可能包括vcl包.有一种方法可以在您的项目中部署Borland提供的bpls.您可以创建一个"迷你"rtl包,其中仅包含项目所需的单位.难以确定要包括哪些单位.
从您给出的描述创建共享单元文件库以静态链接可能是最方便的路由.我还建议尝试一个名为Simian的程序.它将帮助您跟踪代码库中的重复代码,以包含在您的共享库中.它并不直接支持pascal,但它使用纯文本解析器做了一个不错的工作,稍微调整了一下它的配置.
另外,我不能强调单元测试的价值.特别是如果您正在转向共享库.经常运行的一套编写良好的单元测试将在开发人员更改课程时为您提供即时反馈,并且会打破不相关的项目.
想象一下,你有一个带有EXE和两个不同BPL模块的项目,并且在该代码库的某个地方,有一条线说if MyObject is TStringList then DoSomething;
.所述is
操作员通过检查对象的类的元数据,存储在VMT,然后通过以下VMTS的链ClassParent
指针,以查看是否任何一个匹配的类引用(也是VMT指针)TStringList
.为了确保这一点能正常工作,TStringList需要一个单独的VMT,在整个程序中都是一样的,无论它分成多少个BPL,这意味着它必须在自己的包中.这就是为什么像rtl*.bpl和vcl*.bpl这样的系统运行时是必要的,而且你无能为力.这是使用BPL的价格的一部分.
至于无法调试,您需要确保在启用调试信息的情况下构建BPL,并且调试器知道如何找到DCP(包含BPL调试信息的文件)所在的文件夹.并且您将无法跟踪系统BPL,因为启用了调试的DCP不随您的版本一起提供.它们最近才被添加,我认为在XE中它可能已经在D2010中了.