views:

624

answers:

5

This question is similar to this one, but not a duplicate because I'm asking about issues not discussed in that question.

I have a client-server project in Delphi 7 with the following directory structure:

\MyApp
  \MyClientApp
  \MyServerApp
  \lib

There are 2 actual Delphi projects (.dpr), one each in the MyClientApp and MyServerApp folders.

The lib folder has .pas units that have common code to the client and server apps. What I'm wondering is if I should include those .pas files in the client and server projects? Or should I create a package in the lib folder which includes those units? Or should I just leave the .pas files sitting in the lib folder and not add them to any app/package?

What are the pros/cons of each approach? Which way is "best"? Is there any issue with having those units from the lib folder be included in more than one project?

Right now the units in the lib folder are not a part of any app/package. One disadvantage of this is that when I have my client app open in Delphi, for example, and I want to search in all files in the project for something, it doesn't also search in the units in the lib folder. I get around this by opening those units and doing a find in all open files, or using grep search (but I'd prefer a better solution).

I would also greatly prefer a solution where I will not have to go and open some separate package and recompile it when I make changes to those files in the lib folder (is this where I should use a project group?).

+1  A: 

I usually create a package with all shared unit, and just use the units. If you do not explicitly mark "Build with run time packages" the package content (all used dcu's) will be linked to your project as any other unit.

Cesar Romero
The _package_ won't be linked at all. It will just link the DCU files like any ordinary build.
Rob Kennedy
@Rob, you are right, I mean the package content = .dcu.
Cesar Romero
+2  A: 

I dislike having files shared by projects. All too often, you'll be tempted to edit one of the shared files, and you'll either break something in the other project, or you'll forget that you have to rebuild the other project at all.

When the shared files are instead separated into their own library (package), then there's a little extra barrier to editing them. I consider that a good thing. It will be a light reminder that you're switching from project-specific code to shared code. You can use project groups to let you keep every together in a single IDE instance. arrange the library projects ahead of the executable projects. The "build all" command will build everything in order, starting with the first project.

Keep your DCU files separate from your PAS files. You can do this easily by setting the "DCU output directory" project option to send your package's units to some other location. Then put that destination directory on your other projects' "search path." They'll find the DCU, but they won't find the PAS file, and so no other project will accidentally recompile a unit that isn't really a member.

Having a separate package also discourages use of project-specific conditional defines. Those cause all sorts of trouble when you're sharing units between projects. Find a way to instead keep all project-specific options within the respective projects. A shared library shouldn't require project-specific modifications. If a library needs to act differently based on who's using it, then employ techniques like callback functions that the library user can set to modify the library's behavior.

Rob Kennedy
+6  A: 

Sharing units between applications always carries the risk of incompatible changes done in one application that breaks the other. On the other hand, making copies of these units is even worse, so your approcach of moving them to their own subdirectory at least adds a psychological barrier to changing them without considering other programs.

As for adding them to the project files: I usually add some units which I frequently access (either for expanding or for reference) from the IDE to the project, and leave others out for the compiler to pick using the search path. I do that on per project basis, that means, some units may be part of several projects, why not?

Putting them into a package only makes sense, if you actually want to create a package based application, otherwise, why bother?

For more info on how I organize my projects and libraries, see http://www.dummzeuch.de/delphi/subversion/english.html

dummzeuch
Nice article; thanks!
onnodb
+3  A: 

I would need to have a very good reason to add shared code to a package. If you just have a few shared files stick them all in a directory called Shared. This should make it obvious the files are shared between projects.

Also use a good build tool to do automated builds so you will find out soon enough if you break something.

.bpl files are fine for components, but bring in serious added complexity for things like this, unless you have a huge amount of shared files.

Toby Allen
+1  A: 

I would only use runtime packages if you actually had two binaries that were supposed to run on the same physical machine and that shared some code. Keep in mind that runtime packages are mostly an all-or-nothing approach. Once you decide to use them you will also no longer be able to link the RTL and VCL units straight into your projects and will instead have to deploy those separately as well.

However, packages might still be a good solution to your problem when combined with project groups which is exactly what I'm doing. I hate having shared units included in multiple projects. Including the shared units in a package (but not compiling your actual projects with runtime packages) allows you to add that package to your project group so you (and the IDE!) will always have them easily accessible yet nicely separated from the project-specific code. Strictly speaking you don't even ever have to compile those packages. They can merely serve as an organisational unit in the project manager.

Note that for the Find in Files, you can also specify "in all files in project group"

Oliver Giesen