Related
I am a C# developer by trade, but have been tasked with making some changes to a Delphi desktop application. I have never used Delphi and I'm trying to identify the version of Delphi the application has been compiled with, and find a (preferably free) development stack/environment.
Looking at the source, there are a bunch of .DCU, .DPP, .DFM, and .PAS files.
There's a .DPR file that would seem to reference all of the forms.
In the .CFG file I found reference to
c:\program files (x86)\borland\delphi7\Projects\Bpl
Based on the above info, what tools should I use to design and compile this application?
That entry in the .cfg file is a clear cut indication that the project was most recently built with Delphi 7.
However, it may be more tricky to build this project than just to install Delphi 7 and press build. You may very well need to install a bunch of third party packages and libraries. In an ideal world there would be documentation on how to build, or at the very least, how to create a build environment. Sadly, you may not be living in an ideal world!
I'm trying to .... find a (preferably free) development stack/environment.
Well, Delphi 7 is not free. There are free Delphi like development environments, most specifically Lazarus. However, you would need to port the existing code, which is likely to be a non-trivial task. Especially if you have no Delphi experience.
It appears to be Delphi 7.
But the compiler version is only part of your problem, as several commentators have mentioned here. Unless they used only Delphi components, you're going to need to find the component libraries that were used, and that could be nearly impossible in itself. If it used any DLLs or BPLs, you'll need to find them as well, and possibly re-build the interfaces.
I'd spend some time trying to track down a backup that was made of the system that this software was developed on, as that's your best bet. It won't cost you anything assuming everything is still intact.
But trying to load up the source into D7, or anything for that matter, is going to be a total crap shoot unless you know all of the libraries that were used, how things were configured, all that good stuff.
It would be far more efficient for your company to just hire a Delphi expert for a day or so to dig into this for you than for you to waste a week or two of your time on something you don't know anything about.
For over a decade, I have been stuck (lovingly) on Delphi 6 and have developed hundred of thousands of lines of code in a logical (to me) unit structure, where a project tends to be a few hundred lines of code referencing high-level work routines in my "library". In trying to migrate to XE5, I just can't find a way to have all my library units compiled in one place on the search path, and then just used by the project (and recompiled if necessary), but that the dcus are off with the library source rather than in with each individual project.
I'm just starting to accept that "hello world" takes 2.5Mb in XE5, and I can't stand the idea that each library unit has to be separately compiled into dcus at the project level. In the "old" days, these unit dcus would sit next to the pas files and not be recompiled if nothing changed in the source file.
The obvious place to look is the Project options, but I can't find the right setting to make the project stop keeping copies of each dcu.
I am vaguely aware that multi-platform development will cause restructuring, but I can't help feeling that there is some compromise position.
There must be something big I am missing.
Starting in Delphi XE2, Delphi supports compilation for multiple platforms, as well as different build configurations. Because of this, Delphi needs to create DCU files for each combination. For example, Win32, Win64, and OS-X DCU files are saved in separate folders by default. Otherwise, if it weren't like this, the DCU files would overwrite each other, which you should avoid (if you use different configurations/platforms).
These settings can be changed in the Project Options in the very first section Delphi Compiler by modifying the Unit output directory. This is by default .\$(Platform)\$(Config) which creates a subfolder for the platform, then another subfolder for the config, for example \Win32\Debug\. Careful for the Target at the very top, which by default is set to your current platform/config. You would typically want to first change it to All Configurations. If you clear this field completely from the options, it will produce your default behavior from older versions.
It sounds like you should create a Package. This would allow you to group all your "library" units together in one place (BPL). This package can then be installed into your IDE, and if you have any components, those components can then be installed into your component pallet.
Or you can do without a package too. All the units from all these different projects should be moved to this central place though - a single folder containing all your "library" units. This way it's less maintenance, and you can just add that one folder to your global library path.
If you put your files in a central folder, and use those files from a project, the DCU files for both the project and this "library" will be saved for that project. Delphi doesn't know that these files are a "library", it just knows that you're using them, and since it can't find an already compiled version of those units, it creates one in your project. If you want the DCU files to be saved only once and in this central place, then you would need a package.
First, let me thank all the respondents to this question - all provided useful insight. I experimented with the various suggestions (including breaking XE5 so badly that I had to reinstall -- at least I learned some areas not to mess with.)
Important to me, but a known bad coding practice, is having individual projects edit shared library units (only my own units - I do not mess with code belonging to Delphi or 3-rd party). This is critical to having multiple applications working on the same data, but in bite-sized pieces. the shared code lets me make high-level pieces of an app available to other projects. There might be better ways (I would love to hear about them), but this has worked for me for a long time.
The multi-platform model really requires the dcu structure used by default, so I will adapt to it. Share the source code, but accept multiple compilations to individual projects. A good suggestion by JensG is just to clean up the dcus when the project is not actively being worked on. Should be straight-forward utility program.
The D6 -> XE5 migration (which will take months for some of the less used areas) requires me to know which units compile successfully, so I will maintain one project whose function is to include all units and recompile them all. This will make it practical to map library unit pas files to dcu files.
The AnsiString/AnsiChar <-> String/Char problem is the major migration problem area. Simply making edit-level changes may get the code past the compiler, but there is no guarantee that the code still works the same way. Especially troubling is at interface points to Windows calls and such. My answer will be to make the units compilable first, but then write test code for trouble areas. This is what will take the months - I need to get on with new stuff, as well as fixing the old. I REALLY don't know yet if I will be able to substitute the XE5 compatible code back into Delphi 6 without another layer of testing. I THINK it should work, but it will take careful checking.
A second major migration problem is 3rd party code such as VCLZip. XE5 has its own zip support, but I have a lot of places where I use VCLzip and the conversion will not be trivial. For this specific library, it may be possible to find XE5-level source and simply work it in. There are other pieces of code gotten from the internet that I used, but never needed to truly understand which will cause significant hassles.
Again - thank you to all. This has been an interesting 24 hours. Howard
The company I work for develops a system in Delphi, that contains dozens of exe modules, and each of them is identical to a certain degree if it comes to source code. Sadly, nobody has ever cared about using libraries to put the shared code in. This means that each time there is a bug fix to do in the code all these modules share, a programmer has to make corrections in all of them separately! It always takes so much time...
I decided to find a method to put the shared code into libraries. I considered DLLs and BPLs. In this case BPLs seemed much more programmer-friendly and much less troublesome, especially that the code is used only in our software and only in Delphi.
I put all the code shared by all the exe modules into BPLs and everything seems fine, but there are certain things I don't understand and would be grateful if you explained them to me.
What I expected after dividing the code into BPLs was that it would be enough to deploy exe files with the BPLs I created. But it turned out that they need an rtl100.bpl and vcl100.bpl as well. Why is it so? I want to deploy exes and my BPLs only. I don't want to provide end users with a whole bunch of libraries supplied by Borland and third party companies :). I want them to be compiled within exes as they used to be compiled before. Is it possible to do that?
What I did so far was:
I put all shared pas units to BPLs. Each BPL contains units belonging to the same category so it is clear for programmers what code to expect in a given BPL.
Each BPL is a "runtime and designtime" library.
Each BPL is "rebuilt explicitly".
The two latter are default project settings for BPLs.
And if it comes to the exe projects:
I deleted all units that I had earlier put to BPLs.
I installed my BPLs from the Tools->Install package menu in BDS 2006.
In my exe project settings I checked the option "build with runtime packages" and I listed all my BPL packages in the edit box below (only my packages, as I cleared all other ones that appeared there).
This is all I did. The exe projects compile properly, but I have no access to the source code of BPLs (I can't navigate into that code from my exe projects), even though all BPLs are stored together with their source code files. Why? It seems strange to me.
I always tend to write lengthy descriptions - sorry for that :). I will appreciate your help. I just need a few words of explanation to the points I mentioned: deploying exe with my BPLs only, the correctness of what I did as a whole, and the inability to navigate into BPL source codes. Thank you very much in advance!
Thank you all for the discussion. Some said the approach I chose was not a good idea. Our software consists of more than 100 modules (most of them being something like drivers for different devices). Most of them share the same code - in most cases classes. The problem is that those classes are not always put into separate, standalone pas units. I mean that the shared code is often put into units containing code specific to a module. This means that when you fix a bug in a shared class, it is not enough to copy the pas unit it is defined in into all software modules and recompile them. Unfortunately, you have to copy and paste the fixed pieces of code into each module, one by one, into a proper unit and class. This takes a lot of time and this is what I would like to eliminate, choosing a correct approach - please help me.
I thought that using BPLs would be a good solution, but it has some downsides, as some of you mentioned. The worst problem is that if each EXE needs several BPLs, our technical support people will have to know which EXE needs which BPLs and then provide end users with proper files. As long as we don't have a software updater, this will be a great deal for both our technicians and end user. They will certainly get lost and angry :-/.
Also compatibility issues may happen - if one BPL is shared by many EXEs, a modification of one BPL can bee good for one EXE and bad for some other ones - #Warren P.
What should I do then to make bug fixes quicker to make in so many projects? I think of one of the following approaches. If you have better ideas, please let me know.
Put shared code into separate and standalone pas units, so when there is a bug fix in one of them, it is enough to copy it to all projects (overwrite the old files) and recompile all of them.
This solution seems to be OK as far as a rearly modified code is concrened. But we also have pas units with general use functions and procedures, which often undrego modifications - we add new functions there whenever necessary, but in single projects. So imagine that you write a new function in one of the 100 modules and put it into its general use unit. After a month or two you modify a different module and you think you need the same function you wrote 2 months ago. You have to find the module (it's difficult if you don't remember which one it was) and copy the function to your code. And obviously - the general use units become completely different in each module as long as they are stored in each project separately. And then, if there is a bug fix to do... the whole story repeats.
Create BPLs for all the shared code, but link them into EXEs, so that EXEs are standalone.
For me it seems the best solution now, but there are several cons. If I do a bug fix in a BPL, each programmer will have to update the BPLs on their computer. What if they forget? But still, I think it is a minor problem. If we take care of informing each other about changes, everything should be fine.
#CodeInChaos: I don't know if I understood you properly. Do you mean sharing pas files between projects? How to do that? We store source codes in SVN. This means that we would have to store shared code in a separate folder and make all projects search for that code there, right? And download from the SVN a project and all folders it is dependent on...
Please, help me choose a good solution. I just don't want the company to lose much more time and money than necessary on bugfixes just because of a stupid approach to software development.
Thank you very much.
Even though this question has an accepted answer I'm going to take a stab at it.
The title asks how to divide a project into bpls but the real question appears to be:
"What's the best way to share code between projects?"
There are a few ways to do this:
Shared units
Dlls
BPLs
Regardless of which direction you go you will likely need to restructure your projects. From your description it sounds like each project is developed in relative isolation. Code is shared using copy/paste, which quickly gets out of sync and result in a lot of duplicated effort. So lets examine each of the techniques for sharing code.
Shared units
This is the most straightforward approach. You create a shared location and place code you would like to reuse among your projects into this location. The units are statically linked into your projects so you don't need to worry about deploying extra dependencies along with the main executables. Statically linked units are by far the easiest to troubleshoot and debug.
The compiler needs to be able to find your shared units. There are 4 ways to tell the compiler where to look.
Add them to the project - SHIFT+F11 - Adds a reference to the unit into the project files (dpr, dproj). The IDE will normally use relative paths if the unit is located under the same directory tree as the project files, otherwise it will use absolute paths, which can be problematic if developer machines aren't configured identically.
The project's Search Path - CTRL+SHIFT+F11 Delphi Compiler > Search path - Add a directory and the compiler will look there to find units mentioned in the uses clause of any unit in the project. Its best to use relative paths if you can. You can also use environment variables: $(MyPath)
Global Search Path - Tools > Options > Environment Options > Delphi Options > Library - Win32 > Library Path - Any paths listed here are available to all projects on a machine. This is machine dependant
Command line - If you build from a script or build automation tool you can set the search path using the dcc32's -U switch or msbuild's /property:UnitSearchPath= switch.
Options 1 and 2 will be the most useful.
As far as your SVN repository goes you have a few options for organizing the projects and shared units. The simplest would be to place all projects under single trunk along with the shared units:
Projects
trunk
ProjectA
ProjectB
ProjectC
Library (shared units)
If for some reason the above structure isn't possible you could try this alternative:
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)
In this configuration changes made to each project's library folder would not be immediately available to the other projects. Each project would need to synchronize changes with the main Library project on a regular basis. A side effect of this is that changes that break other projects will be delayed until the other projects are synchronized. Whether you consider this a good or bad thing depends. On the one hand bugs are easier and cheaper to fix when the code they involve is still fresh in the developer's mind. On the other hand if you don't practice unit testing (which I highly recommend you do) or the code is very fragile or you just have developers prone to making reckless changes you may want to control how frequently those changes get pushed into other projects.
Dlls
Dlls allow you to share code by linking to it at runtime. They expose functions that can be called from a main executable or another dll.
While dlls are always linked at runtime you decide whether they are loaded at application startup or only when needed. Loading at startup is called static loading and in Delphi is accomplished using the external directive. The vast majority of the rtl/vcl classes that wrap system api calls use static loading. Dynamic loading lets to delay the loading of a dll until it is required. This uses the WinAPI functions LoadLibrary and GetProcAddress. A corresponding call to FreeLibrary will unload a dll.
Unfortunately standard dlls limit what kind of datatypes can be passed. If you need to access a dll from non-Delphi projects you will need to limit yourself to using c style data types. If you will only be using a dll with Delphi projects you can safely use Delphi strings and dynamic arrays as well if you use the SharedMem unit in the dll and any projects that use it.
You can safely use object's within the dll without problems but if you want to pass objects between the dll and the application you'll need to extract the object's data and pass it as primitive types and reassemble it into an object on the other end. This is called (de)serialization or marshalling and there are much easier ways to do this than rolling your own.
COM (Component Object Model) is well supported in Delphi but it has a bit of a learning curve. Consuming COM objects is pretty straightforward but designing one will take time if you're not familiar with COM. COM has the advantage that it is language neutral and is supported in the majority of languages targeting the Windows platform (including languages targeting the .NET framework).
Bpls
Bpls (also called simply "packages") are specially formatted dlls that make working with objects a lot easier. Like standard dlls they are linked at runtime and can be statically or dynamically loaded. They are easier to learn and use than COM dlls and provide more seamles integration into your projects than COM. Packages are composed of two parts: the bpl and the dcp. The dcp is like the dcu files generated when you compile a normal unit file except it contains a whole bunch of units in it. Using a class that is compiled in a bpl is as simple as adding the dcp to the project's package list then adding a unit to a uses clause of one of the project's units.
When you deploy the app you'll need to install the bpl as well. As other's have noted you have to include the rtl package at a minimum and most likely the vcl package if you use any forms. There is a way around deploying Borland supplied bpls with your projects. You can create a "mini" rtl package that contains only the units your project need. The difficultly is in determining which units to include.
Summary
From the description you've given creating a library of shared unit files to statically link against may be the most expedient route. I would also suggest trying out a program called Simian. It will help you track down duplicate code in your code base for inclusion in your shared library. It doesn't directly support pascal but it does a decent enough job using the plain text parser with a little tweaking of its configuration.
Also I can't stress enough the value of unit testing. Especially if you're moving toward shared libraries. A suite of well written unit tests run on a frequent basis will give you instant feedback when a developer changes a class and it breaks an unrelated project.
Imagine you have a project with an EXE and two different BPL modules, and somewhere in that codebase, there's a line that says if MyObject is TStringList then DoSomething;. The is operator works by examining the object's class metadata, stored in the VMT, and then following a chain of VMTs through the ClassParent pointer, to see if any of them match the class reference (also a VMT pointer) for TStringList. In order to make sure that this will work correctly, there needs to be one single VMT for TStringList that's the same throughout your entire program, no matter how many BPLs it's divided up into, which means it has to be in its own package. That's why system runtimes like rtl*.bpl and vcl*.bpl are necessary, and there's not much you can do about that. It's part of the price of using BPLs.
As for not being able to debug, you need to make sure that the BPLs are built with debug info enabled and that the debugger knows how to find the folder where the DCP (the file containing the debug info for the BPL) is located. And you won't be able to trace into system BPLs, because debug-enabled DCPs weren't shipped with your version. They were added pretty recently, I think in XE but it might have been in D2010.
Why can't I browse my source code? Is there a way to fix this?
You can not browse the source code of the units included in the packages because they are neither in your project, your library or search path.
The way I solve this is adding the directories to the project search path. This way the compiler does not know about those files (and does not try to recompile them) but the IDE let's you browse their content and debug into them.
"In my exe project settings I checked the option "build with runtime packages"
That is why you cannot deploy without the BPL's etc - this option is confusing for a lot of developers -"build with runtime packages" means that you will need the bpl's present at runtime. Uncheck that option and the packages will be linked into your exe at compileTime. (Your exe will g-r-o-w in size.) The idea behind the "build with runtime packages" is to keep the size of exe's down and allow several apps to share common bpl's because they are NOT linked into the exe # compileTime - that's the upside. The downside you are now experiencing - you must distribute your bpl's with your exe.
I release a single executable (.EXE) for a desktop program using Delphi 2009. I have no external DLLs or resources that I need for the program to run.
I use two components: LMD Innovative's ELPack and Sergey Tkachenko's TRichView that are compiled into my executable.
When I build my production version, using the "Release" build configuration, the executable file produced is 13,533 KB.
Prior to using Delphi 2009, I was using Delphi 4. The executable it produced was only 2,671 KB while incorporating the same two components and basically having the same code as my current version.
I do understand that Delphi 2009 is completely Unicode (which is the main reason why I upgraded), and being Unicode can cause up to a doubling of size. But this is about 5 times larger.
Is there a reason why my executable has to remain 5 times larger? Or are there some simple ways to cut down a significant chunk of the executable size?
Please note. Some people are answering with ways to compress the Delphi EXE. That is not what I am trying to do. I am trying to simply see why so much space is being used to remove what might not be necessary. If that is done, compression can still be done afterwards if so desired.
It really doesn't matter how big or small the executable is once it is installed. It is for downloading purposes and to minimize server load and download times that you want to compress it. I prefer to use Inno Setup and compress the program inside the install routine itself. Then when it is installed, it is expanded to full size. That both prevents possible detection as a virus and eliminates the extra startup time needed to uncompress the program in memory. Also I code sign both my executable and my install routine and some compression techniques are incompatible with that.
For more info about compressing, see the StackOverflow question: Delphi EXE compressor?
ldsandon asked me to provide exactly what options I'm using, so here they are:
(source: beholdgenealogy.com)
(source: beholdgenealogy.com)
When moving from Delphi 7 to Delphi 2010., our .exe's grew for example from 16 megs to 35 megs.
I asked a question similar to yours on the Embarcadero forum a few weeks ago. (link) In my OP, I listed a series of links on this subject that you might find helpful.
We tried using UPX to compress our .exe's. Letting it work for hours significantly reduced our .exe, but we probably won't use it in production for these reasons:
We have quite a few .exe's and don't want to wait 1/2-day on each build. (It's possible that we could find a non-brute force set of parameters to UPX that would reduce this...)
Although the size of the .exe is reduced, our shippable was not, because our installer (not surprisingly) is unable squeeze much more compression out of the already compressed file... whereas it was able to reduce the original 16 meg .exe down to 8 megs.
I've read some reports that at some time (rarely, but not never), UPX exe's triggered various anti-virus programs to report the application contained a virus. (I don't recall the date, site, or details of where I saw this, so it's a bit unfair of me to report it here.) But, we are so adverse to taking a risk of that even possibility happening, that UPX is off the table...
The link on the Embarcadero forum also includes a link to another SO thread on this topic.
I continue to be surprised and disappointed at the code bloat we found when moving to Delphi 2010. As Nick notes, 2X for Unicode is quite excessive.
However, the bloat is a relatively minor trade-off when moving to D2010, because, IMO, D2010 is such a terrific upgrade in so many other ways. But, it does mean that we'll probably have to move to shipping 2 CDs rather than one. I'm not looking forward to seeing the reaction to this from our organization...
Without seeing the actual settings that your "Release" build configuration uses explaining this increase in size requires a great deal of speculation.
Beyond some perhaps unlikely factors resulting in a vast increase in the amount of code being "dragged in" even though it isn't used, that magnitude of increase would most easily be explained by the inclusion of debug information.
I would check your compiler and linker settings for:
Debug Information (compiler setting)
TD32 info (linker)
Remote debug info (linker)
Compare these settings in your Delphi 2009 project with the equivalents in Delphi 4.
Factor out the expected 2X increase from Unicode and you end up with a 2.5X increase unaccounted for. This makes sense considering how many versions you've skipped. A lot's been added to the VCL and RTL since Delphi 4, and not all of it is stuff that can be easily smartlinked out, even if you never use it. Depending on how many units you're using, you could be hauling in quite a bit of extra baggage.
Allen Bauer and the compiler team added a new feature into D2010 to help reduce this, but apparently they're treading cautiously and didn't use it in as many places as they could have. Hopefully we'll see more cruft reduction in 2011 and subsequent releases.
I will add my few words.
Linker can remove unused procedures and functions only if it can follow the code hierarchy. The nightmare list for linker listed below:
Message-driven code, the sad news is that this code can't be removed whatsoever, that's why Delphi blank project size continues growing from version to version. Every new windows message (WM_TOUCH for example as long as I know introduced recently) creates procedure call hierarchy that can't be removed (even if you don't have plan to use Touch API at all). This is because every case WM_: fragment is something linker can't decide whether it will be used or not.
Code and data structures accessed from the begin end, initialization, finalization secions of the units. Here you have some control, remove unnecessary calls or object creation. Even if you create objects on demand and only free them in finalization section, make it carefully
Use "upx - compress or expand executable files" # http://upx.sourceforge.net
If you go to tools/configure tools, and set it up like this, you can compress the executable that you're working on easily via a menu item in the IDE.
Another way is to have a look to 'what unit increase the size ?'.
To do this, I use the JCL 'Project Analyser IDE', integrated in the IDE with the JCL/JVCL installation, it show you all the units with their respective size. You can export it in text file.
If you do it with the 2 environnements (D4 & D2009) you will have a lot of pertinent informations.
I've done some tests to see the difference between D2007 and D2010, because we are upgrading to D2010. I've tested a medium sized management GUI application, with about 60 forms (grids with detail forms, frames, etc). We're using TMS components + Remobjects.
D2007:
"normal" compilation: 18.8mb
with debug dcu's: 18.8mb (same size!)
D2010
normal: 23.9
debug dcu's: 48.8mb (!)
So using debug dcu's doubles our exe size...
Test with our business service (no big dfm's):
D2007: 12.3mb
D2010: 17.1mb
So yes, D2010 increases the exe (a bit), but this is not a problem for my customer.
Edit: some information about compiled size:
D2007:
D2010:
So an increase of code size, but a more than doubling of the data!
If you don't want to use an exe compressor then you should give StripReloc a try.
Check format of your dfm-s. They must be in binary format if you want to make your exe smaller.
1) You are generating a detailed map file, and because you've set "used debug dcus" it will also contains symbols for the RTL/VCL units. If it is used by an exception handling systems to generate call stacks and the like, it could be added to the executable. And if not compressed somehow, it could make your .exe size pretty large.
2) Using debug dcus will also make your .exe somewhat larger because usually they are compiled without optimization and debug options set, and they will make also your code slower. They shouldn't be used in a release version.
3) Debug information should add debig info only to the unit and not to the executable, although it is required IIRC to generate the map file.
Since D2010 adds extended RTTI, and RTTI is a notorious factor in increasing exe size, it would be interesting to see how big D2009 binaries are for that application.
If D2009 binaries are significantly smaller, it is not Unicode etc. For my own binaries, I only have a 30% increase or so going from D7 to D2009.
It has been stated earlier that using an executable compresser reduces the size of the exe but not of the install package. However, if you want a good compressor then try ASPack.
#Tom1952: ASPack is pretty fast, just a few seconds to compress a file
Also you can change the Icon. Icon in newest delphi IDE (ie XE3) is Vista/7 compatible and contains all sizes (up to 256x256 as far as I know). So you can reduce exe file size with changing the Icon.
The standard units in you newer delphi may contain more strings and constants such as error strings, that is included even if you disable debug information. Check your uses.
Don't have much of a somution besides not using a specific unit, or removing unneeded data from it.
(My experiences are with Delphi 5)
For Delphi 10.3 Rio with default setiings:
Step 1: Switch from Debug to Release in "Projects" window. This reduced my exe file from 22 MB to 5 MB !
Step 2: Use an exe compresor like ASPack. It further reduced my exe file to 1.3 MB. Unbelievable, isn't it ? :)
Uncheck debug information in project options.
If embarcadero can't provide any solution or explanation!!! I think the solution is simple: don't stuck only with Delphi there is a lot of programming languages, every one is limited only by programmer imagination.
I'm writing delphi app which should have capability of loading plugins. I'm using JvPluginManager as plugin system/manager ;) Now, in the new plugin wizard they say it's better to use .bpl type plugins instead of .dll plugins ... What are the pros of this solution versus dll type plugins?
So far I've found only cons of this solution:
I have to put all the common interface units in separate package so that while loading plugins it won't throw any error about the other package containing common unit
if, let's say, one of plugin developers decides to uses some well-known unit (like synapse), which doesn't have runtime package by default, and the second plugin developer does the same, than bump... it's crash here ...
So, what are actually the pros of using bpls instead of dlls compiled with runtime packages?
Thanks in advance
Another disadvantage to BPL's. When you switch Delphi versions you will have to re-distribute new plugins. After many attempts at attempting to find the perfect plugin system, I ended up with COM and I have never regretted that decision. In a commercial application which has had the plugin requirement for over 8 years, the application has continued to move forward and yet some of the plugins that were released with the first iteration still exist in their ORIGINAL form.
If you choose this method, do yourself a favor and start with a simple interface and then add new interfaces upon it. You don't want to ever change your base interface, so keep it simple and sweet.
As Alexander said, a BPL is basically a DLL. But there are some conditions (from a not-so-short summary I made: http://wiki.freepascal.org/packages ):
A unit can only exist once in BPL's +Exe. This avoids duplication of state (twice the heapmanager and other global vars of system etc, VMT tables etc)
.BPL's can only USE other .BPLs.
This means that dynamic types like ansistring and IS/AS work properly over BPL interfaces.
Initialization/finalization are separate procedure and their initialization order is strictly controlled. For static dynamic loading this is simpler, for dynamic loading (plugin-like) all dependancies on units are checked .
Everything is essentially one big program, which means that the BPL's must be compiled with the same compiler version and RTL and depends on the versions other dependancies. It might be harder to make .BPL's to plugin to an existing EXE, since the Delphi version must match.
This also means that you must deliver .dcp's for (non Delphi) .BPLs the plugin .BPLs depend on
In short: if the plugin architecture is open, make it a DLL. Otherwise people have to have the exact same Delphi version to write plugins.
Hybrid is also possible. An higher level .BPL interface for functionality you factor out into .BPL yourself and selected devels, and a rock bottom procedure DLL interface for the rest.
A third option is using DLLs, but ordain Sharemem. Strings will work, multiple Delphi versions will work. Objects can work but are unsafe (e.g. I guess e.g. D2009 with an earlier version wouldn't work). Even other language users might be able to allocate over COM, not entirely excluding non Delphi.
Your first con is also a pro. If you replicate shared code in each dll the dlls get bigger and bigger. Even when using dlls you can prevent this by moving shared code in a separate dll.
Pros:
Types are shared. No TFont is not a TFont problem
Memory manager is shared. Strings and classes can be used as parameter between plugins without problems.
Cons:
Plugins can be built using Delphi or BCB only.
Plugins should use the same Delphi or BCB version.
Have you considerd using COM? COM makes it possible to share types, strings and classes and the plugins can be written in many programming languages.
I'm not familiar of JvPluginManager, but it depends on how you're going to use BPLs.
Basically, BPL - is just a usual DLL, but its initialization/finalization work is stripped from DllMain to separate functions: 'Initialize'/'Finalize'.
So, if you're going to use BPL like usual DLL, there are no cons that I'm aware of, only pros: there will be no more troubles with DllMain. That's all. The only difference.
But BPL in Delphi also provide a convient way to share code. This means great advantages (common memory manager, no duplicated code, etc, etc). So usual BPL does a lot more than "being just a DLL".
But this also means, that now your plugin system is limited to Delphi only (well, may be C++ Builder too). I.e. both plugins and exe MUST be compiled in the very same compiler to run smoothly.
If this is acceptable for you (i.e. no MS Visual Studio, no, sir, never) - then go ahead, you can use all power of BPLs.
P.S. But upgrading such BPLs plugins can be also a nightmare, if you do not design interface side carefully. In certain worst cases, you may need to recompile everything.
P.P.S. Like I said: I have no idea, how it is applied to plugins, created by JvPluginManager.
Avoid blp approach as you will have to ship a big bag of bpl with you software and thus, distribution will become bulky.
why do we use Delphi to compile small stand alone programs that just RUN anywhere without any runtime dependency. Using bpls means defeating this very purpose.
I don't know as to how comfortable you are with DLLs, but I would suggest you to use DLLs.
This will give other developers (who
may get interested in your software)
a chance to use any development
language (as long as that language
can spit out dll) to write their own
plugins that can be used in your
developed software.
Another thing is that you will be
saved from Delphi's vcl version
dependency tyranny. A major weak
point of Delphi till date.