C++Builder 5 Developers Guide
This section discusses packages and how they are used both in applications and by the IDE. The term package refers specifically to a source module with the .bpk extension (Borland Package) and more generally to a Borland Package Library (BPL) file ( .bpl extension) that is created when the Package is built. A BPL file is very similar to a dynamic link library (DLL) and is used for the same purpose, to dynamically link executable code to an application.
A package can be one of three types: design time-only , runtime-only , or dual design time/runtime . Essentially, the only difference between these packages is that a design time package can be installed into the IDE, whereas a runtime package cannot. Runtime packages can be used only at runtime. The dual package can be used in either situation and is often used for convenience when initially developing components .
A package consists of two sections: a Contains section and a Requires section. Files that appear in the Contains section are those that the package contains and are compiled and linked when the package itself is compiled and linked. These files are part of the package.
Import files appearing in the Requires section are references to runtime packages that this package must access to function correctly. This mechanism will be explained more clearly later in this section. When a package is built, all the files in the Contains section, where appropriate, are compiled and linked, and object files for each are created.
In addition, a BPL file is generated, as are a Borland Package Import (BPI) file ( .bpi extension) and a static library (LIB) file ( .lib extension). To not generate a BPI file, select the Linker tab in the Project, Options dialog, and uncheck the Generate Import Library option in the Linking group . To not generate a LIB file, uncheck the Generate .lib File option.
You always need to generate a BPI file, which is used by the IDE during linking, so that the executable can use the respective BPL file at runtime. This is true except in a design time “only Package. Therefore, the same types of files are produced for all Package types. The difference is only apparent in their use. Figure 2.15 shows the structure of a Package in the Project Manager.
Figure 2.15. The structure and output of a package.
Note that in Figure 2.15, the contained units are implied to be C++ translation units. Hence, for each translation unit, an object file is produced when the package is compiled and linked successfully. Other files can also be added to a package's Contains section, most notably resource files and object files. These files are commonly needed when a Package is used to Package components.
The Requires section in Figure 2.15 includes the import file rtl.bpi . This indicates that the Package requires executable code contained in rtl.bpl (the runtime library BPL). Placing the .bpi file in the package's Requires section enables the linker to resolve any external references to the runtime library.
Three files are produced when a package is built. One is the BPL file. The nature of the BPL file depends on whether the package is a design time package or a runtime package. If it is a dual package, the functionality of both package types is available. With design time packages, the BPL file is used to install the package into the IDE. You do this by selecting Install Packages from the Components menu and browsing for the design time package .bpl file. With runtime packages, the BPL file is used specifically to allow applications to link dynamically to the functions and data that the package contains at runtime.
For an application to link dynamically to a .bpl file, the linker must be able to resolve references to the functions and data contained by the .bpl file during linking. Such a reference is referred to as an external reference because it refers to something external to the current application. To resolve an external reference, the linker searches for an import record for the function called by the application. The import record is contained in the corresponding Borland Package Import (BPI) file ( .bpi extension) for the package.
For every function that is exported from the package (basically any function that is declared in a unit contained by the package), there is an entry that states the internal name of the function and the name of the module that contains the function. In this case, that is the name of the package's BPL file (more information than this is presented, but it is not relevant to this discussion). The linker to the application's executable file copies this information. This creates a dynamic link to the function that will be resolved by Windows each time the application is executed. It does this by searching all the files on the system path (for example, files in the Windows system directory) for the file named by the import record when the application is loaded into memory. If it finds the required BPL file, that also is loaded into memory. The external reference is then referenced to the correct location within the BPL file. If the required BPL is already loaded into memory, all the better. The overhead for this operation is not incurred, and the BPL is shared by the applications using it. The function or data can then be used by the application.
It should be clear that the BPL and BPI files produced by a runtime package are used to support dynamic linking of the code that the package exports. The LIB file is used to support static linking of the code that the package exports. Essentially, the LIB file contains the OBJ files of the units contained by the package itself and is in fact an object library . When a function is required from the package, the appropriate OBJ from the LIB file is copied to the target executable. Each such executable, therefore, has its own copy.
Table 2.2 summarizes the purpose of the files produced by a package when it is successfully built.
Table 2.2. Package Files Created on a Successful Build
Extension | Description | Type | Purpose |
---|---|---|---|
.bpl | Borland Package Library (BPL) | Dynamically linkable library | Contains executable code of the package and exports the functions and data of the package. A runtime library accessed by applications that are dynamically linked to it. A design time library that can be installed into the IDE to make new components or editors available at design time. |
.bpi | Borland Import Library (BPI) | Import library | Contains import records for the functions and data exported by the corresponding BPL file required for dynamic linking to the BPL file. |
.lib | Static Library File (LIB) | Object library | A static library containing the object files of the units contained by the package. Used to statically link exported functions and data to a target application. |
Table 2.2 shows that, in order to use a runtime BPL, the corresponding BPI file must be available at link time for the external references to the BPL to be resolved.
When you compile and link a project, you can choose whether the project is dynamically linked or statically linked to the packages it requires. By default, dynamic linking is used. To change this setting for the project, uncheck the Build with Runtime Packages option on the Project, Options, Packages page.
It is also possible to choose units from a package that you want to be statically linked to a given unit within a project. To do this, add the #pragma link " unitname " directive, generally near the top of the unit, where unitname is the name of the unit that you want to be statically linked to the unit within your project. This results in the linker copying the required object file from the package's .lib file.
Considerations When Using Packages
For a class to be properly imported and exported from a package, the PACKAGE macro must be used after the class keyword in the class definition, as shown in the following code:
class PACKAGE TMyComponent : public TComponent { // Component class definition here. };
The PACKAGE macro must also be given in the declaration of the Register() function for the component. This is shown in the following code:
namespace Newcomponent { void __fastcall PACKAGE Register() { TComponentClass classes[1] = {__classid(TMyComponent)}; RegisterComponents("Samples", classes, 0); } }
Of course, this applies to component class definitions. If you use the Component Wizard (Component, New Component) to create the component, C++Builder will insert the PACKAGE macro for you.
To ensure that functions and data are properly imported and exported from a unit contained in a package, the #pragma package(smart_init) directive should be placed in the unit's source file, typically after any #include statements, but before any source code. Failing to do so will not prevent the unit from compiling, but it will prevent the package from statically linking. The purpose of the directive is to ensure that the packaged units are initialized in the order determined by their dependencies.
The #pragma package(smart_init,weak) directive is also available. This is used when you do not want a particular unit to be contained in the BPL file. Instead, the unit is placed in the BPI file. When the unit is required, it is copied from the BPI file and statically linked to the target application. Such a unit is said to be weakly packaged and is used to eliminate conflicts among packages that might depend on the same external library.
|
Top |