Programming Guide: Building Sample Parts

# Programming Guide

## Building Sample Parts

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.

### The Build Environment

Here are some guidelines to follow in order to use the samples build environment:

- The OpenDoc 1.1 runtime must be installed.

- An appropriate compiler must be installed.

  - On the OS/2 platform, the Visual Age compiler must be installed.

  - On the Windows platform, you can use either the VisualAge or Visual C++ compiler however, more samples can be compiled with the VisualAge compiler.

  - On the AIX platform, the xlC compiler must be installed.

- On the AIX platform, if you plan to build helps, the ipfx.adt file must be installed. Besides the tools themselves, the build process uses the environment variables set by these components; xlC, ipfx.adt, VisualAge, etc. The environment variables set by the installation program must be available.

- When you build the samples, the utils directory must be built before you can build any other samples.

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:

- Optimized build

        set ICC = -O+ -Om-
        set ILINK = -NOBROWSE

- Debug build

        set ICC = -Ti+ -DDEBUG
        set ILINK = -DEBUG

#### The Windows platform

VisualAge Compiler options are as follows:

- Optimized build

        set ICC = -O+ -Om-
        set ILINK = -NOBROWSE

- Debug build

        set ICC = -Ti+ -DDEBUG
        set ILINK = -DEBUG

Visual C++ Compiler options are as follows:

- Optimized build

        set CL = -Ox
        set LINK =

- Debug build

        set CL = -Ge -Zi -DDEBUG
        set LINK = -DEBUG -DEBUGTYPE:BOTH

#### The AIX platform

xlC Compiler options are as follows:

- Optimized build

           export ICC = -O
           export ILINK =

- Debug build

           export ICC = -g -DDEBUG
           export ILINK =

### Part Developer's Makefile

Each part developer's makefile is divided into 3 sections:

- Macro definitions that determine what should be built
- The include of Platform.mak
- Simplified inference rules to build the targets

#### Macro Definitions

The following are macros that can be used in a part developer's makefile:

ExtraSOMCompileOptions | This macro lists any extra options passed to all calls of the SOMObjects compiler for this part.
ExtraCompileOptions | This macro lists extra options passed to all calls of the compiler for this part. If you want your code to be compilable on different compilers be very careful what you add. Flags mean different things to different compilers.
ExtraLinkOptions | This macro lists any extra options to be passed to all calls of the linker for this part. If you want your code to be linkable by different compilers be very careful what you add. Flags mean different things to different compilers.
Subdirs | This macro lists all subdirectories to be visited by the build process.
IdlTargets | This macro lists the idls in the local directory that needs to be shared with other part developers. The idls in this macro are copied to `ODSRC/include` and will have .xh headers generated.
HdrTargets | This macro lists the C++ headers in the local directory that needs to be shared with other part developers. All the headers in this macro are copied to `ODSRC/include`.
RcTargets | On platforms that have resources (OS/2 and Windows) this macro lists the resource script files that are input to the resource compiler.
MsgTargets | MsgTargets are language-specific targets that need to be built. They include message catalogs and help files because you may have a separate message catalog or help file for each language your part supports.
LibTargets | This is one of the few macros that requires a full path length. It specifies the full path length of every export library to be built.
DllTargets | This is one of the few macros that requires full path length. It specifies the full path length of every dll, or shared library, to be built.
RegeditTarget | This macro is only for the Windows platform. Some dlls may need to be registered with the Registry Editor. If so, specifying the registry input file in this target will mean the regedit tool is called when the ForceRegistry target is used. You do not need to use the RegeditTarget macro if you are building an OpenDoc part (that is, you have the series of Reg* and Files* macros set), and if you have an associated typelib (you have the Ctypelib* macros set). Your part will be registered through the generated typelib when the ForceRegistry target is used.

The following macros build a typelib for the Windows platform only. Note that they must be used concurrently.

CtypelibIDL | This macro specifies the idl that must be registered in the SOMIR environment variable before the typelib can be built.
CtypelibDir | This macro specifies the directory where you are building the typelib.
CtypelibCmdLineArgsFile | This macro specifies the file that contains the rest of the command line arguments for the ctypelib command. The -t flag is set using the CtypelibDir macro since you cannot specify a relative path in a command line arguments file. The rest of the arguments to ctypelib are in this file.

##### 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 Include of Platform.mak

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
      !ENDIF

Delineate macros you want set on the Windows NT platform with:

      !IF "$(OS)"=="Windows_NT"
      !ENDIF

The Windows 95 platform can be referenced by:

      !IFNDEF OS2_SHELL
      !IF "$(OS)"!="Windows_NT"
      !ENDIF
      !ENDIF

#### Simplified Inference Rules

At the bottom of each part developer's makefile are the simplified inference rules. Typically, they consist of:

- Inference rules that provide the build process with what sources are needed by the targets.

- Macros set and used only by the local makefile that supplies the list of object files or library lists.

- Build rules for each target.

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.xih

Files 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".

### Platform.mak

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:

- The top of the file contains the macros that are used to reset the environment variables.

- General use macros are next.

- Then the default target and its corresponding rules are defined.

- Followed by the rest of the rules.

- Ending with the clean rules.

Considering the smallest Platform.mak file is over 700 lines long, a lot occurs in each of these sections.

#### Environment Variable Macros

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

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.

#### Default Target and Its Corresponding Rules

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.

- Idl

  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.

- Hdr

  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:

  - It creates the message catalogs and helps that are listed in MsgTargets. This is done first because the creation of the message catalog could create a header that needs to be copied to $(ODSRC)/include. If you want such a header copied, it needs to be listed in the HdrTargets macro.

  - It copies the files listed in the HdrTargets macro to $(ODSRC)/include.

  - On the OS/2 and Windows platforms, it runs the resource compiler on the files listed in the RcTargets macro.

  - It creates the .xh files in $(ODSRC)/include from the files listed in the IdlTargets macro.
  subdirectories listed in the Subdirs macro.

- Lib

  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.

- Dll

  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.

#### Remaining Rules

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.

##### ForceRegistry

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.

##### Pkg

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

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

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:

1. Use the SOMObjects compiler to create your .def file.
2. Remove the underscore "_" in front of each export.
3. Remove the "@8" at the end of each NewClass.
4. Rename or copy the new .def file to an .mdf file.

This should change your .def file into one that will work with the Visual C++ compiler.

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

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

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

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" BuildDllRule

The 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.

#### Clean Rules

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:

- CleanIdl
- CleanHdr
- CleanLib
- CleanDll
- CleanPkg

The Cleans rule calls all of these specific clean rules.

[ Top | Previous | Next | Contents | Index | Documentation Homepage ]