CONFIGURING AND BUILDING Mach 3.0 --------------------------------Michael N. Condict OSF Research Institute Grenoble, France Introduction -----------Mach is a general-purpose, portable operating system developed as a research project by Carnegie Mellon University (CMU). It provides full support for multi-processors that use shared memory. It also provides networking operations and support for writing distributed applications, although it is not a distributed operating system. At its center is a message-passing kernel that supports the client-server paradigm, and makes heavy use of shared virtual memory to facilitate communication between tasks. Surrounding this core is an emulation of BSD UNIX, providing a versatile and familiar environment for programmers and users. The emulation uses the original AT&T and U.C. Berkeley source code and is complete enough to run executable files compiled and linked under BSD UNIX (on a VAX). In previous versions of Mach, the UNIX emulation took place inside the Mach kernel, just as in an ordinary implementation of UNIX, although care was taken to keep this separate from the native Mach functionality. Mach version 3.0 has had the UNIX emulation removed from the kernel and implemented in a user-mode server. Because the kernel is small (not including device drivers) and contains only a basic set of memory-management, process-management and IPC functions, it is called a "micro-kernel". Programs compiled for the previous version of Mach, 2.5, will continue to run under 3.0, because the UNIX system calls, after trapping into the micro-kernel, are redirected to functions in an emulator library that is mapped into each UNIX process. These functions, in turn, send messages to the UNIX server to carry out requests that cannot be fully implemented in the emulator function itself. It would be a little faster, of course, to invoke the emulator functions directly from the user code, but the processing of the trap to kernel mode is efficient enough that this is not a performance problem. In practice, this means that Mach 2.5 and Mach 3.0 can co-exist in the same root file system, sharing a single set of utility programs. The user can choose either of the two kernels at boot time. The only noticable difference is that when Mach 3.0 is booted, "ps" will report an extra process (the UNIX server, which has UNIX process id 0). Directory Structure and Contents -------------------------------- The source distribution consists of two directory trees, which must be placed side by side in the same parent directory (although this restriction can be removed by modifying a few "make" variables in the source files). The first directory tree is "mk". It contains the micro-kernel itself, the object library for user programs to call kernel functions, and header files that allow user programs to interface to this library. There is also a C-based threads package (lightweight processes), built on top of the kernel threads package. It provides lighter-weight processes than the threads managed by the kernel, because multiple C threads are implemented with fewer kernel threads. Thus, context switching between two C threads that are running in the same kernel thread can take place without any traps to the kernel. The other directory tree, "ux", contains the UNIX server and the emulator library. Both the emulator and the server are ordinary user programs to the Mach 3.0 micro-kernel. Each one has a "main" function which is invoked once upon boot-strap of the UNIX emulation. (This happens automatically as part of the Mach kernel bootstrap.) While it is obvious that the UNIX server should be a program, it may not be clear why the emulation library is also. Suffice it to say that the UNIX server, as part of its bootstrap, invokes the emulator program, which then invokes the first real UNIX process. The functions of the emulator are left mapped into the memory of this and all subsequent UNIX processes. The top-level structure of both trees is similar. The source files are in a completely separate subtree from the object files, executable files and other products of the build. There is a separate object subtree for each machine type. These top level directories (showing just two machine types -- vax and i386) are: mk/src/latest /obj/i386_mach/latest /obj/vax_mach/latest . . . /release ux/src/latest /obj/i386_mach/latest /obj/vax_mach/latest . . . The "latest" directories have no sibling directories, since only the latest version of Mach is normally present when the system is distributed. They can in principle be ignored, treating "src/latest" or "i386_mach/latest" as a single directory name for the purpose of comprehension. Thus, for example, the top-level source directory in both trees is the "src/latest" directory, containing a Makefile that can be used to construct all of the objects in the corresponding "obj" directories. Note that there is an extra directory, "release", under the mk tree. This subtree contains symbolic links to those header files, libraries and other objects of the micro-kernel that are intended to be made public to Mach user programs (the files themselves are under the "obj" and "src" directories). The C source files of the server and emulator include header files from this subtree. This is the only reference that exists between the two distribution trees (and it explains why the "mk" directory must be at a known location with respect to the "ux" tree). Warning: These symbolic links are a potential source of trouble. After copying or moving the kernel distribution tree all such links must be removed or altered in the resulting tree, because they are specified as absolute path names, hence they point into the original "mk" tree, not into the copy or renamed tree. If removed, they will be reconstructed automatically during the build of the kernel. Any file under "obj" named Makedep (e.g. mk/obj/i386_mach/latest/user/threads/Makedep) may also contain pathnames referring to the original kernel tree and should be edited or removed. Source Directories Under "mk/src/latest" there are two directories, "kernel" and "user". The first contains the source for the kernel itself and the other contains the source for the user object library (in "user/libmach") and the C-threads package (in "user/threads"). Similarly, under "ux/src/latest" there are two directories, "emulator" and "server", with the obvious contents: mk/src/latest/kernel /user/libmach /user/threads ux/src/latest/server /emulator The structure of the "mk/src/latest/kernel" and "ux/src/latest/server" directories are similar to one another, which is not surprising given the fact that they used to be a single directory in Mach 2.5. (In fact, many subdirectories under these two directories have the same names as, and contents similar to, directories in the Mach 2.5 "/usr/src/sys" directory.) Both contain a subdirectory, "conf", which contains files for configuring and building the micro-kernel and the UNIX server. This is a complex process that involves at least three intermediate steps (see "Configuring" and "Building"). The most important thing to understand about the process is that all the original files that constitute its input are in "conf". Any files found under "obj", even "Makefile"s, are automatically constructed products, hence they should not be edited to change the configuration. Also under both directories is a source directory for building a "config" program, "src/latest/{kernel,server}/src/config". The two versions of this program, which reads configuration files and outputs Makefiles and header files under the "obj" directories, are almost identical, but are not interchangeable. The program is built automatically under the "obj" directories as part of the normal build process. Note: the use of "src/config" instead of just "config" as the directory name is a historical artifact. As with "src/latest", the path "src/config" can be thought of as a single directory name for the purpose of comprehension. Machine-Specific Directories In various places under both the "mk" and the "ux" tree, there are collections of directories with names indicating a machine type, such as "vax", "mips" or "i386". These contain machine-specific files. In the case of object files, they have been compiled for that machine; with source files, they represent pieces of the system that must use different code on different architectures. Wherever there are two directories "xxx" and "xxx_mach", side by side, one is a symbolic link to the other. The two exist simply because different parts of the configuration/build mechanism use different conventions for machinetype names. In these places there will also sometimes be an entry called "machine" that is a symbolic link to the name "@sys". To understand this it is necessary to know that Mach (including version 2.5) will, during the evaluation of a pathname, translate any "@sys" component to "xxx_mach", where "xxx" is the current machine type. So for example, there is a symbolic link entry: mk/src/latest/kernel/mach/machine -> @sys and this pathname, when evaluated on a VAX, is equivalent to the path: mk/src/latest/kernel/mach/vax_mach The symbolic link is not necessary in order for the "@sys" translation to take place. It merely provides the word "machine" as a synonym for "@sys" (don't ask why). Object Directories Note: In this section pathnames will be shown using the "@sys" notation described above. Recall that "@sys" maps to "xxx_mach", where "xxx" is the type of machine which is evaluating the pathname. The two object directories of most importance to the configuration are the ones where the Mach kernel and the UNIX server are compiled and linked: mk/obj/@sys/latest/kernel/$(CONFIG) and ux/obj/@sys/latest/server/$(CONFIG) The $(CONFIG) variables above denote directory names that are automatically chosen from the configuration parameters selected when the kernel or server is built. This is so that different configurations can be built in different subdirectories under the "kernel" or "server" directories. Usually, both directories will have names of the form "STD*WS", but keep in mind that the server configuration is independent of the kernel configuration and the two directories do not, in general, have the same name. for more information. See "Configuring" In addition to object files and the target executable file, these two directories each contain a set of files generated by the configuration process, and the two sets are similar. There is a "Makefile", and it is the one that is ultimately used to carry out the compilation and linking of the kernel and server. It is not possible, however, for a user to "cd" to this directory and invoke "make" directly, without intimate knowledge of the internals of the configuration/build system (see "Configuring" and "Building"), and this is not recommended. There are also a number of one-line header files, each containing a single C-preprocessor definition. These files, together with "-D" flags appearing in the Makefile and the selection of a particular subset of the C source files for compilation, are the result of selecting a configuration. Other major object directories are "mk/obj/@sys/latest/user/libmach" (the mach object library), "mk/obj/@sys/latest/user/threads" (the C-threads object library) and "ux/obj/@sys/latest/emulator" (the emulator program). The pathnames of the major products resulting from the build are: mk/obj/@sys/latest/kernel/$(CONFIG)/mach_kernel mk/obj/@sys/latest/user/libmach/libmach_sa.a mk/obj/@sys/latest/user/threads/libthreads.a ux/obj/@sys/latest/server/$(CONFIG)/vmunix ux/obj/@sys/latest/emulator/emulator The "mach_kernel" and "vmunix" files are also hard-linked to longer file names that contain version information as part of the name. Installation of Object Files As described above, there are three executable files that result from the compilation process: the kernel, the server and the emulator. Each of these must be copied to the right place on the root disk in order to bootstrap the newly compiled server and kernel. These places are shown below: mk/obj/@sys/latest/kernel/$(CONFIG)/mach_kernel -> /mach ux/obj/@sys/latest/server/$(CONFIG)/vmunix -> /mach_servers/startup ux/obj/@sys/latest/emulator/emulator -> /mach_servers/emulator When the system is booted, it prompts for the Mach kernel name, and only defaults to "/mach" if nothing is typed before a short timeout. The other two pathnames are compiled into the bootstrap code for the kernel and the UNIX server, and cannot be altered without changing the code. The Mach kernel invokes "/mach_servers/startup" (the UNIX server) at the end of its bootstrap. The server then invokes /mach_servers/emulator as the first real UNIX process. Configuring ----------In this manual, configuring the micro-kernel or the UNIX server refers to the selection of a subset of the source files for compilation and inclusion in the executable file, and the definition of a set of C preprocessor definitions to control conditional compilation of code in the source files. The process is necessary in order to tailor the system for a particular set of hardware devices, or for the optional inclusion of various modules and functionality, such as the Andrew File System (AFS) or the built-in kernel debugger. Although the kernel and the server are configured independently, the mechanisms are so similar that they will be described together here. Note: Throughout this manual, there are references to a $(CONFIG) variable and a $(CONFIG) directory. This is because "CONFIG" is the name of the make variable whose definition selects a configuration, and its value is the name of the object directory within which the configured kernel or server is built, specifically: obj/@sys/latest/{kernel,server}/$(CONFIG) The default value for this variable is of the form "STD*", but it varies from machine to machine. On i386 machine types it is "STD+WS" in the kernel and "STDVICE+WS" in the server. It is difficult to describe how the configuration of the kernel and server is accomplished, without describing the mechanism by which the configuration files are processed. That is, there is not so much a well-defined configuration language as there is a set of input, output and intermediate files of the configuration process. We start by describing the contents of the input files. They are: src/latest/{kernel,server}/conf/MASTER /MASTER.local /MASTER.$(cputype) /MASTER.$(cputype).local /files /files.$(cputype) where $(cputype) is the type of machine, e.g. "i386" or "vax". MASTER* Files The MASTER* files define the set of possible configurations and the set of words that can be used in the definition of the $(CONFIG) variable to select one of these possibilities. They also specify which binary cpp options should be turned on in each configuration, the value that various numeric parameters should have, and other aspects of the configuration. The four MASTER* files are concatenated together before processing. Any of them are allowed to be empty, but they must exist. As distributed, the intention is that the machine- and host-independent entries are in the MASTER file, any machine-dependent entries are in the MASTER.$(cputype) file, and any entry that is specific to the local host or site is put in the appropriate file that has ".local" appended to its name. Other than this, there is a single set of syntax rules and semantics that apply to the collective contents of these files. The collective MASTER file input is little more than a template for producing the "real" configuration file, namely: obj/@sys/latest/{kernel,server}/$(CONFIG)/$(CONFIG) More precisely, it contains all the entries of any possible configuration file. In addition, it contains information hidden in the comments of the configuration-file language. These comments define the set of words that can be concatenated together (with "+" as a separator) in the definition of $(CONFIG), to specify any particular configuration. The comments also describe which subset of lines from the MASTER files go in the real configuration file, for any particular definition of $(CONFIG). A csh script ("conf/mkconfig.csh") reads the MASTER* files, processing the special comments, and produces the configuration file as output. As in Makefiles and shell scripts, comments begin with a "#" and continue to the end of the line. There are two types of comments that have meaning to the configuration shell script. The first of these should always appear on a line that has an entry intended to be optionally included in the configuration file. An example is: options MACH_NBC # No buffer cache # <nbc> The string "<nbc>" appearing in the comment causes the non-comment portion of this line to be included in the configuration file if and only if the word "nbc" appears as one of the "+"-separated words in the $(CONFIG) variable definition. Such words are called configuration attributes, not to be confused with the configuration options, such as "MACH_NBC". A set of attributes is used to select the set of option definitions and other parameter definitions that appear in the configuration file. After this is done, the attribute names play no further role in the configuration. The second type of comment appears on a line by itself and is of the form: # STD = [debug mach unix small MSTD nfs afs ] This is an alias definition. It says that, if "STD" appears in the definition of $(CONFIG), it should be replaced with: debug+mach+unix+small+MSTD+nfs+afs This process is recursive, so the aliased name "MSTD" would in turn be replaced by its definition. Note that the alias names must be in upper case to distinguish them from the lower-case configuration attributes for the rudimentary tool that processes the MASTER files. Configuration File After mkconfig.csh produces the configuration file, it is processed by the config program (obj/@sys/latest/server/bin/config), along with the files* files, to produce the Makefile and other final products of the configuration. There are several types of entries that can appear in a configuration file. Each must be on a separate line. The most important is the one that turns on an option: options FLUB This tells the config program to arrange that some or all source files are compiled with the cpp symbol "FLUB" defined to be 1. To do so, it will use either a "-D" flag or a "#define" statement in a generated header file, depending on the contents of the files* files. It can also result in certain optional source files being included in the program (see below). Some parameters cannot be described as a single binary option, rather it is desired to specify a quantity of something. This is done with a "pseudo-device" entry. A good example is the definition of the number of pseudo-ttys: pseudo-device pty 80 This line causes a header file "pty.h" to be created in the $(CONFIG) directory with the contents: #define NPTY 80 Note that the name is in upper case, unlike the lower-case pseudodevice name, and has the letter "N" prefixed. As described below, the pseudo-device name may also be used in an "optional" clause in the files* files, to include a source file only when the pseudo-device is present. In the configuration file for the UNIX server only, there are several special entries defining parameters that are built in to the config program. These get translated into a single header file called "confdep.h". For example, the entries: timezone maxdsiz maxusers 5 dst 33554432 16 defining the time zone, maximum virtual memory usage, and maximum number of users, produce the following contents in confdep.h: #define #define #define #define TIMEZONE 300 MAXUSERS 16 DST 1 MAXDSIZ 0x2000000 Finally, a line of the form: config mach_kernel swap generic specifies, among other things, that the name of the executable file should be "mach_kernel". Files* Files The files* files (pardon the expression) are concatenated together before processing, just like the MASTER files. The one with the "$(cputype)" suffix is intended to contain entries specific to that machine type. There are no ".local" versions of these files. These files control two things: first, it can be specified that some or all of the binary options (whether turned on in the configuration or not) should be implemented by creating a one-line include file in the $(CONFIG) directory. This is done with lines that look like: OPTIONS/simple_clock optional simple_clock For reasons that are not worth describing, the name of the option must appear twice, in lower case (unlike in the MASTER file, where it must be in upper case). This example causes the config program to create a header file named "simple_clock.h". Its contents (assuming that the option "simple_clock" is selected in the configuration file) are: #define SIMPLE_CLOCK 1 The definition is 0, if the option is not selected. Any option selected by the MASTER files that does not appear in a line like this will turn into a "-D" flag for the C compiler. An important difference between the two methods is that implementing the option in a header file requires C source files to use a "#include" statement to control the conditional compilation. The advantage of this requirement is that the dependency of the C files on this header file will be known to the make command, so changing the value of the option (hence the contents of the header file) will automatically cause recompilation of all files that depend on the value. This is not true for the options that turn into "-D" flags. The other thing controlled by these files is selection of the set of source files to be compiled for inclusion in the target program. This is accomplished with lines of the following form: bsd/kern_proc.c bsd/quota_kern.c standard optional quota bsd/sys_process.c optional not mach The first says that file "bsd/kern_proc.c" (specified relative to the "src/latest/server" directory) should always be compiled into the server, regardless of any options. The second line says that file "bsd/quota_kern.c" should only be included if option "QUOTA" is selected. Finally, the third line says that file "bsd/sys_process.c" should be included only if the option "MACH" is not selected. Again note that the option names are specified here in lower case, although they are upper-case in the MASTER files and as cpp symbols in the generated header files. Depending on local requirements, it may be necessary to manually edit some of the configuration input files in the src directories, in order to obtain the desired configuration. In particular, this will always be true if new source files or conditional compilation options are to be added. The best advice that can be given is that the changes should be made in the appropriate MASTER* file and/or file* file, according to whether they are machine- or site-specific, and they should of course follow the syntax and semantic rules outlined above. Step-by-Step Instructions The sequence of steps in the configuration process is summarized below. All but the first is performed automatically as part of the build process, assuming the default configuration is being used. Step 2 can be invoked manually in order to build a non-default configuration. 1) If necessary, one or more of the MASTER* or files* files in the src/latest/{kernel,server}/conf directory can be edited, to add new options or configuration attributes or to change the set of source files. 2) Make is invoked in the src/latest/{kernel,server} directory, with an optional argument of the form CONFIG=xxx+xxx... where each xxx is one of the configuration attributes, or an upper-case alias for a set of attributes. It ensures that the config program is compiled and up to date and that the $(CONFIG) directory exists and has the appropriate initial contents. 3) The mkconfig.csh script reads the MASTER files and, based on the value of $(CONFIG), puts a subset of the input lines, with comments stripped, into a newly created configuration file. This file is $(CONFIG)/$(CONFIG). 4) The config program reads the configuration file and the files* files and produces: a Makefile with the specified source files and "-D" compiler flags; a set of 1-line header files for the options; and, in the UNIX server configuration, a confdep.h file, defining the special parameters. At this point compilation of the kernel begins, as described in the next section. Building -------Step-by-Step Instructions To build both distribution trees from scratch is simple, if tedious, in the absence of errors. The steps are: 1) Go to the parent directory of "mk" and "ux" and ensure that the follow directories exist and are empty: mk/obj/@sys/latest/kernel, mk/obj/@sys/latest/user, mk/release/@sys/latest/include, mk/release/@sys/latest/lib, mk/release/@sys/latest/bin, ux/obj/@sys/latest/server, ux/obj/@sys/latest/emulator 2) Tell the Makefiles what type of machine you are building a system for (xxx must be one of I386, VAX, SUN3, PMAX): setenv TARGET_MACHINE xxx If the target machine is a 386, the following bug work-around may also be necessary: setenv I386_REG_EXP '*' 3) To build the kernel: cd mk/src/latest; make This will invoke make on the Makefiles in the subdirectories in the right order and with the proper arguments. 4) To build the server: cd ux/src/latest; make This will also invoke make recursively, just as for the kernel. To build just the Mach kernel or just the UNIX server: cd mk/src/latest/kernel; make CONFIG=... or: cd ux/src/latest/server; make CONFIG=... The CONFIG argument is only necessary to build a non-default configuration (see "Configuring", for legal values). Note that the kernel must be built before the server, since source files under ux include header files from mk/release (among other reasons). After an initial build of the system, only steps 2-4 are necessary to rebuild after a change to a source file (or to build a new configuration). But if either of the two directory trees is moved to a new location, or a copy of one or both is made in a new location, it is necessary to delete all the target files under the obj and release directories and rebuild from scratch. This is because there are symbolic links under the release directory and Makedep files under the obj directory that still refer to the old pathnames. To rebuild just a specified set of object files in the kernel or server, one of the following will suffice. Note that this will not link a new version of the kernel or the server. cd mk/src/latest/kernel; make MAKE_ARGS="file1.o file2.o . . ." cd ux/src/latest/server; make MAKE_ARGS="file1.o file2.o . . ." Warning: all make commands are to be invoked from src directories. It is possible (but not straightforward) to successfully invoke make on the Makefile in one of $(CONFIG) directory. It is certainly not recommended, since the methods for doing so require internal knowledge of the configuration/build system and may change from time to time. If a make command is interrupted or fails before it runs to completion, it may in certain cases leave some of the target directories in an inconsistent state, after which subsequent make commands will fail. This can usually be resolved by removing the target files that make was most recently working on at the time of interruption. (Use "ls -lt" in the relevant target directory, to find the most recent files.) If all else fails, remove everything from the obj and release directories and start over again using the 4 steps above. Build Internals The mechanism of building and rebuilding the system depends intimately on CMU's extended version of the make command (the relevant extensions are documented in the next section, and nowhere else, as far as we know). In the kernel, as well as the server, all the Makefiles and the Makeconf files they include are in the src tree (with one exception). The complete set of src Make* files is: mk/src/latest/Makeconf ux/src/latest/Makeconf /Makefile /Makefile /include/Makeconf /emulator/Makeconf /Makefile /Makefile /kernel/Makeconf /server/Makeconf /Makefile /Makefile /src/config/Makefile /src/config/Makefile /mach_debug/Makefile /src/mig/Makefile /vax/inline/Makefile /user/Makeconf /Makefile /libmach/Makefile /threads/Makefile The one exception is the Makefile that is in the $(CONFIG) directories. This one is automatically generated by the configuration from a file "Makefile.template" in the src tree. It is intended to be used only by a make command generated from the Makefile in "src/latest/{kernel,server}". Among other requirements, it must be used in conjunction with the "make" shell script that is also generated from a template and located in the $(CONFIG) directory. The contents of the $(CONFIG)/Makefiles reflect several aspects of the configuration. There are variables defining the list of source files and corresponding object files that were selected and this controls what gets compiled and linked into the kernel or server: CFILES=\ $(SOURCE_DIR)/bsd/cmu_syscalls.c \ $(SOURCE_DIR)/bsd/init_sysent.c \ $(SOURCE_DIR)/bsd/kern_acct.c \ . . . OBJS=cmu_syscalls.o init_sysent.o kern_acct.o kern_descrip.o \ kern_mman.o kern_proc.o kern_prot.o kern_resource.o \ . . . Also, there is a variable defining a set of "-D" flags for the C compiler, determined by the set of options that were turned on in the $(CONFIG)/$(CONFIG) file but not put in a header file: IDENT=-DCMU -DINET -DBSD_A_OUT Every selected option will appear either in the Makefile or in a ".h" file bearing the name of the option (see "Configuring"). The $(CONFIG)/Makefile includes the Makedep file in the same directory (using make's "-include" statement -- see "CMU Make Enhancements"). This file contains a complete list of the header file dependencies for all the C source files, including the configuration-generated header files in the $(CONFIG) directory. A typical fragment of this file (in the server $(CONFIG) directory) is; # Dependencies for File: block_io.o: block_io.o: ../../../../../src/latest/server/machine/machparam.h block_io.o: ../../../../../src/latest/server/sys/buf.h block_io.o: ../../../../../src/latest/server/sys/errno.h . . . block_io.o: /usr/src/mk/release/@sys/latest/include/cthreads.h . . . Makedep is automatically constructed by the "-MD" flag of the C compiler and the subsequent use of the "md" command whenever a C compilation takes place. The ".EXIT" rule ensures that the Makedep file is brought up to date at the end of each use of make in this directory. However, it must be noted that since this file is both an input to make and part of the output of make, its contents at the beginning of a make command are not necessarily up to date. That is, after adding an "#include" statement to a C source file, the new dependency will not appear in the Makedep file until *after* the make command that causes the compilation of this source file. This should not be a problem in practice, because the updating of the C source file is enough to cause its recompilation. The Makefiles and Makeconf files under "mk/src/latest/kernel" and under "ux/src/latest/server" implement two source trees (rooted at these two directories), in the sense described under "CMU Make Enhancements", below. The Makefiles under each of these source trees share a common Makeconf file in the root directory of each tree. This file defines OBJECTDIR to be the appropriate pathname relative to the directory containing the Makeconf file: OBJECTDIR = ../../../obj/@sys/latest/{kernel,server} This explains why the corresponding object directories do not have to be manually created -- they are automatically created by make, as necessary. There are several other interesting definitions in the Makeconf file, including the default definition of the configuration variable, $(CONFIG), and the location of the "mk/release" directory, relative to the "ux" directory (this is what needs to be changed if the mk and ux directories are not together in the same parent directory). CMU Make Enhancements --------------------In cases where the build process goes wrong, it will be helpful to know about some of the changes that CMU has made in their version of the "make" command, so that the contents of the Makefiles can be analyzed. Here is a summary of the relevant differences, all of which are used in one or more Makefiles or Makeconf files. (Note: "evaluation of the Makefile", below, refers to the initial parsing of the entire file, in order to define variables and construct the dependency graph. It takes place before any shell commands are invoked to bring targets up to date.) o Before evaluating the Makefile, make will search upward in the directory tree, starting at the current directory, for a file named "Makeconf". If found, it will be evaluated as though its contents occurred at the top of the Makefile (except note the special processing of the OBJECTDIR variable, below). Only the first such file that is found will be evaluated, so the search can be terminated at any level by putting an empty Makeconf file in one of the searched directories (empty files have no effect when evaluated). o In addition to the implicit "include" of the Makeconf file, there is an explicit include statement. A line of the form "-include file" may appear anywhere in the Makefile, and the contents of the specified file will be evaluated at the point where the line is encountered. o This version of make supports the organization of a software project into completely separate source and object directory trees. The source tree contains only files that are not modified or created by the process of running make, and all file creation and modification takes place under the object tree. Furthermore, it is assumed that both trees have the same structure, that is, that each subdirectory under the root source directory has a counterpart with the same name under the object directory. The mechanism is that the user may define two make variables, OBJECTDIR and SOURCEDIR whose values are pathnames that specify the object directory and the source directory, respectively. Since it is expected that the make command will usually be invoked on a Makefile in the source directory tree, SOURCEDIR defaults to the current directory if not specified. In the simplest case, OBJECTDIR is specified in the Makefile of the corresponding source directory, which is where make is invoked. Make will do a "cd" command to the object directory before invoking any of the shell commands. It will also modify any source file name appearing explicitly in the shell commands by prefixing the source directory, so that they can be found by the command. It will not do this for any files it recognizes as target files of the Makefile. Thus all target files that are specified as simple file names will be created in the object directory. Warning: make cannot know what source files might be read implicitly by the shell commands, and could not do anything about it in any case. Thus, shell commands that implicitly read relative pathnames, will look for them relative to the object directory, not the source directory. Since it is not desirable to specify OBJECTDIR redundantly in each Makefile of the source directory tree (a maintaince problem if the shape or location of the object directory tree changes), make allows the OBJECTDIR to be meaningfully specified in a Makeconf file, which can be shared by the Makefiles in all the subdirectories of the source tree. To make this meaningful, make has to perform special processing on the definition of OBJECTDIR: if Makeconf is found in a directory above the current directory, the definition is modified by appending the relative path from that directory down to the current directory (containing the Makefile). This is to support the assumption that the object directory tree has the same structure as the source directory tree. In addition, make will automatically invoke "mkdir" commands as necessary to create any missing directories on this relative path under the specified OBJECTDIR. For example, if the root source directory, call it $SRC, has a Makeconf file containing "OBJECTDIR=/foobar/obj" and has a subdirectory "glerp" containing a Makefile but no Makeconf, invocation of make from within $SRC/glerp will cause it to use /foobar/obj/glerp as the object directory, and it will create this directory if it doesn't exist. The OBJECTDIR definition in a Makeconf file can also be specified as a pathname relative to the directory containing the Makeconf file, and will be treated correctly. When the Makeconf file is found from a directory n levels down the tree, n occurrences of "../" will be prefixed to the definition of the object directory, in addition to appending the relative path between Makeconf and Makefile. This will be the correct way to refer to the specified object directory from the directory containing the Makefile. The SOURCEDIR definition can also be specified as a relative pathname and will be treated similarly to the OBJECTDIR. Note that if source directory and object directory are both relative pathnames (including the case where SOURCEDIR is left unspecified, in which case it defaults to "."), the path that is prefixed to source files appearing in the shell commands will also be a relative path. It will first contain enough occurrences of "../" to get to the root object directory specified as the value of OBJECTDIR. Then it will contain the appropriate components to get from there to the source directory. For example if the root source directory (with the name "src", let's say) has a Makeconf file containing "OBJECTDIR=../obj" and has a subdirectory "glerp" containing a Makefile but no Makeconf, invocation of make from within src/glerp will cause it to use "../../obj/glerp" as the object directory, and "../../src/glerp/" will be prefixed to each source file in the shell commands. o In order to allow the building of targets to depend on the machine type, there are four built-in variables with definitions dependent on the machine on which make is running. The variables "$(MACHINE)" and "$(machine)" are defined to be an identifier for the machine type, the former in upper case and the latter in lower case. The variables "$(CPUTYPE)" and "$(cputype)" similarly identify the type of CPU in the machine. For example, on an IBM PC/AT, the values would be: MACHINE machine CPUTYPE cputype = = = = AT386 at386 I386 i386 o The rules for replacement of a variable reference with the definition of the variable have been extended. One enhancement is that the name of the variable can be formed by the expansion of other (nested) variable references. For example, if the Makefile contains: MACHDEP_I386 = flerb.c MACHDEP_VAX = gronk.c MD_FILE = ${MACHDEP_${MACHINE}} the variable "MD_FILE" will be defined to be "flerb.c" if make is running on a 386-based machine and will be "gronk.c" on a VAX. o Another enhancement is that some of the C shell modifiers for variable substitution have been implemented, including ":h", ":t", ":e" and ":r". One of these modifiers can appear between the variable name and the closing "}" or ")". After the variable is expanded to its definition, the resulting string is modified as described below (taken from the csh man page): h r e t Remove Remove Remove Remove a trailing pathname component, leaving the head. a trailing `.xxx' component, leaving the root name. all but the extension `.xxx' part. all leading pathname components, leaving the tail. So, given the following Makefile: NAME=foo/bar/glerp.c flub: : "NAME:h = ${NAME:h} : "NAME:t = ${NAME:t} NAME:r = ${NAME:r}" NAME:e = ${NAME:e}" the output of "make flub" is: : "NAME:h = foo/bar : "NAME:t = glerp.c NAME:r = foo/bar/glerp" NAME:e = c" o There is also conditional variable expansion, based on whether or not a variable is defined. Its syntax is similar to the if-then-else expression of C: ${X ? foo ... bar : fritz ... nobble} will expand to "foo ... bar", if "X" is a defined make variable, and to "fritz ... nobble", otherwise. One obvious use for this feature is to specify default values for make variables, that can be overridden by the user in the shell environment: X = ${X ? $(X) : default X value } (Note: this mechanism is not necessary in order for the user to override the value of X with a command-line assignment, e.g. "make X=foo ...", but it is necessary to cause a variable in the shell environment to override a definition in the Makefile, e.g. "setenv X foo; make ...".) o The meaning of a rule of the form ".EXPORT: name1 name2 . . .", where "name1", etc. are names of make variables, is that the specified variables should be exported to the environment of any shell commands that are invoked by make. Otherwise, only those make variables that were defined in the shell environment of the make command are exported to the environment of such commands. So, using the Makefile: X = foobar Y = glerp foo: printenv | grep '^[XY]=' .EXPORT: X the command "make foo" produces the following output: printenv | grep '^[XY]=' X=foobar The .EXPORT rule is most often used to export make variables to another make command. o If a rule is defined with the target name ".EXIT", it will be processed after all other targets have been brought up to date, exactly as though the make command had ".EXIT" as an additional final argument. This can be used to carry out some form of postprocessing, such as updating a list of header-file dependencies for a group of C source files, after running the C compiler with the -M (or -MD) flag has produced new dependency information. --------