# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. Writing Library Makefiles in ON =============================== Introduction ------------ This document guides you through the gnarly process of writing library Makefiles for the ON consolidation. It assumes that you're comfortable with make(1) and are somewhat familiar with the ON Makefile standards outlined in /shared/ON/general_docs/make_std.txt. Makefile Overview ----------------- Your library should consist of a hierarchical collection of Makefiles: lib/<library>/Makefile: This is your library's top-level Makefile. It should contain rules for building any ISA-independent targets, such as installing header files and building message catalogs, but should defer all other targets to ISA-specific Makefiles. lib/<library>/Makefile.com This is your library's common Makefile. It should contain rules and macros which are common to all ISAs. This Makefile should never be built explicitly, but instead should be included (using the make include mechanism) by all of your ISA-specific Makefiles. lib/<library>/<isa>/Makefile These are your library's ISA-specific Makefiles, one per ISA (usually sparc and i386, and often sparcv9 and amd64). These Makefiles should include your common Makefile and then provide any needed ISA-specific rules and definitions, perhaps overriding those provided in your common Makefile. To simplify their maintenance and construction, $(SRC)/lib has a handful of provided Makefiles that yours must include; the examples provided throughout the document will show how to use them. Please be sure to consult these Makefiles before introducing your own custom build macros or rules. lib/Makefile.lib: This contains the bulk of the macros for building shared objects. lib/Makefile.lib.64 This contains macros for building 64-bit objects, and should be included in Makefiles for 64-bit native ISAs. lib/Makefile.rootfs This contains macro overrides for libraries that install into /lib (rather than /usr/lib). lib/Makefile.targ This contains rules for building shared objects. The remainder of this document discusses how to write each of your Makefiles in detail, and provides examples from the libinetutil library. The Library Top-level Makefile ------------------------------ As described above, your top-level library Makefile should contain rules for building ISA-independent targets, but should defer the building of all other targets to ISA-specific Makefiles. The ISA-independent targets usually consist of: install_h Install all library header files into the proto area. Can be omitted if your library has no header files. check Check all library header files for hdrchk compliance. Can be omitted if your library has no header files. _msg Build and install a message catalog. Can be omitted if your library has no message catalog. Of course, other targets (such as `cstyle') are fine as well, as long as they are ISA-independent. The ROOTHDRS and CHECKHDRS targets are provided in lib/Makefile.lib to make it easy for you to install and check your library's header files. To use these targets, your Makefile must set the HDRS to the list of your library's header files to install and HDRDIR to the their location in the source tree. In addition, if your header files need to be installed in a location other than $(ROOT)/usr/include, your Makefile must also set ROOTHDRDIR to the appropriate location in the proto area. Once HDRS, HDRDIR and (optionally) ROOTHDRDIR have been set, your Makefile need only contain install_h: $(ROOTHDRS) check: $(CHECKHDRS) to bind the provided targets to the standard `install_h' and `check' rules. Similar rules are provided (in $(SRC)/Makefile.msg.targ) to make it easy for you to build and install message catalogs from your library's source files. To install a catalog into the catalog directory in the proto area, define the POFILE macro to be the name of your catalog, and specify that the _msg target depends on $(MSGDOMAINPOFILE). The examples below should clarify this. To build a message catalog from arbitrarily many message source files, use the BUILDPO.msgfiles macro. include ../Makefile.lib POFILE = libfoo.po MSGFILES = $(OBJECTS:%.o=%.i) # ... $(POFILE): $(MSGFILES) $(BUILDPO.msgfiles) _msg: $(MSGDOMAINPOFILE) include $(SRC)/Makefile.msg.targ Note that this example doesn't use grep to find message files, since that can mask unreferenced files, and potentially lead to the inclusion of unwanted messages or omission of intended messages in the catalogs. As such, MSGFILES should be derived from a known list of objects or sources. It is usually preferable to run the source through the C preprocessor prior to extracting messages. To do this, use the ".i" suffix, as shown in the above example. If you need to skip the C preprocessor, just use the native (.[ch]) suffix. The only time you shouldn't use BUILDPO.msgfiles as the preferred means of extracting messages is when you're extracting them from shell scripts; in that case, you can use the BUILDPO.pofiles macro as explained below. To build a message catalog from other message catalogs, or from source files that include shell scripts, use the BUILDPO.pofiles macro: include ../Makefile.lib SUBDIRS = $(MACH) POFILE = libfoo.po POFILES = $(SUBDIRS:%=%/_%.po) _msg := TARGET = _msg # ... $(POFILE): $(POFILES) $(BUILDPO.pofiles) _msg: $(MSGDOMAINPOFILE) include $(SRC)/Makefile.msg.targ The Makefile above would work in conjunction with the following in its subdirectories' Makefiles: POFILE = _thissubdir.po MSGFILES = $(OBJECTS:%.o=%.i) $(POFILE): $(MSGFILES) $(BUILDPO.msgfiles) _msg: $(POFILE) include $(SRC)/Makefile.msg.targ Since this POFILE will be combined with those in other subdirectories by the parent Makefile and that merged file will be installed into the proto area via MSGDOMAINPOFILE, there is no need to use MSGDOMAINPOFILE in this Makefile (in fact, using it would lead to duplicate messages in the catalog). When using any of these targets, keep in mind that other macros, like XGETFLAGS and TEXT_DOMAIN may also be set in your Makefile to override or augment the defaults provided in higher-level Makefiles. As previously mentioned, you should defer all ISA-specific targets to your ISA-specific Makefiles. You can do this by: 1. Setting SUBDIRS to the list of directories to descend into: SUBDIRS = $(MACH) Note that if your library is also built 64-bit, then you should also specify $(BUILD64)SUBDIRS += $(MACH64) so that SUBDIRS contains $(MACH64) if and only if you're compiling on a 64-bit ISA. 2. Providing a common "descend into SUBDIRS" rule: $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: 3. Providing a collection of conditional assignments that set TARGET appropriately: all := TARGET= all clean := TARGET= clean clobber := TARGET= clobber install := TARGET= install The order doesn't matter, but alphabetical is preferable. 4. Having the aforementioned targets depend on SUBDIRS: all clean clobber install: $(SUBDIRS) The `all' target must be listed first so that make uses it as the default target; the others might as well be listed alphabetically. As an example of how all of this goes together, here's libinetutil's top-level library Makefile (license notice and copyright omitted): include ../Makefile.lib HDRS = libinetutil.h HDRDIR = common SUBDIRS = $(MACH) $(BUILD64)SUBDIRS += $(MACH64) all := TARGET = all clean := TARGET = clean clobber := TARGET = clobber install := TARGET = install .KEEP_STATE: all clean clobber install: $(SUBDIRS) install_h: $(ROOTHDRS) check: $(CHECKHDRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include ../Makefile.targ The Common Makefile ------------------- In concept, your common Makefile should contain all of the rules and definitions that are the same on all ISAs. However, for reasons of maintainability and cleanliness, you're encouraged to place even ISA-dependent rules and definitions, as long you express them in an ISA-independent way (e.g., by using $(MACH), $(TARGETMACH), and their kin). (TARGETMACH is the same as MACH for 32-bit targets, and the same as MACH64 for 64-bit targets). The common Makefile can be conceptually split up into four sections: 1. A copyright and comments section. Please see the prototype files in usr/src/prototypes for examples of how to format the copyright message properly. For brevity and clarity, this section has been omitted from the examples shown here. 2. A list of macros that must be defined prior to the inclusion of Makefile.lib. This section is conceptually terminated by the inclusion of Makefile.lib, followed, if necessary, by the inclusion of Makefile.rootfs (only if the library is to be installed in /lib rather than the default /usr/lib). 3. A list of macros that need not be defined prior to the inclusion of Makefile.lib (or which must be defined following the inclusion of Makefile.lib, to override or augment its definitions). This section is conceptually terminated by the .KEEP_STATE directive. 4. A list of targets. The first section is self-explanatory. The second typically consists of the following macros: LIBRARY Set to the name of the static version of your library, such as `libinetutil.a'. You should always specify the `.a' suffix, since pattern-matching rules in higher-level Makefiles rely on it, even though static libraries are not normally built in ON, and are never installed in the proto area. Note that the LIBS macro (described below) controls the types of libraries that are built when building your library. If you are building a loadable module (i.e., a shared object that is only linked at runtime with dlopen(3dl)), specify the name of the loadable module with a `.a' suffix, such as `devfsadm_mod.a'. VERS Set to the version of your shared library, such as `.1'. You actually do not need to set this prior to the inclusion of Makefile.lib, but it is good practice to do so since VERS and LIBRARY are so closely related. OBJECTS Set to the list of object files contained in your library, such as `a.o b.o'. Usually, this will be the same as your library's source files (except with .o extensions), but if your library compiles source files outside of the library directory itself, it will differ. We'll see an example of this with libinetutil. The third section typically consists of the following macros: LIBS Set to the list of the types of libraries to build when building your library. For dynamic libraries, you should set this to `$(DYNLIB)' so that a dynamic library is built. If your library needs to be built as a static library (typically to be used in other parts of the build), you should set LIBS to `$(LIBRARY)'. However, you should do this only when absolutely necessary, and you must *never* ship static libraries to customers. ROOTLIBDIR (if your library installs to a nonstandard directory) Set to the directory your 32-bit shared objects will install into with the standard $(ROOTxxx) macros. Since this defaults to $(ROOT)/usr/lib ($(ROOT)/lib if you included Makefile.rootfs), you usually do not need to set this. ROOTLIBDIR64 (if your library installs to a nonstandard directory) Set to the directory your 64-bit shared objects will install into with the standard $(ROOTxxx64) macros. Since this defaults to $(ROOT)/usr/lib/$(MACH64) ($(ROOT)/lib/$(MACH64) if you included Makefile.rootfs), you usually do not need to set this. SRCDIR Set to the directory containing your library's source files, such as `../common'. Because this Makefile is actually included from your ISA-specific Makefiles, make sure you specify the directory relative to your library's <isa> directory. SRCS (if necessary) Set to the list of source files required to build your library. This defaults to $(OBJECTS:%.o=$(SRCDIR)/%.c) in Makefile.lib, so you only need to set this when source files from directories other than SRCDIR are needed. Keep in mind that SRCS should be set to a list of source file *pathnames*, not just a list of filenames. LDLIBS Appended with the list of libraries and library directories needed to build your library; minimally "-lc". Note that this should *never* be set, since that will inadvertently clear the library search path, causing the linker to look in the wrong place for the libraries. MAPFILES (if necessary) Set to the list of mapfiles used to link each ISA-specific version of your library. This defaults to `$(SRCDIR)/mapfile-vers' in Makefile.lib, so you only need to change this if you have additional mapfiles or your mapfile doesn't follow the standard naming convention. If you have supplemental ISA-dependent mapfiles that reside in the respective <isa> directories, you can augment MAPFILES like this: MAPFILES += mapfile-vers CPPFLAGS (if necessary) Appended with any flags that need to be passed to the C preprocessor (typically -D and -I flags). When compiling MT-safe code, CPPFLAGS *must* include -D_REENTRANT. When compiling large file aware code, CPPFLAGS *must* include -D_FILE_OFFSET_BITS=64. CFLAGS Appended with any flags that need to be passed to the C compiler. Minimally, append `$(CCVERBOSE)'. Keep in mind that you should add any C preprocessor flags to CPPFLAGS, not CFLAGS. CFLAGS64 (if necessary) Appended with any flags that need to be passed to the C compiler when compiling 64-bit code. Since all 64-bit code is compiled $(CCVERBOSE), you usually do not need to modify CFLAGS64. COPTFLAG (if necessary) Set to control the optimization level used by the C compiler when compiling 32-bit code. You should only set this if absolutely necessary, and it should only contain optimization-related settings (or -g). COPTFLAG64 (if necessary) Set to control the optimization level used by the C compiler when compiling 64-bit code. You should only set this if absolutely necessary, and it should only contain optimization-related settings (or -g). COMPATLINKS (if necessary) Set to a list of symbolic links that should also be provided for this library. Each should also have a target-specific assignment to COMPATLINKTARGET stating what the target of each link should be COMPATLINKS= usr/lib/libfoo.so $(ROOT)/usr/lib/libfoo.so := COMPATLINKTARGET= libbar.so COMPATLINKS64 (if necessary) As COMPATLINKS, above, for 64bit objects. Of course, you may use other macros as necessary. The fourth section typically consists of the following targets: all Build all of the types of the libraries named by LIBS. Must always be the first real target in common Makefile. Since the higher-level Makefiles already contain rules to build all of the different types of libraries, you can usually just specify all: $(LIBS) though it should be listed as an empty target if LIBS is set by your ISA-specific Makefiles (see above). Conspicuously absent from this section are the `clean' and `clobber' targets. These targets are already provided by lib/Makefile.targ and thus should not be provided by your common Makefile. Instead, your common Makefile should list any additional files to remove during a `clean' and `clobber' by appending to the CLEANFILES and CLOBBERFILES macros. Once again, here's libinetutil's common Makefile, which shows how many of these directives go together. Note that Makefile.rootfs is included to cause libinetutil.so.1 to be installed in /lib rather than /usr/lib: LIBRARY = libinetutil.a VERS = .1 OBJECTS = octet.o inetutil4.o ifspec.o ifaddrlist.o eh.o tq.o include ../../Makefile.lib include ../../Makefile.rootfs LIBS = $(DYNLIB) SRCDIR = ../common COMDIR = $(SRC)/common/net/dhcp SRCS = $(COMDIR)/octet.c $(SRCDIR)/inetutil4.c \ $(SRCDIR)/ifspec.c $(SRCDIR)/eh.c $(SRCDIR)/tq.c \ $(SRCDIR)/ifaddrlist.c LDLIBS += -lsocket -lc CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRCDIR) .KEEP_STATE: all: $(LIBS) pics/%.o: $(COMDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) include ../../Makefile.targ The mapfile for libinetutil is named `mapfile-vers' and resides in $(SRCDIR), so the MAPFILES definition is omitted, defaulting to $(SRCDIR)/mapfile-vers. Note that for libinetutil, not all of the object files come from SRCDIR. To support this, an alternate source file directory named COMDIR is defined, and the source files listed in SRCS are specified using both COMDIR and SRCDIR. Additionally, a special build rule is provided to build object files from the sources in COMDIR; the rule uses COMPILE.c and POST_PROCESS_O so that any changes to the compilation and object-post-processing phases will be automatically picked up. The ISA-Specific Makefiles -------------------------- As the name implies, your ISA-specific Makefiles should contain macros and rules that cannot be expressed in an ISA-independent way. Usually, the only rule you will need to put here is `install', which has different dependencies for 32-bit and 64-bit libraries. For instance, here are the ISA-specific Makefiles for libinetutil: sparc/Makefile: include ../Makefile.com install: all $(ROOTLIBS) $(ROOTLINKS) sparcv9/Makefile: include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) i386/Makefile: include ../Makefile.com install: all $(ROOTLIBS) $(ROOTLINKS) amd64/Makefile: include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) If you included Makefile.rootfs to install your library into /lib, you should also add $(ROOTCOMPATLINKS) and $(ROOTCOMPATLINKS64) to your install: target to install compatibility symlinks into /usr/lib. Observe that there is no .KEEP_STATE directive in these Makefiles, since all of these Makefiles include libinetutil/Makefile.com, and it already has a .KEEP_STATE directive. Also, note that the 64-bit Makefiles also include Makefile.lib.64, which overrides some of the definitions contained in the higher level Makefiles included by the common Makefile so that 64-bit compiles work correctly. CTF Data in Libraries --------------------- By default, all position-independent objects are built with CTF data using ctfconvert, which is then merged together using ctfmerge when the shared object is built. All C-source objects processed via ctfmerge need to be processed via ctfconvert or the build will fail. Objects built from non-C sources (such as assembly or C++) are silently ignored for CTF processing. Filter libraries that have no source files will need to explicitly disable CTF by setting CTFMERGE_LIB to ":"; see libw/Makefile.com for an example. More Information ---------------- Other issues and questions will undoubtedly arise while you work on your library's Makefiles. To help in this regard, a number of libraries of varying complexity have been updated to follow the guidelines and practices outlined in this document: lib/libdhcputil Example of a simple 32-bit only library. lib/libdhcpagent Example of a simple 32/64-bit library that obtains its sources from multiple directories. lib/nametoaddr/straddr Example of a simple loadable module. lib/libipmp Example of a simple library that builds a message catalog. lib/libdhcpsvc Example of a Makefile hierarchy for a library and a collection of related pluggable modules. lib/lvm Example of a Makefile hierarchy for a collection of related libraries and pluggable modules. Also an example of a Makefile hierarchy that supports the _dc target for domain and category specific messages. Of course, if you still have questions, please do not hesitate to send email to the ON gatekeepers.