OpenDoc provides a set of samples that you can build. On the OS/2 and Windows platforms, the samples are built with nmake. On the AIX platform, the samples are built with /usr/bin/make.
The simplified, straightforward makefiles include Platform.mak. This is a platform specific makefile that contains the rules that builds the parts on each platform.
The part developer's makefile for the OS/2, Windows NT, and Windows 95 platforms are the same however, a different makefile exists for the AIX platform.
As a common format, we chose the unix approach to full path delineation and we chose to use a build tool that was already available to every part developer. The nmake tool comes with both the VisualAge and Visual C++ compilers. The /usr/bin/make tool is native on the AIX operating system. Anyone who has the correct compiler installed on their platform already has the correct build tool available with the current nmake tool and /usr/bin/make tool approach.
Here are some guidelines to follow in order to use the samples build environment:
For all three platforms, if you plan to build packages, a 32-bit compression tool must be installed. You can use Info-ZIP. It is freely available from Compuserve in the IBMPRO forum and by anonymous ftp from the Internet site ftp.uu.net:/pub/archiving.zip.
The build process does a generic compile and link. It does this so that the decision to build either optimized or debug is not hard-coded in the makefiles. The build process uses the following environment variables as part of the compile and link process. This allows the developer to change from building a debug build to an optimized build without having to edit any makefiles. The following shows settings for builds on the various platforms.
The OS/2 Platform
VisualAge Compiler options are as follows:
set ICC = -O+ -Om- set ILINK = -NOBROWSE
set ICC = -Ti+ -DDEBUG set ILINK = -DEBUG
The Windows platform
VisualAge Compiler options are as follows:
set ICC = -O+ -Om- set ILINK = -NOBROWSE
set ICC = -Ti+ -DDEBUG set ILINK = -DEBUG
Visual C++ Compiler options are as follows:
set CL = -Ox set LINK =
set CL = -Ge -Zi -DDEBUG set LINK = -DEBUG -DEBUGTYPE:BOTH
The AIX platform
xlC Compiler options are as follows:
export ICC = -O export ILINK =
export ICC = -g -DDEBUG export ILINK =
Each part developer's makefile is divided into 3 sections:
The following are macros that can be used in a part developer's makefile:
The following macros build a typelib for the Windows platform only. Note that they must be used concurrently.
Macro Definitions for Reg* and Files*
Reg* and Files* macros are used to build a table of contents and an installable package for an OpenDoc part. None of these macros take a full path length as Platform.mak knows where to look for them. See "Defining the Table of Content Entries for Your Part Handler" for details.
The Platform.mak include brings in the main build logic. Platform.mak on AIX and OS/2 has the logic used to build the samples. Platform.mak on Windows determines whether to include Platform.nt or Platform.w95 (the shell script language works differently on Windows NT and Windows 95). For simplicity's sake, we will only refer to Platform.mak, even though the logic is in Platform.nt and Platform.w95 on Windows NT and Windows 95 respectively.
The problem with including Platform.mak after the macros is if you want to use platform-specific references before the include of Platform.mak. The Platform macro is set in Platform.mak, so you can not reference it before it is set. It is recommended that you delineate macros you want set for the OS/2 platform with:
!IFDEF OS2_SHELL !ENDIFDelineate macros you want set on the Windows NT platform with:
!IF "$(OS)"=="Windows_NT" !ENDIFThe Windows 95 platform can be referenced by:
!IFNDEF OS2_SHELL !IF "$(OS)"!="Windows_NT" !ENDIF !ENDIF
At the bottom of each part developer's makefile are the simplified inference rules. Typically, they consist of:
The first thing you may notice about the inference rules is the use of some generic macros. For example, $(Obj), $(Def), $(Exp), or $(LibSuffix). These macros help promote usability across different platforms. An object file has an extension of .obj on the Windows 95 platform, but has an extension of .o on the AIX platform.
The $(Def) and $(Exp) macros may be confusing because a file with an extension of .def and a file with an extension of .exp mean different things on different platforms.
$(Def) = the input file to building the export library (.lib or .a) $(Exp) = the input file to building the shared library (.dll)On the OS/2 and AIX platforms, the $(Def) and the $(Exp) files are the same file. On those platforms the macros resolve to the same value. On the Windows platform, they are different files and the macros resolve to different values. We used $(LibSuffix) instead of $(Lib) because the Windows NT and Windows 95 platforms have a LIB environment variable therefore, a $(Lib) macro would get confused about when it is referring to the environment variable verses the macro. Also, on the AIX platform, there is a corresponding $(LibPrefix) being that libraries there begin with 'lib'.
A complete list of the macros that can be used to promote portability between platforms can be found in the Platform.mak file.
The inference rules in the part developer's makefile specify what are the sources needed to build the target. If the .xih header is needed, it must be placed in these inference rules so it can be built. For example:
iodlink.$(Obj) : iodlink.xihFiles with an extension of .cpp are optional when building $(Obj)s, because the rule that builds the $(Obj)s forces the relationship with the .cpp. But all headers should be mentioned so the $(Obj) will re-build if a header is touched.
The macros set in this section, typically *ObjList and *LibList macros, are mostly for readability. They also provide a single place where the object files and libraries are listed. That helps if a list has to be used twice, such as *ObjList, which is used in building the export library as well as the shared library.
The build rules for each target are defined in Platform.mak. These rules are implemented through recursion. For example, to build an export library, the part developer's makefile uses $(BuildLib) as the rule. $(BuildLib) is a macro defined in Platform.mak that calls the BuildLibRule. Therefore, Platform.mak has some common rules and a set of macros so the part developers can easily call them in their specific inference rules. For more information about the common set of rules and macros, see "Remaining Rules".
Because Platform.mak is different on every platform, we cannot provide specific rules and approaches however, there are some general concepts common to all Platform.mak files.
They all use shell script logic. Because neither nmake nor /usr/bin/make provide any kind of looping capability, the Platform.mak files must rely on the shell script logic available on each platform. But in both nmake and /usr/bin/make, the build tool does not run each rule as a separate script. It runs each line as a separate script. Therefore, each loop can only be one line long. This is why, on the AIX platform, you see long shell script sections held together with backslashes. You can see the same thing on the Windows NT platform, where each FOR loop can only be one line long.
The following is the layout of each Platform.mak file:
A build environment needs to be able to run on a variety of different machines. Each machine probably has a unique set of environment variables. Some environment variables are essential for a command to run correctly. For example, the SOMObjects compiler looks at the .idl in SMINCLUDE before it looks at the .idl specified through the -I flags. Therefore, the setting of SMINCLUDE greatly affects the output of the SOMObjects compiler. The build environment needs to control certain environment variables during the build in order to build correctly. It does this by setting specific critical environment variables before a command was run, and by clearing the setting afterwards. This does not affect the environment on your machine, being that the build is run in a separate shell.
Whenever an environment variable has to be modified to ensure subsequent commands will run correctly, it is displayed to the developer. If the developer wonders why the SOMObjects compiler runs correctly in the build, but not from the command line, the environment variables that were modified to ensure correct execution are visible in the build.
The macros that are used to reset the environment variables are at the top of Platform.mak. This makes it easy to see what environment variables are used during the build and you can change them if necessary.
A word of caution about nmake on the OS/2 platform. Sometimes nmake cannot correctly parse a 'set <environment variable>=<null>' statement. For example:
set SMINCLUDE=Sometimes it will work but other times it will not handle the null correctly in nmake's local copy of the environment variables. For the time being, do not set an environment variable to null on OS/2.
General use macros are self explanatory. They can be a little confusing on the Windows platform, where there are two sets, one for the VisualAge compiler, and one for the Visual C++ compiler.
The default target is what is built if no target is specified. In Platform.mak, the default target is 'Idls Hdrs Libs Dlls'. This means there are four passes through the build tree, one for each of these settings.
The Idl target copies the .idls in the IdlTargets macro to $(ODSRC)/include.
Idls (with an 's') does this for the current directory, and all subdirectories listed in the Subdirs macro.
All of the .idls have to be accessible before the first .xh header is built. This is why Hdr is a separate, subsequent target to Idl.
The Hdr target does everything else that is necessary to prepare for the compile. It does the following:
All of the headers have to be accessible before the first object file is built. This is why Lib is a separate, subsequent target to Hdr.
The Lib target builds the targets listed in the LibTargets macro. Because the library depends on the object files, the object files are built first. Because some object files depend on .xih files existing, the .xih file is created before the object file. These dependences were specified in the simplified inference rules of the parts developers makefile.
Libs (with an 's') does this for the current directory, and all subdirectories listed in the Subdirs macro.
All of the export libraries have to be created before the first dll is built, because the dlls need the export libraries. This is why Dll is a separate, subsequent target to Lib.
The Dll target builds the targets listed in the DllTargets macro.
After the dll is successfully built, the .idl file listed in the RegIDL macro is compared with the opendoc.ir file. If the .idl file listed in the RegIDL macro is newer, it means a dll was built for an OpenDoc part, and the .idl for that part has changed. Therefore, the .idl listed in the RegIDL macro is re-registered (or registered, if you are developing a part for the first time).
Finally, on the Windows platform, any typelibs specified by the Ctypelib* macros are built.
Dlls (with an 's') does this for the current directory, and all subdirectories listed in the Subdirs macro.
After the default target, is the section that contains the default and miscellaneous build rules. There are some straight-forward rules, such as how to build an .xh file from an .idl file but there are also some other interesting rules.
There are build actions that you do not want to have happen on a regular basis, therefore they are not part of the default target. ForceRegistry is one of them. Ordinarily, the default target handles the case of registering your part when your .idl is newer than the opendoc.ir file. For a single part developer, this is the correct time to register your part but in a build environment the logic may not work as well. If you are doing a complete rebuild, using a new opendoc.ir, the first .idl that the build encounters will get registered correctly with the default target rule. This initial registry updates the opendoc.ir file and it is now newer than subsequent .idl files encountered in the build. Keep in mind that the default rule only allowed the first .idl encountered to be registered.
If you want to force the registration of every RegIDL in the build tree, execute the following after the regular build:
nmake ForceRegistrys (on Windows or OS/2 platforms)or
/usr/bin/make ForceRegistrys (on AIX platforms)
On the Windows platform, running ForceRegistry runs scriptrg for every OpenDoc part that has a typelib, and it runs regedit if the RegeditTarget macro indicates you have input files for the RegistryEditor. These actions were put in the ForceRegistry rule because a parts developer does not want to have them happen every time they rebuild their dll.
ForceRegistrys (with an 's') does this for the current directory, and the subdirectories listed in the Subdirs macro.
Opendoc parts can be built into installable packages that other OpenDoc developers can install. For more information on packages and how to install them see "Installation of OpenDoc Software".
The Pkg target allows the developer to build an OpenDoc installable package. It creates the table of contents for the package (the .odt file), as well as the parts.lst file, before building the package. The build environment used the Reg* and Files* macros to build these files and the package. By using macros to build the table of contents and the package, the build ensures that everything exists that you believe should be in your package, and only the files mentioned in the macros get placed in the package. A package cannot be built unless everything exists and extraneous files are not placed in the package.
On the OS/2 and Windows platforms, the package is built using 32-bit zip tools.
On the AIX platform, the package is built using tar and compress, which are native to the operating system.
Pkgs (with an 's') builds packages for the current directory, and the subdirectories listed in the Subdirs macro.
BuildCopy is not a rule, it is a macro that recursively calls the BuildCopyRule. However, BuildCopy is used as the rule in many of the part developers makefile to copy files. For example:
$(ODSRC)/lib/iodbasec.$(Def) : iodbasec.$(Def) @$(BuildCopy)
This copies iodbasec.$(Def) to $(ODSRC)/lib/iodbasec.$(Def) on all platforms.
BuildDef is not a rule, it is a macro that recursively calls the BuildDefRule. However, BuildDef is used as the rule in the part developers makefile to build the $(Def) file for C++ export libraries.
Note:
AIX developers, a $(Def) is an .exp file.
This rule should not be used to build the $(Def) for SOMObjects export libraries. The $(Def) files for SOMObjects export libraries only need three symbols per class, and BuildDef builds the $(Def) from the symbols in the object files. The $(Def) file for C++ export libraries usually has to be remade every time you build an export library because any change to the API changes the $(Def) file for C++. With SOMObjects, once you build the $(Def) file, the only reason you would need to change it is if you added or deleted a SOMObjects class in your dll. Most SOMObjects developers create their $(Def) once, and use the BuildCopy rule to copy it to the correct location.
The usual way a parts developer creates their $(Def) file for SOMObjects export libraries is to use the SOMObjects compiler. Instructions for how to do this are in the SOMObjects Base Toolkit User's Guide. On the Windows platform, the .def file created by the SOMObjects compiler only works for the VisualAge compiler. If you are working on the Windows platform and you are using the Visual C++ compiler, you may want to take these steps:
Because some parts on the Windows platform needed to be able to compile with both the VisualAge and the Visual C++ compilers, the build environment set the standard that .def files were used as input to the VisualAge compiler, and .mdf files were input to the Visual C++ compiler. The BuildLib rule discussed in the next section uses these conventions to build an export library.
Because of this manual editing that needs to be done for the .def file on the Windows platform for the Visual C++ compiler, Platform.mak has no rule to automate the creation of the $(Def) from the .idl.
BuildLib is not a rule, it is a macro that recursively calls the BuildLibRule. BuildLib is used as the rule in many of the part developers makefile to build the export library.
The sources to build the export library always include the list of object files. If you are using the VisualAge compiler, it also includes the $(Def) file. If you are using the Visual C++ compiler, it cannot include the $(Def) file. These are restrictions of the two compilers. If you are working on the Windows platform, and you want your code to be able to be compiled with both compilers, the following is an example:
$(ODSRC)/lib/iodbasec.$(LibSuffix) : \ !IFDEF CPPMAIN $(ODSRC)/lib/iodbasec.$(Def) \ !ENDIF $(bcpartObjList) @$(BuildLib)
BuildDll is not a rule, it is a macro that recursively calls the BuildDllRule. However, BuildDll is used as the rule in many of the part developers makefile to build the shared library.
BuildDllWithRes is not a rule, it is a macro that allows the developer to recursively call the BuildDllRule if the developer is building a Dll with a resource file.
Resource files only exist on the OS/2 and Windows platforms. They do not exist on the AIX platform. The rule exists on the AIX platform to aid in portability, but it does nothing different than the BuildDll macro would do.
On the platforms that support resource files, the developer must pass the resource file to the BuildDllRule in the RecFile macro. For example:
$(ODSRC)/dll/iodbasec.dll : $(ODSRC)/lib/iodbasec.$(Exp) \ $(bcpartLibList) \ $(bcpartObjList) @$(BuildDllWithRes) "RecFile=colordlg.res" BuildDllRuleThe resource file should have been compiled prior to the building of the Dll, which means the RcTargets macro should have been used to create the .res file from the .rc file.
There are times when the developer wants to clean the build tree, removing everything created by the build. The clean rules remove the generated files so the developer can build in a "clean" tree.
The easiest clean rule to use is Cleans. This rule removes the generated files in the current directory and in all subdirectories listed in the Subdirs macro.
In addition to the overall Cleans rule, there are rules to clean the files generated by each main rule. These rules are: