Department of Mathematics - University of Utah

HomeComputingCourse SchedulesCSMECurrent PositionsFAQ (Computing)FormsGraduateHigh SchoolLecture VideosMailbox AccessMath BiologyMath EducationNewsletterPeopleResearchRTG GrantsSeminars

Make FAQ

Last updates: Thu Jul 7 15:34:45 2016   .. Thu Mar 23 14:02:41 2017                Valid HTML 4.0!

Serious use of computers generally involves repetitive, and complex, tasks. For example, during software development, edit / compile / link / test steps are repeated many times. Document production is similar: write / typeset / proofread. So is accounting: record cash flow / analyze accounts / generate bills.

This is so obvious that it is surprising how few operating systems outside the Unix-like world have addressed it. During the development of Unix at Bell Laboratories, in 1978, Steve Johnson, author of the Portable C Compiler, pcc, and the parser generator, yacc, posed a challenge to his friend and colleague, Stuart Feldman, author of the world's first Fortran 77 compiler, to write a tool that would remove the repetitive drudgery. In about a day, the latter came up with a working program that he called make.

Many programmers undoubtedly view it now as possibly the world's greatest software tool, and even casual users of computers can benefit by learning how to read, and write, the file that controls its actions.

The great beauty of make is that it has a fair number of built-in rules that allow it to handle common software development tasks, and that once its control file has been written, no human ever needs to learn how to carry out those repetitive steps again, because they have been recorded in that file. Indeed, in simple cases, the default rules suffice, and no control file is needed.

Table of contents

  1. How do I create a control file for make?
  2. What are dependencies?
  3. How do I add a new default rule?
  4. What are the built-in special macros?
  5. What are conventional target names?
  6. How can I force a rule evaluation?
  7. How can I build in a directory tree?
  8. How can I build in parallel?
  9. How can I debug a Makefile?
  10. When can I avoid having a Makefile?
  11. How can I automatically produce a Makefile?
  12. How can I include fragments into a Makefile?
  13. What other command-line options are available?
  14. How to I build standard software packages?

