kernel_build

advertisement
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.
--------
Download