Questions and answers

  1.   How do I create a control file for make?

    When it is invoked to carry out a task, make looks for a file in the current directory named makefile, and if that is not found, it looks for Makefile. Otherwise, it proceeds with its own built-in rules. The control file is just an ordinary text file that you can create with your preferred editor.

    The capitalized name for the control file is now almost universal, because Unix practice has generally been to name important files in UPPERCASE, and most other files in lowercase, and the default filename sorting order is that of the underlying computer character set, such as ASCII, or ISO 8859-1 (Latin-1), or …, or Unicode in UTF-8 encoding. The latter is a huge character set that contains ASCII in its first 128 slots. The most widely used character sets have control characters, followed by punctuation and digits, followed by uppercase letters, followed by lowercase letters. Thus, a sorted directory listing might look like this:

    % ls
    Makefile       README	      backup.c	     goodbye.c
    hello.c	       transact.c
    

    The most important files appear first in that list.

    Here is a minimal control file that exhibits the five kinds of lines that make recognizes:

    % cat Makefile
    # This is a simple Makefile for this tutorial
    
    PROGRAM    = billing
    all:	$(PROGRAM)
    billing:
    	$(CC) $(CFLAGS) -o billing backup.c goodbye.c hello.c \
    	                           transact.c
    
    1. Empty lines, and lines containing only whitespace, are discarded.

    2. Comments begin with a sharp (#) and continue to end of line. They too are discarded.

    3. Macro definitions take the form name = value where the right-hand side is zero or more space-separated words. The name must begin with a letter, followed by zero or more alphanumeric characters. If the same name is assigned a value multiple times, only the last value is used. Just as in the C and C++ programming languages, and the shell, it is customary, but not required, to spell macro names in uppercase to improve their visual separation from surrounding text.

    4. A dependency line has the form name(s) : name(s). It says that the whitespace-separated names to the left of the colon depend on the possibly empty list of names to the right of the colon. The names to the left of the colon are called targets, and make can be asked to build particular targets by supplying them as its command-line arguments. If there are no arguments, then the first target in the control file is assumed.

    5. A command line begins with a tab character (not spaces), and there can be any number of commands for a given set of targets. Each is to be executed in a separate instance of the default Unix shell, normally, /bin/sh, the IEEE POSIX Standard command shell available on every Unix and Unix-like operating system in the world. You might even have it inside your smart watch!

      If a command fails, make normally terminates with a nonzero shell status code. However, if the command is prefixed with a hyphen, any failure is ignored, and execution continues. You can force continuation after command failure with the options -i and -k, normally used together, as in make -i -k.

      Alternatively, use shell command syntax to ensure apparent command success, as in this example:

      check:
      	./mytest 1 || true
      	./mytest 2 || true
      	./mytest 3 || true
      

      In those examples, the test commands either succeed and the command line terminates, or they fail and execution continues with the true command, which always succeeds.

      Commands are echoed to standard output immediately before they are executed, but that echo is suppressed if the command begins with an at-sign.

      Prefixing at-sign and hyphen can appear together, in either order.

    As the end of the sample Makefile shows, long lines can be continued with a backslash-newline pair to make them more readable. That pair, and any surrounding whitespace, are collapsed to a single space as the line is collected.

    The Makefile is read completely before any macros are expanded, or rules are applied. Thus, the order of dependency declarations, rules, and macro assignments does not matter, except that only the last of several assignments to the same name is the one that holds. However, it is good practice to write the Makefile in five standard blocks:

    # 1: General comments
    
    # 2: Macro definitions
    
    # 3: Private rules
    
    # 4: Targets and their rule command-blocks, with the first target named all
    
    # 5: General file dependencies
    

    To make it easier for the maintainer and human readers of the control file, within each of those sections, it is a good idea to order their parts alphanumerically.

    When make needs to build a target, it first checks whether the dependents are named elsewhere in the control file, and if so, builds them first. If not, it assumes that they are existing filenames, in which case it compares their last-modification timestamps with those of the targets, and if any of the dependents are newer than the targets, it applies the rule commands to build them. If the target is undefined in the Makefile, and is not a file either, then make terminates with an error. Otherwise, it does nothing. Thus, once make has built a target, it does not do so again in subsequent runs, until the dependencies are found to be newer than the targets.

    Macro names are evaluated by wrapping them in parentheses or braces, and prefixing them with a dollar sign. As a special case, a one-letter macro name does not need the wrappers: $X, $(X), and ${X} are equivalent, and $XYZ means the same as $(X)YZ: the value of X followed by YZ.

    By contrast, the shell does macro expansion only for unbraced or braced macro names, so $FOO is the same as ${FOO}. The meaning of parentheses around shell macro names is shell dependent. In the original Bourne and POSIX shells, $(FOO) is a syntax error, whereas in the Korn shell (ksh) and other extended shells, $(FOO) expands to the output of the command FOO. For portability, avoid parenthesized shell macros.

    Evaluation of an undefined macro name is not an error: it just produces an empty string. Thus, in our small control file, $(PROGRAM) expands to billing, unless that name is overridden on the command line, like this:

    % make PROGRAM=test-billing
    

    You could do that, for example, to create or use an alternate program.

    In the rule for the billing target, there are references to two other macros that we did not define. They are standard macro names for the C-language compiler, and its compilation flags, that are part of the built-in macros and rules in all make implementations.

    There is one particularly convenient feature supported in all modern implementations that avoids duplication of long lists of related filenames: ${NAME:.old=.new} expands the variable, replacing old suffixes with new ones. Here is a typical Makefile fragment that illustrates its use:

    SRC	= backup.c goodbye.c hello.c transact.c
    OBJ	= ${SRC:.c=.o}                    
    

    The effect is that OBJ is assigned the value backup.o goodbye.o hello.o transact.o.

    You can display the built-in settings with the -p option, like this:

    % make -p
    ...
    CC               = cc
    CFLAGS           = -O2
    CPP              = cpp
    CXX              = c++
    CXXFLAGS         = -O2
    F77              = $(FC)
    F77FLAGS         = $(FFLAGS)
    FC               = f77
    FFLAGS           = -O
    ...
    

    With that option, no commands in the Makefile are executed. The output is lengthy – from 300 to 1500 lines – and the macro definitions are not usually sorted alphanumerically. The pairs CC / CFLAGS for C, and CXX / CXXFLAGS for C++, are universally recognized.

    CPP is the name of the C preprocessor, which might be a separate program, as in the above sample, or it might be something like $(CC) -E, to ask the C compiler to just run the preprocessor and skip further processing.

    The default macro name for the Fortran compiler is messier: it was F77 in Feldman's original implementation, because at the time, the Unix compiler was named after the year of the draft ANSI Standard for that language. Later implementations introduced the neutral FC and FFLAGS instead. Both conventions are supported on the sample system.

    Regrettably, the suffix for C++ files was not quickly standardized: .cc, .cpp, .cxx, and .C have all been used. The latter is a particularly poor choice, because it works only on a case-sensitive filesystem. Not all implementations of make have rules for all four suffixes: the portable choices are the first two.

  2.   What are dependencies?

    A dependency of target x on y means that y must be handled before x. For example, if a C source-code file hello.c is changed, then its compiled object file hello.o is now incorrect and out of date, and in need of regenerating by the compiler. Thus, hello.o depends on hello.c. Thus, we might have in the Makefile this line:

    hello.o:	hello.c
    

    However, because there are built-in rules for compiling and linking files in common programming languages, that line is not required, because it can be inferred by make.

    Simple dependencies like that are easy, but when there is a chain of dependencies, confusion sometimes arises. For example, if the file hello.c contains the directive #include "project.h", it might appear that the C source file depends on the header file. It does not. If the header file is changed, usually no changes would be needed in the C file. Instead, it is the object file that depends on both of them, expressed with this Makefile line:

    hello.o:	hello.c project.h
    

    That dependency declaration is essential, because there are no built-in rules that relate header files to object files, and there is no relation between their names. If the header file includes other private header files, they need to be added to the dependency lists. However, to avoid duplication of dependencies, it is better to supply them in one place, like this:

    hello.o:	hello.c   project.h
    
    goodbye.o:	goodbye.c project.h
    
    project.h:	mydefs.h  mytypes.h  mystructs.h
    

    That might seem to violate our prescription that source files do not depend on included files, but here, it is just used for simplification.

    All nontrivial C programs include system header files in angle brackets, meaning that they are to be found by the compiler in a compiler- and platform-dependent header-file search path. Programmers sometimes want to include such dependencies in a Makefile, but that is not a good idea, for two reasons.

  3.   How do I add a new default rule?

    A common convention across many operating systems is that related files are named with a common prefix, and distinguished by unique suffixes. Thus, the C source file hello.c might be accompanied by a compiled object file hello.o, a preprocessor output file hello.i, a debugger symbol file hello.dsym, and an executable program hello, whose suffix is empty.

    For C compilation and linking, make has rules something like these:

    .c.o:
    	$(CC) $(CFLAGS) -c $<
    .c:
    	$(CC) $(CFLAGS) -o $* $(LDFLAGS) $< $(LDLIBS)
    

    The special macro $< is the name of the input file, and $* is the basename produced by stripping the input filename's suffix.

    The first of the two target lines says that to convert a .c source file to a compiled .o file, make should run the C compiler with its flags, plus the compile-only option, -c, passing it the name of the input source file as the last argument.

    The second target line says that to convert the C source file to a suffix-less executable file, make should omit the compile-only option, and add the output option -o that requires a following output filename, supplied by the value of the special macro $*. There are additional flags and libraries needed by the loader, ld.

    Thus, to supply new rules for file types that make does not yet know about, you could add them to your Makefile like this:

    .SUFFIXES: .dvi .pdf .tex
    
    .tex.dvi:
    	$(TEX) $(TEXFLAGS) $<
    
    .tex.pdf:
    	$(PDFTEX) $(PDFTEXFLAGS) $<
    

    The example introduces a special feature: only suffixes supplied in the dependency list for the special name .SUFFIXES are used to find needed rules, and the specified suffixes augment, rather than replace, the built-in list. Suffix order in that list does not matter, but alphanumerical ordering is generally advisable.

    Rarely, you make wish to eliminate all built-in suffix-pair rules. You do that by first supplying an empty list, then providing your own additions:

    .SUFFIXES:
    .SUFFIXES: .dvi .pdf .tex
    

    Notice that .SUFFIXES is a special target, rather than a macro; the only way to learn its default value is examination of the output of make -p.

  4.   What are the built-in special macros?

    The macro SHELL expands to the pathname of the shell used to execute commands in rules. It is almost always set to the Bourne shell, /bin/sh, independent of the user's login shell, but there have been systems where instead it is set to the login shell, which might expect a radically different command language. For portability, insert a line SHELL = /bin/sh in each new Makefile that you create.

    There are five special one-character macros that are always available:

    1. $* is the suffix-less basename of the input file referred to in dotted-pair rules. It cannot be used in ordinary rules.
    2. $< is the input name in a dotted-pair rule. It cannot be used in ordinary rules.
    3. $@ is the name of the current target in an ordinary rule. As a memory aid, think of the at-sign as a bullseye target.
    4. $? is the list of dependents that are out-of-date with respect to the current target. It is important to remember that it excludes dependents that are up-to-date.
    5. $$ represents a single dollar sign that does not cause further macro expansion. Thus $$foo represents the verbatim string $foo.

    Because the shell also uses dollar sign for macro expansion, care is needed in coding commands. The Makefile fragment

    TESTFILES	= t1 t2 t3 t4
    
    check:
    	for f in $(TESTFILES) ;			\
    	do					\
                    echo ====== Testing $$f ;	\
    		$(MAKE) $$f ;                	\
    	done
    

    would result in the shell seeing the code line

    for f in t1 t2 t3 t4 ; do echo ====== Testing $f ; make $f ; done
    

    Macro substitution is simplistic, and applied to make's input after it has been read, and before any shell commands are executed. In particular, substitution is not affected by backslashes or any quoting marks, unlike in the shell, where there are distinct rules for those.

  5.   What are conventional target names?

    Long experience with make shows the desirability of having a few standard target names that appear in almost every Makefile, and that do an expected task, so that other humans, and computer programs, can use them without having to know what they do. The standard names are not built-in; they are merely widely used beneficial conventions.

    The GNU Project recommends at least these:

    1. all: build everything that can be built in the current directory. That target should always be the first one.
    2. check: run a validation suite to verify correct operation. Typically, that might involve running several small test cases, comparing their newly created output files against a stored set of expected output files, and using the cmp and/or diff commands to detect and report differences.
    3. clean: remove intermediate files that are not needed after the targets are built. A typical rule for that target might look like this:

      clean:
      	-$(RM) *.o *~ #* a.out core.* core
      

      The private macro RM would be defined elsewhere in the Makefile, usually to /bin/rm -f.

      You could have used rm -f in place of $(RM). However, it is considered good practice to never hardcode program names in a Makefile, but to instead define them with macros that can easily be overridden on the command line, or redefined in just one place.

    4. distclean: remove everything in the directory that make could have built, returning it to its state before the build was first done. That action never removes files that humans created. To avoid duplication of rules, a typical rule would likely depend on another target, like this:

      distclean:	clean
      	-$(RM) billing
      
    5. maintainer-clean: remove all buildable files, and additional files that might be creatable only at certain sites, or that would require special tools that might not be present at other than the development site. A good example is typeset documentation, for which we might have a rule:

      maintainer-clean:	distclean
      	-$(RM) billing.pdf
      

      That documentation would have to be regenerated by the developer before the directory could be given to others.

    6. install: copy built files into standard locations in the filesystem, with suitable file permissions and ownership, so that they can be used by others. The pathnames of those locations generally share a common prefix, represented in the Makefile by the macro prefix whose value might be something like /usr, /usr/local, or /opt. At our site, on all newer systems, we use instead /usr/uumath to avoid collision with locations assumed by other software packages, and package installation systems.

      For complex systems with lots of installed files, the installation path should normally end with a package name and desirably, package version, to avoid conflicts with identically named files in other packages. Thus, we have compiler installation directories with pathnames like these:

      /usr/uumath/ashare/gcc/gcc-4.9.3
      /usr/uumath/ashare/gcc/gcc-5.1.0
      /usr/uumath/ashare/gcc/gcc-5.2.0
      /usr/uumath/ashare/gcc/gcc-5.3.0
      /usr/uumath/ashare/gcc/gcc-5.4.0
      /usr/uumath/ashare/gcc/gcc-6.1.0
      /usr/uumath/ashare/gcc/gcc-7-20160529
      
    7. dist: make one or more distribution files for giving the software to others. That typically would be a compressed tar file containing just what remains after make distclean, but sometimes packaging in additional formats, such as InfoZIP .zip files, or Java archive .jar files, helps to make the package more widely accessible, even outside the Unix-like world.

  6.   How can I force a rule evaluation?

    make does not build a requested target unless timestamps show that the action is necessary. Sometimes, you want the action to be done anyway, and the simple solution is to add a dependency on a target that does not exist as a file. The conventional name for such a target is FRC, for force:

    hello.o:	hello.c FRC
    
    FRC:
    

    The object file hello.o would normally not be regenerated if it is newer than the source file hello.c, but here we say that it also depends on FRC, a target that has neither dependents nor rules. Making that target then acts as if a new file has just been created, and that is guaranteed to be newer than the existing object file, so recompilation is forced. Filesystem clock resolution is system dependent, but is generally fine enough that there is usually at least one clock tick between file creations. Modern systems have microsecond or nanosecond filesystem clocks, so file timestamp tests for newer than are rarely ambiguous.

  7.   How can I build in a directory tree?

    A Unix-like filesystem is a tree beginning at the root directory, called / (slash). Each directory can contain zero or more files and directories. Thus, a pathname /usr/local/bin/gcc refers to the GNU C compiler, gcc, contained in the bin directory inside the local directory inside the usr directory inside the root directory.

    make can easily exploit the filesystem tree structure by having a Makefile that refers to files in the current directory, as well as subdirectories one level below. They in turn can each contain a Makefile that possibly descends one level deeper, and so on.

    Here is a sample Makefile fragment that does just that:

    SUBDIRS = build test report
    all:
    	$(MAKE) $(PROGRAM)
    	for d in $(SUBDIRS) ;		\
    	do				\
    	    ( cd $$d ; $(MAKE) $@ ) ;	\
    	done
    

    The first rule command builds whatever is needed in the current directory, then in a second shell invocation, loops over the subdirectories named in the macro, changes to each in turn in a subshell, and runs the same command as in the top-level directory.

    The macro $@ removes dependence on a target name, so the loop is generic, and therefore better defined in a macro that can be used in multiple places, as in this fragment:

    SUBDIRLOOP	= for d in $(SUBDIRS) ;		\
    		  do				\
    		      ( cd $$d ; $(MAKE) $@ ) ;	\
    		  done
    
    all:
    	$(MAKE) $(PROGRAM)
    	$(SUBDIRLOOP)
    
    clean:
    	-$(RM) *.o *~ #* a.out core.* core
    	$(SUBDIRLOOP)
    
    distclean:	clean
    	-$(RM) billing
    	$(SUBDIRLOOP)
    
    maintainer-clean:	distclean
    	-$(RM) billing.pdf
    	$(SUBDIRLOOP)
    

    Recursive invocations can continue arbitrarily deep, but in each directory, the Makefile only has to know the names of its own subdirectories, but nothing of their contents. Thus, a human-issued four-letter command make can start a complex build that might run for hours, compiling millions of files, and billions of lines of code, or even an entire operating system and all of its software, without the invoker needing to know anything at all about how that enormous job is really done.

    Importantly, with make, it is trivial to repeat a previous run that failed because of errors unrelated to the software itself, such as dropped network connections, filesystem problems, machine crashes, or power failures. Building manually lacks that benefit.

    Notice that make itself is always used in a control file via the special macro $(MAKE), which stands for the name of the top-level program, and all of its command-line assignments and options. Thus, you could run the commands

    % make CC=clang distclean all check
    % make CC=gcc   distclean all check
    % make CC=suncc distclean all check
    

    to build and test software with three different C compilers. In each case, the supplied compiler names would propagate through the entire build, no matter how deeply directories are nested.

    Large software projects are often tested daily like that, ensuring that developers have not introduced new bugs since the previous day, and also that compiler bugs, or other software changes from system updates, have not produced unwanted surprises.

    Similar techniques can be used to test software on different CPU and operating-system architectures, a valuable practice to ensure software portability and broader markets. Here, however, you can exploit parallelism by running the builds simultaneously in a shell session, then wait for them all to complete:

    % ssh arm-box     'cd /path/to/software; make distclean all check' > arm-box.log     &
    % ssh ia64-box    'cd /path/to/software; make distclean all check' > ia64-box.log    &
    % ssh mips-box    'cd /path/to/software; make distclean all check' > mips-box.log    &
    % ssh ppc-box     'cd /path/to/software; make distclean all check' > ppc-box.log     &
    % ssh sparc-box   'cd /path/to/software; make distclean all check' > sparc-box.log   &
    % ssh x86-64-box  'cd /path/to/software; make distclean all check' > x86-64-box.log  &
    % ssh x86-box     'cd /path/to/software; make distclean all check' > x86-box.log     &
    % ssh zsystem-box 'cd /path/to/software; make distclean all check' > zsystem-box.log &
    % wait
    
  8.   How can I build in parallel?

    Older machines had limited memory and CPU power, but modern ones have many more resources that it is useful to exploit. In particular, there are often multiple CPUs, or cores, or threads, that we want to use at the same time. The original make implementation had no possibility of doing that, but the modern BSD and GNU ones do: their command-line option -jN requests the running of up to N jobs in parallel. For example, a large program might need compilation of hundreds or thousands of source files before it can be linked into a single executable. Each of those source files can be compiled independently of all the others, either sequentially, or in parallel. Only when linking is done must the compilations all have completed, and make can handle that easily simply by waiting at each target for all dependent jobs to finish.

    At our site, many of our larger servers have 64 to 128 cores, so we often run commands like make -j256 to achieve dramatic speedups in build times. There is no harm in running more jobs than there are cores, because most jobs have to be briefly suspended for I/O or network activity, or because they have used their processor time slice: during those suspensions, the cores can process other jobs.

  9.   How can I debug a Makefile?

    The command-line option -n, for no execution, tells make to show you the commands that it would pass to the shell, without invoking the shell. You can use that to test changes to a control file without any risk from errors in that file. You should always check such changes carefully; otherwise, a trivial typo that inserted a space before an asterisk in a file-removal command could wipe out everything!

  10.   When can I avoid having a Makefile?

    When dependencies can be inferred from built-in dotted-pair rules, you may not need to have a Makefile at all. Consider a directory full of independent test files, say t1.f, t2.f, t3.f, …. Without a Makefile, you can just issue a command make t3 to compile and link a particular test program. You could even change the compiler and its flags with make FC=gfortran FFLAGS='-O3 -g -Wall' t3.

    However, without a control file, there is no default target, so running the bare command make produces an error message. Nor is there any way to clean up after yourself: make clean also results in an error message.

    Thus, it is generally a good idea to create at least a minimal file Makefile to define common targets.

  11.   How can I automatically produce a Makefile?

    The need for a well-designed Makefile with standard target names is so common that it makes sense to automate that job, and decades ago, the author of this tutorial wrote the make.el library for the powerful emacs text editor. The library file knows about many different file types, and customizes the generated control file according to the directory contents. As an example of its use, in a directory containing just three Fortran test files, the two editor commands M-x load-library<ret>make and M-x make-makefile produce an editor buffer with this complete ready-to-customize file:

    #=======================================================================
    # Makefile for files in /path/to/this/direcotry
    #
    # [08-Jul-2016]
    #=======================================================================
    # Program definitions
    
    PROGRAM		= CHANGE-THIS-VALUE
    PROGLIB		= CHANGE-THIS-VALUE
    VERSION		= 1.0
    
    #=======================================================================
    # Installation locations
    
    prefix		= /usr/local
    bindir		= $(prefix)/bin
    catdir		= $(prefix)/man/cat$(manext)
    libdir		= $(libtop)/$(PROGRAM)-$(VERSION)
    libtop		= $(prefix)/$(PROGLIB)/
    mandir		= $(prefix)/man/man$(manext)
    manext		= 1
    shrlibdir	= $(shrlibtop)/$(PROGRAM)-$(VERSION)
    shrlibtop	= $(prefix)/share/$(PROGLIB)/
    
    #=======================================================================
    # Macro definitions
    
    CAT		= cat
    
    CHKDELIM	= chkdelim
    
    CHKDELIMFLAGS	= -tex -i LG\'
    
    CHMOD		= chmod
    
    CP		= /bin/cp -p
    CP		= rcp -p
    
    DETEX		= detex
    
    DETEXFLAGS	= -m -n -s
    
    DW		= dw
    
    ISPELL		= ispell
    
    LIBFILES	= /dev/null
    
    LN		= ln
    
    LS		= ls
    
    MKDIR		= mkdir -p
    
    MV		= /bin/mv
    
    RM		= /bin/rm -f
    
    SED		= sed
    
    SHELL		= /bin/sh
    
    SHRLIBFILES	= /dev/null
    
    SORT		= sort
    
    SPELL		= spell
    
    SPELLFILTER	= $(CAT)
    SPELLFILTER	= $(SED) -e 's/[0-9,.:;?&]/ /g'
    
    #=======================================================================
    # Compiler flags and source files
    
    FFLAGS		= $(FOPT)
    FOPT		= -g
    FOBJS		= $(FSRCS:.f=.o)
    FSRCS		= t1.f t2.f t3.f
    
    #=======================================================================
    # Suffixes and extra rule(s)
    
    #=======================================================================
    # Targets:
    
    all:
    	@echo CHANGE THIS TARGET TO RUN THE DEFAULT ACTION
    
    check:
    	@echo 'There is no validation suite for this program (yet)'
    
    clean:
    	-$(RM) *.dw
    	-$(RM) *.i
    	-$(RM) *.o
    	-$(RM) *.ser
    	-$(RM) *~
    	-$(RM) \#*
    	-$(RM) a.out
    	-$(RM) core core.*
    
    clobber:	distclean
    
    distclean:	mostlyclean
    	-$(RM) $(PROGRAM)
    
    install:	install-exe install-lib install-shrlib install-man install-show
    
    install-exe:	uninstall-exe
    	$(CP) $(PROGRAM) $(bindir)/$(PROGRAM)
    	$(LN) $(bindir)/$(PROGRAM) $(bindir)/$(PROGRAM)-$(VERSION)
    	$(CHMOD) 775 $(bindir)/$(PROGRAM) $(bindir)/$(PROGRAM)-$(VERSION)
    
    install-lib:	uninstall-lib
    ##	$(MKDIR) $(libtop)
    ##	@if test -d $(libtop) ; then true ; else echo Cannot create $(libtop) ; exit 1 ; fi
    ##	$(MKDIR) $(libdir)
    ##	@if test -d $(libdir) ; then true ; else echo Cannot create $(libdir) ; exit 1 ; fi
    ##	$(CP) $(LIBFILES) $(libdir)/
    ##	$(CHMOD) 664 $(libdir)/*
    
    install-man:	uninstall-man
    	$(CP) $(PROGRAM).man $(mandir)/$(PROGRAM).$(manext)
    	$(CHMOD) 664 $(mandir)/$(PROGRAM).$(manext)
    
    install-show:
    	@echo ''
    	@echo Installed files...
    	@$(LS) -l $(bindir)/$(PROGRAM) $(bindir)/$(PROGRAM)-$(VERSION) \
    		$(mandir)/$(PROGRAM).$(manext)
    	@if test -d $(libdir) ; then $(LS) -lR $(libdir) ; fi
    	@if test -d $(shrlibdir) ; then $(LS) -lR $(shrlibdir) ; fi
    	@echo ''
    
    install-shrlib:	uninstall-shrlib
    ##	$(MKDIR) $(shrlibtop)
    ##	@if test -d $(shrlibtop) ; then true ; else echo Cannot create $(shrlibtop) ; exit 1 ; fi
    ##	$(MKDIR) $(shrlibdir)
    ##	@if test -d $(shrlibdir) ; then true ; else echo Cannot create $(shrlibdir) ; exit 1 ; fi
    ##	$(CP) $(SHRLIBFILES) $(shrlibdir)/
    ##	$(CHMOD) 664 $(shrlibdir)/*
    
    maintainer-clean:	distclean
    	@echo "This command is intended for maintainers to use;"
    	@echo "it deletes files that may require special tools to rebuild."
    
    mostlyclean:	clean
    
    uninstall:	uninstall-exe uninstall-lib uninstall-shrlib uninstall-man
    
    uninstall-exe:
    	-$(RM) $(bindir)/$(PROGRAM)
    	-$(RM) $(bindir)/$(PROGRAM)-$(VERSION)
    
    uninstall-lib:
    ##	-$(RM) -r $(libdir)
    
    uninstall-man:
    	-$(RM) $(mandir)/$(PROGRAM).$(manext)
    	-$(RM) $(catdir)/$(PROGRAM).$(manext)
    
    uninstall-shrlib:
    ##	-$(RM) -r $(shrlibdir)
    
    #=======================================================================
    # File dependencies
    
    #=======================================================================
    
  12.   How can I include fragments into a Makefile?

    Although Feldman's original implementation of make had no way to include other files into a Makefile, all modern implementations have an include directive to do so.

    You could therefore create a Makefile that looks like this:

    include Readme.mk
    include Macros.mk
    include Rules.mk
    include Targets.mk
    include Dependencies.mk
    

    Some people find that an improvement, but in practice, it makes things worse for the end user, especially when the included fragments are eclectically named, and scattered around the directory tree.

    Quite often, the user wants to override a setting in a control file, such as to change a compiler name or compiler flags, and when multiple files have to be searched in strange places, finding out what to change, and where it is defined, can be difficult.

    The cmake utility is particularly guilty of such practices. The X11 Window System with its Imakefile files and xmkmf utility is another egregious example: even for simple programs, xmkmf produces huge control files filled with unwanted dependencies on system header files, large numbers of unused macro definitions, and copiously repeated hard-coded pathname prefixes.

  13.   What other command-line options are available?

    Elsewhere in this document, we introduced these command-line options for make:

    Most users may never need more than the -n option, but there are a few others that are occasionally useful:

    Particular make implementations may offer additional command-line options, and document them in manual pages and info nodes, but they are so rarely needed that you can certainly live without them, and if you do use them, you lose portability. This document's author has written thousands of Makefile instances, without ever feeling the need for any of those extensions.

    Both BSD and GNU implementations of make offer many extensions inside the control files, but for portability, avoid their use, and stick to the IEEE POSIX Standard definition of how the program behaves.

  14.   How to I build standard software packages?

    Some of the great contributions of the GNU Project have been the development of the autoconf program, the standardization of Makefile target names, and the naming of package distribution files. The success of those practices has led many other software developers, particularly in the open-source community, to adopt them as well, so it is likely that after downloading a source-code bundle, you can build the software like this, without reading a single line of documentation about how to do so!

    % cd /path/to/temporary/build/location
    % tar xf /path/to/download/directory/package-12.3.9.tar.xz
    % cd package-12.3.9
    % ./configure && make all check install
    

    If your site uses a convention other than the GNU default installation prefix /usr/local, you can specify it like this:

    % ./configure --prefix=/usr/uumath && make all check install
    

    If you want to change compilers and their flags, you can provide them in the environment like this:

    % env CC=clang CXX=clang++ CFLAGS=-Wall ./configure && make all check install
    

    The job of the configure script is to make build-time feature tests, and create files, typically at least the C header file config.h, and the Makefile, where the latter is derived by simple text substitutions on a template file, Makefile.in, provided by the developer. Nearly five decades of evolution of Unix-like environments has meant that, despite standardization efforts by IEEE POSIX, X/Open, the Linux Standards Base, the GNU Project, and other groups, there are differences in the software environments across systems, and of course, there are also often hardware differences, with a few score CPU architecture families having been marketed since the early 1970s. Build-time configuration allows developers to hide those differences from end users, who need only remember a five-word recipe with the acronym CMACI.

    The build-all system developed by the authors of the book Classic Shell Scripting has been used in hundreds of thousands of software builds since the book was written. It exploits the GNU recipe, and manages builds in parallel on an unlimited number of systems, or build environments. For example, when a new release of a software package appears, the author runs commands like these:

    % build-all package-x.y.z
    
    % build-all -u cc-one package-x.y.z
    

    There can, of course, be multiple packages named on the command line, but here, we use only one.

    In the first, multiple builds are done on each build host in different compilation environments, to give the package code a thorough build workout.

    In the second, the argument cc-one is the name of a configuration file stored in the directory $HOME/.build. That file contains one-line entries for each target system to build just one instance of the package using the default C compiler. Thousands of such recipes have been developed to test software in particular compilation environments, or on particular classes of machines. The build-all system, and the creation of numerous physical and virtual machines, has made it possible to develop a world-class software-testing laboratory at our site that takes no more effort than building on a single machine. Only the log examination and package installations need to be done for each build, but even there, when packages produce easily recognizable reports of validation success, such as ALL TESTS PASSED!, another script can quickly identify the passes so their logs can be ignored, and usually, also compressed to save space.

    The build-all command sends an e-mail report at the start of the build, recording the package name and the location of the directory where the build logs are accumulated. Some time later, the logs are examined, and for each system where the build was successful, and the validation suite exhibited a satisfactory passing grade, all that is needed is to step into the build directory on that system and run the command make install.

    Long experience with building software developed by others shows that it is unsafe to install the software until the validation report has been examined. However, for highly portable software, you could override the defaults like this:

    % build-all --check "check install" -u cc-one package-x.y.z
    

    That just adds another target in the validation step. If the package has been carefully written, a validation error terminates execution without installing the software.


Dept Info Outreach College of Science Newsletter

Department of Mathematics
University of Utah
155 South 1400 East, JWB 233
Salt Lake City, Utah 84112-0090
Tel: 801 581 6851, Fax: 801 581 4148
Webmaster


Entire Web                     Only http://www.math.utah.edu/