From: John Marino Date: Sun, 28 Oct 2012 13:27:26 +0000 (+0100) Subject: usr.bin/make: Remove legacy make files X-Git-Tag: v3.4.0rc~940 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/c9e065d383e21ab9bb636ce06152982597b83587 usr.bin/make: Remove legacy make files usr.bin/make has been replaced with usr.bin/bmake, retaining the program name "make". usr.bin/make was previously disconnected from the build, so these files were not longer used. --- diff --git a/usr.bin/make/GNode.h b/usr.bin/make/GNode.h deleted file mode 100644 index 0a7b570b79..0000000000 --- a/usr.bin/make/GNode.h +++ /dev/null @@ -1,224 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/GNode.h,v 1.8 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef GNode_h_39503bf2 -#define GNode_h_39503bf2 - -#include - -#include "lst.h" - -struct Suff; - -/* - * The structure for an individual graph node. Each node has several - * pieces of data associated with it. - */ -typedef struct GNode { - char *name; /* The target's name */ - char *path; /* The full pathname of the target file */ - - /* - * The type of operator used to define the sources (qv. parse.c) - * - * The OP_ constants are used when parsing a dependency line as a way of - * communicating to other parts of the program the way in which a target - * should be made. These constants are bitwise-OR'ed together and - * placed in the 'type' field of each node. Any node that has - * a 'type' field which satisfies the OP_NOP function was never never on - * the lefthand side of an operator, though it may have been on the - * righthand side... - */ - int type; -#define OP_DEPENDS 0x00000001 /* Execution of commands depends on - * kids (:) */ -#define OP_FORCE 0x00000002 /* Always execute commands (!) */ -#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on - * kids per line (::) */ -#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) - -#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't - * exist and can't be created */ -#define OP_USE 0x00000010 /* - * Use associated commands for - * parents - */ -#define OP_EXEC 0x00000020 /* Target is never out of date, but - * always execute commands anyway. - * Its time doesn't matter, so it has - * none...sort of - */ -#define OP_IGNORE 0x00000040 /* - * Ignore errors when creating the node - */ -#define OP_PRECIOUS 0x00000080 /* Don't remove the target when - * interrupted */ -#define OP_SILENT 0x00000100 /* Don't echo commands when executed */ -#define OP_MAKE 0x00000200 /* - * Target is a recurrsive make so its - * commands should always be executed - * when it is out of date, regardless - * of the state of the -n or -t flags - */ -#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of - * its children was out-of-date */ -#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. - * I.e. it doesn't show up in the - * parents's local variables. */ -#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main - * target' processing in parse.c */ -#define OP_PHONY 0x00010000 /* Not a file target; run always */ -/* Attributes applied by PMake */ -#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ -#define OP_MEMBER 0x40000000 /* Target is a member of an archive */ -#define OP_LIB 0x20000000 /* Target is a library */ -#define OP_ARCHV 0x10000000 /* Target is an archive construct */ -#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it - * should. Used when parsing to catch - * multiple commands for a target */ -#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ -#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ - -/* - * OP_NOP will return true if the node with the given type was not the - * object of a dependency operator - */ -#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) - - int order; /* Its wait weight */ - - bool make; /* true if this target needs to be remade */ - - /* Set to reflect the state of processing on this node */ - enum { - UNMADE, /* Not examined yet */ - - /* - * Target is already being made. Indicates a cycle in the graph. - * (compat mode only) - */ - BEINGMADE, - - MADE, /* Was out-of-date and has been made */ - UPTODATE, /* Was already up-to-date */ - - /* - * An error occured while it was being - * made (used only in compat mode) - */ - ERROR, - - /* - * The target was aborted due to an - * error making an inferior (compat). - */ - ABORTED, - - /* - * Marked as potentially being part of a graph cycle. If we - * come back to a node marked this way, it is printed and - * 'made' is changed to ENDCYCLE. - */ - CYCLE, - - /* - * The cycle has been completely printed. Go back and - * unmark all its members. - */ - ENDCYCLE - } made; - - /* true if one of this target's children was made */ - bool childMade; - - int unmade; /* The number of unmade children */ - int mtime; /* Its modification time */ - int cmtime; /* Modification time of its youngest child */ - - /* - * Links to parents for which this is an implied source, if any. (nodes - * that depend on this, as gleaned from the transformation rules. - */ - Lst iParents; - - /* List of nodes of the same name created by the :: operator */ - Lst cohorts; - - /* Lst of nodes for which this is a source (that depend on this one) */ - Lst parents; - - /* List of nodes on which this depends */ - Lst children; - - /* - * List of nodes that must be made (if they're made) after this node is, - * but that do not depend on this node, in the normal sense. - */ - Lst successors; - - /* - * List of nodes that must be made (if they're made) before this node - * can be, but that do no enter into the datedness of this node. - */ - Lst preds; - - /* - * List of ``local'' variables that are specific to this target - * and this target only (qv. var.c [$@ $< $?, etc.]) - */ - Lst context; - - /* - * List of strings that are commands to be given to a shell - * to create this target. - */ - Lst commands; - - /* current command executing in compat mode */ - LstNode *compat_command; - - /* - * Suffix for the node (determined by Suff_FindDeps and opaque to - * everyone but the Suff module) - */ - struct Suff *suffix; -} GNode; - -#endif /* GNode_h_39503bf2 */ diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile deleted file mode 100644 index 6d92d482e5..0000000000 --- a/usr.bin/make/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -.INTERRUPT: - echo Hey that hurt! -# @(#)Makefile 5.2 (Berkeley) 12/28/90 -# $FreeBSD: src/usr.bin/make/Makefile,v 1.13.2.1 2001/05/25 08:33:40 sobomax Exp $ - -PROG= make -CFLAGS+=-I${.CURDIR} -SRCS= arch.c buf.c cond.c dir.c for.c hash.c hash_tables.c job.c \ - lst.c main.c make.c parse.c proc.c shell.c str.c suff.c targ.c \ - util.c var.c - -NOSHARED?= YES - -CFLAGS+=-DMAKE_VERSION=\"5200408120\" - -# Make object files which depend on preprocessor symbols defined -# in the Makefile which are not compilation options but rather -# configuration options dependend on the Makefile. main.c needs -# MAKE_VERSION, and DEFSHELLNAME. This will cause recompilation -# in the case the definition is changed in the makefile. It will -# of course not cause recompilation if one does 'make MAKE_SHELL=csh'. -main.o: ${MAKEFILE} - -# Directive and keyword tables. We use hash tables. These hash tables have been -# generated with mph which can be found in pkgsrc/devel/mph. -# If you change the directives or keywords (adding, deleting, reordering) you -# need to create new tables and hash functions (directive_hash, keyword_hash). -# -# The following changes have been made to the generated code: -# -# o prefix the names of the g, T0 and T1 arrays with 'directive_' -# resp. 'keyword_'. -# -# o make the type of the tables 'const [un]signed char' (if you change -# anything make sure that the numbers fit into a char). -# -# o make the hash function use the length for termination, -# not the trailing '\0', via the -l flag in emitc and some editing -# (only for directive_hash). - -LOCALBASE ?= /usr/pkg -MPH ?= ${LOCALBASE}/bin/mph -EMITC ?= ${LOCALBASE}/bin/emitc - -.PRECIOUS: hash - -hash: - ( echo '/*' ; \ - echo ' * DO NOT EDIT' ; \ - echo -n ' * auto-generated from ' ; \ - echo ${.CURDIR}/parse.c ; \ - echo ' */' ; \ - echo '#include ' ; \ - echo ; \ - echo '#include "hash_tables.h"' ; \ - echo ; \ - cat ${.CURDIR}/parse.c | sed \ - -e '1,/DIRECTIVES-START-TAG/d' \ - -e '/DIRECTIVES-END-TAG/,$$d' \ - -e 's/^[^"]*"\([^"]*\)".*$$/\1/' | \ - ${MPH} -d2 -m1 | ${EMITC} -l -s | \ - sed \ - -e 's/^static int g\[\]/static const signed char directive_g[]/' \ - -e 's/^static int T0\[\]/static const u_char directive_T0[]/' \ - -e 's/^static int T1\[\]/static const u_char directive_T1[]/' \ - -e '/^#define uchar unsigned char/d' \ - -e 's/uchar/u_char/g' \ - -e 's/^hash(/directive_hash(/' \ - -e 's/; \*kp;/; kp < key + len;/' \ - -e 's/int len)/size_t len)/' \ - -e 's/= T0\[/= directive_T0\[/' \ - -e 's/= T1\[/= directive_T1\[/' \ - -e 's/g\[f/directive_g[f/g' ; \ - cat ${.CURDIR}/parse.c | sed \ - -e '1,/KEYWORD-START-TAG/d' \ - -e '/KEYWORD-END-TAG/,$$d' \ - -e 's/^[^"]*"\([^"]*\)".*$$/\1/' | \ - ${MPH} -d2 -m1 | ${EMITC} -l -s | \ - sed \ - -e 's/^static int g\[\]/static const signed char keyword_g[]/' \ - -e 's/^static int T0\[\]/static const u_char keyword_T0[]/' \ - -e 's/^static int T1\[\]/static const u_char keyword_T1[]/' \ - -e '/^#define uchar unsigned char/d' \ - -e 's/uchar/u_char/g' \ - -e 's/^hash(/keyword_hash(/' \ - -e 's/int len)/size_t len)/' \ - -e 's/= T0\[/= keyword_T0\[/' \ - -e 's/= T1\[/= keyword_T1\[/' \ - -e 's/g\[f/keyword_g[f/g' \ - ) > ${.CURDIR}/hash_tables.c - -# Set the shell which make(1) uses. Bourne is the default, but a decent -# Korn shell works fine, and much faster. Using the C shell for this -# will almost certainly break everything, but it's Unix tradition to -# allow you to shoot yourself in the foot if you want to :-) - -MAKE_SHELL?= sh -.if ${MAKE_SHELL} == "csh" || ${MAKE_SHELL} == "sh" || ${MAKE_SHELL} == "ksh" -CFLAGS+= -DDEFSHELLNAME=\"${MAKE_SHELL}\" -.else -.error "MAKE_SHELL must be set to one of \"csh\", \"sh\" or \"ksh\"." -.endif - -.include diff --git a/usr.bin/make/Makefile.dist b/usr.bin/make/Makefile.dist deleted file mode 100644 index 0e7a1700d8..0000000000 --- a/usr.bin/make/Makefile.dist +++ /dev/null @@ -1,7 +0,0 @@ -# $DragonFly: src/usr.bin/make/Makefile.dist,v 1.4 2005/06/16 20:44:13 okumoto Exp $ -# a very simple makefile... -pmake: - @echo 'make started.' - cc -DDEFSHELLNAME=\"sh\" -I. -c *.c - cc *.o -o pmake - @echo 'make completed.' diff --git a/usr.bin/make/PSD.doc/stubs b/usr.bin/make/PSD.doc/stubs deleted file mode 100644 index 614d180de8..0000000000 --- a/usr.bin/make/PSD.doc/stubs +++ /dev/null @@ -1,10 +0,0 @@ -.\" $FreeBSD: src/usr.bin/make/PSD.doc/stubs,v 1.1.2.1 2000/11/24 10:08:45 ru Exp $ -.\" $DragonFly: src/usr.bin/make/PSD.doc/stubs,v 1.2 2003/06/17 04:29:29 dillon Exp $ -.\" -.de Ix -.. -.de Rd -.. -.de Rm -.. -.if n .ftr CR R diff --git a/usr.bin/make/PSD.doc/tutorial.ms b/usr.bin/make/PSD.doc/tutorial.ms deleted file mode 100644 index bf508fa964..0000000000 --- a/usr.bin/make/PSD.doc/tutorial.ms +++ /dev/null @@ -1,3748 +0,0 @@ -.\" Copyright (c) 1988, 1989 by Adam de Boor -.\" Copyright (c) 1989 by Berkeley Softworks -.\" Copyright (c) 1988, 1989, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Adam de Boor. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93 -.\" $FreeBSD: src/usr.bin/make/PSD.doc/tutorial.ms,v 1.8.2.2 2000/11/24 10:08:45 ru Exp $ -.\" $DragonFly: src/usr.bin/make/PSD.doc/tutorial.ms,v 1.5 2004/11/24 07:20:51 dillon Exp $ -.\" -.EH 'PSD:12-%''PMake \*- A Tutorial' -.OH 'PMake \*- A Tutorial''PSD:12-%' -.\" xH is a macro to provide numbered headers that are automatically stuffed -.\" into a table-of-contents, properly indented, etc. If the first argument -.\" is numeric, it is taken as the depth for numbering (as for .NH), else -.\" the default (1) is assumed. -.\" -.\" @P The initial paragraph distance. -.\" @Q The piece of section number to increment (or 0 if none given) -.\" @R Section header. -.\" @S Indent for toc entry -.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter) -.de xH -.NH \\$1 -\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.nr PD .1v -.XS \\n% -.ta 0.6i -\\*(SN \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 -.XE -.nr PD .3v -.. -.ig -.\" CW is used to place a string in fixed-width or switch to a -.\" fixed-width font. -.\" C is a typewriter font for a laserwriter. Use something else if -.\" you don't have one... -.de CW -.ie !\\n(.$ .ft S -.el \&\\$3\fS\\$1\fP\\$2 -.. -.\" Anything I put in a display I want to be in fixed-width -.am DS -.CW -.. -.\" The stuff in .No produces a little stop sign in the left margin -.\" that says NOTE in it. Unfortunately, it does cause a break, but -.\" hey. Can't have everything. In case you're wondering how I came -.\" up with such weird commands, they came from running grn on a -.\" gremlin file... -.de No -.br -.ne 0.5i -.po -0.5i -.br -.mk -.nr g3 \\n(.f -.nr g4 \\n(.s -.ig ft -.sp -1 -.\" .st cf -\D's -1u'\D't 5u' -.sp -1 -\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u' -.sp -1 -\D't 3u' -.sp -1 -.sp 7u -\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u' -.sp -1 -.ft R -.ps 6 -.nr g8 \\n(.d -.ds g9 "NOTE -.sp 74u -\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 -.sp |\\n(g8u -.sp 166u -.ig br -\D't 3u'\D's -1u' -.br -.po -.rt -.ft \\n(g3 -.ps \\n(g4 -.. -.de Bp -.ie !\\n(.$ .IP \(bu 2 -.el .IP "\&" 2 -.. -.po +.3i -.TL -PMake \*- A Tutorial -.AU -Adam de Boor -.AI -Berkeley Softworks -2150 Shattuck Ave, Penthouse -Berkeley, CA 94704 -adam@bsw.uu.net -\&...!uunet!bsw!adam -.FS -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appears in all copies. -The University of California, Berkeley Softworks, and Adam de Boor make no -representations about the suitability of this software for any -purpose. It is provided "as is" without express or implied warranty. -.FE -.PP -.xH 1 Introduction -.LP -PMake is a program for creating other programs, or anything else you -can think of for it to do. The basic idea behind PMake is that, for -any given system, be it a program or a document or whatever, there -will be some files that depend on the state of other files (on when -they were last modified). PMake takes these dependencies, which you -must specify, and uses them to build whatever it is you want it to -build. -.LP -PMake is almost fully-compatible with Make, with which you may already -be familiar. PMake's most important feature is its ability to run -several different jobs at once, making the creation of systems -considerably faster. It also has a great deal more functionality than -Make. Throughout the text, whenever something is mentioned that is an -important difference between PMake and Make (i.e. something that will -cause a makefile to fail if you don't do something about it), or is -simply important, it will be flagged with a little sign in the left -margin, like this: -.No -.LP -This tutorial is divided into three main sections corresponding to basic, -intermediate and advanced PMake usage. If you already know Make well, -you will only need to skim chapter 2 (there are some aspects of -PMake that I consider basic to its use that didn't exist in Make). -Things in chapter 3 make life much easier, while those in chapter 4 -are strictly for those who know what they are doing. Chapter 5 has -definitions for the jargon I use and chapter 6 contains possible -solutions to the problems presented throughout the tutorial. -.xH 1 The Basics of PMake -.LP -PMake takes as input a file that tells a) which files depend on which -other files to be complete and b) what to do about files that are -``out-of-date.'' This file is known as a ``makefile'' and is usually -.Ix 0 def makefile -kept in the top-most directory of the system to be built. While you -can call the makefile anything you want, PMake will look for -.CW Makefile -and -.CW makefile -(in that order) in the current directory if you don't tell it -otherwise. -.Ix 0 def makefile default -To specify a different makefile, use the -.B \-f -flag (e.g. -.CW "pmake -f program.mk" ''). `` -.Ix 0 ref flags -f -.Ix 0 ref makefile other -.LP -A makefile has four different types of lines in it: -.RS -.IP \(bu 2 -File dependency specifications -.IP \(bu 2 -Creation commands -.IP \(bu 2 -Variable assignments -.IP \(bu 2 -Comments, include statements and conditional directives -.RE -.LP -Any line may be continued over multiple lines by ending it with a -backslash. -.Ix 0 def "continuation line" -The backslash, following newline and any initial whitespace -on the following line are compressed into a single space before the -input line is examined by PMake. -.xH 2 Dependency Lines -.LP -As mentioned in the introduction, in any system, there are -dependencies between the files that make up the system. For instance, -in a program made up of several C source files and one header file, -the C files will need to be re-compiled should the header file be -changed. For a document of several chapters and one macro file, the -chapters will need to be reprocessed if any of the macros changes. -.Ix 0 def "dependency" -These are dependencies and are specified by means of dependency lines in -the makefile. -.LP -.Ix 0 def "dependency line" -On a dependency line, there are targets and sources, separated by a -one- or two-character operator. -The targets ``depend'' on the sources and are usually created from -them. -.Ix 0 def target -.Ix 0 def source -.Ix 0 ref operator -Any number of targets and sources may be specified on a dependency line. -All the targets in the line are made to depend on all the sources. -Targets and sources need not be actual files, but every source must be -either an actual file or another target in the makefile. -If you run out of room, use a backslash at the end of the line to continue onto -the next one. -.LP -Any file may be a target and any file may be a source, but the -relationship between the two (or however many) is determined by the -``operator'' that separates them. -.Ix 0 def operator -Three types of operators exist: one specifies that the datedness of a -target is determined by the state of its sources, while another -specifies other files (the sources) that need to be dealt with before -the target can be re-created. The third operator is very similar to -the first, with the additional condition that the target is -out-of-date if it has no sources. These operations are represented by -the colon, the exclamation point and the double-colon, respectively, and are -mutually exclusive. Their exact semantics are as follows: -.IP ":" -.Ix 0 def operator colon -.Ix 0 def : -If a colon is used, a target on the line is considered to be -``out-of-date'' (and in need of creation) if -.RS -.IP \(bu 2 -any of the sources has been modified more recently than the target, or -.IP \(bu 2 -the target doesn't exist. -.RE -.Ix 0 def out-of-date -.IP "\&" -Under this operation, steps will be taken to re-create the target only -if it is found to be out-of-date by using these two rules. -.IP "!" -.Ix 0 def operator force -.Ix 0 def ! -If an exclamation point is used, the target will always be re-created, -but this will not happen until all of its sources have been examined -and re-created, if necessary. -.IP "::" -.Ix 0 def operator double-colon -.Ix 0 def :: -If a double-colon is used, a target is out-of-date if: -.RS -.IP \(bu 2 -any of the sources has been modified more recently than the target, or -.IP \(bu 2 -the target doesn't exist, or -.IP \(bu 2 -the target has no sources. -.RE -.IP "\&" -If the target is out-of-date according to these rules, it will be re-created. -This operator also does something else to the targets, but I'll go -into that in the next section (``Shell Commands''). -.LP -Enough words, now for an example. Take that C program I mentioned -earlier. Say there are three C files -.CW a.c , ( -.CW b.c -and -.CW c.c ) -each of which -includes the file -.CW defs.h . -The dependencies between the files could then be expressed as follows: -.DS -program : a.o b.o c.o -a.o b.o c.o : defs.h -a.o : a.c -b.o : b.c -c.o : c.c -.DE -.LP -You may be wondering at this point, where -.CW a.o , -.CW b.o -and -.CW c.o -came in and why -.I they -depend on -.CW defs.h -and the C files don't. The reason is quite simple: -.CW program -cannot be made by linking together .c files \*- it must be -made from .o files. Likewise, if you change -.CW defs.h , -it isn't the .c files that need to be re-created, it's the .o files. -If you think of dependencies in these terms \*- which files (targets) -need to be created from which files (sources) \*- you should have no problems. -.LP -An important thing to notice about the above example, is that all the -\&.o files appear as targets on more than one line. This is perfectly -all right: the target is made to depend on all the sources mentioned -on all the dependency lines. E.g. -.CW a.o -depends on both -.CW defs.h -and -.CW a.c . -.Ix 0 ref dependency -.No -.LP -The order of the dependency lines in the makefile is -important: the first target on the first dependency line in the -makefile will be the one that gets made if you don't say otherwise. -That's why -.CW program -comes first in the example makefile, above. -.LP -Both targets and sources may contain the standard C-Shell wildcard -characters -.CW { , ( -.CW } , -.CW * , -.CW ? , -.CW [ , -and -.CW ] ), -but the non-curly-brace ones may only appear in the final component -(the file portion) of the target or source. The characters mean the -following things: -.IP \fB{}\fP -These enclose a comma-separated list of options and cause the pattern -to be expanded once for each element of the list. Each expansion -contains a different element. For example, -.CW src/{whiffle,beep,fish}.c -expands to the three words -.CW src/whiffle.c , -.CW src/beep.c , -and -.CW src/fish.c . -These braces may be nested and, unlike the other wildcard characters, -the resulting words need not be actual files. All other wildcard -characters are expanded using the files that exist when PMake is -started. -.IP \fB*\fP -This matches zero or more characters of any sort. -.CW src/*.c -will expand to the same three words as above as long as -.CW src -contains those three files (and no other files that end in -.CW .c ). -.IP \fB?\fP -Matches any single character. -.IP \fB[]\fP -This is known as a character class and contains either a list of -single characters, or a series of character ranges -.CW a-z , ( -for example means all characters between a and z), or both. It matches -any single character contained in the list. E.g. -.CW [A-Za-z] -will match all letters, while -.CW [0123456789] -will match all numbers. -.xH 2 Shell Commands -.LP -``Isn't that nice,'' you say to yourself, ``but how are files -actually `re-created,' as he likes to spell it?'' -The re-creation is accomplished by commands you place in the makefile. -These commands are passed to the Bourne shell (better known as -``/bin/sh'') to be executed and are -.Ix 0 ref shell -.Ix 0 ref re-creation -.Ix 0 ref update -expected to do what's necessary to update the target file (PMake -doesn't actually check to see if the target was created. It just -assumes it's there). -.Ix 0 ref target -.LP -Shell commands in a makefile look a lot like shell commands you would -type at a terminal, with one important exception: each command in a -makefile -.I must -be preceded by at least one tab. -.LP -Each target has associated with it a shell script made up of -one or more of these shell commands. The creation script for a target -should immediately follow the dependency line for that target. While -any given target may appear on more than one dependency line, only one -of these dependency lines may be followed by a creation script, unless -the `::' operator was used on the dependency line. -.Ix 0 ref operator double-colon -.Ix 0 ref :: -.No -.LP -If the double-colon was used, each dependency line for the target -may be followed by a shell script. That script will only be executed -if the target on the associated dependency line is out-of-date with -respect to the sources on that line, according to the rules I gave -earlier. -I'll give you a good example of this later on. -.LP -To expand on the earlier makefile, you might add commands as follows: -.DS -program : a.o b.o c.o - cc a.o b.o c.o \-o program -a.o b.o c.o : defs.h -a.o : a.c - cc \-c a.c -b.o : b.c - cc \-c b.c -c.o : c.c - cc \-c c.c -.DE -.LP -Something you should remember when writing a makefile is, the -commands will be executed if the -.I target -on the dependency line is out-of-date, not the sources. -.Ix 0 ref target -.Ix 0 ref source -.Ix 0 ref out-of-date -In this example, the command -.CW "cc \-c a.c" '' `` -will be executed if -.CW a.o -is out-of-date. Because of the `:' operator, -.Ix 0 ref : -.Ix 0 ref operator colon -this means that should -.CW a.c -.I or -.CW defs.h -have been modified more recently than -.CW a.o , -the command will be executed -.CW a.o "\&" ( -will be considered out-of-date). -.Ix 0 ref out-of-date -.LP -Remember how I said the only difference between a makefile shell -command and a regular shell command was the leading tab? I lied. There -is another way in which makefile commands differ from regular ones. -The first two characters after the initial whitespace are treated -specially. -If they are any combination of `@' and `\-', they cause PMake to do -different things. -.LP -In most cases, shell commands are printed before they're -actually executed. This is to keep you informed of what's going on. If -an `@' appears, however, this echoing is suppressed. In the case of an -.CW echo -command, say -.CW "echo Linking index" ,'' `` -it would be -rather silly to see -.DS -echo Linking index -Linking index -.DE -.LP -so PMake allows you to place an `@' before the command -.CW "@echo Linking index" '') (`` -to prevent the command from being printed. -.LP -The other special character is the `\-'. In case you didn't know, -shell commands finish with a certain ``exit status.'' This status is -made available by the operating system to whatever program invoked the -command. Normally this status will be 0 if everything went ok and -non-zero if something went wrong. For this reason, PMake will consider -an error to have occurred if one of the shells it invokes returns a non-zero -status. When it detects an error, PMake's usual action is to abort -whatever it's doing and exit with a non-zero status itself (any other -targets that were being created will continue being made, but nothing -new will be started. PMake will exit after the last job finishes). -This behavior can be altered, however, by placing a `\-' at the front -of a command -.CW "\-mv index index.old" ''), (`` -certain command-line arguments, -or doing other things, to be detailed later. In such -a case, the non-zero status is simply ignored and PMake keeps chugging -along. -.No -.LP -Because all the commands are given to a single shell to execute, such -things as setting shell variables, changing directories, etc., last -beyond the command in which they are found. This also allows shell -compound commands (like -.CW for -loops) to be entered in a natural manner. -Since this could cause problems for some makefiles that depend on -each command being executed by a single shell, PMake has a -.B \-B -.Ix 0 ref compatibility -.Ix 0 ref flags -B -flag (it stands for backwards-compatible) that forces each command to -be given to a separate shell. It also does several other things, all -of which I discourage since they are now old-fashioned.\|.\|.\|. -.No -.LP -A target's shell script is fed to the shell on its (the shell's) input stream. -This means that any commands, such as -.CW ci -that need to get input from the terminal won't work right \*- they'll -get the shell's input, something they probably won't find to their -liking. A simple way around this is to give a command like this: -.DS -ci $(SRCS) < /dev/tty -.DE -This would force the program's input to come from the terminal. If you -can't do this for some reason, your only other alternative is to use -PMake in its fullest compatibility mode. See -.B Compatibility -in chapter 4. -.Ix 0 ref compatibility -.LP -.xH 2 Variables -.LP -PMake, like Make before it, has the ability to save text in variables -to be recalled later at your convenience. Variables in PMake are used -much like variables in the shell and, by tradition, consist of -all upper-case letters (you don't -.I have -to use all upper-case letters. -In fact there's nothing to stop you from calling a variable -.CW @^&$%$ . -Just tradition). Variables are assigned-to using lines of the form -.Ix 0 def variable assignment -.DS -VARIABLE = value -.DE -.Ix 0 def variable assignment -appended-to by -.DS -VARIABLE += value -.DE -.Ix 0 def variable appending -.Ix 0 def variable assignment appended -.Ix 0 def += -conditionally assigned-to (if the variable isn't already defined) by -.DS -VARIABLE ?= value -.DE -.Ix 0 def variable assignment conditional -.Ix 0 def ?= -and assigned-to with expansion (i.e. the value is expanded (see below) -before being assigned to the variable\*-useful for placing a value at -the beginning of a variable, or other things) by -.DS -VARIABLE := value -.DE -.Ix 0 def variable assignment expanded -.Ix 0 def := -.LP -Any whitespace before -.I value -is stripped off. When appending, a space is placed between the old -value and the stuff being appended. -.LP -The final way a variable may be assigned to is using -.DS -VARIABLE != shell-command -.DE -.Ix 0 def variable assignment shell-output -.Ix 0 def != -In this case, -.I shell-command -has all its variables expanded (see below) and is passed off to a -shell to execute. The output of the shell is then placed in the -variable. Any newlines (other than the final one) are replaced by -spaces before the assignment is made. This is typically used to find -the current directory via a line like: -.DS -CWD != pwd -.DE -.LP -.B Note: -this is intended to be used to execute commands that produce small amounts -of output (e.g. ``pwd''). The implementation is less than intelligent and will -likely freeze if you execute something that produces thousands of -bytes of output (8 Kb is the limit on many UNIX systems). -.LP -The value of a variable may be retrieved by enclosing the variable -name in parentheses or curly braces and preceding the whole thing -with a dollar sign. -.LP -For example, to set the variable CFLAGS to the string -.CW "\-I/sprite/src/lib/libc \-O" ,'' `` -you would place a line -.DS -CFLAGS = \-I/sprite/src/lib/libc \-O -.DE -in the makefile and use the word -.CW "$(CFLAGS)" -wherever you would like the string -.CW "\-I/sprite/src/lib/libc \-O" -to appear. This is called variable expansion. -.Ix 0 def variable expansion -.No -.LP -Unlike Make, PMake will not expand a variable unless it knows -the variable exists. E.g. if you have a -.CW "${i}" -in a shell command and you have not assigned a value to the variable -.CW i -(the empty string is considered a value, by the way), where Make would have -substituted the empty string, PMake will leave the -.CW "${i}" -alone. -To keep PMake from substituting for a variable it knows, precede the -dollar sign with another dollar sign. -(e.g. to pass -.CW "${HOME}" -to the shell, use -.CW "$${HOME}" ). -This causes PMake, in effect, to expand the -.CW $ -macro, which expands to a single -.CW $ . -For compatibility, Make's style of variable expansion will be used -if you invoke PMake with any of the compatibility flags (\c -.B \-V , -.B \-B -or -.B \-M . -The -.B \-V -flag alters just the variable expansion). -.Ix 0 ref flags -V -.Ix 0 ref flags -B -.Ix 0 ref flags -M -.Ix 0 ref compatibility -.LP -.Ix 0 ref variable expansion -There are two different times at which variable expansion occurs: -When parsing a dependency line, the expansion occurs immediately -upon reading the line. If any variable used on a dependency line is -undefined, PMake will print a message and exit. -Variables in shell commands are expanded when the command is -executed. -Variables used inside another variable are expanded whenever the outer -variable is expanded (the expansion of an inner variable has no effect -on the outer variable. I.e. if the outer variable is used on a dependency -line and in a shell command, and the inner variable changes value -between when the dependency line is read and the shell command is -executed, two different values will be substituted for the outer -variable). -.Ix 0 def variable types -.LP -Variables come in four flavors, though they are all expanded the same -and all look about the same. They are (in order of expanding scope): -.RS -.IP \(bu 2 -Local variables. -.Ix 0 ref variable local -.IP \(bu 2 -Command-line variables. -.Ix 0 ref variable command-line -.IP \(bu 2 -Global variables. -.Ix 0 ref variable global -.IP \(bu 2 -Environment variables. -.Ix 0 ref variable environment -.RE -.LP -The classification of variables doesn't matter much, except that the -classes are searched from the top (local) to the bottom (environment) -when looking up a variable. The first one found wins. -.xH 3 Local Variables -.LP -.Ix 0 def variable local -Each target can have as many as seven local variables. These are -variables that are only ``visible'' within that target's shell script -and contain such things as the target's name, all of its sources (from -all its dependency lines), those sources that were out-of-date, etc. -Four local variables are defined for all targets. They are: -.RS -.IP ".TARGET" -.Ix 0 def variable local .TARGET -.Ix 0 def .TARGET -The name of the target. -.IP ".OODATE" -.Ix 0 def variable local .OODATE -.Ix 0 def .OODATE -The list of the sources for the target that were considered out-of-date. -The order in the list is not guaranteed to be the same as the order in -which the dependencies were given. -.IP ".ALLSRC" -.Ix 0 def variable local .ALLSRC -.Ix 0 def .ALLSRC -The list of all sources for this target in the order in which they -were given. -.IP ".PREFIX" -.Ix 0 def variable local .PREFIX -.Ix 0 def .PREFIX -The target without its suffix and without any leading path. E.g. for -the target -.CW ../../lib/compat/fsRead.c , -this variable would contain -.CW fsRead . -.RE -.LP -Three other local variables are set only for certain targets under -special circumstances. These are the ``.IMPSRC,'' -.Ix 0 ref variable local .IMPSRC -.Ix 0 ref .IMPSRC -``.ARCHIVE,'' -.Ix 0 ref variable local .ARCHIVE -.Ix 0 ref .ARCHIVE -and ``.MEMBER'' -.Ix 0 ref variable local .MEMBER -.Ix 0 ref .MEMBER -variables. When they are set and how they are used is described later. -.LP -Four of these variables may be used in sources as well as in shell -scripts. -.Ix 0 def "dynamic source" -.Ix 0 def source dynamic -These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The -variables in the sources are expanded once for each target on the -dependency line, providing what is known as a ``dynamic source,'' -.Rd 0 -allowing you to specify several dependency lines at once. For example, -.DS -$(OBJS) : $(.PREFIX).c -.DE -will create a dependency between each object file and its -corresponding C source file. -.xH 3 Command-line Variables -.LP -.Ix 0 def variable command-line -Command-line variables are set when PMake is first invoked by giving a -variable assignment as one of the arguments. For example, -.DS -pmake "CFLAGS = -I/sprite/src/lib/libc -O" -.DE -would make -.CW CFLAGS -be a command-line variable with the given value. Any assignments to -.CW CFLAGS -in the makefile will have no effect, because once it -is set, there is (almost) nothing you can do to change a command-line -variable (the search order, you see). Command-line variables may be -set using any of the four assignment operators, though only -.CW = -and -.CW ?= -behave as you would expect them to, mostly because assignments to -command-line variables are performed before the makefile is read, thus -the values set in the makefile are unavailable at the time. -.CW += -.Ix 0 ref += -.Ix 0 ref variable assignment appended -is the same as -.CW = , -because the old value of the variable is sought only in the scope in -which the assignment is taking place (for reasons of efficiency that I -won't get into here). -.CW := -and -.CW ?= -.Ix 0 ref := -.Ix 0 ref ?= -.Ix 0 ref variable assignment expanded -.Ix 0 ref variable assignment conditional -will work if the only variables used are in the environment. -.CW != -is sort of pointless to use from the command line, since the same -effect can no doubt be accomplished using the shell's own command -substitution mechanisms (backquotes and all that). -.xH 3 Global Variables -.LP -.Ix 0 def variable global -Global variables are those set or appended-to in the makefile. -There are two classes of global variables: those you set and those PMake sets. -As I said before, the ones you set can have any name you want them to have, -except they may not contain a colon or an exclamation point. -The variables PMake sets (almost) always begin with a -period and always contain upper-case letters, only. The variables are -as follows: -.RS -.IP .PMAKE -.Ix 0 def variable global .PMAKE -.Ix 0 def .PMAKE -.Ix 0 def variable global MAKE -.Ix 0 def MAKE -The name by which PMake was invoked is stored in this variable. For -compatibility, the name is also stored in the MAKE variable. -.IP .MAKEFLAGS -.Ix 0 def variable global .MAKEFLAGS -.Ix 0 def .MAKEFLAGS variable -.Ix 0 def variable global MFLAGS -.Ix 0 def MFLAGS -All the relevant flags with which PMake was invoked. This does not -include such things as -.B \-f -or variable assignments. Again for compatibility, this value is stored -in the MFLAGS variable as well. -.RE -.LP -Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the -section on special targets in chapter 3. -.Ix 0 ref variable global .INCLUDES -.Ix 0 ref variable global .LIBS -.LP -Global variables may be deleted using lines of the form: -.Ix 0 def #undef -.Ix 0 def variable deletion -.DS -#undef \fIvariable\fP -.DE -The -.CW # ' ` -must be the first character on the line. Note that this may only be -done on global variables. -.xH 3 Environment Variables -.LP -.Ix 0 def variable environment -Environment variables are passed by the shell that invoked PMake and -are given by PMake to each shell it invokes. They are expanded like -any other variable, but they cannot be altered in any way. -.LP -One special environment variable, -.CW PMAKE , -.Ix 0 def variable environment PMAKE -is examined by PMake for command-line flags, variable assignments, -etc., it should always use. This variable is examined before the -actual arguments to PMake are. In addition, all flags given to PMake, -either through the -.CW PMAKE -variable or on the command line, are placed in this environment -variable and exported to each shell PMake executes. Thus recursive -invocations of PMake automatically receive the same flags as the -top-most one. -.LP -Using all these variables, you can compress the sample makefile even more: -.DS -OBJS = a.o b.o c.o -program : $(OBJS) - cc $(.ALLSRC) \-o $(.TARGET) -$(OBJS) : defs.h -a.o : a.c - cc \-c a.c -b.o : b.c - cc \-c b.c -c.o : c.c - cc \-c c.c -.DE -.Ix 0 ref variable local .ALLSRC -.Ix 0 ref .ALLSRC -.Ix 0 ref variable local .TARGET -.Ix 0 ref .TARGET -.Rd 3 -.xH 2 Comments -.LP -.Ix 0 def comments -Comments in a makefile start with a `#' character and extend to the -end of the line. They may appear -anywhere you want them, except in a shell command (though the shell -will treat it as a comment, too). If, for some reason, you need to use the `#' -in a variable or on a dependency line, put a backslash in front of it. -PMake will compress the two into a single `#' (Note: this isn't true -if PMake is operating in full-compatibility mode). -.Ix 0 ref flags -M -.Ix 0 ref compatibility -.xH 2 Parallelism -.No -.LP -PMake was specifically designed to re-create several targets at once, -when possible. You do not have to do anything special to cause this to -happen (unless PMake was configured to not act in parallel, in which -case you will have to make use of the -.B \-L -and -.B \-J -flags (see below)), -.Ix 0 ref flags -L -.Ix 0 ref flags -J -but you do have to be careful at times. -.LP -There are several problems you are likely to encounter. One is -that some makefiles (and programs) are written in such a way that it is -impossible for two targets to be made at once. The program -.CW xstr , -for example, -always modifies the files -.CW strings -and -.CW x.c . -There is no way to change it. Thus you cannot run two of them at once -without something being trashed. Similarly, if you have commands -in the makefile that always send output to the same file, you will not -be able to make more than one target at once unless you change the -file you use. You can, for instance, add a -.CW $$$$ -to the end of the file name to tack on the process ID of the shell -executing the command (each -.CW $$ -expands to a single -.CW $ , -thus giving you the shell variable -.CW $$ ). -Since only one shell is used for all the -commands, you'll get the same file name for each command in the -script. -.LP -The other problem comes from improperly-specified dependencies that -worked in Make because of its sequential, depth-first way of examining -them. While I don't want to go into depth on how PMake -works (look in chapter 4 if you're interested), I will warn you that -files in two different ``levels'' of the dependency tree may be -examined in a different order in PMake than they were in Make. For -example, given the makefile -.DS -a : b c -b : d -.DE -PMake will examine the targets in the order -.CW c , -.CW d , -.CW b , -.CW a . -If the makefile's author expected PMake to abort before making -.CW c -if an error occurred while making -.CW b , -or if -.CW b -needed to exist before -.CW c -was made, -s/he will be sorely disappointed. The dependencies are -incomplete, since in both these cases, -.CW c -would depend on -.CW b . -So watch out. -.LP -Another problem you may face is that, while PMake is set up to handle the -output from multiple jobs in a graceful fashion, the same is not so for input. -It has no way to regulate input to different jobs, -so if you use the redirection from -.CW /dev/tty -I mentioned earlier, you must be careful not to run two of the jobs at once. -.xH 2 Writing and Debugging a Makefile -.LP -Now you know most of what's in a makefile, what do you do next? There -are two choices: (1) use one of the uncommonly-available makefile -generators or (2) write your own makefile (I leave out the third choice of -ignoring PMake and doing everything by hand as being beyond the bounds -of common sense). -.LP -When faced with the writing of a makefile, it is usually best to start -from first principles: just what -.I are -you trying to do? What do you want the makefile finally to produce? -.LP -To begin with a somewhat traditional example, let's say you need to -write a makefile to create a program, -.CW expr , -that takes standard infix expressions and converts them to prefix form (for -no readily apparent reason). You've got three source files, in C, that -make up the program: -.CW main.c , -.CW parse.c , -and -.CW output.c . -Harking back to my pithy advice about dependency lines, you write the -first line of the file: -.DS -expr : main.o parse.o output.o -.DE -because you remember -.CW expr -is made from -.CW .o -files, not -.CW .c -files. Similarly for the -.CW .o -files you produce the lines: -.DS -main.o : main.c -parse.o : parse.c -output.o : output.c -main.o parse.o output.o : defs.h -.DE -.LP -Great. You've now got the dependencies specified. What you need now is -commands. These commands, remember, must produce the target on the -dependency line, usually by using the sources you've listed. -You remember about local variables? Good, so it should come -to you as no surprise when you write -.DS -expr : main.o parse.o output.o - cc -o $(.TARGET) $(.ALLSRC) -.DE -Why use the variables? If your program grows to produce postfix -expressions too (which, of course, requires a name change or two), it -is one fewer place you have to change the file. You cannot do this for -the object files, however, because they depend on their corresponding -source files -.I and -.CW defs.h , -thus if you said -.DS - cc -c $(.ALLSRC) -.DE -you'd get (for -.CW main.o ): -.DS - cc -c main.c defs.h -.DE -which is wrong. So you round out the makefile with these lines: -.DS -main.o : main.c - cc -c main.c -parse.o : parse.c - cc -c parse.c -output.o : output.c - cc -c output.c -.DE -.LP -The makefile is now complete and will, in fact, create the program you -want it to without unnecessary compilations or excessive typing on -your part. There are two things wrong with it, however (aside from it -being altogether too long, something I'll address in chapter 3): -.IP 1) -The string -.CW "main.o parse.o output.o" '' `` -is repeated twice, necessitating two changes when you add postfix -(you were planning on that, weren't you?). This is in direct violation -of de Boor's First Rule of writing makefiles: -.QP -.I -Anything that needs to be written more than once -should be placed in a variable. -.IP "\&" -I cannot emphasize this enough as being very important to the -maintenance of a makefile and its program. -.IP 2) -There is no way to alter the way compilations are performed short of -editing the makefile and making the change in all places. This is evil -and violates de Boor's Second Rule, which follows directly from the -first: -.QP -.I -Any flags or programs used inside a makefile should be placed in a variable so -they may be changed, temporarily or permanently, with the greatest ease. -.LP -The makefile should more properly read: -.DS -OBJS = main.o parse.o output.o -expr : $(OBJS) - $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) -main.o : main.c - $(CC) $(CFLAGS) -c main.c -parse.o : parse.c - $(CC) $(CFLAGS) -c parse.c -output.o : output.c - $(CC) $(CFLAGS) -c output.c -$(OBJS) : defs.h -.DE -Alternatively, if you like the idea of dynamic sources mentioned in -section 2.3.1, -.Rm 0 2.3.1 -.Rd 4 -.Ix 0 ref "dynamic source" -.Ix 0 ref source dynamic -you could write it like this: -.DS -OBJS = main.o parse.o output.o -expr : $(OBJS) - $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) -$(OBJS) : $(.PREFIX).c defs.h - $(CC) $(CFLAGS) -c $(.PREFIX).c -.DE -These two rules and examples lead to de Boor's First Corollary: -.QP -.I -Variables are your friends. -.LP -Once you've written the makefile comes the sometimes-difficult task of -.Ix 0 ref debugging -making sure the darn thing works. Your most helpful tool to make sure -the makefile is at least syntactically correct is the -.B \-n -.Ix 0 ref flags -n -flag, which allows you to see if PMake will choke on the makefile. The -second thing the -.B \-n -flag lets you do is see what PMake would do without it actually doing -it, thus you can make sure the right commands would be executed were -you to give PMake its head. -.LP -When you find your makefile isn't behaving as you hoped, the first -question that comes to mind (after ``What time is it, anyway?'') is -``Why not?'' In answering this, two flags will serve you well: -.CW "-d m" '' `` -.Ix 0 ref flags -d -and -.CW "-p 2" .'' `` -.Ix 0 ref flags -p -The first causes PMake to tell you as it examines each target in the -makefile and indicate why it is deciding whatever it is deciding. You -can then use the information printed for other targets to see where -you went wrong. The -.CW "-p 2" '' `` -flag makes PMake print out its internal state when it is done, -allowing you to see that you forgot to make that one chapter depend on -that file of macros you just got a new version of. The output from -.CW "-p 2" '' `` -is intended to resemble closely a real makefile, but with additional -information provided and with variables expanded in those commands -PMake actually printed or executed. -.LP -Something to be especially careful about is circular dependencies. -.Ix 0 def dependency circular -E.g. -.DS -a : b -b : c d -d : a -.DE -In this case, because of how PMake works, -.CW c -is the only thing PMake will examine, because -.CW d -and -.CW a -will effectively fall off the edge of the universe, making it -impossible to examine -.CW b -(or them, for that matter). -PMake will tell you (if run in its normal mode) all the targets -involved in any cycle it looked at (i.e. if you have two cycles in the -graph (naughty, naughty), but only try to make a target in one of -them, PMake will only tell you about that one. You'll have to try to -make the other to find the second cycle). When run as Make, it will -only print the first target in the cycle. -.xH 2 Invoking PMake -.LP -.Ix 0 ref flags -.Ix 0 ref arguments -.Ix 0 ref usage -PMake comes with a wide variety of flags to choose from. -They may appear in any order, interspersed with command-line variable -assignments and targets to create. -The flags are as follows: -.IP "\fB\-d\fP \fIwhat\fP" -.Ix 0 def flags -d -.Ix 0 ref debugging -This causes PMake to spew out debugging information that -may prove useful to you. If you can't -figure out why PMake is doing what it's doing, you might try using -this flag. The -.I what -parameter is a string of single characters that tell PMake what -aspects you are interested in. Most of what I describe will make -little sense to you, unless you've dealt with Make before. Just -remember where this table is and come back to it as you read on. -The characters and the information they produce are as follows: -.RS -.IP a -Archive searching and caching. -.IP c -Conditional evaluation. -.IP d -The searching and caching of directories. -.IP j -Various snippets of information related to the running of the multiple -shells. Not particularly interesting. -.IP m -The making of each target: what target is being examined; when it was -last modified; whether it is out-of-date; etc. -.IP p -Makefile parsing. -.IP r -Remote execution. -.IP s -The application of suffix-transformation rules. (See chapter 3) -.IP t -The maintenance of the list of targets. -.IP v -Variable assignment. -.RE -.IP "\&" -Of these all, the -.CW m -and -.CW s -letters will be most useful to you. -If the -.B \-d -is the final argument or the argument from which it would get these -key letters (see below for a note about which argument would be used) -begins with a -.B \- , -all of these debugging flags will be set, resulting in massive amounts -of output. -.IP "\fB\-f\fP \fImakefile\fP" -.Ix 0 def flags -f -Specify a makefile to read different from the standard makefiles -.CW Makefile "\&" ( -or -.CW makefile ). -.Ix 0 ref makefile default -.Ix 0 ref makefile other -If -.I makefile -is ``\-'', PMake uses the standard input. This is useful for making -quick and dirty makefiles.\|.\|. -.Ix 0 ref makefile "quick and dirty" -.IP \fB\-h\fP -.Ix 0 def flags -h -Prints out a summary of the various flags PMake accepts. It can also -be used to find out what level of concurrency was compiled into the -version of PMake you are using (look at -.B \-J -and -.B \-L ) -and various other information on how PMake was configured. -.Ix 0 ref configuration -.Ix 0 ref makefilesystem -.IP \fB\-i\fP -.Ix 0 def flags -i -If you give this flag, PMake will ignore non-zero status returned -by any of its shells. It's like placing a `\-' before all the commands -in the makefile. -.IP \fB\-k\fP -.Ix 0 def flags -k -This is similar to -.B \-i -in that it allows PMake to continue when it sees an error, but unlike -.B \-i , -where PMake continues blithely as if nothing went wrong, -.B \-k -causes it to recognize the error and only continue work on those -things that don't depend on the target, either directly or indirectly (through -depending on something that depends on it), whose creation returned the error. -The `k' is for ``keep going''.\|.\|. -.Ix 0 ref target -.IP \fB\-l\fP -.Ix 0 def flags -l -PMake has the ability to lock a directory against other -people executing it in the same directory (by means of a file called -``LOCK.make'' that it creates and checks for in the directory). This -is a Good Thing because two people doing the same thing in the same place -can be disastrous for the final product (too many cooks and all that). -Whether this locking is the default is up to your system -administrator. If locking is on, -.B \-l -will turn it off, and vice versa. Note that this locking will not -prevent \fIyou\fP from invoking PMake twice in the same place \*- if -you own the lock file, PMake will warn you about it but continue to execute. -.IP "\fB\-m\fP \fIdirectory\fP" -.Ix 0 def flags -m -Tells PMake another place to search for included makefiles via the <...> -style. Several -.B \-m -options can be given to form a search path. If this construct is used the -default system makefile search path is completely overridden. -To be explained in chapter 3, section 3.2. -.Rm 2 3.2 -.IP \fB\-n\fP -.Ix 0 def flags -n -This flag tells PMake not to execute the commands needed to update the -out-of-date targets in the makefile. Rather, PMake will simply print -the commands it would have executed and exit. This is particularly -useful for checking the correctness of a makefile. If PMake doesn't do -what you expect it to, it's a good chance the makefile is wrong. -.IP "\fB\-p\fP \fInumber\fP" -.Ix 0 def flags -p -.Ix 0 ref debugging -This causes PMake to print its input in a reasonable form, though -not necessarily one that would make immediate sense to anyone but me. The -.I number -is a bitwise-or of 1 and 2 where 1 means it should print the input -before doing any processing and 2 says it should print it after -everything has been re-created. Thus -.CW "\-p 3" -would print it twice\*-once before processing and once after (you -might find the difference between the two interesting). This is mostly -useful to me, but you may find it informative in some bizarre circumstances. -.IP \fB\-q\fP -.Ix 0 def flags -q -If you give PMake this flag, it will not try to re-create anything. It -will just see if anything is out-of-date and exit non-zero if so. -.IP \fB\-r\fP -.Ix 0 def flags -r -When PMake starts up, it reads a default makefile that tells it what -sort of system it's on and gives it some idea of what to do if you -don't tell it anything. I'll tell you about it in chapter 3. If you -give this flag, PMake won't read the default makefile. -.IP \fB\-s\fP -.Ix 0 def flags -s -This causes PMake to not print commands before they're executed. It -is the equivalent of putting an `@' before every command in the -makefile. -.IP \fB\-t\fP -.Ix 0 def flags -t -Rather than try to re-create a target, PMake will simply ``touch'' it -so as to make it appear up-to-date. If the target didn't exist before, -it will when PMake finishes, but if the target did exist, it will -appear to have been updated. -.IP \fB\-v\fP -.Ix 0 def flags -v -This is a mixed-compatibility flag intended to mimic the System V -version of Make. It is the same as giving -.B \-B , -and -.B \-V -as well as turning off directory locking. Targets can still be created -in parallel, however. This is the mode PMake will enter if it is -invoked either as -.CW smake '' `` -or -.CW vmake ''. `` -.IP \fB\-x\fP -.Ix 0 def flags -x -This tells PMake it's ok to export jobs to other machines, if they're -available. It is used when running in Make mode, as exporting in this -mode tends to make things run slower than if the commands were just -executed locally. -.IP \fB\-B\fP -.Ix 0 ref compatibility -.Ix 0 def flags -B -Forces PMake to be as backwards-compatible with Make as possible while -still being itself. -This includes: -.RS -.IP \(bu 2 -Executing one shell per shell command -.IP \(bu 2 -Expanding anything that looks even vaguely like a variable, with the -empty string replacing any variable PMake doesn't know. -.IP \(bu 2 -Refusing to allow you to escape a `#' with a backslash. -.IP \(bu 2 -Permitting undefined variables on dependency lines and conditionals -(see below). Normally this causes PMake to abort. -.RE -.IP \fB\-C\fP -.Ix 0 def flags -C -This nullifies any and all compatibility mode flags you may have given -or implied up to the time the -.B \-C -is encountered. It is useful mostly in a makefile that you wrote for PMake -to avoid bad things happening when someone runs PMake as -.CW make '' `` -or has things set in the environment that tell it to be compatible. -.B \-C -is -.I not -placed in the -.CW PMAKE -environment variable or the -.CW .MAKEFLAGS -or -.CW MFLAGS -global variables. -.Ix 0 ref variable environment PMAKE -.Ix 0 ref variable global .MAKEFLAGS -.Ix 0 ref variable global MFLAGS -.Ix 0 ref .MAKEFLAGS variable -.Ix 0 ref MFLAGS -.IP "\fB\-D\fP \fIvariable\fP" -.Ix 0 def flags -D -Allows you to define a variable to have -.CW 1 '' `` -as its value. The variable is a global variable, not a command-line -variable. This is useful mostly for people who are used to the C -compiler arguments and those using conditionals, which I'll get into -in section 4.3 -.Rm 1 4.3 -.IP "\fB\-I\fP \fIdirectory\fP" -.Ix 0 def flags -I -Tells PMake another place to search for included makefiles. Yet -another thing to be explained in chapter 3 (section 3.2, to be -precise). -.Rm 2 3.2 -.IP "\fB\-J\fP \fInumber\fP" -.Ix 0 def flags -J -Gives the absolute maximum number of targets to create at once on both -local and remote machines. -.IP "\fB\-L\fP \fInumber\fP" -.Ix 0 def flags -L -This specifies the maximum number of targets to create on the local -machine at once. This may be 0, though you should be wary of doing -this, as PMake may hang until a remote machine becomes available, if -one is not available when it is started. -.IP \fB\-M\fP -.Ix 0 ref compatibility -.Ix 0 def flags -M -This is the flag that provides absolute, complete, full compatibility -with Make. It still allows you to use all but a few of the features of -PMake, but it is non-parallel. This is the mode PMake enters if you -call it -.CW make .'' `` -.IP \fB\-P\fP -.Ix 0 def flags -P -.Ix 0 ref "output control" -When creating targets in parallel, several shells are executing at -once, each wanting to write its own two cent's-worth to the screen. -This output must be captured by PMake in some way in order to prevent -the screen from being filled with garbage even more indecipherable -than you usually see. PMake has two ways of doing this, one of which -provides for much cleaner output and a clear separation between the -output of different jobs, the other of which provides a more immediate -response so one can tell what is really happening. The former is done -by notifying you when the creation of a target starts, capturing the -output and transferring it to the screen all at once when the job -finishes. The latter is done by catching the output of the shell (and -its children) and buffering it until an entire line is received, then -printing that line preceded by an indication of which job produced -the output. Since I prefer this second method, it is the one used by -default. The first method will be used if you give the -.B \-P -flag to PMake. -.IP \fB\-V\fP -.Ix 0 def flags -V -As mentioned before, the -.B \-V -flag tells PMake to use Make's style of expanding variables, -substituting the empty string for any variable it doesn't know. -.IP \fB\-W\fP -.Ix 0 def flags -W -There are several times when PMake will print a message at you that is -only a warning, i.e. it can continue to work in spite of your having -done something silly (such as forgotten a leading tab for a shell -command). Sometimes you are well aware of silly things you have done -and would like PMake to stop bothering you. This flag tells it to shut -up about anything non-fatal. -.IP \fB\-X\fP -.Ix 0 def flags -X -This flag causes PMake to not attempt to export any jobs to another -machine. -.LP -Several flags may follow a single `\-'. Those flags that require -arguments take them from successive parameters. E.g. -.DS -pmake -fDnI server.mk DEBUG /chip2/X/server/include -.DE -will cause PMake to read -.CW server.mk -as the input makefile, define the variable -.CW DEBUG -as a global variable and look for included makefiles in the directory -.CW /chip2/X/server/include . -.xH 2 Summary -.LP -A makefile is made of four types of lines: -.RS -.IP \(bu 2 -Dependency lines -.IP \(bu 2 -Creation commands -.IP \(bu 2 -Variable assignments -.IP \(bu 2 -Comments, include statements and conditional directives -.RE -.LP -A dependency line is a list of one or more targets, an operator -.CW : ', (` -.CW :: ', ` -or -.CW ! '), ` -and a list of zero or more sources. Sources may contain wildcards and -certain local variables. -.LP -A creation command is a regular shell command preceded by a tab. In -addition, if the first two characters after the tab (and other -whitespace) are a combination of -.CW @ ' ` -or -.CW - ', ` -PMake will cause the command to not be printed (if the character is -.CW @ ') ` -or errors from it to be ignored (if -.CW - '). ` -A blank line, dependency line or variable assignment terminates a -creation script. There may be only one creation script for each target -with a -.CW : ' ` -or -.CW ! ' ` -operator. -.LP -Variables are places to store text. They may be unconditionally -assigned-to using the -.CW = ' ` -.Ix 0 ref = -.Ix 0 ref variable assignment -operator, appended-to using the -.CW += ' ` -.Ix 0 ref += -.Ix 0 ref variable assignment appended -operator, conditionally (if the variable is undefined) assigned-to -with the -.CW ?= ' ` -.Ix 0 ref ?= -.Ix 0 ref variable assignment conditional -operator, and assigned-to with variable expansion with the -.CW := ' ` -.Ix 0 ref := -.Ix 0 ref variable assignment expanded -operator. The output of a shell command may be assigned to a variable -using the -.CW != ' ` -.Ix 0 ref != -.Ix 0 ref variable assignment shell-output -operator. Variables may be expanded (their value inserted) by enclosing -their name in parentheses or curly braces, preceded by a dollar sign. -A dollar sign may be escaped with another dollar sign. Variables are -not expanded if PMake doesn't know about them. There are seven local -variables: -.CW .TARGET , -.CW .ALLSRC , -.CW .OODATE , -.CW .PREFIX , -.CW .IMPSRC , -.CW .ARCHIVE , -and -.CW .MEMBER . -Four of them -.CW .TARGET , ( -.CW .PREFIX , -.CW .ARCHIVE , -and -.CW .MEMBER ) -may be used to specify ``dynamic sources.'' -.Ix 0 ref "dynamic source" -.Ix 0 ref source dynamic -Variables are good. Know them. Love them. Live them. -.LP -Debugging of makefiles is best accomplished using the -.B \-n , -.B "\-d m" , -and -.B "\-p 2" -flags. -.xH 2 Exercises -.ce -\s+4\fBTBA\fP\s0 -.xH 1 Short-cuts and Other Nice Things -.LP -Based on what I've told you so far, you may have gotten the impression -that PMake is just a way of storing away commands and making sure you -don't forget to compile something. Good. That's just what it is. -However, the ways I've described have been inelegant, at best, and -painful, at worst. -This chapter contains things that make the -writing of makefiles easier and the makefiles themselves shorter and -easier to modify (and, occasionally, simpler). In this chapter, I -assume you are somewhat more -familiar with Sprite (or UNIX, if that's what you're using) than I did -in chapter 2, just so you're on your toes. -So without further ado... -.xH 2 Transformation Rules -.LP -As you know, a file's name consists of two parts: a base name, which -gives some hint as to the contents of the file, and a suffix, which -usually indicates the format of the file. -Over the years, as -.UX -has developed, -naming conventions, with regard to suffixes, have also developed that have -become almost as incontrovertible as Law. E.g. a file ending in -.CW .c -is assumed to contain C source code; one with a -.CW .o -suffix is assumed to be a compiled, relocatable object file that may -be linked into any program; a file with a -.CW .ms -suffix is usually a text file to be processed by Troff with the \-ms -macro package, and so on. -One of the best aspects of both Make and PMake comes from their -understanding of how the suffix of a file pertains to its contents and -their ability to do things with a file based solely on its suffix. This -ability comes from something known as a transformation rule. A -transformation rule specifies how to change a file with one suffix -into a file with another suffix. -.LP -A transformation rule looks much like a dependency line, except the -target is made of two known suffixes stuck together. Suffixes are made -known to PMake by placing them as sources on a dependency line whose -target is the special target -.CW .SUFFIXES . -E.g. -.DS -\&.SUFFIXES : .o .c -\&.c.o : - $(CC) $(CFLAGS) -c $(.IMPSRC) -.DE -The creation script attached to the target is used to transform a file with -the first suffix (in this case, -.CW .c ) -into a file with the second suffix (here, -.CW .o ). -In addition, the target inherits whatever attributes have been applied -to the transformation rule. -The simple rule given above says that to transform a C source file -into an object file, you compile it using -.CW cc -with the -.CW \-c -flag. -This rule is taken straight from the system makefile. Many -transformation rules (and suffixes) are defined there, and I refer you -to it for more examples (type -.CW "pmake -h" '' `` -to find out where it is). -.LP -There are several things to note about the transformation rule given -above: -.RS -.IP 1) -The -.CW .IMPSRC -variable. -.Ix 0 def variable local .IMPSRC -.Ix 0 def .IMPSRC -This variable is set to the ``implied source'' (the file from which -the target is being created; the one with the first suffix), which, in this -case, is the .c file. -.IP 2) -The -.CW CFLAGS -variable. Almost all of the transformation rules in the system -makefile are set up using variables that you can alter in your -makefile to tailor the rule to your needs. In this case, if you want -all your C files to be compiled with the -.B \-g -flag, to provide information for -.CW dbx , -you would set the -.CW CFLAGS -variable to contain -.CW -g -.CW "CFLAGS = -g" '') (`` -and PMake would take care of the rest. -.RE -.LP -To give you a quick example, the makefile in 2.3.4 -.Rm 3 2.3.4 -could be changed to this: -.DS -OBJS = a.o b.o c.o -program : $(OBJS) - $(CC) -o $(.TARGET) $(.ALLSRC) -$(OBJS) : defs.h -.DE -The transformation rule I gave above takes the place of the 6 lines\** -.FS -This is also somewhat cleaner, I think, than the dynamic source -solution presented in 2.6 -.FE -.Rm 4 2.6 -.DS -a.o : a.c - cc -c a.c -b.o : b.c - cc -c b.c -c.o : c.c - cc -c c.c -.DE -.LP -Now you may be wondering about the dependency between the -.CW .o -and -.CW .c -files \*- it's not mentioned anywhere in the new makefile. This is -because it isn't needed: one of the effects of applying a -transformation rule is the target comes to depend on the implied -source. That's why it's called the implied -.I source . -.LP -For a more detailed example. Say you have a makefile like this: -.DS -a.out : a.o b.o - $(CC) $(.ALLSRC) -.DE -and a directory set up like this: -.DS -total 4 --rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile --rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c --rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o --rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c -.DE -While just typing -.CW pmake '' `` -will do the right thing, it's much more informative to type -.CW "pmake -d s" ''. `` -This will show you what PMake is up to as it processes the files. In -this case, PMake prints the following: -.DS -Suff_FindDeps (a.out) - using existing source a.o - applying .o -> .out to "a.o" -Suff_FindDeps (a.o) - trying a.c...got it - applying .c -> .o to "a.c" -Suff_FindDeps (b.o) - trying b.c...got it - applying .c -> .o to "b.c" -Suff_FindDeps (a.c) - trying a.y...not there - trying a.l...not there - trying a.c,v...not there - trying a.y,v...not there - trying a.l,v...not there -Suff_FindDeps (b.c) - trying b.y...not there - trying b.l...not there - trying b.c,v...not there - trying b.y,v...not there - trying b.l,v...not there ---- a.o --- -cc -c a.c ---- b.o --- -cc -c b.c ---- a.out --- -cc a.o b.o -.DE -.LP -.CW Suff_FindDeps -is the name of a function in PMake that is called to check for implied -sources for a target using transformation rules. -The transformations it tries are, naturally -enough, limited to the ones that have been defined (a transformation -may be defined multiple times, by the way, but only the most recent -one will be used). You will notice, however, that there is a definite -order to the suffixes that are tried. This order is set by the -relative positions of the suffixes on the -.CW .SUFFIXES -line \*- the earlier a suffix appears, the earlier it is checked as -the source of a transformation. Once a suffix has been defined, the -only way to change its position in the pecking order is to remove all -the suffixes (by having a -.CW .SUFFIXES -dependency line with no sources) and redefine them in the order you -want. (Previously-defined transformation rules will be automatically -redefined as the suffixes they involve are re-entered.) -.LP -Another way to affect the search order is to make the dependency -explicit. In the above example, -.CW a.out -depends on -.CW a.o -and -.CW b.o . -Since a transformation exists from -.CW .o -to -.CW .out , -PMake uses that, as indicated by the -.CW "using existing source a.o" '' `` -message. -.LP -The search for a transformation starts from the suffix of the target -and continues through all the defined transformations, in the order -dictated by the suffix ranking, until an existing file with the same -base (the target name minus the suffix and any leading directories) is -found. At that point, one or more transformation rules will have been -found to change the one existing file into the target. -.LP -For example, ignoring what's in the system makefile for now, say you -have a makefile like this: -.DS -\&.SUFFIXES : .out .o .c .y .l -\&.l.c : - lex $(.IMPSRC) - mv lex.yy.c $(.TARGET) -\&.y.c : - yacc $(.IMPSRC) - mv y.tab.c $(.TARGET) -\&.c.o : - cc -c $(.IMPSRC) -\&.o.out : - cc -o $(.TARGET) $(.IMPSRC) -.DE -and the single file -.CW jive.l . -If you were to type -.CW "pmake -rd ms jive.out" ,'' `` -you would get the following output for -.CW jive.out : -.DS -Suff_FindDeps (jive.out) - trying jive.o...not there - trying jive.c...not there - trying jive.y...not there - trying jive.l...got it - applying .l -> .c to "jive.l" - applying .c -> .o to "jive.c" - applying .o -> .out to "jive.o" -.DE -and this is why: PMake starts with the target -.CW jive.out , -figures out its suffix -.CW .out ) ( -and looks for things it can transform to a -.CW .out -file. In this case, it only finds -.CW .o , -so it looks for the file -.CW jive.o . -It fails to find it, so it looks for transformations into a -.CW .o -file. Again it has only one choice: -.CW .c . -So it looks for -.CW jive.c -and, as you know, fails to find it. At this point it has two choices: -it can create the -.CW .c -file from either a -.CW .y -file or a -.CW .l -file. Since -.CW .y -came first on the -.CW .SUFFIXES -line, it checks for -.CW jive.y -first, but can't find it, so it looks for -.CW jive.l -and, lo and behold, there it is. -At this point, it has defined a transformation path as follows: -.CW .l -\(-> -.CW .c -\(-> -.CW .o -\(-> -.CW .out -and applies the transformation rules accordingly. For completeness, -and to give you a better idea of what PMake actually did with this -three-step transformation, this is what PMake printed for the rest of -the process: -.DS -Suff_FindDeps (jive.o) - using existing source jive.c - applying .c -> .o to "jive.c" -Suff_FindDeps (jive.c) - using existing source jive.l - applying .l -> .c to "jive.l" -Suff_FindDeps (jive.l) -Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date -Examining jive.c...non-existent...out-of-date ---- jive.c --- -lex jive.l -\&.\|.\|. meaningless lex output deleted .\|.\|. -mv lex.yy.c jive.c -Examining jive.o...non-existent...out-of-date ---- jive.o --- -cc -c jive.c -Examining jive.out...non-existent...out-of-date ---- jive.out --- -cc -o jive.out jive.o -.DE -.LP -One final question remains: what does PMake do with targets that have -no known suffix? PMake simply pretends it actually has a known suffix -and searches for transformations accordingly. -The suffix it chooses is the source for the -.CW .NULL -.Ix 0 ref .NULL -target mentioned later. In the system makefile, -.CW .out -is chosen as the ``null suffix'' -.Ix 0 def suffix null -.Ix 0 def "null suffix" -because most people use PMake to create programs. You are, however, -free and welcome to change it to a suffix of your own choosing. -The null suffix is ignored, however, when PMake is in compatibility -mode (see chapter 4). -.xH 2 Including Other Makefiles -.Ix 0 def makefile inclusion -.Rd 2 -.LP -Just as for programs, it is often useful to extract certain parts of a -makefile into another file and just include it in other makefiles -somehow. Many compilers allow you say something like -.DS -#include "defs.h" -.DE -to include the contents of -.CW defs.h -in the source file. PMake allows you to do the same thing for -makefiles, with the added ability to use variables in the filenames. -An include directive in a makefile looks either like this: -.DS -#include -.DE -or this -.DS -#include "file" -.DE -The difference between the two is where PMake searches for the file: -the first way, PMake will look for -the file only in the system makefile directory (or directories) -(to find out what that directory is, give PMake the -.B \-h -flag). -.Ix 0 ref flags -h -The system makefile directory search path can be overridden via the -.B \-m -option. -.Ix 0 ref flags -m -For files in double-quotes, the search is more complex: -.RS -.IP 1) -The directory of the makefile that's including the file. -.IP 2) -The current directory (the one in which you invoked PMake). -.IP 3) -The directories given by you using -.B \-I -flags, in the order in which you gave them. -.IP 4) -Directories given by -.CW .PATH -dependency lines (see chapter 4). -.IP 5) -The system makefile directory. -.RE -.LP -in that order. -.LP -You are free to use PMake variables in the filename\*-PMake will -expand them before searching for the file. You must specify the -searching method with either angle brackets or double-quotes -.I outside -of a variable expansion. I.e. the following -.DS -SYSTEM = - -#include $(SYSTEM) -.DE -won't work. -.xH 2 Saving Commands -.LP -.Ix 0 def ... -There may come a time when you will want to save certain commands to -be executed when everything else is done. For instance: you're -making several different libraries at one time and you want to create the -members in parallel. Problem is, -.CW ranlib -is another one of those programs that can't be run more than once in -the same directory at the same time (each one creates a file called -.CW __.SYMDEF -into which it stuffs information for the linker to use. Two of them -running at once will overwrite each other's file and the result will -be garbage for both parties). You might want a way to save the ranlib -commands til the end so they can be run one after the other, thus -keeping them from trashing each other's file. PMake allows you to do -this by inserting an ellipsis (``.\|.\|.'') as a command between -commands to be run at once and those to be run later. -.LP -So for the -.CW ranlib -case above, you might do this: -.Rd 5 -.DS -lib1.a : $(LIB1OBJS) - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) - -lib2.a : $(LIB2OBJS) - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -This would save both -.DS -ranlib $(.TARGET) -.DE -commands until the end, when they would run one after the other -(using the correct value for the -.CW .TARGET -variable, of course). -.LP -Commands saved in this manner are only executed if PMake manages to -re-create everything without an error. -.xH 2 Target Attributes -.LP -PMake allows you to give attributes to targets by means of special -sources. Like everything else PMake uses, these sources begin with a -period and are made up of all upper-case letters. There are various -reasons for using them, and I will try to give examples for most of -them. Others you'll have to find uses for yourself. Think of it as ``an -exercise for the reader.'' By placing one (or more) of these as a source on a -dependency line, you are ``marking the target(s) with that -attribute.'' That's just the way I phrase it, so you know. -.LP -Any attributes given as sources for a transformation rule are applied -to the target of the transformation rule when the rule is applied. -.Ix 0 def attributes -.Ix 0 ref source -.Ix 0 ref target -.nr pw 12 -.IP .DONTCARE \n(pw -.Ix 0 def attributes .DONTCARE -.Ix 0 def .DONTCARE -If a target is marked with this attribute and PMake can't figure out -how to create it, it will ignore this fact and assume the file isn't -really needed or actually exists and PMake just can't find it. This may prove -wrong, but the error will be noted later on, not when PMake tries to create -the target so marked. This attribute also prevents PMake from -attempting to touch the target if it is given the -.B \-t -flag. -.Ix 0 ref flags -t -.IP .EXEC \n(pw -.Ix 0 def attributes .EXEC -.Ix 0 def .EXEC -This attribute causes its shell script to be executed while having no -effect on targets that depend on it. This makes the target into a sort -of subroutine. An example. Say you have some LISP files that need to -be compiled and loaded into a LISP process. To do this, you echo LISP -commands into a file and execute a LISP with this file as its input -when everything's done. Say also that you have to load other files -from another system before you can compile your files and further, -that you don't want to go through the loading and dumping unless one -of -.I your -files has changed. Your makefile might look a little bit -like this (remember, this is an educational example, and don't worry -about the -.CW COMPILE -rule, all will soon become clear, grasshopper): -.DS -system : init a.fasl b.fasl c.fasl - for i in $(.ALLSRC); - do - echo -n '(load "' >> input - echo -n ${i} >> input - echo '")' >> input - done - echo '(dump "$(.TARGET)")' >> input - lisp < input - -a.fasl : a.l init COMPILE -b.fasl : b.l init COMPILE -c.fasl : c.l init COMPILE -COMPILE : .USE - echo '(compile "$(.ALLSRC)")' >> input -init : .EXEC - echo '(load-system)' > input -.DE -.Ix 0 ref .USE -.Ix 0 ref attributes .USE -.Ix 0 ref variable local .ALLSRC -.IP "\&" -.CW .EXEC -sources, don't appear in the local variables of targets that depend on -them (nor are they touched if PMake is given the -.B \-t -flag). -.Ix 0 ref flags -t -Note that all the rules, not just that for -.CW system , -include -.CW init -as a source. This is because none of the other targets can be made -until -.CW init -has been made, thus they depend on it. -.IP .EXPORT \n(pw -.Ix 0 def attributes .EXPORT -.Ix 0 def .EXPORT -This is used to mark those targets whose creation should be sent to -another machine if at all possible. This may be used by some -exportation schemes if the exportation is expensive. You should ask -your system administrator if it is necessary. -.IP .EXPORTSAME \n(pw -.Ix 0 def attributes .EXPORTSAME -.Ix 0 def .EXPORTSAME -Tells the export system that the job should be exported to a machine -of the same architecture as the current one. Certain operations (e.g. -running text through -.CW nroff ) -can be performed the same on any architecture (CPU and -operating system type), while others (e.g. compiling a program with -.CW cc ) -must be performed on a machine with the same architecture. Not all -export systems will support this attribute. -.IP .IGNORE \n(pw -.Ix 0 def attributes .IGNORE -.Ix 0 def .IGNORE attribute -Giving a target the -.CW .IGNORE -attribute causes PMake to ignore errors from any of the target's commands, as -if they all had `\-' before them. -.IP .INVISIBLE \n(pw -.Ix 0 def attributes .INVISIBLE -.Ix 0 def .INVISIBLE -This allows you to specify one target as a source for another without -the one affecting the other's local variables. Useful if, say, you -have a makefile that creates two programs, one of which is used to -create the other, so it must exist before the other is created. You -could say -.DS -prog1 : $(PROG1OBJS) prog2 MAKEINSTALL -prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL -.DE -where -.CW MAKEINSTALL -is some complex .USE rule (see below) that depends on the -.Ix 0 ref .USE -.CW .ALLSRC -variable containing the right things. Without the -.CW .INVISIBLE -attribute for -.CW prog2 , -the -.CW MAKEINSTALL -rule couldn't be applied. This is not as useful as it should be, and -the semantics may change (or the whole thing go away) in the -not-too-distant future. -.IP .JOIN \n(pw -.Ix 0 def attributes .JOIN -.Ix 0 def .JOIN -This is another way to avoid performing some operations in parallel -while permitting everything else to be done so. Specifically it -forces the target's shell script to be executed only if one or more of the -sources was out-of-date. In addition, the target's name, -in both its -.CW .TARGET -variable and all the local variables of any target that depends on it, -is replaced by the value of its -.CW .ALLSRC -variable. -As an example, suppose you have a program that has four libraries that -compile in the same directory along with, and at the same time as, the -program. You again have the problem with -.CW ranlib -that I mentioned earlier, only this time it's more severe: you -can't just put the ranlib off to the end since the program -will need those libraries before it can be re-created. You can do -something like this: -.DS -program : $(OBJS) libraries - cc -o $(.TARGET) $(.ALLSRC) - -libraries : lib1.a lib2.a lib3.a lib4.a .JOIN - ranlib $(.OODATE) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -.Ix 0 ref variable local .OODATE -.Ix 0 ref .TARGET -.Ix 0 ref .ALLSRC -.Ix 0 ref .OODATE -In this case, PMake will re-create the -.CW $(OBJS) -as necessary, along with -.CW lib1.a , -.CW lib2.a , -.CW lib3.a -and -.CW lib4.a . -It will then execute -.CW ranlib -on any library that was changed and set -.CW program 's -.CW .ALLSRC -variable to contain what's in -.CW $(OBJS) -followed by -.CW "lib1.a lib2.a lib3.a lib4.a" .'' `` -In case you're wondering, it's called -.CW .JOIN -because it joins together different threads of the ``input graph'' at -the target marked with the attribute. -Another aspect of the .JOIN attribute is it keeps the target from -being created if the -.B \-t -flag was given. -.Ix 0 ref flags -t -.IP .MAKE \n(pw -.Ix 0 def attributes .MAKE -.Ix 0 def .MAKE -The -.CW .MAKE -attribute marks its target as being a recursive invocation of PMake. -This forces PMake to execute the script associated with the target (if -it's out-of-date) even if you gave the -.B \-n -or -.B \-t -flag. By doing this, you can start at the top of a system and type -.DS -pmake -n -.DE -and have it descend the directory tree (if your makefiles are set up -correctly), printing what it would have executed if you hadn't -included the -.B \-n -flag. -.IP .NOEXPORT \n(pw -.Ix 0 def attributes .NOEXPORT -.Ix 0 def .NOEXPORT attribute -If possible, PMake will attempt to export the creation of all targets to -another machine (this depends on how PMake was configured). Sometimes, -the creation is so simple, it is pointless to send it to another -machine. If you give the target the -.CW .NOEXPORT -attribute, it will be run locally, even if you've given PMake the -.B "\-L 0" -flag. -.IP .NOTMAIN \n(pw -.Ix 0 def attributes .NOTMAIN -.Ix 0 def .NOTMAIN -Normally, if you do not specify a target to make in any other way, -PMake will take the first target on the first dependency line of a -makefile as the target to create. That target is known as the ``Main -Target'' and is labeled as such if you print the dependencies out -using the -.B \-p -flag. -.Ix 0 ref flags -p -Giving a target this attribute tells PMake that the target is -definitely -.I not -the Main Target. -This allows you to place targets in an included makefile and -have PMake create something else by default. -.IP .PRECIOUS \n(pw -.Ix 0 def attributes .PRECIOUS -.Ix 0 def .PRECIOUS attribute -When PMake is interrupted (you type control-C at the keyboard), it -will attempt to clean up after itself by removing any half-made -targets. If a target has the -.CW .PRECIOUS -attribute, however, PMake will leave it alone. An additional side -effect of the `::' operator is to mark the targets as -.CW .PRECIOUS . -.Ix 0 ref operator double-colon -.Ix 0 ref :: -.IP .SILENT \n(pw -.Ix 0 def attributes .SILENT -.Ix 0 def .SILENT attribute -Marking a target with this attribute keeps its commands from being -printed when they're executed, just as if they had an `@' in front of them. -.IP .USE \n(pw -.Ix 0 def attributes .USE -.Ix 0 def .USE -By giving a target this attribute, you turn it into PMake's equivalent -of a macro. When the target is used as a source for another target, -the other target acquires the commands, sources and attributes (except -.CW .USE ) -of the source. -If the target already has commands, the -.CW .USE -target's commands are added to the end. If more than one .USE-marked -source is given to a target, the rules are applied sequentially. -.IP "\&" \n(pw -The typical .USE rule (as I call them) will use the sources of the -target to which it is applied (as stored in the -.CW .ALLSRC -variable for the target) as its ``arguments,'' if you will. -For example, you probably noticed that the commands for creating -.CW lib1.a -and -.CW lib2.a -in the example in section 3.3 -.Rm 5 3.3 -were exactly the same. You can use the -.CW .USE -attribute to eliminate the repetition, like so: -.DS -lib1.a : $(LIB1OBJS) MAKELIB -lib2.a : $(LIB2OBJS) MAKELIB - -MAKELIB : .USE - rm -f $(.TARGET) - ar cr $(.TARGET) $(.ALLSRC) - ... - ranlib $(.TARGET) -.DE -.Ix 0 ref variable local .TARGET -.Ix 0 ref variable local .ALLSRC -.IP "\&" \n(pw -Several system makefiles (not to be confused with The System Makefile) -make use of these .USE rules to make your -life easier (they're in the default, system makefile directory...take a look). -Note that the .USE rule source itself -.CW MAKELIB ) ( -does not appear in any of the targets's local variables. -There is no limit to the number of times I could use the -.CW MAKELIB -rule. If there were more libraries, I could continue with -.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' `` -and so on and so forth. -.xH 2 Special Targets -.LP -As there were in Make, so there are certain targets that have special -meaning to PMake. When you use one on a dependency line, it is the -only target that may appear on the left-hand-side of the operator. -.Ix 0 ref target -.Ix 0 ref operator -As for the attributes and variables, all the special targets -begin with a period and consist of upper-case letters only. -I won't describe them all in detail because some of them are rather -complex and I'll describe them in more detail than you'll want in -chapter 4. -The targets are as follows: -.nr pw 10 -.IP .BEGIN \n(pw -.Ix 0 def .BEGIN -Any commands attached to this target are executed before anything else -is done. You can use it for any initialization that needs doing. -.IP .DEFAULT \n(pw -.Ix 0 def .DEFAULT -This is sort of a .USE rule for any target (that was used only as a -source) that PMake can't figure out any other way to create. It's only -``sort of'' a .USE rule because only the shell script attached to the -.CW .DEFAULT -target is used. The -.CW .IMPSRC -variable of a target that inherits -.CW .DEFAULT 's -commands is set to the target's own name. -.Ix 0 ref .IMPSRC -.Ix 0 ref variable local .IMPSRC -.IP .END \n(pw -.Ix 0 def .END -This serves a function similar to -.CW .BEGIN , -in that commands attached to it are executed once everything has been -re-created (so long as no errors occurred). It also serves the extra -function of being a place on which PMake can hang commands you put off -to the end. Thus the script for this target will be executed before -any of the commands you save with the ``.\|.\|.''. -.Ix 0 ref ... -.IP .EXPORT \n(pw -The sources for this target are passed to the exportation system compiled -into PMake. Some systems will use these sources to configure -themselves. You should ask your system administrator about this. -.IP .IGNORE \n(pw -.Ix 0 def .IGNORE target -.Ix 0 ref .IGNORE attribute -.Ix 0 ref attributes .IGNORE -This target marks each of its sources with the -.CW .IGNORE -attribute. If you don't give it any sources, then it is like -giving the -.B \-i -flag when you invoke PMake \*- errors are ignored for all commands. -.Ix 0 ref flags -i -.IP .INCLUDES \n(pw -.Ix 0 def .INCLUDES target -.Ix 0 def variable global .INCLUDES -.Ix 0 def .INCLUDES variable -The sources for this target are taken to be suffixes that indicate a -file that can be included in a program source file. -The suffix must have already been declared with -.CW .SUFFIXES -(see below). -Any suffix so marked will have the directories on its search path -(see -.CW .PATH , -below) placed in the -.CW .INCLUDES -variable, each preceded by a -.B \-I -flag. This variable can then be used as an argument for the compiler -in the normal fashion. The -.CW .h -suffix is already marked in this way in the system makefile. -.Ix 0 ref makefilesystem -E.g. if you have -.DS -\&.SUFFIXES : .bitmap -\&.PATH.bitmap : /usr/local/X/lib/bitmaps -\&.INCLUDES : .bitmap -.DE -PMake will place -.CW "-I/usr/local/X/lib/bitmaps" '' `` -in the -.CW .INCLUDES -variable and you can then say -.DS -cc $(.INCLUDES) -c xprogram.c -.DE -(Note: the -.CW .INCLUDES -variable is not actually filled in until the entire makefile has been read.) -.IP .INTERRUPT \n(pw -.Ix 0 def .INTERRUPT -When PMake is interrupted, -it will execute the commands in the script for this target, if it -exists. -.IP .LIBS \n(pw -.Ix 0 def .LIBS target -.Ix 0 def .LIBS variable -.Ix 0 def variable global .LIBS -This does for libraries what -.CW .INCLUDES -does for include files, except the flag used is -.B \-L , -as required by those linkers that allow you to tell them where to find -libraries. The variable used is -.CW .LIBS . -Be forewarned that PMake may not have been compiled to do this if the -linker on your system doesn't accept the -.B \-L -flag, though the -.CW .LIBS -variable will always be defined once the makefile has been read. -.IP .MAIN \n(pw -.Ix 0 def .MAIN -If you didn't give a target (or targets) to create when you invoked -PMake, it will take the sources of this target as the targets to -create. -.IP .MAKEFLAGS \n(pw -.Ix 0 def .MAKEFLAGS target -This target provides a way for you to always specify flags for PMake -when the makefile is used. The flags are just as they would be typed -to the shell (except you can't use shell variables unless they're in -the environment), -though the -.B \-f -and -.B \-r -flags have no effect. -.IP .NULL \n(pw -.Ix 0 def .NULL -.Ix 0 ref suffix null -.Ix 0 ref "null suffix" -This allows you to specify what suffix PMake should pretend a file has -if, in fact, it has no known suffix. Only one suffix may be so -designated. The last source on the dependency line is the suffix that -is used (you should, however, only give one suffix.\|.\|.). -.IP .PATH \n(pw -.Ix 0 def .PATH -If you give sources for this target, PMake will take them as -directories in which to search for files it cannot find in the current -directory. If you give no sources, it will clear out any directories -added to the search path before. Since the effects of this all get -very complex, I'll leave it til chapter four to give you a complete -explanation. -.IP .PATH\fIsuffix\fP \n(pw -.Ix 0 ref .PATH -This does a similar thing to -.CW .PATH , -but it does it only for files with the given suffix. The suffix must -have been defined already. Look at -.B "Search Paths" -(section 4.1) -.Rm 6 4.1 -for more information. -.IP .PRECIOUS \n(pw -.Ix 0 def .PRECIOUS target -.Ix 0 ref .PRECIOUS attribute -.Ix 0 ref attributes .PRECIOUS -Similar to -.CW .IGNORE , -this gives the -.CW .PRECIOUS -attribute to each source on the dependency line, unless there are no -sources, in which case the -.CW .PRECIOUS -attribute is given to every target in the file. -.IP .RECURSIVE \n(pw -.Ix 0 def .RECURSIVE -.Ix 0 ref attributes .MAKE -.Ix 0 ref .MAKE -This target applies the -.CW .MAKE -attribute to all its sources. It does nothing if you don't give it any sources. -.IP .SHELL \n(pw -.Ix 0 def .SHELL -PMake is not constrained to only using the Bourne shell to execute -the commands you put in the makefile. You can tell it some other shell -to use with this target. Check out -.B "A Shell is a Shell is a Shell" -(section 4.4) -.Rm 7 4.4 -for more information. -.IP .SILENT \n(pw -.Ix 0 def .SILENT target -.Ix 0 ref .SILENT attribute -.Ix 0 ref attributes .SILENT -When you use -.CW .SILENT -as a target, it applies the -.CW .SILENT -attribute to each of its sources. If there are no sources on the -dependency line, then it is as if you gave PMake the -.B \-s -flag and no commands will be echoed. -.IP .SUFFIXES \n(pw -.Ix 0 def .SUFFIXES -This is used to give new file suffixes for PMake to handle. Each -source is a suffix PMake should recognize. If you give a -.CW .SUFFIXES -dependency line with no sources, PMake will forget about all the -suffixes it knew (this also nukes the null suffix). -For those targets that need to have suffixes defined, this is how you do it. -.LP -In addition to these targets, a line of the form -.DS -\fIattribute\fP : \fIsources\fP -.DE -applies the -.I attribute -to all the targets listed as -.I sources . -.xH 2 Modifying Variable Expansion -.LP -.Ix 0 def variable expansion modified -.Ix 0 ref variable expansion -.Ix 0 def variable modifiers -Variables need not always be expanded verbatim. PMake defines several -modifiers that may be applied to a variable's value before it is -expanded. You apply a modifier by placing it after the variable name -with a colon between the two, like so: -.DS -${\fIVARIABLE\fP:\fImodifier\fP} -.DE -Each modifier is a single character followed by something specific to -the modifier itself. -You may apply as many modifiers as you want \*- each one is applied to -the result of the previous and is separated from the previous by -another colon. -.LP -There are seven ways to modify a variable's expansion, most of which -come from the C shell variable modification characters: -.RS -.IP "M\fIpattern\fP" -.Ix 0 def :M -.Ix 0 def modifier match -This is used to select only those words (a word is a series of -characters that are neither spaces nor tabs) that match the given -.I pattern . -The pattern is a wildcard pattern like that used by the shell, where -.CW * -means 0 or more characters of any sort; -.CW ? -is any single character; -.CW [abcd] -matches any single character that is either `a', `b', `c' or `d' -(there may be any number of characters between the brackets); -.CW [0-9] -matches any single character that is between `0' and `9' (i.e. any -digit. This form may be freely mixed with the other bracket form), and -`\\' is used to escape any of the characters `*', `?', `[' or `:', -leaving them as regular characters to match themselves in a word. -For example, the system makefile -.CW -uses -.CW "$(CFLAGS:M-[ID]*)" '' `` -to extract all the -.CW \-I -and -.CW \-D -flags that would be passed to the C compiler. This allows it to -properly locate include files and generate the correct dependencies. -.IP "N\fIpattern\fP" -.Ix 0 def :N -.Ix 0 def modifier nomatch -This is identical to -.CW :M -except it substitutes all words that don't match the given pattern. -.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]" -.Ix 0 def :S -.Ix 0 def modifier substitute -Causes the first occurrence of -.I search-string -in the variable to be replaced by -.I replacement-string , -unless the -.CW g -flag is given at the end, in which case all occurrences of the string -are replaced. The substitution is performed on each word in the -variable in turn. If -.I search-string -begins with a -.CW ^ , -the string must match starting at the beginning of the word. If -.I search-string -ends with a -.CW $ , -the string must match to the end of the word (these two may be -combined to force an exact match). If a backslash precedes these two -characters, however, they lose their special meaning. Variable -expansion also occurs in the normal fashion inside both the -.I search-string -and the -.I replacement-string , -.B except -that a backslash is used to prevent the expansion of a -.CW $ , -not another dollar sign, as is usual. -Note that -.I search-string -is just a string, not a pattern, so none of the usual -regular-expression/wildcard characters have any special meaning save -.CW ^ -and -.CW $ . -In the replacement string, -the -.CW & -character is replaced by the -.I search-string -unless it is preceded by a backslash. -You are allowed to use any character except -colon or exclamation point to separate the two strings. This so-called -delimiter character may be placed in either string by preceding it -with a backslash. -.IP T -.Ix 0 def :T -.Ix 0 def modifier tail -Replaces each word in the variable expansion by its last -component (its ``tail''). For example, given -.DS -OBJS = ../lib/a.o b /usr/lib/libm.a -TAILS = $(OBJS:T) -.DE -the variable -.CW TAILS -would expand to -.CW "a.o b libm.a" .'' `` -.IP H -.Ix 0 def :H -.Ix 0 def modifier head -This is similar to -.CW :T , -except that every word is replaced by everything but the tail (the -``head''). Using the same definition of -.CW OBJS , -the string -.CW "$(OBJS:H)" '' `` -would expand to -.CW "../lib /usr/lib" .'' `` -Note that the final slash on the heads is removed and -anything without a head is replaced by the empty string. -.IP E -.Ix 0 def :E -.Ix 0 def modifier extension -.Ix 0 def modifier suffix -.Ix 0 ref suffix "variable modifier" -.CW :E -replaces each word by its suffix (``extension''). So -.CW "$(OBJS:E)" '' `` -would give you -.CW ".o .a" .'' `` -.IP R -.Ix 0 def :R -.Ix 0 def modifier root -.Ix 0 def modifier base -This replaces each word by everything but the suffix (the ``root'' of -the word). -.CW "$(OBJS:R)" '' `` -expands to `` -.CW "../lib/a b /usr/lib/libm" .'' -.RE -.LP -In addition, the System V style of substitution is also supported. -This looks like: -.DS -$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP) -.DE -It must be the last modifier in the chain. The search is anchored at -the end of each word, so only suffixes or whole words may be replaced. -.xH 2 More on Debugging -.xH 2 More Exercises -.IP (3.1) -You've got a set programs, each of which is created from its own -assembly-language source file (suffix -.CW .asm ). -Each program can be assembled into two versions, one with error-checking -code assembled in and one without. You could assemble them into files -with different suffixes -.CW .eobj \& ( -and -.CW .obj , -for instance), but your linker only understands files that end in -.CW .obj . -To top it all off, the final executables -.I must -have the suffix -.CW .exe . -How can you still use transformation rules to make your life easier -(Hint: assume the error-checking versions have -.CW ec -tacked onto their prefix)? -.IP (3.2) -Assume, for a moment or two, you want to perform a sort of -``indirection'' by placing the name of a variable into another one, -then you want to get the value of the first by expanding the second -somehow. Unfortunately, PMake doesn't allow constructs like -.DS I -$($(FOO)) -.DE -What do you do? Hint: no further variable expansion is performed after -modifiers are applied, thus if you cause a $ to occur in the -expansion, that's what will be in the result. -.xH 1 PMake for Gods -.LP -This chapter is devoted to those facilities in PMake that allow you to -do a great deal in a makefile with very little work, as well as do -some things you couldn't do in Make without a great deal of work (and -perhaps the use of other programs). The problem with these features, -is they must be handled with care, or you will end up with a mess. -.LP -Once more, I assume a greater familiarity with -.UX -or Sprite than I did in the previous two chapters. -.xH 2 Search Paths -.Rd 6 -.LP -PMake supports the dispersal of files into multiple directories by -allowing you to specify places to look for sources with -.CW .PATH -targets in the makefile. The directories you give as sources for these -targets make up a ``search path.'' Only those files used exclusively -as sources are actually sought on a search path, the assumption being -that anything listed as a target in the makefile can be created by the -makefile and thus should be in the current directory. -.LP -There are two types of search paths -in PMake: one is used for all types of files (including included -makefiles) and is specified with a plain -.CW .PATH -target (e.g. -.CW ".PATH : RCS" ''), `` -while the other is specific to a certain type of file, as indicated by -the file's suffix. A specific search path is indicated by immediately following -the -.CW .PATH -with the suffix of the file. For instance -.DS -\&.PATH.h : /sprite/lib/include /sprite/att/lib/include -.DE -would tell PMake to look in the directories -.CW /sprite/lib/include -and -.CW /sprite/att/lib/include -for any files whose suffix is -.CW .h . -.LP -The current directory is always consulted first to see if a file -exists. Only if it cannot be found there are the directories in the -specific search path, followed by those in the general search path, -consulted. -.LP -A search path is also used when expanding wildcard characters. If the -pattern has a recognizable suffix on it, the path for that suffix will -be used for the expansion. Otherwise the default search path is employed. -.LP -When a file is found in some directory other than the current one, all -local variables that would have contained the target's name -.CW .ALLSRC , ( -and -.CW .IMPSRC ) -will instead contain the path to the file, as found by PMake. -Thus if you have a file -.CW ../lib/mumble.c -and a makefile -.DS -\&.PATH.c : ../lib -mumble : mumble.c - $(CC) -o $(.TARGET) $(.ALLSRC) -.DE -the command executed to create -.CW mumble -would be -.CW "cc -o mumble ../lib/mumble.c" .'' `` -(As an aside, the command in this case isn't strictly necessary, since -it will be found using transformation rules if it isn't given. This is because -.CW .out -is the null suffix by default and a transformation exists from -.CW .c -to -.CW .out . -Just thought I'd throw that in.) -.LP -If a file exists in two directories on the same search path, the file -in the first directory on the path will be the one PMake uses. So if -you have a large system spread over many directories, it would behoove -you to follow a naming convention that avoids such conflicts. -.LP -Something you should know about the way search paths are implemented -is that each directory is read, and its contents cached, exactly once -\&\*- when it is first encountered \*- so any changes to the -directories while PMake is running will not be noted when searching -for implicit sources, nor will they be found when PMake attempts to -discover when the file was last modified, unless the file was created in the -current directory. While people have suggested that PMake should read -the directories each time, my experience suggests that the caching seldom -causes problems. In addition, not caching the directories slows things -down enormously because of PMake's attempts to apply transformation -rules through non-existent files \*- the number of extra file-system -searches is truly staggering, especially if many files without -suffixes are used and the null suffix isn't changed from -.CW .out . -.xH 2 Archives and Libraries -.LP -.UX -and Sprite allow you to merge files into an archive using the -.CW ar -command. Further, if the files are relocatable object files, you can -run -.CW ranlib -on the archive and get yourself a library that you can link into any -program you want. The main problem with archives is they double the -space you need to store the archived files, since there's one copy in -the archive and one copy out by itself. The problem with libraries is -you usually think of them as -.CW -lm -rather than -.CW /usr/lib/libm.a -and the linker thinks they're out-of-date if you so much as look at -them. -.LP -PMake solves the problem with archives by allowing you to tell it to -examine the files in the archives (so you can remove the individual -files without having to regenerate them later). To handle the problem -with libraries, PMake adds an additional way of deciding if a library -is out-of-date: -.IP \(bu 2 -If the table of contents is older than the library, or is missing, the -library is out-of-date. -.LP -A library is any target that looks like -.CW \-l name'' `` -or that ends in a suffix that was marked as a library using the -.CW .LIBS -target. -.CW .a -is so marked in the system makefile. -.LP -Members of an archive are specified as -``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''. -Thus -.CW libdix.a(window.o) '' ``' -specifies the file -.CW window.o -in the archive -.CW libdix.a . -You may also use wildcards to specify the members of the archive. Just -remember that most the wildcard characters will only find -.I existing -files. -.LP -A file that is a member of an archive is treated specially. If the -file doesn't exist, but it is in the archive, the modification time -recorded in the archive is used for the file when determining if the -file is out-of-date. When figuring out how to make an archived member target -(not the file itself, but the file in the archive \*- the -\fIarchive\fP(\fImember\fP) target), special care is -taken with the transformation rules, as follows: -.IP \(bu 2 -\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP. -.IP \(bu 2 -The transformation from the \fImember\fP's suffix to the -\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target. -.IP \(bu 2 -The \fIarchive\fP(\fImember\fP)'s -.CW .TARGET -variable is set to the name of the \fImember\fP if \fImember\fP is -actually a target, or the path to the member file if \fImember\fP is -only a source. -.IP \(bu 2 -The -.CW .ARCHIVE -variable for the \fIarchive\fP(\fImember\fP) target is set to the name -of the \fIarchive\fP. -.Ix 0 def variable local .ARCHIVE -.Ix 0 def .ARCHIVE -.IP \(bu 2 -The -.CW .MEMBER -variable is set to the actual string inside the parentheses. In most -cases, this will be the same as the -.CW .TARGET -variable. -.Ix 0 def variable local .MEMBER -.Ix 0 def .MEMBER -.IP \(bu 2 -The \fIarchive\fP(\fImember\fP)'s place in the local variables of the -targets that depend on it is taken by the value of its -.CW .TARGET -variable. -.LP -Thus, a program library could be created with the following makefile: -.DS -\&.o.a : - ... - rm -f $(.TARGET:T) -OBJS = obj1.o obj2.o obj3.o -libprog.a : libprog.a($(OBJS)) - ar cru $(.TARGET) $(.OODATE) - ranlib $(.TARGET) -.DE -This will cause the three object files to be compiled (if the -corresponding source files were modified after the object file or, if -that doesn't exist, the archived object file), the out-of-date ones -archived in -.CW libprog.a , -a table of contents placed in the archive and the newly-archived -object files to be removed. -.LP -All this is used in the -.CW makelib.mk -system makefile to create a single library with ease. This makefile -looks like this: -.DS -.SM -# -# Rules for making libraries. The object files that make up the library -# are removed once they are archived. -# -# To make several libraries in parallel, you should define the variable -# "many_libraries". This will serialize the invocations of ranlib. -# -# To use, do something like this: -# -# OBJECTS = -# -# fish.a: fish.a($(OBJECTS)) MAKELIB -# -# - -#ifndef _MAKELIB_MK -_MAKELIB_MK = - -#include - -\&.po.a .o.a : - ... - rm -f $(.MEMBER) - -ARFLAGS ?= crl - -# -# Re-archive the out-of-date members and recreate the library's table of -# contents using ranlib. If many_libraries is defined, put the ranlib -# off til the end so many libraries can be made at once. -# -MAKELIB : .USE .PRECIOUS - ar $(ARFLAGS) $(.TARGET) $(.OODATE) -#ifndef no_ranlib -# ifdef many_libraries - ... -# endif many_libraries - ranlib $(.TARGET) -#endif no_ranlib - -#endif _MAKELIB_MK -.DE -.xH 2 On the Condition... -.Rd 1 -.LP -Like the C compiler before it, PMake allows you to configure the makefile, -based on the current environment, using conditional statements. A -conditional looks like this: -.DS -#if \fIboolean expression\fP -\fIlines\fP -#elif \fIanother boolean expression\fP -\fImore lines\fP -#else -\fIstill more lines\fP -#endif -.DE -They may be nested to a maximum depth of 30 and may occur anywhere -(except in a comment, of course). The -.CW # '' `` -must the very first character on the line. -.LP -Each -.I "boolean expression" -is made up of terms that look like function calls, the standard C -boolean operators -.CW && , -.CW || , -and -.CW ! , -and the standard relational operators -.CW == , -.CW != , -.CW > , -.CW >= , -.CW < , -and -.CW <= , -with -.CW == -and -.CW != -being overloaded to allow string comparisons as well. -.CW && -represents logical AND; -.CW || -is logical OR and -.CW ! -is logical NOT. The arithmetic and string operators take precedence -over all three of these operators, while NOT takes precedence over -AND, which takes precedence over OR. This precedence may be -overridden with parentheses, and an expression may be parenthesized to -your heart's content. Each term looks like a call on one of four -functions: -.nr pw 9 -.Ix 0 def make -.Ix 0 def conditional make -.Ix 0 def if make -.IP make \n(pw -The syntax is -.CW make( \fItarget\fP\c -.CW ) -where -.I target -is a target in the makefile. This is true if the given target was -specified on the command line, or as the source for a -.CW .MAIN -target (note that the sources for -.CW .MAIN -are only used if no targets were given on the command line). -.IP defined \n(pw -.Ix 0 def defined -.Ix 0 def conditional defined -.Ix 0 def if defined -The syntax is -.CW defined( \fIvariable\fP\c -.CW ) -and is true if -.I variable -is defined. Certain variables are defined in the system makefile that -identify the system on which PMake is being run. -.IP exists \n(pw -.Ix 0 def exists -.Ix 0 def conditional exists -.Ix 0 def if exists -The syntax is -.CW exists( \fIfile\fP\c -.CW ) -and is true if the file can be found on the global search path (i.e. -that defined by -.CW .PATH -targets, not by -.CW .PATH \fIsuffix\fP -targets). -.IP empty \n(pw -.Ix 0 def empty -.Ix 0 def conditional empty -.Ix 0 def if empty -This syntax is much like the others, except the string inside the -parentheses is of the same form as you would put between parentheses -when expanding a variable, complete with modifiers and everything. The -function returns true if the resulting string is empty (NOTE: an undefined -variable in this context will cause at the very least a warning -message about a malformed conditional, and at the worst will cause the -process to stop once it has read the makefile. If you want to check -for a variable being defined or empty, use the expression -.CW !defined( \fIvar\fP\c `` -.CW ") || empty(" \fIvar\fP\c -.CW ) '' -as the definition of -.CW || -will prevent the -.CW empty() -from being evaluated and causing an error, if the variable is -undefined). This can be used to see if a variable contains a given -word, for example: -.DS -#if !empty(\fIvar\fP:M\fIword\fP) -.DE -.LP -The arithmetic and string operators may only be used to test the value -of a variable. The lefthand side must contain the variable expansion, -while the righthand side contains either a string, enclosed in -double-quotes, or a number. The standard C numeric conventions (except -for specifying an octal number) apply to both sides. E.g. -.DS -#if $(OS) == 4.3 - -#if $(MACHINE) == "sun3" - -#if $(LOAD_ADDR) < 0xc000 -.DE -are all valid conditionals. In addition, the numeric value of a -variable can be tested as a boolean as follows: -.DS -#if $(LOAD) -.DE -would see if -.CW LOAD -contains a non-zero value and -.DS -#if !$(LOAD) -.DE -would test if -.CW LOAD -contains a zero value. -.LP -In addition to the bare -.CW #if ,'' `` -there are other forms that apply one of the first two functions to each -term. They are as follows: -.DS - ifdef \fRdefined\fP - ifndef \fR!defined\fP - ifmake \fRmake\fP - ifnmake \fR!make\fP -.DE -There are also the ``else if'' forms: -.CW elif , -.CW elifdef , -.CW elifndef , -.CW elifmake , -and -.CW elifnmake . -.LP -For instance, if you wish to create two versions of a program, one of which -is optimized (the production version) and the other of which is for debugging -(has symbols for dbx), you have two choices: you can create two -makefiles, one of which uses the -.CW \-g -flag for the compilation, while the other uses the -.CW \-O -flag, or you can use another target (call it -.CW debug ) -to create the debug version. The construct below will take care of -this for you. I have also made it so defining the variable -.CW DEBUG -(say with -.CW "pmake -D DEBUG" ) -will also cause the debug version to be made. -.DS -#if defined(DEBUG) || make(debug) -CFLAGS += -g -#else -CFLAGS += -O -#endif -.DE -There are, of course, problems with this approach. The most glaring -annoyance is that if you want to go from making a debug version to -making a production version, you have to remove all the object files, -or you will get some optimized and some debug versions in the same -program. Another annoyance is you have to be careful not to make two -targets that ``conflict'' because of some conditionals in the -makefile. For instance -.DS -#if make(print) -FORMATTER = ditroff -Plaser_printer -#endif -#if make(draft) -FORMATTER = nroff -Pdot_matrix_printer -#endif -.DE -would wreak havoc if you tried -.CW "pmake draft print" '' `` -since you would use the same formatter for each target. As I said, -this all gets somewhat complicated. -.xH 2 A Shell is a Shell is a Shell -.Rd 7 -.LP -In normal operation, the Bourne Shell (better known as -.CW sh '') `` -is used to execute the commands to re-create targets. PMake also allows you -to specify a different shell for it to use when executing these -commands. There are several things PMake must know about the shell you -wish to use. These things are specified as the sources for the -.CW .SHELL -.Ix 0 ref .SHELL -.Ix 0 ref target .SHELL -target by keyword, as follows: -.IP "\fBpath=\fP\fIpath\fP" -PMake needs to know where the shell actually resides, so it can -execute it. If you specify this and nothing else, PMake will use the -last component of the path and look in its table of the shells it -knows and use the specification it finds, if any. Use this if you just -want to use a different version of the Bourne or C Shell (yes, PMake knows -how to use the C Shell too). -.IP "\fBname=\fP\fIname\fP" -This is the name by which the shell is to be known. It is a single -word and, if no other keywords are specified (other than -.B path ), -it is the name by which PMake attempts to find a specification for -it (as mentioned above). You can use this if you would just rather use -the C Shell than the Bourne Shell -.CW ".SHELL: name=csh" '' (`` -will do it). -.IP "\fBquiet=\fP\fIecho-off command\fP" -As mentioned before, PMake actually controls whether commands are -printed by introducing commands into the shell's input stream. This -keyword, and the next two, control what those commands are. The -.B quiet -keyword is the command used to turn echoing off. Once it is turned -off, echoing is expected to remain off until the echo-on command is given. -.IP "\fBecho=\fP\fIecho-on command\fP" -The command PMake should give to turn echoing back on again. -.IP "\fBfilter=\fP\fIprinted echo-off command\fP" -Many shells will echo the echo-off command when it is given. This -keyword tells PMake in what format the shell actually prints the -echo-off command. Wherever PMake sees this string in the shell's -output, it will delete it and any following whitespace, up to and -including the next newline. See the example at the end of this section -for more details. -.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP" -Unless a target has been marked -.CW .SILENT , -PMake wants to start the shell running with echoing on. To do this, it -passes this flag to the shell as one of its arguments. If either this -or the next flag begins with a `\-', the flags will be passed to the -shell as separate arguments. Otherwise, the two will be concatenated -(if they are used at the same time, of course). -.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP" -Likewise, unless a target is marked -.CW .IGNORE , -PMake wishes error-checking to be on from the very start. To this end, -it will pass this flag to the shell as an argument. The same rules for -an initial `\-' apply as for the -.B echoFlag . -.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP" -Just as for echo-control, error-control is achieved by inserting -commands into the shell's input stream. This is the command to make -the shell check for errors. It also serves another purpose if the -shell doesn't have error-control as commands, but I'll get into that -in a minute. Again, once error checking has been turned on, it is -expected to remain on until it is turned off again. -.IP "\fBignore=\fP\fIcommand to turn error checking off\fP" -This is the command PMake uses to turn error checking off. It has -another use if the shell doesn't do error-control, but I'll tell you -about that.\|.\|.\|now. -.IP "\fBhasErrCtl=\fP\fIyes or no\fP" -This takes a value that is either -.B yes -or -.B no . -Now you might think that the existence of the -.B check -and -.B ignore -keywords would be enough to tell PMake if the shell can do -error-control, but you'd be wrong. If -.B hasErrCtl -is -.B yes , -PMake uses the check and ignore commands in a straight-forward manner. -If this is -.B no , -however, their use is rather different. In this case, the check -command is used as a template, in which the string -.B %s -is replaced by the command that's about to be executed, to produce a -command for the shell that will echo the command to be executed. The -ignore command is also used as a template, again with -.B %s -replaced by the command to be executed, to produce a command that will -execute the command to be executed and ignore any error it returns. -When these strings are used as templates, you must provide newline(s) -.CW \en '') (`` -in the appropriate place(s). -.LP -The strings that follow these keywords may be enclosed in single or -double quotes (the quotes will be stripped off) and may contain the -usual C backslash-characters (\en is newline, \er is return, \eb is -backspace, \e' escapes a single-quote inside single-quotes, \e" -escapes a double-quote inside double-quotes). Now for an example. -.LP -This is actually the contents of the -.CW -system makefile, and causes PMake to use the Bourne Shell in such a -way that each command is printed as it is executed. That is, if more -than one command is given on a line, each will be printed separately. -Similarly, each time the body of a loop is executed, the commands -within that loop will be printed, etc. The specification runs like -this: -.DS -# -# This is a shell specification to have the Bourne shell echo -# the commands just before executing them, rather than when it reads -# them. Useful if you want to see how variables are being expanded, etc. -# -\&.SHELL : path=/bin/sh \e - quiet="set -" \e - echo="set -x" \e - filter="+ set - " \e - echoFlag=x \e - errFlag=e \e - hasErrCtl=yes \e - check="set -e" \e - ignore="set +e" -.DE -.LP -It tells PMake the following: -.Bp -The shell is located in the file -.CW /bin/sh . -It need not tell PMake that the name of the shell is -.CW sh -as PMake can figure that out for itself (it's the last component of -the path). -.Bp -The command to stop echoing is -.CW "set -" . -.Bp -The command to start echoing is -.CW "set -x" . -.Bp -When the echo off command is executed, the shell will print -.CW "+ set - " -(The `+' comes from using the -.CW \-x -flag (rather than the -.CW \-v -flag PMake usually uses)). PMake will remove all occurrences of this -string from the output, so you don't notice extra commands you didn't -put there. -.Bp -The flag the Bourne Shell will take to start echoing in this way is -the -.CW \-x -flag. The Bourne Shell will only take its flag arguments concatenated -as its first argument, so neither this nor the -.B errFlag -specification begins with a \-. -.Bp -The flag to use to turn error-checking on from the start is -.CW \-e . -.Bp -The shell can turn error-checking on and off, and the commands to do -so are -.CW "set +e" -and -.CW "set -e" , -respectively. -.LP -I should note that this specification is for Bourne Shells that are -not part of Berkeley -.UX , -as shells from Berkeley don't do error control. You can get a similar -effect, however, by changing the last three lines to be: -.DS - hasErrCtl=no \e - check="echo \e"+ %s\e"\en" \e - ignore="sh -c '%s || exit 0\en" -.DE -.LP -This will cause PMake to execute the two commands -.DS -echo "+ \fIcmd\fP" -sh -c '\fIcmd\fP || true' -.DE -for each command for which errors are to be ignored. (In case you are -wondering, the thing for -.CW ignore -tells the shell to execute another shell without error checking on and -always exit 0, since the -.B || -causes the -.CW "exit 0" -to be executed only if the first command exited non-zero, and if the -first command exited zero, the shell will also exit zero, since that's -the last command it executed). -.xH 2 Compatibility -.Ix 0 ref compatibility -.LP -There are three (well, 3 \(12) levels of backwards-compatibility built -into PMake. Most makefiles will need none at all. Some may need a -little bit of work to operate correctly when run in parallel. Each -level encompasses the previous levels (e.g. -.B \-B -(one shell per command) implies -.B \-V ) -The three levels are described in the following three sections. -.xH 3 DEFCON 3 \*- Variable Expansion -.Ix 0 ref compatibility -.LP -As noted before, PMake will not expand a variable unless it knows of a -value for it. This can cause problems for makefiles that expect to -leave variables undefined except in special circumstances (e.g. if -more flags need to be passed to the C compiler or the output from a -text processor should be sent to a different printer). If the -variables are enclosed in curly braces -.CW ${PRINTER} ''), (`` -the shell will let them pass. If they are enclosed in parentheses, -however, the shell will declare a syntax error and the make will come -to a grinding halt. -.LP -You have two choices: change the makefile to define the variables -(their values can be overridden on the command line, since that's -where they would have been set if you used Make, anyway) or always give the -.B \-V -flag (this can be done with the -.CW .MAKEFLAGS -target, if you want). -.xH 3 DEFCON 2 \*- The Number of the Beast -.Ix 0 ref compatibility -.LP -Then there are the makefiles that expect certain commands, such as -changing to a different directory, to not affect other commands in a -target's creation script. You can solve this is either by going -back to executing one shell per command (which is what the -.B \-B -flag forces PMake to do), which slows the process down a good bit and -requires you to use semicolons and escaped newlines for shell constructs, or -by changing the makefile to execute the offending command(s) in a subshell -(by placing the line inside parentheses), like so: -.DS -install :: .MAKE - (cd src; $(.PMAKE) install) - (cd lib; $(.PMAKE) install) - (cd man; $(.PMAKE) install) -.DE -.Ix 0 ref operator double-colon -.Ix 0 ref variable global .PMAKE -.Ix 0 ref .PMAKE -.Ix 0 ref .MAKE -.Ix 0 ref attribute .MAKE -This will always execute the three makes (even if the -.B \-n -flag was given) because of the combination of the ``::'' operator and -the -.CW .MAKE -attribute. Each command will change to the proper directory to perform -the install, leaving the main shell in the directory in which it started. -.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery" -.Ix 0 ref compatibility -.LP -The final category of makefile is the one where every command requires -input, the dependencies are incompletely specified, or you simply -cannot create more than one target at a time, as mentioned earlier. In -addition, you may not have the time or desire to upgrade the makefile -to run smoothly with PMake. If you are the conservative sort, this is -the compatibility mode for you. It is entered either by giving PMake -the -.B \-M -flag (for Make), or by executing PMake as -.CW make .'' `` -In either case, PMake performs things exactly like Make (while still -supporting most of the nice new features PMake provides). This -includes: -.IP \(bu 2 -No parallel execution. -.IP \(bu 2 -Targets are made in the exact order specified by the makefile. The -sources for each target are made in strict left-to-right order, etc. -.IP \(bu 2 -A single Bourne shell is used to execute each command, thus the -shell's -.CW $$ -variable is useless, changing directories doesn't work across command -lines, etc. -.IP \(bu 2 -If no special characters exist in a command line, PMake will break the -command into words itself and execute the command directly, without -executing a shell first. The characters that cause PMake to execute a -shell are: -.CW # , -.CW = , -.CW | , -.CW ^ , -.CW ( , -.CW ) , -.CW { , -.CW } , -.CW ; , -.CW & , -.CW < , -.CW > , -.CW * , -.CW ? , -.CW [ , -.CW ] , -.CW : , -.CW $ , -.CW ` , -and -.CW \e . -You should notice that these are all the characters that are given -special meaning by the shell (except -.CW ' -and -.CW " , -which PMake deals with all by its lonesome). -.IP \(bu 2 -The use of the null suffix is turned off. -.Ix 0 ref "null suffix" -.Ix 0 ref suffix null -.xH 2 The Way Things Work -.LP -When PMake reads the makefile, it parses sources and targets into -nodes in a graph. The graph is directed only in the sense that PMake -knows which way is up. Each node contains not only links to all its -parents and children (the nodes that depend on it and those on which -it depends, respectively), but also a count of the number of its -children that have already been processed. -.LP -The most important thing to know about how PMake uses this graph is -that the traversal is breadth-first and occurs in two passes. -.LP -After PMake has parsed the makefile, it begins with the nodes the user -has told it to make (either on the command line, or via a -.CW .MAIN -target, or by the target being the first in the file not labeled with -the -.CW .NOTMAIN -attribute) placed in a queue. It continues to take the node off the -front of the queue, mark it as something that needs to be made, pass -the node to -.CW Suff_FindDeps -(mentioned earlier) to find any implicit sources for the node, and -place all the node's children that have yet to be marked at the end of -the queue. If any of the children is a -.CW .USE -rule, its attributes are applied to the parent, then its commands are -appended to the parent's list of commands and its children are linked -to its parent. The parent's unmade children counter is then decremented -(since the -.CW .USE -node has been processed). You will note that this allows a -.CW .USE -node to have children that are -.CW .USE -nodes and the rules will be applied in sequence. -If the node has no children, it is placed at the end of -another queue to be examined in the second pass. This process -continues until the first queue is empty. -.LP -At this point, all the leaves of the graph are in the examination -queue. PMake removes the node at the head of the queue and sees if it -is out-of-date. If it is, it is passed to a function that will execute -the commands for the node asynchronously. When the commands have -completed, all the node's parents have their unmade children counter -decremented and, if the counter is then 0, they are placed on the -examination queue. Likewise, if the node is up-to-date. Only those -parents that were marked on the downward pass are processed in this -way. Thus PMake traverses the graph back up to the nodes the user -instructed it to create. When the examination queue is empty and no -shells are running to create a target, PMake is finished. -.LP -Once all targets have been processed, PMake executes the commands -attached to the -.CW .END -target, either explicitly or through the use of an ellipsis in a shell -script. If there were no errors during the entire process but there -are still some targets unmade (PMake keeps a running count of how many -targets are left to be made), there is a cycle in the graph. PMake does -a depth-first traversal of the graph to find all the targets that -weren't made and prints them out one by one. -.xH 1 Answers to Exercises -.IP (3.1) -This is something of a trick question, for which I apologize. The -trick comes from the UNIX definition of a suffix, which PMake doesn't -necessarily share. You will have noticed that all the suffixes used in -this tutorial (and in UNIX in general) begin with a period -.CW .ms , ( -.CW .c , -etc.). Now, PMake's idea of a suffix is more like English's: it's the -characters at the end of a word. With this in mind, one possible -.Ix 0 def suffix -solution to this problem goes as follows: -.DS I -\&.SUFFIXES : ec.exe .exe ec.obj .obj .asm -ec.objec.exe .obj.exe : - link -o $(.TARGET) $(.IMPSRC) -\&.asmec.obj : - asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC) -\&.asm.obj : - asm -o $(.TARGET) $(.IMPSRC) -.DE -.IP (3.2) -The trick to this one lies in the ``:='' variable-assignment operator -and the ``:S'' variable-expansion modifier. -.Ix 0 ref variable assignment expanded -.Ix 0 ref variable expansion modified -.Ix 0 ref modifier substitute -.Ix 0 ref :S -.Ix 0 ref := -Basically what you want is to take the pointer variable, so to speak, -and transform it into an invocation of the variable at which it -points. You might try something like -.DS I -$(PTR:S/^/\e$(/:S/$/)) -.DE -which places -.CW $( '' `` -at the front of the variable name and -.CW ) '' `` -at the end, thus transforming -.CW VAR ,'' `` -for example, into -.CW $(VAR) ,'' `` -which is just what we want. Unfortunately (as you know if you've tried -it), since, as it says in the hint, PMake does no further substitution -on the result of a modified expansion, that's \fIall\fP you get. The -solution is to make use of ``:='' to place that string into yet -another variable, then invoke the other variable directly: -.DS I -*PTR := $(PTR:S/^/\e$(/:S/$/)/) -.DE -You can then use -.CW $(*PTR) '' `` -to your heart's content. -.de Gp -.XP -\&\fB\\$1:\fP -.. -.xH 1 Glossary of Jargon -.Gp "attribute" -A property given to a target that causes PMake to treat it differently. -.Gp "command script" -The lines immediately following a dependency line that specify -commands to execute to create each of the targets on the dependency -line. Each line in the command script must begin with a tab. -.Gp "command-line variable" -A variable defined in an argument when PMake is first executed. -Overrides all assignments to the same variable name in the makefile. -.Gp "conditional" -A construct much like that used in C that allows a makefile to be -configured on the fly based on the local environment, or on what is being -made by that invocation of PMake. -.Gp "creation script" -Commands used to create a target. See ``command script.'' -.Gp "dependency" -The relationship between a source and a target. This comes in three -flavors, as indicated by the operator between the target and the -source. `:' gives a straight time-wise dependency (if the target is -older than the source, the target is out-of-date), while `!' provides -simply an ordering and always considers the target out-of-date. `::' -is much like `:', save it creates multiple instances of a target each -of which depends on its own list of sources. -.Gp "dynamic source" -This refers to a source that has a local variable invocation in it. It -allows a single dependency line to specify a different source for each -target on the line. -.Gp "global variable" -Any variable defined in a makefile. Takes precedence over variables -defined in the environment, but not over command-line or local variables. -.Gp "input graph" -What PMake constructs from a makefile. Consists of nodes made of the -targets in the makefile, and the links between them (the -dependencies). The links are directed (from source to target) and -there may not be any cycles (loops) in the graph. -.Gp "local variable" -A variable defined by PMake visible only in a target's shell script. -There are seven local variables, not all of which are defined for -every target: -.CW .TARGET , -.CW .ALLSRC , -.CW .OODATE , -.CW .PREFIX , -.CW .IMPSRC , -.CW .ARCHIVE , -and -.CW .MEMBER . -.CW .TARGET , -.CW .PREFIX , -.CW .ARCHIVE , -and -.CW .MEMBER -may be used on dependency lines to create ``dynamic sources.'' -.Gp "makefile" -A file that describes how a system is built. If you don't know what it -is after reading this tutorial.\|.\|.\|. -.Gp "modifier" -A letter, following a colon, used to alter how a variable is expanded. -It has no effect on the variable itself. -.Gp "operator" -What separates a source from a target (on a dependency line) and specifies -the relationship between the two. There are three: -.CW : ', ` -.CW :: ', ` -and -.CW ! '. ` -.Gp "search path" -A list of directories in which a file should be sought. PMake's view -of the contents of directories in a search path does not change once -the makefile has been read. A file is sought on a search path only if -it is exclusively a source. -.Gp "shell" -A program to which commands are passed in order to create targets. -.Gp "source" -Anything to the right of an operator on a dependency line. Targets on -the dependency line are usually created from the sources. -.Gp "special target" -A target that causes PMake to do special things when it's encountered. -.Gp "suffix" -The tail end of a file name. Usually begins with a period, -.CW .c -or -.CW .ms , -e.g. -.Gp "target" -A word to the left of the operator on a dependency line. More -generally, any file that PMake might create. A file may be (and often -is) both a target and a source (what it is depends on how PMake is -looking at it at the time \*- sort of like the wave/particle duality -of light, you know). -.Gp "transformation rule" -A special construct in a makefile that specifies how to create a file -of one type from a file of another, as indicated by their suffixes. -.Gp "variable expansion" -The process of substituting the value of a variable for a reference to -it. Expansion may be altered by means of modifiers. -.Gp "variable" -A place in which to store text that may be retrieved later. Also used -to define the local environment. Conditionals exist that test whether -a variable is defined or not. -.bp -.\" Output table of contents last, with an entry for the index, making -.\" sure to save and restore the last real page number for the index... -.nr @n \n(PN+1 -.\" We are not generating an index -.\" .XS \n(@n -.\" Index -.\" .XE -.nr %% \n% -.PX -.nr % \n(%% diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c deleted file mode 100644 index e795f08399..0000000000 --- a/usr.bin/make/arch.c +++ /dev/null @@ -1,1226 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)arch.c 8.2 (Berkeley) 1/2/94 - * $FreeBSD: src/usr.bin/make/arch.c,v 1.48 2005/02/10 14:39:05 harti Exp $ - * $DragonFly: src/usr.bin/make/arch.c,v 1.55 2005/09/24 07:37:01 okumoto Exp $ - */ - -/*- - * arch.c -- - * Functions to manipulate libraries, archives and their members. - * - * Once again, cacheing/hashing comes into play in the manipulation - * of archives. The first time an archive is referenced, all of its members' - * headers are read and hashed and the archive closed again. All hashed - * archives are kept on a list which is searched each time an archive member - * is referenced. - * - * The interface to this module is: - * Arch_ParseArchive Given an archive specification, return a list - * of GNode's, one for each member in the spec. - * false is returned if the specification is - * invalid for some reason. - * - * Arch_Touch Alter the modification time of the archive - * member described by the given node to be - * the current time. - * - * Arch_TouchLib Update the modification time of the library - * described by the given node. This is special - * because it also updates the modification time - * of the library's table of contents. - * - * Arch_MTime Find the modification time of a member of - * an archive *in the archive*. The time is also - * placed in the member's GNode. Returns the - * modification time. - * - * Arch_MemTime Find the modification time of a member of - * an archive. Called when the member doesn't - * already exist. Looks in the archive for the - * modification time. Returns the modification - * time. - * - * Arch_FindLib Search for a library along a path. The - * library name in the GNode should be in - * -l format. - * - * Arch_LibOODate Special function to decide if a library node - * is out-of-date. - * - * Arch_Init Initialize this module. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arch.h" -#include "buf.h" -#include "config.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "hash.h" -#include "make.h" -#include "parse.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -typedef struct Arch { - char *name; /* Name of archive */ - - /* - * All the members of the archive described - * by key/value pairs - */ - Hash_Table members; - - TAILQ_ENTRY(Arch) link; /* link all cached archives */ -} Arch; - -/* Lst of archives we've already examined */ -static TAILQ_HEAD(, Arch) archives = TAILQ_HEAD_INITIALIZER(archives); - - -/* size of the name field in the archive member header */ -#define AR_NAMSIZ sizeof(((struct ar_hdr *)0)->ar_name) - -/* - * This structure is used while reading/writing an archive - */ -struct arfile { - FILE *fp; /* archive file */ - char *fname; /* name of the file */ - struct ar_hdr hdr; /* current header */ - char sname[AR_NAMSIZ + 1]; /* short name */ - char *member; /* (long) member name */ - size_t mlen; /* size of the above */ - char *nametab; /* name table */ - size_t nametablen; /* size of the table */ - int64_t time; /* from ar_date */ - uint64_t size; /* from ar_size */ - off_t pos; /* header pos of current entry */ -}; - -/* - * Name of the symbol table. The original BSD used "__.SYMDEF". Rumours go - * that this name may have a slash appended sometimes. Actually FreeBSD - * uses "/" which probably came from SVR4. - */ -#define SVR4_RANLIBMAG "/" -#define BSD_RANLIBMAG "__.SYMDEF" - -/* - * Name of the filename table. The 4.4BSD ar format did not use this, but - * puts long filenames directly between the member header and the object - * file. - */ -#define SVR4_NAMEMAG "//" -#define BSD_NAMEMAG "ARFILENAMES/" - -/* - * 44BSD long filename key. Use a local define here instead of relying - * on ar.h because we want this to continue working even when the - * definition is removed from ar.h. - */ -#define BSD_EXT1 "#1/" -#define BSD_EXT1LEN 3 - -/* if this is true make archive errors fatal */ -bool arch_fatal = true; - -/** - * ArchError - * An error happend while handling an archive. BSDmake traditionally - * ignored these errors. Now this is dependend on the global arch_fatal - * which, if true, makes these errors fatal and, if false, just emits an - * error message. - */ -#define ArchError(ARGS) do { \ - if (arch_fatal) \ - Fatal ARGS; \ - else \ - Error ARGS; \ - } while (0) - -/*- - *----------------------------------------------------------------------- - * Arch_ParseArchive -- - * Parse the archive specification in the given line and find/create - * the nodes for the specified archive members, placing their nodes - * on the given list, given the pointer to the start of the - * specification, a Lst on which to place the nodes, and a context - * in which to expand variables. - * - * Results: - * true if it was a valid specification. The linePtr is updated - * to point to the first non-space after the archive spec. The - * nodes for the members are placed on the given list. - * - * Side Effects: - * Some nodes may be created. The given list is extended. - * - *----------------------------------------------------------------------- - */ -bool -Arch_ParseArchive(char **linePtr, Lst *nodeLst, GNode *ctxt) -{ - char *cp; /* Pointer into line */ - GNode *gn; /* New node */ - char *libName; /* Library-part of specification */ - char *memName; /* Member-part of specification */ - char *nameBuf; /* temporary place for node name */ - char saveChar; /* Ending delimiter of member-name */ - bool subLibName; /* true if libName should have/had - * variable substitution performed on it */ - - libName = *linePtr; - - subLibName = false; - - for (cp = libName; *cp != OPEN_PAREN && *cp != '\0'; cp++) { - if (*cp == '$') { - /* - * Variable spec, so call the Var module to parse the - * puppy so we can safely advance beyond it... - */ - size_t length = 0; - bool freeIt; - char *result; - - result = Var_Parse(cp, ctxt, true, &length, &freeIt); - if (result == var_Error) { - return (false); - } - subLibName = true; - - if (freeIt) { - free(result); - } - cp += length - 1; - } - } - - *cp++ = '\0'; - if (subLibName) { - libName = Buf_Peel(Var_Subst(libName, ctxt, true)); - } - - for (;;) { - /* - * First skip to the start of the member's name, mark that - * place and skip to the end of it (either white-space or - * a close paren). - */ - - /* - * true if need to substitute in memName - */ - bool doSubst = false; - - while (*cp != '\0' && *cp != CLOSE_PAREN && - isspace((unsigned char)*cp)) { - cp++; - } - - memName = cp; - while (*cp != '\0' && *cp != CLOSE_PAREN && - !isspace((unsigned char)*cp)) { - if (*cp == '$') { - /* - * Variable spec, so call the Var module to - * parse the puppy so we can safely advance - * beyond it... - */ - size_t length = 0; - bool freeIt; - char *result; - - result = Var_Parse(cp, ctxt, true, - &length, &freeIt); - if (result == var_Error) { - return (false); - } - doSubst = true; - - if (freeIt) { - free(result); - } - cp += length; - } else { - cp++; - } - } - - /* - * If the specification ends without a closing parenthesis, - * chances are there's something wrong (like a missing - * backslash), so it's better to return failure than allow - * such things to happen - */ - if (*cp == '\0') { - printf("No closing parenthesis in archive " - "specification\n"); - return (false); - } - - /* - * If we didn't move anywhere, we must be done - */ - if (cp == memName) { - break; - } - - saveChar = *cp; - *cp = '\0'; - - /* - * XXX: This should be taken care of intelligently by - * SuffExpandChildren, both for the archive and the member - * portions. - */ - /* - * If member contains variables, try and substitute for them. - * This will slow down archive specs with dynamic sources, of - * course, since we'll be (non-)substituting them three times, - * but them's the breaks -- we need to do this since - * SuffExpandChildren calls us, otherwise we could assume the - * thing would be taken care of later. - */ - if (doSubst) { - char *buf; - char *sacrifice; - char *oldMemName = memName; - size_t sz; - Buffer *buf1; - - /* - * Now form an archive spec and recurse to deal with - * nested variables and multi-word variable values.... - * The results are just placed at the end of the - * nodeLst we're returning. - */ - buf1 = Var_Subst(memName, ctxt, true); - memName = Buf_Data(buf1); - - sz = strlen(memName) + strlen(libName) + 3; - buf = emalloc(sz); - - snprintf(buf, sz, "%s(%s)", libName, memName); - - sacrifice = buf; - - if (strchr(memName, '$') && - strcmp(memName, oldMemName) == 0) { - /* - * Must contain dynamic sources, so we can't - * deal with it now. - * Just create an ARCHV node for the thing and - * let SuffExpandChildren handle it... - */ - gn = Targ_FindNode(buf, TARG_CREATE); - - if (gn == NULL) { - free(buf); - Buf_Destroy(buf1, false); - return (false); - } - gn->type |= OP_ARCHV; - Lst_AtEnd(nodeLst, (void *)gn); - } else if (!Arch_ParseArchive(&sacrifice, nodeLst, - ctxt)) { - /* - * Error in nested call -- free buffer and - * return false ourselves. - */ - free(buf); - Buf_Destroy(buf1, false); - return (false); - } - - /* Free buffer and continue with our work. */ - free(buf); - Buf_Destroy(buf1, false); - - } else if (Dir_HasWildcards(memName)) { - Lst members = Lst_Initializer(members); - char *member; - size_t sz = MAXPATHLEN; - size_t nsz; - - nameBuf = emalloc(sz); - - Path_Expand(memName, &dirSearchPath, &members); - while (!Lst_IsEmpty(&members)) { - member = Lst_DeQueue(&members); - nsz = strlen(libName) + strlen(member) + 3; - if (nsz > sz) { - sz = nsz * 2; - nameBuf = erealloc(nameBuf, sz); - } - - snprintf(nameBuf, sz, "%s(%s)", - libName, member); - free(member); - gn = Targ_FindNode(nameBuf, TARG_CREATE); - if (gn == NULL) { - free(nameBuf); - /* XXXHB Lst_Destroy(&members) */ - return (false); - } - /* - * We've found the node, but have to make sure - * the rest of the world knows it's an archive - * member, without having to constantly check - * for parentheses, so we type the thing with - * the OP_ARCHV bit before we place it on the - * end of the provided list. - */ - gn->type |= OP_ARCHV; - Lst_AtEnd(nodeLst, gn); - } - free(nameBuf); - } else { - size_t sz = strlen(libName) + strlen(memName) + 3; - - nameBuf = emalloc(sz); - snprintf(nameBuf, sz, "%s(%s)", libName, memName); - gn = Targ_FindNode(nameBuf, TARG_CREATE); - free(nameBuf); - if (gn == NULL) { - return (false); - } - /* - * We've found the node, but have to make sure the - * rest of the world knows it's an archive member, - * without having to constantly check for parentheses, - * so we type the thing with the OP_ARCHV bit before - * we place it on the end of the provided list. - */ - gn->type |= OP_ARCHV; - Lst_AtEnd(nodeLst, gn); - } - if (doSubst) { - free(memName); - } - - *cp = saveChar; - } - - /* - * If substituted libName, free it now, since we need it no longer. - */ - if (subLibName) { - free(libName); - } - - /* - * We promised the pointer would be set up at the next non-space, so - * we must advance cp there before setting *linePtr... (note that on - * entrance to the loop, cp is guaranteed to point at a CLOSE_PAREN) - */ - do { - cp++; - } while (*cp != '\0' && isspace((unsigned char)*cp)); - - *linePtr = cp; - return (true); -} - -/* - * Close an archive file an free all resources - */ -static void -ArchArchiveClose(struct arfile *ar) -{ - - if (ar->nametab != NULL) - free(ar->nametab); - free(ar->member); - if (ar->fp != NULL) { - if (fclose(ar->fp) == EOF) - ArchError(("%s: close error", ar->fname)); - } - free(ar->fname); - free(ar); -} - -/* - * Open an archive file. - */ -static struct arfile * -ArchArchiveOpen(const char *archive, const char *mode) -{ - struct arfile *ar; - char magic[SARMAG]; - - ar = emalloc(sizeof(*ar)); - ar->fname = estrdup(archive); - ar->mlen = 100; - ar->member = emalloc(ar->mlen); - ar->nametab = NULL; - ar->nametablen = 0; - - if ((ar->fp = fopen(ar->fname, mode)) == NULL) { - DEBUGM(ARCH, ("%s", ar->fname)); - ArchArchiveClose(ar); - return (NULL); - } - - /* read MAGIC */ - if (fread(magic, SARMAG, 1, ar->fp) != 1 || - strncmp(magic, ARMAG, SARMAG) != 0) { - ArchError(("%s: bad archive magic\n", ar->fname)); - ArchArchiveClose(ar); - return (NULL); - } - - ar->pos = 0; - return (ar); -} - -/* - * Read the next header from the archive. The return value will be +1 if - * the header is read successfully, 0 on EOF and -1 if an error happend. - * On a successful return sname contains the truncated member name and - * member the full name. hdr contains the member header. For the symbol table - * names of length 0 are returned. The entry for the file name table is never - * returned. - */ -static int -ArchArchiveNext(struct arfile *ar) -{ - char *end; - int have_long_name; - u_long offs; - char *ptr; - size_t ret; - char buf[MAX(sizeof(ar->hdr.ar_size), sizeof(ar->hdr.ar_date)) + 1]; - - next: - /* - * Seek to the next header. - */ - if (ar->pos == 0) { - ar->pos = SARMAG; - } else { - ar->pos += sizeof(ar->hdr) + ar->size; - if (ar->size % 2 == 1) - ar->pos++; - } - - if (fseeko(ar->fp, ar->pos, SEEK_SET) == -1) { - ArchError(("%s: cannot seek to %jd: %s", ar->fname, - (intmax_t)ar->pos, strerror(errno))); - return (-1); - } - - /* - * Read next member header - */ - ret = fread(&ar->hdr, sizeof(ar->hdr), 1, ar->fp); - if (ret != 1) { - if (feof(ar->fp)) - return (0); - ArchError(("%s: error reading member header: %s", ar->fname, - strerror(errno))); - return (-1); - } - if (strncmp(ar->hdr.ar_fmag, ARFMAG, sizeof(ar->hdr.ar_fmag)) != 0) { - ArchError(("%s: bad entry magic", ar->fname)); - return (-1); - } - - /* - * looks like a member - get name by stripping trailing spaces - * and NUL terminating. - */ - strncpy(ar->sname, ar->hdr.ar_name, AR_NAMSIZ); - ar->sname[AR_NAMSIZ] = '\0'; - for (ptr = ar->sname + AR_NAMSIZ; ptr > ar->sname; ptr--) - if (ptr[-1] != ' ') - break; - - *ptr = '\0'; - - /* - * Parse the size. All entries need to have a size. Be careful - * to not allow buffer overruns. - */ - strncpy(buf, ar->hdr.ar_size, sizeof(ar->hdr.ar_size)); - buf[sizeof(ar->hdr.ar_size)] = '\0'; - - errno = 0; - ar->size = strtoumax(buf, &end, 10); - if (errno != 0 || strspn(end, " ") != strlen(end)) { - ArchError(("%s: bad size format in archive '%s'", - ar->fname, buf)); - return (-1); - } - - /* - * Look for the extended name table. Do this before parsing - * the date because this table doesn't need a date. - */ - if (strcmp(ar->sname, BSD_NAMEMAG) == 0 || - strcmp(ar->sname, SVR4_NAMEMAG) == 0) { - /* filename table - read it in */ - ar->nametablen = ar->size; - ar->nametab = emalloc(ar->nametablen); - - ret = fread(ar->nametab, 1, ar->nametablen, ar->fp); - if (ret != ar->nametablen) { - if (ferror(ar->fp)) { - ArchError(("%s: cannot read nametab: %s", - ar->fname, strerror(errno))); - } else { - ArchError(("%s: cannot read nametab: " - "short read", ar->fname)); - } - return (-1); - } - - /* - * NUL terminate the entries. Entries are \n terminated - * and may have a trailing / or \. - */ - ptr = ar->nametab; - while (ptr < ar->nametab + ar->nametablen) { - if (*ptr == '\n') { - if (ptr[-1] == '/' || ptr[-1] == '\\') - ptr[-1] = '\0'; - *ptr = '\0'; - } - ptr++; - } - - /* get next archive entry */ - goto next; - } - - /* - * Now parse the modification date. Be careful to not overrun - * buffers. - */ - strncpy(buf, ar->hdr.ar_date, sizeof(ar->hdr.ar_date)); - buf[sizeof(ar->hdr.ar_date)] = '\0'; - - errno = 0; - ar->time = (int64_t)strtoll(buf, &end, 10); - if (errno != 0 || strspn(end, " ") != strlen(end)) { - ArchError(("%s: bad date format in archive '%s'", - ar->fname, buf)); - return (-1); - } - - /* - * Now check for the symbol table. This should really be the first - * entry, but we don't check this. - */ - if (strcmp(ar->sname, BSD_RANLIBMAG) == 0 || - strcmp(ar->sname, SVR4_RANLIBMAG) == 0) { - /* symbol table - return a zero length name */ - ar->member[0] = '\0'; - ar->sname[0] = '\0'; - return (1); - } - - have_long_name = 0; - - /* - * Look whether this is a long name. There are several variants - * of long names: - * "#1/12 " - 12 length of following filename - * "/17 " - index into name table - * " 17 " - index into name table - * Note that in the last case we must also check that there is no - * slash in the name because of filenames with leading spaces: - * " 777.o/ " - filename 777.o - */ - if (ar->sname[0] == '/' || (ar->sname[0] == ' ' && - strchr(ar->sname, '/') == NULL)) { - /* SVR4 extended name */ - errno = 0; - offs = strtoul(ar->sname + 1, &end, 10); - if (errno != 0 || *end != '\0' || offs >= ar->nametablen || - end == ar->sname + 1) { - ArchError(("%s: bad extended name '%s'", ar->fname, - ar->sname)); - return (-1); - } - - /* fetch the name */ - if (ar->mlen <= strlen(ar->nametab + offs)) { - ar->mlen = strlen(ar->nametab + offs) + 1; - ar->member = erealloc(ar->member, ar->mlen); - } - strcpy(ar->member, ar->nametab + offs); - - have_long_name = 1; - - } else if (strncmp(ar->sname, BSD_EXT1, BSD_EXT1LEN) == 0 && - isdigit((unsigned char)ar->sname[BSD_EXT1LEN])) { - /* BSD4.4 extended name */ - errno = 0; - offs = strtoul(ar->sname + BSD_EXT1LEN, &end, 10); - if (errno != 0 || *end != '\0' || - end == ar->sname + BSD_EXT1LEN) { - ArchError(("%s: bad extended name '%s'", ar->fname, - ar->sname)); - return (-1); - } - - /* read it from the archive */ - if (ar->mlen <= offs) { - ar->mlen = offs + 1; - ar->member = erealloc(ar->member, ar->mlen); - } - ret = fread(ar->member, 1, offs, ar->fp); - if (ret != offs) { - if (ferror(ar->fp)) { - ArchError(("%s: reading extended name: %s", - ar->fname, strerror(errno))); - } else { - ArchError(("%s: reading extended name: " - "short read", ar->fname)); - } - return (-1); - } - ar->member[offs] = '\0'; - - have_long_name = 1; - } - - /* - * Now remove the trailing slash that Svr4 puts at - * the end of the member name to support trailing spaces in names. - */ - if (ptr > ar->sname && ptr[-1] == '/') - *--ptr = '\0'; - - if (!have_long_name) { - if (strlen(ar->sname) >= ar->mlen) { - ar->mlen = strlen(ar->sname) + 1; - ar->member = erealloc(ar->member, ar->mlen); - } - strcpy(ar->member, ar->sname); - } - - return (1); -} - -/* - * Touch the current archive member by writing a new header with an - * updated timestamp. The return value is 0 for success and -1 for errors. - */ -static int -ArchArchiveTouch(struct arfile *ar, int64_t ts) -{ - - /* seek to our header */ - if (fseeko(ar->fp, ar->pos, SEEK_SET) == -1) { - ArchError(("%s: cannot seek to %jd: %s", ar->fname, - (intmax_t)ar->pos, strerror(errno))); - return (-1); - } - - /* - * change timestamp, be sure to not NUL-terminated it, but - * to fill with spaces. - */ - snprintf(ar->hdr.ar_date, sizeof(ar->hdr.ar_date), "%jd", - (intmax_t)ts); - memset(ar->hdr.ar_date + strlen(ar->hdr.ar_date), - ' ', sizeof(ar->hdr.ar_date) - strlen(ar->hdr.ar_date)); - - if (fwrite(&ar->hdr, sizeof(ar->hdr), 1, ar->fp) != 1) { - ArchError(("%s: cannot touch: %s", ar->fname, strerror(errno))); - return (-1); - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * ArchFindMember -- - * Locate a member of an archive, given the path of the archive and - * the path of the desired member. If the archive is to be modified, - * the mode should be "r+", if not, it should be "r". The archive - * file is returned positioned at the correct header. - * - * Results: - * A struct arfile *, opened for reading and, possibly writing, - * positioned at the member's header, or NULL if the member was - * nonexistent. - * - *----------------------------------------------------------------------- - */ -static struct arfile * -ArchFindMember(const char *archive, const char *member, const char *mode) -{ - struct arfile *ar; - const char *cp; /* Useful character pointer */ - - if ((ar = ArchArchiveOpen(archive, mode)) == NULL) - return (NULL); - - /* - * Because of space constraints and similar things, files are archived - * using their final path components, not the entire thing, so we need - * to point 'member' to the final component, if there is one, to make - * the comparisons easier... - */ - if (member != NULL) { - cp = strrchr(member, '/'); - if (cp != NULL) { - member = cp + 1; - } - } - - while (ArchArchiveNext(ar) > 0) { - /* - * When comparing there are actually three cases: - * (1) the name fits into the limit og af_name, - * (2) the name is longer and the archive supports long names, - * (3) the name is longer and the archive doesn't support long - * names. - * Because we don't know whether the archive supports long - * names or not we need to be carefull. - */ - if (member == NULL) { - /* special case - symbol table */ - if (ar->member[0] == '\0') - return (ar); - } else if (strlen(member) <= AR_NAMSIZ) { - /* case (1) */ - if (strcmp(ar->member, member) == 0) - return (ar); - } else if (strcmp(ar->member, member) == 0) { - /* case (3) */ - return (ar); - } else { - /* case (2) */ - if (strlen(ar->member) == AR_NAMSIZ && - strncmp(member, ar->member, AR_NAMSIZ) == 0) - return (ar); - } - } - - /* not found */ - ArchArchiveClose(ar); - return (NULL); -} - -/*- - *----------------------------------------------------------------------- - * ArchStatMember -- - * Locate a member of an archive, given the path of the archive and - * the path of the desired member, and a boolean representing whether - * or not the archive should be hashed (if not already hashed). - * - * Results: - * A pointer to the current struct ar_hdr structure for the member. Note - * That no position is returned, so this is not useful for touching - * archive members. This is mostly because we have no assurances that - * The archive will remain constant after we read all the headers, so - * there's not much point in remembering the position... - * - * Side Effects: - * - *----------------------------------------------------------------------- - */ -static int64_t -ArchStatMember(const char *archive, const char *member, bool hash) -{ - struct arfile *arf; - int64_t ret; - int t; - char *cp; /* Useful character pointer */ - Arch *ar; /* Archive descriptor */ - Hash_Entry *he; /* Entry containing member's description */ - char copy[AR_NAMSIZ + 1]; - - /* - * Because of space constraints and similar things, files are archived - * using their final path components, not the entire thing, so we need - * to point 'member' to the final component, if there is one, to make - * the comparisons easier... - */ - if (member != NULL) { - cp = strrchr(member, '/'); - if (cp != NULL) - member = cp + 1; - } - - TAILQ_FOREACH(ar, &archives, link) { - if (strcmp(archive, ar->name) == 0) - break; - } - if (ar == NULL) { - /* archive not found */ - if (!hash) { - /* - * Caller doesn't want the thing hashed, just use - * ArchFindMember to read the header for the member - * out and close down the stream again. - */ - arf = ArchFindMember(archive, member, "r"); - if (arf == NULL) { - return (INT64_MIN); - } - ret = arf->time; - ArchArchiveClose(arf); - return (ret); - } - - /* - * We don't have this archive on the list yet, so we want to - * find out everything that's in it and cache it so we can get - * at it quickly. - */ - arf = ArchArchiveOpen(archive, "r"); - if (arf == NULL) { - return (INT64_MIN); - } - - /* create archive data structure */ - ar = emalloc(sizeof(*ar)); - ar->name = estrdup(archive); - Hash_InitTable(&ar->members, -1); - - while ((t = ArchArchiveNext(arf)) > 0) { - he = Hash_CreateEntry(&ar->members, arf->member, NULL); - Hash_SetValue(he, emalloc(sizeof(int64_t))); - *(int64_t *)Hash_GetValue(he) = arf->time; - } - - ArchArchiveClose(arf); - - if (t < 0) { - /* error happend - throw away everything */ - Hash_DeleteTable(&ar->members); - free(ar->name); - free(ar); - return (INT64_MIN); - } - - TAILQ_INSERT_TAIL(&archives, ar, link); - } - - /* - * Now that the archive has been read and cached, we can look into - * the hash table to find the desired member's header. - */ - he = Hash_FindEntry(&ar->members, member); - if (he != NULL) - return (*(int64_t *)Hash_GetValue(he)); - - if (member != NULL && strlen(member) > AR_NAMSIZ) { - /* Try truncated name */ - strncpy(copy, member, AR_NAMSIZ); - copy[AR_NAMSIZ] = '\0'; - - if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) - return (*(int64_t *)Hash_GetValue(he)); - } - - return (INT64_MIN); -} - -/*- - *----------------------------------------------------------------------- - * Arch_Touch -- - * Touch a member of an archive. - * - * Results: - * The 'time' field of the member's header is updated. - * - * Side Effects: - * The modification time of the entire archive is also changed. - * For a library, this could necessitate the re-ranlib'ing of the - * whole thing. - * - *----------------------------------------------------------------------- - */ -void -Arch_Touch(GNode *gn) -{ - struct arfile *ar; - - ar = ArchFindMember( - Var_Value(ARCHIVE, gn), Var_Value(TARGET, gn), "r+"); - - if (ar != NULL) { - ArchArchiveTouch(ar, (int64_t)now); - ArchArchiveClose(ar); - } -} - -/*- - *----------------------------------------------------------------------- - * Arch_TouchLib -- - * Given a node which represents a library, touch the thing, making - * sure that the table of contents also is touched. - * - * Results: - * None. - * - * Side Effects: - * Both the modification time of the library and of the RANLIBMAG - * member are set to 'now'. - * - *----------------------------------------------------------------------- - */ -void -Arch_TouchLib(GNode *gn) -{ - struct arfile *ar; /* Open archive */ - struct utimbuf times; /* Times for utime() call */ - - ar = ArchFindMember(gn->path, NULL, "r+"); - if (ar != NULL) { - ArchArchiveTouch(ar, (int64_t)now); - ArchArchiveClose(ar); - - times.actime = times.modtime = now; - utime(gn->path, ×); - } -} - -/*- - *----------------------------------------------------------------------- - * Arch_MTime -- - * Return the modification time of a member of an archive, given its - * name. - * - * Results: - * The modification time(seconds). - * XXXHB this should be a long. - * - * Side Effects: - * The mtime field of the given node is filled in with the value - * returned by the function. - * - *----------------------------------------------------------------------- - */ -int -Arch_MTime(GNode *gn) -{ - int64_t mtime; - - mtime = ArchStatMember( - Var_Value(ARCHIVE, gn), Var_Value(TARGET, gn), true); - - if (mtime == INT_MIN) { - mtime = 0; - } - gn->mtime = (int)mtime; /* XXX */ - return (gn->mtime); -} - -/*- - *----------------------------------------------------------------------- - * Arch_MemMTime -- - * Given a non-existent archive member's node, get its modification - * time from its archived form, if it exists. - * - * Results: - * The modification time. - * - * Side Effects: - * The mtime field is filled in. - * - *----------------------------------------------------------------------- - */ -int -Arch_MemMTime(GNode *gn) -{ - LstNode *ln; - GNode *pgn; - char *nameStart; - char *nameEnd; - - for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Succ(ln)) { - pgn = Lst_Datum(ln); - - if (pgn->type & OP_ARCHV) { - /* - * If the parent is an archive specification and is - * being made and its member's name matches the name of - * the node we were given, record the modification time - * of the parent in the child. We keep searching its - * parents in case some other parent requires this - * child to exist... - */ - nameStart = strchr(pgn->name, OPEN_PAREN) + 1; - nameEnd = strchr(nameStart, CLOSE_PAREN); - - if (pgn->make && strncmp(nameStart, gn->name, - nameEnd - nameStart) == 0) { - gn->mtime = Arch_MTime(pgn); - } - } else if (pgn->make) { - /* - * Something which isn't a library depends on the - * existence of this target, so it needs to exist. - */ - gn->mtime = 0; - break; - } - } - return (gn->mtime); -} - -/*- - *----------------------------------------------------------------------- - * Arch_FindLib -- - * Search for a named library along the given search path. - * - * Results: - * None. - * - * Side Effects: - * The node's 'path' field is set to the found path (including the - * actual file name, not -l...). If the system can handle the -L - * flag when linking (or we cannot find the library), we assume that - * the user has placed the .LIBRARIES variable in the final linking - * command (or the linker will know where to find it) and set the - * TARGET variable for this node to be the node's name. Otherwise, - * we set the TARGET variable to be the full path of the library, - * as returned by Dir_FindFile. - * - *----------------------------------------------------------------------- - */ -void -Arch_FindLib(GNode *gn, struct Path *path) -{ - char *libName; /* file name for archive */ - size_t sz; - - sz = strlen(gn->name) + 4; - libName = emalloc(sz); - snprintf(libName, sz, "lib%s.a", &gn->name[2]); - - gn->path = Path_FindFile(libName, path); - - free(libName); - -#ifdef LIBRARIES - Var_Set(TARGET, gn->name, gn); -#else - Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); -#endif /* LIBRARIES */ -} - -/*- - *----------------------------------------------------------------------- - * Arch_LibOODate -- - * Decide if a node with the OP_LIB attribute is out-of-date. Called - * from Make_OODate to make its life easier, with the library's - * graph node. - * - * There are several ways for a library to be out-of-date that are - * not available to ordinary files. In addition, there are ways - * that are open to regular files that are not available to - * libraries. A library that is only used as a source is never - * considered out-of-date by itself. This does not preclude the - * library's modification time from making its parent be out-of-date. - * A library will be considered out-of-date for any of these reasons, - * given that it is a target on a dependency line somewhere: - * Its modification time is less than that of one of its - * sources (gn->mtime < gn->cmtime). - * Its modification time is greater than the time at which the - * make began (i.e. it's been modified in the course - * of the make, probably by archiving). - * The modification time of one of its sources is greater than - * the one of its RANLIBMAG member (i.e. its table of contents - * is out-of-date). We don't compare of the archive time - * vs. TOC time because they can be too close. In my - * opinion we should not bother with the TOC at all since - * this is used by 'ar' rules that affect the data contents - * of the archive, not by ranlib rules, which affect the - * TOC. - * - * Results: - * true if the library is out-of-date. false otherwise. - * - * Side Effects: - * The library will be hashed if it hasn't been already. - * - *----------------------------------------------------------------------- - */ -bool -Arch_LibOODate(GNode *gn) -{ - int64_t mtime; /* The table-of-contents's mod time */ - - if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->children)) { - return (false); - } - if (gn->mtime > now || gn->mtime < gn->cmtime) { - return (true); - } - - mtime = ArchStatMember(gn->path, NULL, false); - if (mtime == INT64_MIN) { - /* - * Not found. A library w/o a table of contents is out-of-date - */ - if (DEBUG(ARCH) || DEBUG(MAKE)) { - Debug("No TOC..."); - } - return (true); - } - - /* XXX choose one. */ - if (DEBUG(ARCH) || DEBUG(MAKE)) { - Debug("TOC modified %s...", Targ_FmtTime(mtime)); - } - return (gn->cmtime > mtime); -} diff --git a/usr.bin/make/arch.h b/usr.bin/make/arch.h deleted file mode 100644 index 3e27711f87..0000000000 --- a/usr.bin/make/arch.h +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/arch.h,v 1.8 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef arch_h_488adf7a -#define arch_h_488adf7a - -#include - -struct GNode; -struct Lst; -struct Path; - -/* archive errors are fatal */ -extern bool arch_fatal; - -bool Arch_ParseArchive(char **, struct Lst *, struct GNode *); -void Arch_Touch(struct GNode *); -void Arch_TouchLib(struct GNode *); -int Arch_MTime(struct GNode *); -int Arch_MemMTime(struct GNode *); -void Arch_FindLib(struct GNode *, struct Path *); -bool Arch_LibOODate(struct GNode *); - -#endif /* arch_h_488adf7a */ diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c deleted file mode 100644 index 9b30f93d28..0000000000 --- a/usr.bin/make/buf.c +++ /dev/null @@ -1,263 +0,0 @@ -/*- - * Copyright (c) 2005 Max Okumoto - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)buf.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/buf.c,v 1.32 2005/02/07 11:27:47 harti Exp $ - * $DragonFly: src/usr.bin/make/buf.c,v 1.43 2005/09/24 07:37:38 okumoto Exp $ - */ - -/* - * buf.c - * Functions for automatically-expanded buffers. - */ - -#include -#include - -#include "buf.h" -#include "util.h" - -/** - * Returns the number of bytes in the buffer. Doesn't include the - * null-terminating byte. - * - * @return The number of bytes in Buffer object. - */ -inline size_t -Buf_Size(const Buffer *buf) -{ - return (buf->end - buf->buf); -} - -/** - * Returns a reference to the data contained in the buffer. - * - * @note Adding data to the Buffer object may invalidate the reference. - */ -inline char * -Buf_Data(const Buffer *bp) -{ - return (bp->buf); -} - -/** - * Expand the buffer to hold the number of additional bytes, plus - * space to store a terminating NULL byte. - */ -static inline void -BufExpand(Buffer *bp, size_t nb) -{ - size_t len = Buf_Size(bp); - if (bp->size < len + nb + 1) { - int size = bp->size + MAX(nb + 1, BUF_ADD_INC); - - bp->buf = erealloc(bp->buf, size); - bp->size = size; - bp->end = bp->buf + len; - } -} - -/** - * Add a single byte to the buffer. - */ -inline void -Buf_AddByte(Buffer *bp, char byte) -{ - BufExpand(bp, 1); - - *bp->end = byte; - bp->end++; - *bp->end = '\0'; -} - -/** - * Add bytes to the buffer. - */ -void -Buf_AddBytes(Buffer *bp, size_t len, const char bytes[]) -{ - BufExpand(bp, len); - - memcpy(bp->end, bytes, len); - bp->end += len; - *bp->end = '\0'; -} - -/** - * Get the contents of a buffer and destroy the buffer. If the buffer - * is NULL, return NULL. - * - * Returns: - * the pointer to the data. - */ -char * -Buf_Peel(Buffer *bp) -{ - char *ret; - - if (bp == NULL) - return (NULL); - ret = bp->buf; - free(bp); - return (ret); -} - -/** - * Initialize a buffer. If no initial size is given, a reasonable - * default is used. - * - * @return A buffer object to be given to other functions in this library. - * - * Side Effects: - * Space is allocated for the Buffer object and a internal buffer. - */ -Buffer * -Buf_Init(size_t size) -{ - Buffer *bp; /* New Buffer */ - - if (size <= 0) - size = BUF_DEF_SIZE; - - bp = emalloc(sizeof(*bp)); - bp->size = size; - bp->buf = emalloc(size); - bp->end = bp->buf; - *bp->end = '\0'; - - return (bp); -} - -/** - * Destroy a buffer, and optionally free its data, too. - * - * Side Effects: - * Space for the Buffer object and possibly the internal buffer - * is de-allocated. - */ -void -Buf_Destroy(Buffer *buf, bool freeData) -{ - if (freeData) - free(buf->buf); - free(buf); -} - -/** - * Replace the last byte in a buffer. If the buffer was empty - * intially, then a new byte will be added. - */ -void -Buf_ReplaceLastByte(Buffer *bp, char byte) -{ - if (bp->end == bp->buf) { - Buf_AddByte(bp, byte); - } else { - *(bp->end - 1) = byte; - } -} - -/** - * Append characters in str to Buffer object - */ -void -Buf_Append(Buffer *bp, const char str[]) -{ - Buf_AddBytes(bp, strlen(str), str); -} - -/** - * Append characters in buf to Buffer object - */ -void -Buf_AppendBuf(Buffer *bp, const Buffer *buf) -{ - Buf_AddBytes(bp, Buf_Size(buf), buf->buf); -} - -/** - * Append characters between str and end to Buffer object. - */ -void -Buf_AppendRange(Buffer *bp, const char str[], const char *end) -{ - Buf_AddBytes(bp, end - str, str); -} - -/** - * Convert newlines in buffer to spaces. The trailing newline is - * removed. - */ -void -Buf_StripNewlines(Buffer *bp) -{ - char *ptr = bp->end; - - /* - * If there is anything in the buffer, remove the last - * newline character. - */ - if (ptr != bp->buf) { - if (*(ptr - 1) == '\n') { - /* shorten buffer */ - *(ptr - 1) = '\0'; - --bp->end; - } - --ptr; - } - - /* Convert newline characters to a space characters. */ - while (ptr != bp->buf) { - if (*ptr == '\n') { - *ptr = ' '; - } - --ptr; - } -} - -/** - * Clear the contents of the buffer. - */ -void -Buf_Clear(Buffer *bp) -{ - bp->end = bp->buf; - *bp->end = '\0'; -} - diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h deleted file mode 100644 index cb2ba9643b..0000000000 --- a/usr.bin/make/buf.h +++ /dev/null @@ -1,89 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/buf.h,v 1.24 2005/02/07 11:27:47 harti Exp $ - * $DragonFly: src/usr.bin/make/buf.h,v 1.36 2005/09/24 07:37:38 okumoto Exp $ - */ - -#ifndef buf_h_a61a6812 -#define buf_h_a61a6812 - -/*- - * buf.h -- - * Header for users of the buf library. - */ - -#include -#include - -/* - * There are several places where expandable buffers are used (parse.c and - * var.c). This constant is merely the starting point for those buffers. If - * lines tend to be much shorter than this, it would be best to reduce BSIZE. - * If longer, it should be increased. Reducing it will cause more copying to - * be done for longer lines, but will save space for shorter ones. In any - * case, it ought to be a power of two simply because most storage allocation - * schemes allocate in powers of two. - */ -#define MAKE_BSIZE 256 /* starting size for expandable buffers */ - -#define BUF_DEF_SIZE 256 /* Default buffer size */ -#define BUF_ADD_INC 256 /* Expansion increment when Adding */ - -typedef struct Buffer { - size_t size; /* Current size of the buffer */ - char *buf; /* The buffer itself */ - char *end; /* Place to write to */ -} Buffer; - -void Buf_AddByte(Buffer *, char); -void Buf_AddBytes(Buffer *, size_t, const char []); -void Buf_Append(Buffer *, const char []); -void Buf_AppendBuf(Buffer *, const Buffer *); -void Buf_AppendRange(Buffer *, const char [], const char []); -void Buf_Clear(Buffer *); -char *Buf_Data(const Buffer *); -void Buf_Destroy(Buffer *, bool); -Buffer *Buf_Init(size_t); -char *Buf_Peel(Buffer *); -void Buf_ReplaceLastByte(Buffer *, char); -size_t Buf_Size(const Buffer *); -void Buf_StripNewlines(Buffer *); - -#endif /* buf_h_a61a6812 */ diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c deleted file mode 100644 index 6d6722b949..0000000000 --- a/usr.bin/make/cond.c +++ /dev/null @@ -1,1227 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)cond.c 8.2 (Berkeley) 1/2/94 - * $FreeBSD: src/usr.bin/make/cond.c,v 1.39 2005/02/07 07:49:16 harti Exp $ - * $DragonFly: src/usr.bin/make/cond.c,v 1.51 2005/09/24 07:38:03 okumoto Exp $ - */ - -/* - * Functions to handle conditionals in a makefile. - * - * Interface: - * Cond_Eval Evaluate the conditional in the passed line. - */ - -#include -#include -#include - -#include "buf.h" -#include "cond.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "make.h" -#include "parse.h" -#include "str.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/* - * The parsing of conditional expressions is based on this grammar: - * E -> F || E - * E -> F - * F -> T && F - * F -> T - * T -> defined(variable) - * T -> make(target) - * T -> exists(file) - * T -> empty(varspec) - * T -> target(name) - * T -> symbol - * T -> $(varspec) op value - * T -> $(varspec) == "string" - * T -> $(varspec) != "string" - * T -> ( E ) - * T -> ! T - * op -> == | != | > | < | >= | <= - * - * 'symbol' is some other symbol to which the default function (condDefProc) - * is applied. - * - * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) - * will return And for '&' and '&&', Or for '|' and '||', Not for '!', - * LParen for '(', RParen for ')' and will evaluate the other terminal - * symbols, using either the default function or the function given in the - * terminal, and return the result as either True or False. - * - * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. - */ -typedef enum { - And, - Or, - Not, - True, - False, - LParen, - RParen, - EndOfFile, - None, - Err -} Token; - -typedef bool CondProc(Parser *, int, char *); - -/*- - * Structures to handle elegantly the different forms of #if's. The - * last two fields are stored in condInvert and condDefProc, respectively. - */ -static void CondPushBack(Token); -static int CondGetArg(char **, char **, const char *, bool); -static CondProc CondDoDefined; -static CondProc CondDoMake; -static CondProc CondDoExists; -static CondProc CondDoTarget; -static char *CondCvtArg(char *, double *); -static Token CondToken(Parser *, bool); -static Token CondT(Parser *, bool); -static Token CondF(Parser *, bool); -static Token CondE(Parser *, bool); - -static const struct If { - bool doNot; /* true if default function should be negated */ - CondProc *defProc; /* Default function to apply */ - bool isElse; /* actually el */ -} ifs[] = { - [COND_IF] = { false, CondDoDefined, false }, - [COND_IFDEF] = { false, CondDoDefined, false }, - [COND_IFNDEF] = { true, CondDoDefined, false }, - [COND_IFMAKE] = { false, CondDoMake, false }, - [COND_IFNMAKE] = { true, CondDoMake, false }, - [COND_ELIF] = { false, CondDoDefined, true }, - [COND_ELIFDEF] = { false, CondDoDefined, true }, - [COND_ELIFNDEF] = { true, CondDoDefined, true }, - [COND_ELIFMAKE] = { false, CondDoMake, true }, - [COND_ELIFNMAKE] = { true, CondDoMake, true }, -}; - -static bool condInvert; /* Invert the default function */ -static CondProc *condDefProc; /* default function to apply */ -static char *condExpr; /* The expression to parse */ -static Token condPushBack = None; /* Single push-back token in parsing */ - -#define MAXIF 30 /* greatest depth of #if'ing */ - -static bool condStack[MAXIF]; /* Stack of conditionals's values */ -static int condLineno[MAXIF]; /* Line numbers of the opening .if */ -static int condTop = MAXIF; /* Top-most conditional */ -static int skipIfLevel = 0; /* Depth of skipped conditionals */ -static int skipIfLineno[MAXIF]; /* Line numbers of skipped .ifs */ -bool skipLine = false; /* Whether the parse module is skipping - * lines */ - -/** - * CondPushBack - * Push back the most recent token read. We only need one level of - * this, so the thing is just stored in 'condPushback'. - * - * Side Effects: - * condPushback is overwritten. - */ -static void -CondPushBack(Token t) -{ - - condPushBack = t; -} - -/** - * CondGetArg - * Find the argument of a built-in function. parens is set to true - * if the arguments are bounded by parens. - * - * Results: - * The length of the argument and the address of the argument. - * - * Side Effects: - * The pointer is set to point to the closing parenthesis of the - * function call. - */ -static int -CondGetArg(char **linePtr, char **argPtr, const char *func, bool parens) -{ - char *cp; - size_t argLen; - Buffer *buf; - - cp = *linePtr; - if (parens) { - while (*cp != OPEN_PAREN && *cp != '\0') { - cp++; - } - if (*cp == OPEN_PAREN) { - cp++; - } - } - - if (*cp == '\0') { - /* - * No arguments whatsoever. Because 'make' and 'defined' - * aren't really "reserved words", we don't print a message. - * I think this is better than hitting the user with a warning - * message every time s/he uses the word 'make' or 'defined' - * at the beginning of a symbol... - */ - *argPtr = cp; - return (0); - } - - while (*cp == ' ' || *cp == '\t') { - cp++; - } - - /* - * Create a buffer for the argument and start it out at 16 characters - * long. Why 16? Why not? - */ - buf = Buf_Init(16); - - while ((strchr(" \t)&|", *cp) == NULL) && (*cp != '\0')) { - if (*cp == '$') { - /* - * Parse the variable spec and install it as part of - * the argument if it's valid. We tell Var_Parse to - * complain on an undefined variable, so we don't do - * it too. Nor do we return an error, though perhaps - * we should... - */ - char *cp2; - size_t len = 0; - bool doFree; - - cp2 = Var_Parse(cp, VAR_CMD, true, &len, &doFree); - - Buf_Append(buf, cp2); - if (doFree) { - free(cp2); - } - cp += len; - } else { - Buf_AddByte(buf, *cp); - cp++; - } - } - - while (*cp == ' ' || *cp == '\t') { - cp++; - } - - Buf_AddByte(buf, '\0'); - *argPtr = Buf_Data(buf); - argLen = Buf_Size(buf); - Buf_Destroy(buf, false); - - if (parens) { - if (*cp == CLOSE_PAREN) { - /* - * Advance pointer past close parenthesis. - */ - cp++; - *linePtr = cp; - return (argLen); - } else { - Parse_Error(PARSE_WARNING, - "Missing closing parenthesis for %s()", func); - return (0); - /* XXX memory leak of argPtr? */ - } - } else { - *linePtr = cp; - return (argLen); - } -} - -/** - * CondDoDefined - * Handle the 'defined' function for conditionals. - * - * Results: - * true if the given variable is defined. - */ -static bool -CondDoDefined(Parser *parser __unused, int argLen, char *arg) -{ - char savec = arg[argLen]; - bool result; - - arg[argLen] = '\0'; - if (Var_Value(arg, VAR_CMD) != NULL) { - result = true; - } else { - result = false; - } - arg[argLen] = savec; - return (result); -} - -/** - * CondDoMake - * Handle the 'make' function for conditionals. - * - * Results: - * true if the given target is being made. - */ -static bool -CondDoMake(Parser *parser, int argLen, char *arg) -{ - char savec = arg[argLen]; - bool result; - const LstNode *ln; - - arg[argLen] = '\0'; - result = false; - LST_FOREACH(ln, parser->create) { - if (Str_Match(Lst_Datum(ln), arg)) { - result = true; - break; - } - } - arg[argLen] = savec; - return (result); -} - -/** - * CondDoExists - * See if the given file exists. - * - * Results: - * true if the file exists and false if it does not. - */ -static bool -CondDoExists(Parser *parser __unused, int argLen, char *arg) -{ - char savec = arg[argLen]; - bool result; - char *path; - - arg[argLen] = '\0'; - path = Path_FindFile(arg, &dirSearchPath); - if (path != NULL) { - result = true; - free(path); - } else { - result = false; - } - arg[argLen] = savec; - return (result); -} - -/** - * CondDoTarget - * See if the given node exists and is an actual target. - * - * Results: - * true if the node exists as a target and false if it does not. - */ -static bool -CondDoTarget(Parser *parser __unused, int argLen, char *arg) -{ - char savec = arg[argLen]; - bool result; - GNode *gn; - - arg[argLen] = '\0'; - gn = Targ_FindNode(arg, TARG_NOCREATE); - if ((gn != NULL) && !OP_NOP(gn->type)) { - result = true; - } else { - result = false; - } - arg[argLen] = savec; - return (result); -} - -/** - * CondCvtArg - * Convert the given number into a double. If the number begins - * with 0x, it is interpreted as a hexadecimal integer - * and converted to a double from there. All other strings just have - * strtod called on them. - * - * Results: - * Sets 'value' to double value of string. - * Returns address of the first character after the last valid - * character of the converted number. - * - * Side Effects: - * Can change 'value' even if string is not a valid number. - */ -static char * -CondCvtArg(char *str, double *value) -{ - - if ((*str == '0') && (str[1] == 'x')) { - long i; - - for (str += 2, i = 0; ; str++) { - int x; - - if (isdigit((unsigned char)*str)) - x = *str - '0'; - else if (isxdigit((unsigned char)*str)) - x = 10 + *str - - isupper((unsigned char)*str) ? 'A' : 'a'; - else { - *value = (double)i; - return (str); - } - i = (i << 4) + x; - } - - } else { - char *eptr; - - *value = strtod(str, &eptr); - return (eptr); - } -} - -/** - * CondToken - * Return the next token from the input. - * - * Results: - * A Token for the next lexical token in the stream. - * - * Side Effects: - * condPushback will be set back to None if it is used. - */ -static Token -CondToken(Parser *parser, bool doEval) -{ - Token t; - - if (condPushBack != None) { - t = condPushBack; - condPushBack = None; - return (t); - } - - while (*condExpr == ' ' || *condExpr == '\t') { - condExpr++; - } - - switch (*condExpr) { - case OPEN_PAREN: - t = LParen; - condExpr++; - break; - case CLOSE_PAREN: - t = RParen; - condExpr++; - break; - case '|': - if (condExpr[1] == '|') { - condExpr++; - } - condExpr++; - t = Or; - break; - case '&': - if (condExpr[1] == '&') { - condExpr++; - } - condExpr++; - t = And; - break; - case '!': - t = Not; - condExpr++; - break; - case '\n': - case '\0': - t = EndOfFile; - break; - case '$':{ - char *lhs; - const char *op; - char *rhs; - char zero[] = "0"; - size_t varSpecLen = 0; - bool doFree; - - /* - * Parse the variable spec and skip over it, saving - * its value in lhs. - */ - t = Err; - lhs = Var_Parse(condExpr, VAR_CMD, doEval, - &varSpecLen, &doFree); - if (lhs == var_Error) { - /* - * Even if !doEval, we still report syntax - * errors, which is what getting var_Error - * back with !doEval means. - */ - return (Err); - } - condExpr += varSpecLen; - - if (!isspace((unsigned char)*condExpr) && - strchr("!=><", *condExpr) == NULL) { - Buffer *buf; - - buf = Buf_Init(0); - - Buf_Append(buf, lhs); - - if (doFree) - free(lhs); - - for (; *condExpr && - !isspace((unsigned char)*condExpr); - condExpr++) - Buf_AddByte(buf, *condExpr); - - Buf_AddByte(buf, '\0'); - lhs = Buf_Data(buf); - Buf_Destroy(buf, false); - - doFree = true; - } - - /* - * Skip whitespace to get to the operator - */ - while (isspace((unsigned char)*condExpr)) - condExpr++; - - /* - * Make sure the operator is a valid one. If it isn't - * a known relational operator, pretend we got a != 0 - * comparison. - */ - op = condExpr; - switch (*condExpr) { - case '!': - case '=': - case '<': - case '>': - if (condExpr[1] == '=') { - condExpr += 2; - } else { - condExpr += 1; - } - while (isspace((unsigned char)*condExpr)) { - condExpr++; - } - if (*condExpr == '\0') { - Parse_Error(PARSE_WARNING, - "Missing right-hand-side of operator"); - goto error; - } - rhs = condExpr; - break; - default: - op = "!="; - rhs = zero; - break; - } - - if (*rhs == '"') { - /* - * Doing a string comparison. Only allow == - * and != for * operators. - */ - char *string; - char *cp, *cp2; - int qt; - Buffer *buf; - - do_string_compare: - if (((*op != '!') && (*op != '=')) || - (op[1] != '=')) { - Parse_Error(PARSE_WARNING, - "String comparison operator should " - "be either == or !="); - goto error; - } - buf = Buf_Init(0); - qt = *rhs == '"' ? 1 : 0; - - for (cp = &rhs[qt]; - ((qt && (*cp != '"')) || - (!qt && strchr(" \t)", *cp) == NULL)) && - (*cp != '\0'); cp++) { - if ((*cp == '\\') && (cp[1] != '\0')) { - /* - * Backslash escapes things - * -- skip over next - * character, * - * if it exists. - */ - cp++; - Buf_AddByte(buf, *cp); - - } else if (*cp == '$') { - size_t len = 0; - bool freeIt; - - cp2 = Var_Parse(cp, VAR_CMD, - doEval, &len, &freeIt); - if (cp2 != var_Error) { - Buf_Append(buf, cp2); - if (freeIt) { - free(cp2); - } - cp += len - 1; - } else { - Buf_AddByte(buf, *cp); - } - } else { - Buf_AddByte(buf, *cp); - } - } - - string = Buf_Peel(buf); - - DEBUGF(COND, ("lhs = \"%s\", rhs = \"%s\", " - "op = %.2s\n", lhs, string, op)); - /* - * Null-terminate rhs and perform the - * comparison. t is set to the result. - */ - if (*op == '=') { - t = strcmp(lhs, string) ? False : True; - } else { - t = strcmp(lhs, string) ? True : False; - } - free(string); - if (rhs == condExpr) { - if (*cp == '\0' || (!qt && *cp == CLOSE_PAREN)) - condExpr = cp; - else - condExpr = cp + 1; - } - } else { - /* - * rhs is either a float or an integer. - * Convert both the lhs and the rhs to a - * double and compare the two. - */ - double left, right; - char *string; - - if (*CondCvtArg(lhs, &left) != '\0') - goto do_string_compare; - if (*rhs == '$') { - size_t len = 0; - bool freeIt; - - string = Var_Parse(rhs, VAR_CMD, doEval, - &len, &freeIt); - if (string == var_Error) { - right = 0.0; - } else { - if (*CondCvtArg(string, - &right) != '\0') { - if (freeIt) - free(string); - goto do_string_compare; - } - if (freeIt) - free(string); - if (rhs == condExpr) - condExpr += len; - } - } else { - char *c = CondCvtArg(rhs, &right); - - if (c == rhs) - goto do_string_compare; - if (rhs == condExpr) { - /* - * Skip over the right-hand - * side - */ - condExpr = c; - } - } - - DEBUGF(COND, ("left = %f, right = %f, " - "op = %.2s\n", left, right, op)); - switch (op[0]) { - case '!': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, - "Unknown operator"); - goto error; - } - t = (left != right ? True : False); - break; - case '=': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, - "Unknown operator"); - goto error; - } - t = (left == right ? True : False); - break; - case '<': - if (op[1] == '=') { - t = (left <= right ? True : False); - } else { - t = (left < right ? True : False); - } - break; - case '>': - if (op[1] == '=') { - t = (left >= right ? True : False); - } else { - t = (left > right ? True : False); - } - break; - default: - break; - } - } - error: - if (doFree) - free(lhs); - break; - } - - default:{ - CondProc *evalProc; - bool invert = false; - char *arg; - int arglen; - - if (strncmp(condExpr, "defined", 7) == 0) { - /* - * Use CondDoDefined to evaluate the argument - * and CondGetArg to extract the argument - * from the 'function call'. - */ - evalProc = CondDoDefined; - condExpr += 7; - arglen = CondGetArg(&condExpr, &arg, - "defined", true); - if (arglen == 0) { - condExpr -= 7; - goto use_default; - } - } else if (strncmp(condExpr, "make", 4) == 0) { - /* - * Use CondDoMake to evaluate the argument - * and CondGetArg to extract the argument - * from the 'function call'. - */ - evalProc = CondDoMake; - condExpr += 4; - arglen = CondGetArg(&condExpr, &arg, - "make", true); - if (arglen == 0) { - condExpr -= 4; - goto use_default; - } - } else if (strncmp(condExpr, "exists", 6) == 0) { - /* - * Use CondDoExists to evaluate the argument - * and CondGetArg to extract the argument - * from the 'function call'. - */ - evalProc = CondDoExists; - condExpr += 6; - arglen = CondGetArg(&condExpr, &arg, - "exists", true); - if (arglen == 0) { - condExpr -= 6; - goto use_default; - } - } else if (strncmp(condExpr, "empty", 5) == 0) { - /* - * Use Var_Parse to parse the spec in parens - * and return True if the resulting string is - * empty. - */ - size_t length; - bool doFree; - char *val; - - condExpr += 5; - - for (arglen = 0; - condExpr[arglen] != OPEN_PAREN && - condExpr[arglen] != '\0'; arglen += 1) - continue; - - if (condExpr[arglen] != '\0') { - length = 0; - val = Var_Parse(&condExpr[arglen - 1], - VAR_CMD, false, &length, &doFree); - if (val == var_Error) { - t = Err; - } else { - /* - * A variable is empty when - * it just contains spaces... - * 4/15/92, christos - */ - char *p; - - for (p = val; - *p && - isspace((unsigned char)*p); - p++) - continue; - t = (*p == '\0') ? True : False; - } - if (doFree) { - free(val); - } - /* - * Advance condExpr to beyond the - * closing ). Note that we subtract - * one from arglen + length b/c - * length is calculated from - * condExpr[arglen - 1]. - */ - condExpr += arglen + length - 1; - } else { - condExpr -= 5; - goto use_default; - } - break; - - } else if (strncmp(condExpr, "target", 6) == 0) { - /* - * Use CondDoTarget to evaluate the argument - * and CondGetArg to extract the argument - * from the 'function call'. - */ - evalProc = CondDoTarget; - condExpr += 6; - arglen = CondGetArg(&condExpr, &arg, - "target", true); - if (arglen == 0) { - condExpr -= 6; - goto use_default; - } - } else { - /* - * The symbol is itself the argument to the - * default function. We advance condExpr to - * the end of the symbol by hand (the next - * whitespace, closing paren or binary - * operator) and set to invert the evaluation - * function if condInvert is true. - */ - use_default: - invert = condInvert; - evalProc = condDefProc; - arglen = CondGetArg(&condExpr, &arg, "", false); - } - - /* - * Evaluate the argument using the set function. If - * invert is true, we invert the sense of the - * function. - */ - t = (!doEval || evalProc(parser, arglen, arg) ? - (invert ? False : True) : - (invert ? True : False)); - free(arg); - break; - } - } - return (t); -} - -/** - * CondT - * Parse a single term in the expression. This consists of a terminal - * symbol or Not and a terminal symbol (not including the binary - * operators): - * T -> defined(variable) | make(target) | exists(file) | symbol - * T -> ! T | ( E ) - * - * Results: - * True, False or Err. - * - * Side Effects: - * Tokens are consumed. - */ -static Token -CondT(Parser *parser, bool doEval) -{ - Token t; - - t = CondToken(parser, doEval); - if (t == EndOfFile) { - /* - * If we reached the end of the expression, the expression - * is malformed... - */ - t = Err; - } else if (t == LParen) { - /* - * T -> ( E ) - */ - t = CondE(parser, doEval); - if (t != Err) { - if (CondToken(parser, doEval) != RParen) { - t = Err; - } - } - } else if (t == Not) { - t = CondT(parser, doEval); - if (t == True) { - t = False; - } else if (t == False) { - t = True; - } - } - return (t); -} - -/** - * CondF -- - * Parse a conjunctive factor (nice name, wot?) - * F -> T && F | T - * - * Results: - * True, False or Err - * - * Side Effects: - * Tokens are consumed. - */ -static Token -CondF(Parser *parser, bool doEval) -{ - Token l, o; - - l = CondT(parser, doEval); - if (l != Err) { - o = CondToken(parser, doEval); - - if (o == And) { - /* - * F -> T && F - * - * If T is False, the whole thing will be False, but - * we have to parse the r.h.s. anyway (to throw it - * away). If T is True, the result is the r.h.s., - * be it an Err or no. - */ - if (l == True) { - l = CondF(parser, doEval); - } else { - CondF(parser, false); - } - } else { - /* - * F -> T - */ - CondPushBack(o); - } - } - return (l); -} - -/** - * CondE -- - * Main expression production. - * E -> F || E | F - * - * Results: - * True, False or Err. - * - * Side Effects: - * Tokens are, of course, consumed. - */ -static Token -CondE(Parser *parser, bool doEval) -{ - Token l, o; - - l = CondF(parser, doEval); - if (l != Err) { - o = CondToken(parser, doEval); - - if (o == Or) { - /* - * E -> F || E - * - * A similar thing occurs for ||, except that here we - * make sure the l.h.s. is False before we bother to - * evaluate the r.h.s. Once again, if l is False, the - * result is the r.h.s. and once again if l is True, - * we parse the r.h.s. to throw it away. - */ - if (l == False) { - l = CondE(parser, doEval); - } else { - CondE(parser, false); - } - } else { - /* - * E -> F - */ - CondPushBack(o); - } - } - return (l); -} - -/** - * Cond_If - * Handle .if and .elif directives. - * This function is called even when we're skipping. - */ -void -Cond_If(Parser *parser, char *line, int code, int lineno) -{ - const struct If *ifp; - bool value; - - ifp = &ifs[code]; - - if (ifp->isElse) { - if (condTop == MAXIF) { - Parse_Error(PARSE_FATAL, "if-less elif"); - return; - } - if (skipIfLevel != 0) { - /* - * If skipping this conditional, just ignore - * the whole thing. If we don't, the user - * might be employing a variable that's - * undefined, for which there's an enclosing - * ifdef that we're skipping... - */ - skipIfLineno[skipIfLevel - 1] = lineno; - return; - } - - } else if (skipLine) { - /* - * Don't even try to evaluate a conditional that's - * not an else if we're skipping things... - */ - skipIfLineno[skipIfLevel] = lineno; - skipIfLevel += 1; - return; - } - - /* - * Initialize file-global variables for parsing - */ - condDefProc = ifp->defProc; - condInvert = ifp->doNot; - - while (*line == ' ' || *line == '\t') { - line++; - } - - condExpr = line; - condPushBack = None; - - switch (CondE(parser, true)) { - case True: - if (CondToken(parser, true) != EndOfFile) - goto err; - value = true; - break; - - case False: - if (CondToken(parser, true) != EndOfFile) - goto err; - value = false; - break; - - case Err: - err: Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); - return; - - default: - abort(); - } - - if (!ifp->isElse) { - /* push this value */ - condTop -= 1; - - } else if (skipIfLevel != 0 || condStack[condTop]) { - /* - * If this is an else-type conditional, it should only take - * effect if its corresponding if was evaluated and false. - * If its if was true or skipped, we return COND_SKIP (and - * start skipping in case we weren't already), leaving the - * stack unmolested so later elif's don't screw up... - */ - skipLine = true; - return; - } - - if (condTop < 0) { - /* - * This is the one case where we can definitely proclaim a fatal - * error. If we don't, we're hosed. - */ - Parse_Error(PARSE_FATAL, "Too many nested if's. %d max.",MAXIF); - return; - } - - /* push */ - condStack[condTop] = value; - condLineno[condTop] = lineno; - skipLine = !value; -} - -/** - * Cond_Else - * Handle .else statement. - */ -void -Cond_Else(Parser *parser __unused, char *line __unused, int code __unused, int lineno __unused) -{ - - while (isspace((u_char)*line)) - line++; - - if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { - Parse_Error(PARSE_WARNING, "junk after .else ignored '%s'", - line); - } - - if (condTop == MAXIF) { - Parse_Error(PARSE_FATAL, "if-less else"); - return; - } - if (skipIfLevel != 0) - return; - - if (skipIfLevel != 0 || condStack[condTop]) { - /* - * An else should only take effect if its corresponding if was - * evaluated and false. - * If its if was true or skipped, we return COND_SKIP (and - * start skipping in case we weren't already), leaving the - * stack unmolested so later elif's don't screw up... - * XXX How does this work with two .else's? - */ - skipLine = true; - return; - } - - /* inverse value */ - condStack[condTop] = !condStack[condTop]; - skipLine = !condStack[condTop]; -} - -/** - * Cond_Endif - * Handle .endif statement. - */ -void -Cond_Endif(Parser *parser __unused, char *line __unused, int code __unused, int lineno __unused) -{ - - while (isspace((u_char)*line)) - line++; - - if (*line != '\0' && (warn_flags & WARN_DIRSYNTAX)) { - Parse_Error(PARSE_WARNING, "junk after .endif ignored '%s'", - line); - } - - /* - * End of a conditional section. If skipIfLevel is non-zero, - * that conditional was skipped, so lines following it should - * also be skipped. Hence, we return COND_SKIP. Otherwise, - * the conditional was read so succeeding lines should be - * parsed (think about it...) so we return COND_PARSE, unless - * this endif isn't paired with a decent if. - */ - if (skipIfLevel != 0) { - skipIfLevel -= 1; - return; - } - - if (condTop == MAXIF) { - Parse_Error(PARSE_FATAL, "if-less endif"); - return; - } - - /* pop */ - skipLine = false; - condTop += 1; -} - -/** - * Cond_End - * Make sure everything's clean at the end of a makefile. - * - * Side Effects: - * Parse_Error will be called if open conditionals are around. - */ -void -Cond_End(Parser *parser __unused, char *line __unused, int code __unused, int lineno __unused) -{ - int level; - - if (condTop != MAXIF) { - Parse_Error(PARSE_FATAL, "%d open conditional%s:", - MAXIF - condTop + skipIfLevel, - MAXIF - condTop + skipIfLevel== 1 ? "" : "s"); - - for (level = skipIfLevel; level > 0; level--) - Parse_Error(PARSE_FATAL, "\t%*sat line %d (skipped)", - MAXIF - condTop + level + 1, "", - skipIfLineno[level - 1]); - for (level = condTop; level < MAXIF; level++) - Parse_Error(PARSE_FATAL, "\t%*sat line %d " - "(evaluated to %s)", MAXIF - level + skipIfLevel, - "", condLineno[level], - condStack[level] ? "true" : "false"); - } - condTop = MAXIF; -} diff --git a/usr.bin/make/cond.h b/usr.bin/make/cond.h deleted file mode 100644 index eef3c0351a..0000000000 --- a/usr.bin/make/cond.h +++ /dev/null @@ -1,77 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/cond.h,v 1.7 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef cond_h_6e96ad7c -#define cond_h_6e96ad7c - -#include - -#include "parse.h" - -/* - * Values returned by Cond_Eval. - */ -#define COND_PARSE 0 /* Parse the next lines */ -#define COND_SKIP 1 /* Skip the next lines */ -#define COND_INVALID 2 /* Not a conditional statement */ - -enum { - COND_IF, - COND_IFDEF, - COND_IFNDEF, - COND_IFMAKE, - COND_IFNMAKE, - COND_ELSE, - COND_ELIF, - COND_ELIFDEF, - COND_ELIFNDEF, - COND_ELIFMAKE, - COND_ELIFNMAKE, - COND_ENDIF, -}; - -DirectiveHandler Cond_If; -DirectiveHandler Cond_Else; -DirectiveHandler Cond_Endif; -DirectiveHandler Cond_End; - -extern bool skipLine; - -#endif /* cond_h_6e96ad7c */ diff --git a/usr.bin/make/config.h b/usr.bin/make/config.h deleted file mode 100644 index 1060c6908c..0000000000 --- a/usr.bin/make/config.h +++ /dev/null @@ -1,87 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)config.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/config.h,v 1.16 2005/02/01 10:50:35 harti Exp $ - * $DragonFly: src/usr.bin/make/config.h,v 1.13 2005/08/24 00:09:02 okumoto Exp $ - */ - -#ifndef config_h_efe0765e -#define config_h_efe0765e - -/* - * INCLUDES - * LIBRARIES - * These control the handling of the .INCLUDES and .LIBS variables. - * If INCLUDES is defined, the .INCLUDES variable will be filled - * from the search paths of those suffixes which are marked by - * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS - * See suff.c for more details. - */ -#define INCLUDES -#define LIBRARIES - -/* - * RECHECK - * If defined, Make_Update will check a target for its current - * modification time after it has been re-made, setting it to the - * starting time of the make only if the target still doesn't exist. - * Unfortunately, under NFS the modification time often doesn't - * get updated in time, so a target will appear to not have been - * re-made, causing later targets to appear up-to-date. On systems - * that don't have this problem, you should defined this. Under - * NFS you probably should not, unless you aren't exporting jobs. - */ -#define RECHECK - -/* - * SYSVINCLUDE - * Recognize system V like include directives [include "filename"] - */ -#define SYSVINCLUDE - -/* - * SUNSHCMD - * Recognize SunOS and Solaris: - * VAR :sh= CMD # Assign VAR to the command substitution of CMD - * ${VAR:sh} # Return the command substitution of the value - * # of ${VAR} - */ -#define SUNSHCMD - -#endif /* config_h_efe0765e */ diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c deleted file mode 100644 index 29ed33e476..0000000000 --- a/usr.bin/make/dir.c +++ /dev/null @@ -1,1140 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)dir.c 8.2 (Berkeley) 1/2/94 - * $FreeBSD: src/usr.bin/make/dir.c,v 1.47 2005/02/04 07:50:59 harti Exp $ - * $DragonFly: src/usr.bin/make/dir.c,v 1.44 2005/11/05 15:35:10 swildner Exp $ - */ - -/*- - * dir.c -- - * Directory searching using wildcards and/or normal names... - * Used both for source wildcarding in the Makefile and for finding - * implicit sources. - * - * The interface for this module is: - * Dir_Init Initialize the module. - * - * Dir_HasWildcards Returns true if the name given it needs to - * be wildcard-expanded. - * - * Path_Expand Given a pattern and a path, return a Lst of names - * which match the pattern on the search path. - * - * Path_FindFile Searches for a file on a given search path. - * If it exists, the entire path is returned. - * Otherwise NULL is returned. - * - * Dir_MTime Return the modification time of a node. The file - * is searched for along the default search path. - * The path and mtime fields of the node are filled in. - * - * Path_AddDir Add a directory to a search path. - * - * Dir_MakeFlags Given a search path and a command flag, create - * a string with each of the directories in the path - * preceded by the command flag and all of them - * separated by a space. - * - * Dir_Destroy Destroy an element of a search path. Frees up all - * things that can be freed for the element as long - * as the element is no longer referenced by any other - * search path. - * - * Dir_ClearPath Resets a search path to the empty list. - * - * For debugging: - * Dir_PrintDirectories Print stats about the directory cache. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arch.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "hash.h" -#include "lst.h" -#include "make.h" -#include "str.h" -#include "targ.h" -#include "util.h" - -/* - * A search path consists of a list of Dir structures. A Dir structure - * has in it the name of the directory and a hash table of all the files - * in the directory. This is used to cut down on the number of system - * calls necessary to find implicit dependents and their like. Since - * these searches are made before any actions are taken, we need not - * worry about the directory changing due to creation commands. If this - * hampers the style of some makefiles, they must be changed. - * - * A list of all previously-read directories is kept in the - * openDirectories list. This list is checked first before a directory - * is opened. - * - * The need for the caching of whole directories is brought about by - * the multi-level transformation code in suff.c, which tends to search - * for far more files than regular make does. In the initial - * implementation, the amount of time spent performing "stat" calls was - * truly astronomical. The problem with hashing at the start is, - * of course, that pmake doesn't then detect changes to these directories - * during the course of the make. Three possibilities suggest themselves: - * - * 1) just use stat to test for a file's existence. As mentioned - * above, this is very inefficient due to the number of checks - * engendered by the multi-level transformation code. - * 2) use readdir() and company to search the directories, keeping - * them open between checks. I have tried this and while it - * didn't slow down the process too much, it could severely - * affect the amount of parallelism available as each directory - * open would take another file descriptor out of play for - * handling I/O for another job. Given that it is only recently - * that UNIX OS's have taken to allowing more than 20 or 32 - * file descriptors for a process, this doesn't seem acceptable - * to me. - * 3) record the mtime of the directory in the Dir structure and - * verify the directory hasn't changed since the contents were - * hashed. This will catch the creation or deletion of files, - * but not the updating of files. However, since it is the - * creation and deletion that is the problem, this could be - * a good thing to do. Unfortunately, if the directory (say ".") - * were fairly large and changed fairly frequently, the constant - * rehashing could seriously degrade performance. It might be - * good in such cases to keep track of the number of rehashes - * and if the number goes over a (small) limit, resort to using - * stat in its place. - * - * An additional thing to consider is that pmake is used primarily - * to create C programs and until recently pcc-based compilers refused - * to allow you to specify where the resulting object file should be - * placed. This forced all objects to be created in the current - * directory. This isn't meant as a full excuse, just an explanation of - * some of the reasons for the caching used here. - * - * One more note: the location of a target's file is only performed - * on the downward traversal of the graph and then only for terminal - * nodes in the graph. This could be construed as wrong in some cases, - * but prevents inadvertent modification of files when the "installed" - * directory for a file is provided in the search path. - * - * Another data structure maintained by this module is an mtime - * cache used when the searching of cached directories fails to find - * a file. In the past, Path_FindFile would simply perform an access() - * call in such a case to determine if the file could be found using - * just the name given. When this hit, however, all that was gained - * was the knowledge that the file existed. Given that an access() is - * essentially a stat() without the copyout() call, and that the same - * filesystem overhead would have to be incurred in Dir_MTime, it made - * sense to replace the access() with a stat() and record the mtime - * in a cache for when Dir_MTime was actually called. - */ - -typedef struct Dir { - char *name; /* Name of directory */ - int refCount; /* No. of paths with this directory */ - int hits; /* No. of times a file has been found here */ - Hash_Table files; /* Hash table of files in directory */ - TAILQ_ENTRY(Dir) link; /* allDirs link */ -} Dir; - -/* - * A path is a list of pointers to directories. These directories are - * reference counted so a directory can be on more than one path. - */ -struct PathElement { - struct Dir *dir; /* pointer to the directory */ - TAILQ_ENTRY(PathElement) link; /* path link */ -}; - -/* main search path */ -struct Path dirSearchPath; - -/* the list of all open directories */ -static TAILQ_HEAD(, Dir) openDirectories; - -/* - * Variables for gathering statistics on the efficiency of the hashing - * mechanism. - */ -static int hits; /* Found in directory cache */ -static int misses; /* Sad, but not evil misses */ -static int nearmisses; /* Found under search path */ -static int bigmisses; /* Sought by itself */ - -static Dir *dot; /* contents of current directory */ - -/* Results of doing a last-resort stat in Path_FindFile -- - * if we have to go to the system to find the file, we might as well - * have its mtime on record. - * XXX: If this is done way early, there's a chance other rules will - * have already updated the file, in which case we'll update it again. - * Generally, there won't be two rules to update a single file, so this - * should be ok, but... - */ -static Hash_Table mtimes; - -/** - * initialize things for this module - * add the "." directory - * add curdir if it is not the same as objdir - */ -void -Dir_Init(void) -{ - - Hash_InitTable(&mtimes, 0); - TAILQ_INIT(&dirSearchPath); - TAILQ_INIT(&openDirectories); -} - -/** - * add the "." directory - * add curdir if it is not the same as objdir - */ -void -Dir_CurObj(const char curdir[], const char objdir[]) -{ - - dot = Path_AddDir(NULL, "."); - if (dot == NULL) - err(1, "cannot open current directory"); - - /* - * We always need to have dot around, so we increment its - * reference count to make sure it's not destroyed. - */ - dot->refCount += 1; - - if (strcmp(objdir, curdir) != 0) - Path_AddDir(&dirSearchPath, curdir); -} - -/*- - *----------------------------------------------------------------------- - * Dir_HasWildcards -- - * See if the given name has any wildcard characters in it. - * - * Results: - * returns true if the word should be expanded, false otherwise - * - * Side Effects: - * none - *----------------------------------------------------------------------- - */ -bool -Dir_HasWildcards(const char *name) -{ - const char *cp; - int wild = 0, brace = 0, bracket = 0; - - for (cp = name; *cp; cp++) { - switch (*cp) { - case '{': - brace++; - wild = 1; - break; - case '}': - brace--; - break; - case '[': - bracket++; - wild = 1; - break; - case ']': - bracket--; - break; - case '?': - case '*': - wild = 1; - break; - default: - break; - } - } - return (wild && bracket == 0 && brace == 0); -} - -/*- - *----------------------------------------------------------------------- - * DirMatchFiles -- - * Given a pattern and a Dir structure, see if any files - * match the pattern and add their names to the 'expansions' list if - * any do. This is incomplete -- it doesn't take care of patterns like - * src / *src / *.c properly (just *.c on any of the directories), but it - * will do for now. - * - * Results: - * Always returns 0 - * - * Side Effects: - * File names are added to the expansions lst. The directory will be - * fully hashed when this is done. - *----------------------------------------------------------------------- - */ -static int -DirMatchFiles(const char *pattern, const Dir *p, Lst *expansions) -{ - Hash_Search search; /* Index into the directory's table */ - Hash_Entry *entry; /* Current entry in the table */ - bool isDot; /* true if the directory being searched is . */ - - isDot = (*p->name == '.' && p->name[1] == '\0'); - - for (entry = Hash_EnumFirst(&p->files, &search); - entry != NULL; - entry = Hash_EnumNext(&search)) { - /* - * See if the file matches the given pattern. Note we follow - * the UNIX convention that dot files will only be found if - * the pattern begins with a dot (note also that as a side - * effect of the hashing scheme, .* won't match . or .. - * since they aren't hashed). - */ - if (Str_Match(entry->name, pattern) && - ((entry->name[0] != '.') || - (pattern[0] == '.'))) { - Lst_AtEnd(expansions, (isDot ? estrdup(entry->name) : - str_concat(p->name, '/', entry->name))); - } - } - return (0); -} - -/*- - *----------------------------------------------------------------------- - * DirExpandCurly -- - * Expand curly braces like the C shell. Does this recursively. - * Note the special case: if after the piece of the curly brace is - * done there are no wildcard characters in the result, the result is - * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. The - * given arguments are the entire word to expand, the first curly - * brace in the word, the search path, and the list to store the - * expansions in. - * - * Results: - * None. - * - * Side Effects: - * The given list is filled with the expansions... - * - *----------------------------------------------------------------------- - */ -static void -DirExpandCurly(const char *word, const char *brace, struct Path *path, - Lst *expansions) -{ - const char *end; /* Character after the closing brace */ - const char *cp; /* Current position in brace clause */ - const char *start; /* Start of current piece of brace clause */ - int bracelevel; /* Number of braces we've seen. If we see a right brace - * when this is 0, we've hit the end of the clause. */ - char *file; /* Current expansion */ - int otherLen; /* The length of the other pieces of the expansion - * (chars before and after the clause in 'word') */ - char *cp2; /* Pointer for checking for wildcards in - * expansion before calling Dir_Expand */ - - start = brace + 1; - - /* - * Find the end of the brace clause first, being wary of nested brace - * clauses. - */ - for (end = start, bracelevel = 0; *end != '\0'; end++) { - if (*end == '{') - bracelevel++; - else if ((*end == '}') && (bracelevel-- == 0)) - break; - } - if (*end == '\0') { - Error("Unterminated {} clause \"%s\"", start); - return; - } else - end++; - - otherLen = brace - word + strlen(end); - - for (cp = start; cp < end; cp++) { - /* - * Find the end of this piece of the clause. - */ - bracelevel = 0; - while (*cp != ',') { - if (*cp == '{') - bracelevel++; - else if ((*cp == '}') && (bracelevel-- <= 0)) - break; - cp++; - } - /* - * Allocate room for the combination and install the - * three pieces. - */ - file = emalloc(otherLen + cp - start + 1); - if (brace != word) - strncpy(file, word, brace - word); - if (cp != start) - strncpy(&file[brace - word], start, cp - start); - strcpy(&file[(brace - word) + (cp - start)], end); - - /* - * See if the result has any wildcards in it. If we find one, - * call Dir_Expand right away, telling it to place the result - * on our list of expansions. - */ - for (cp2 = file; *cp2 != '\0'; cp2++) { - switch (*cp2) { - case '*': - case '?': - case '{': - case '[': - Path_Expand(file, path, expansions); - goto next; - default: - break; - } - } - if (*cp2 == '\0') { - /* - * Hit the end w/o finding any wildcards, so stick - * the expansion on the end of the list. - */ - Lst_AtEnd(expansions, file); - } else { - next: - free(file); - } - start = cp + 1; - } -} - -/*- - *----------------------------------------------------------------------- - * DirExpandInt -- - * Internal expand routine. Passes through the directories in the - * path one by one, calling DirMatchFiles for each. NOTE: This still - * doesn't handle patterns in directories... Works given a word to - * expand, a path to look in, and a list to store expansions in. - * - * Results: - * None. - * - * Side Effects: - * Things are added to the expansions list. - * - *----------------------------------------------------------------------- - */ -static void -DirExpandInt(const char *word, const struct Path *path, Lst *expansions) -{ - struct PathElement *pe; - - TAILQ_FOREACH(pe, path, link) - DirMatchFiles(word, pe->dir, expansions); -} - -/*- - *----------------------------------------------------------------------- - * Dir_Expand -- - * Expand the given word into a list of words by globbing it looking - * in the directories on the given search path. - * - * Results: - * A list of words consisting of the files which exist along the search - * path matching the given pattern is placed in expansions. - * - * Side Effects: - * Directories may be opened. Who knows? - *----------------------------------------------------------------------- - */ -void -Path_Expand(char *word, struct Path *path, Lst *expansions) -{ - LstNode *ln; - char *cp; - - DEBUGF(DIR, ("expanding \"%s\"...", word)); - - cp = strchr(word, '{'); - if (cp != NULL) - DirExpandCurly(word, cp, path, expansions); - else { - cp = strchr(word, '/'); - if (cp != NULL) { - /* - * The thing has a directory component -- find the - * first wildcard in the string. - */ - for (cp = word; *cp != '\0'; cp++) { - if (*cp == '?' || *cp == '[' || - *cp == '*' || *cp == '{') { - break; - } - } - if (*cp == '{') { - /* - * This one will be fun. - */ - DirExpandCurly(word, cp, path, expansions); - return; - } else if (*cp != '\0') { - /* - * Back up to the start of the component - */ - char *dirpath; - - while (cp > word && *cp != '/') - cp--; - if (cp != word) { - char sc; - - /* - * If the glob isn't in the first - * component, try and find all the - * components up to the one with a - * wildcard. - */ - sc = cp[1]; - cp[1] = '\0'; - dirpath = Path_FindFile(word, path); - cp[1] = sc; - /* - * dirpath is null if can't find the - * leading component - * XXX: Path_FindFile won't find internal - * components. i.e. if the path contains - * ../Etc/Object and we're looking for - * Etc, * it won't be found. Ah well. - * Probably not important. - */ - if (dirpath != NULL) { - char *dp = - &dirpath[strlen(dirpath) - - 1]; - struct Path tp = - TAILQ_HEAD_INITIALIZER(tp); - - if (*dp == '/') - *dp = '\0'; - Path_AddDir(&tp, dirpath); - DirExpandInt(cp + 1, &tp, - expansions); - Path_Clear(&tp); - } - } else { - /* - * Start the search from the local - * directory - */ - DirExpandInt(word, path, expansions); - } - } else { - /* - * Return the file -- this should never happen. - */ - DirExpandInt(word, path, expansions); - } - } else { - /* - * First the files in dot - */ - DirMatchFiles(word, dot, expansions); - - /* - * Then the files in every other directory on the path. - */ - DirExpandInt(word, path, expansions); - } - } - if (DEBUG(DIR)) { - LST_FOREACH(ln, expansions) - DEBUGF(DIR, ("%s ", (const char *)Lst_Datum(ln))); - DEBUGF(DIR, ("\n")); - } -} - -/** - * Path_FindFile - * Find the file with the given name along the given search path. - * - * Results: - * The path to the file or NULL. This path is guaranteed to be in a - * different part of memory than name and so may be safely free'd. - * - * Side Effects: - * If the file is found in a directory which is not on the path - * already (either 'name' is absolute or it is a relative path - * [ dir1/.../dirn/file ] which exists below one of the directories - * already on the search path), its directory is added to the end - * of the path on the assumption that there will be more files in - * that directory later on. Sometimes this is true. Sometimes not. - */ -char * -Path_FindFile(char *name, struct Path *path) -{ - char *p1; /* pointer into p->name */ - char *p2; /* pointer into name */ - char *file; /* the current filename to check */ - const struct PathElement *pe; /* current path member */ - char *cp; /* final component of the name */ - bool hasSlash; /* true if 'name' contains a / */ - struct stat stb; /* Buffer for stat, if necessary */ - Hash_Entry *entry; /* Entry for mtimes table */ - - /* - * Find the final component of the name and note whether it has a - * slash in it (the name, I mean) - */ - cp = strrchr(name, '/'); - if (cp != NULL) { - hasSlash = true; - cp += 1; - } else { - hasSlash = false; - cp = name; - } - - DEBUGF(DIR, ("Searching for %s...", name)); - /* - * No matter what, we always look for the file in the current directory - * before anywhere else and we *do not* add the ./ to it if it exists. - * This is so there are no conflicts between what the user specifies - * (fish.c) and what pmake finds (./fish.c). - */ - if ((!hasSlash || (cp - name == 2 && *name == '.')) && - (Hash_FindEntry(&dot->files, cp) != NULL)) { - DEBUGF(DIR, ("in '.'\n")); - hits += 1; - dot->hits += 1; - return (estrdup(name)); - } - - /* - * We look through all the directories on the path seeking one which - * contains the final component of the given name and whose final - * component(s) match the name's initial component(s). If such a beast - * is found, we concatenate the directory name and the final component - * and return the resulting string. If we don't find any such thing, - * we go on to phase two... - */ - TAILQ_FOREACH(pe, path, link) { - DEBUGF(DIR, ("%s...", pe->dir->name)); - if (Hash_FindEntry(&pe->dir->files, cp) != NULL) { - DEBUGF(DIR, ("here...")); - if (hasSlash) { - /* - * If the name had a slash, its initial - * components and p's final components must - * match. This is false if a mismatch is - * encountered before all of the initial - * components have been checked (p2 > name at - * the end of the loop), or we matched only - * part of one of the components of p - * along with all the rest of them (*p1 != '/'). - */ - p1 = pe->dir->name + strlen(pe->dir->name) - 1; - p2 = cp - 2; - while (p2 >= name && p1 >= pe->dir->name && - *p1 == *p2) { - p1 -= 1; p2 -= 1; - } - if (p2 >= name || (p1 >= pe->dir->name && - *p1 != '/')) { - DEBUGF(DIR, ("component mismatch -- " - "continuing...")); - continue; - } - } - file = str_concat(pe->dir->name, '/', cp); - DEBUGF(DIR, ("returning %s\n", file)); - pe->dir->hits += 1; - hits += 1; - return (file); - } else if (hasSlash) { - /* - * If the file has a leading path component and that - * component exactly matches the entire name of the - * current search directory, we assume the file - * doesn't exist and return NULL. - */ - for (p1 = pe->dir->name, p2 = name; *p1 && *p1 == *p2; - p1++, p2++) - continue; - if (*p1 == '\0' && p2 == cp - 1) { - if (*cp == '\0' || ISDOT(cp) || ISDOTDOT(cp)) { - DEBUGF(DIR, ("returning %s\n", name)); - return (estrdup(name)); - } else { - DEBUGF(DIR, ("must be here but isn't --" - " returning NULL\n")); - return (NULL); - } - } - } - } - - /* - * We didn't find the file on any existing members of the directory. - * If the name doesn't contain a slash, that means it doesn't exist. - * If it *does* contain a slash, however, there is still hope: it - * could be in a subdirectory of one of the members of the search - * path. (eg. /usr/include and sys/types.h. The above search would - * fail to turn up types.h in /usr/include, but it *is* in - * /usr/include/sys/types.h) If we find such a beast, we assume there - * will be more (what else can we assume?) and add all but the last - * component of the resulting name onto the search path (at the - * end). This phase is only performed if the file is *not* absolute. - */ - if (!hasSlash) { - DEBUGF(DIR, ("failed.\n")); - misses += 1; - return (NULL); - } - - if (*name != '/') { - bool checkedDot = false; - - DEBUGF(DIR, ("failed. Trying subdirectories...")); - TAILQ_FOREACH(pe, path, link) { - if (pe->dir != dot) { - file = str_concat(pe->dir->name, '/', name); - } else { - /* - * Checking in dot -- DON'T put a leading ./ - * on the thing. - */ - file = estrdup(name); - checkedDot = true; - } - DEBUGF(DIR, ("checking %s...", file)); - - if (stat(file, &stb) == 0) { - DEBUGF(DIR, ("got it.\n")); - - /* - * We've found another directory to search. We - * know there's a slash in 'file' because we put - * one there. We nuke it after finding it and - * call Path_AddDir to add this new directory - * onto the existing search path. Once that's - * done, we restore the slash and triumphantly - * return the file name, knowing that should a - * file in this directory every be referenced - * again in such a manner, we will find it - * without having to do numerous numbers of - * access calls. Hurrah! - */ - cp = strrchr(file, '/'); - *cp = '\0'; - Path_AddDir(path, file); - *cp = '/'; - - /* - * Save the modification time so if - * it's needed, we don't have to fetch it again. - */ - DEBUGF(DIR, ("Caching %s for %s\n", - Targ_FmtTime(stb.st_mtime), file)); - entry = Hash_CreateEntry(&mtimes, file, NULL); - Hash_SetValue(entry, - (void *)(long)stb.st_mtime); - nearmisses += 1; - return (file); - } else { - free(file); - } - } - - DEBUGF(DIR, ("failed. ")); - - if (checkedDot) { - /* - * Already checked by the given name, since . was in - * the path, so no point in proceeding... - */ - DEBUGF(DIR, ("Checked . already, returning NULL\n")); - return (NULL); - } - } - - /* - * Didn't find it that way, either. Sigh. Phase 3. Add its directory - * onto the search path in any case, just in case, then look for the - * thing in the hash table. If we find it, grand. We return a new - * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. - * Note that if the directory holding the file doesn't exist, this will - * do an extra search of the final directory on the path. Unless - * something weird happens, this search won't succeed and life will - * be groovy. - * - * Sigh. We cannot add the directory onto the search path because - * of this amusing case: - * $(INSTALLDIR)/$(FILE): $(FILE) - * - * $(FILE) exists in $(INSTALLDIR) but not in the current one. - * When searching for $(FILE), we will find it in $(INSTALLDIR) - * b/c we added it here. This is not good... - */ -#ifdef notdef - cp[-1] = '\0'; - Path_AddDir(path, name); - cp[-1] = '/'; - - bigmisses += 1; - pe = TAILQ_LAST(path, Path); - if (pe == NULL) - return (NULL); - - if (Hash_FindEntry(&pe->dir->files, cp) != NULL) { - return (estrdup(name)); - - return (NULL); -#else /* !notdef */ - DEBUGF(DIR, ("Looking for \"%s\"...", name)); - - bigmisses += 1; - entry = Hash_FindEntry(&mtimes, name); - if (entry != NULL) { - DEBUGF(DIR, ("got it (in mtime cache)\n")); - return (estrdup(name)); - } else if (stat(name, &stb) == 0) { - entry = Hash_CreateEntry(&mtimes, name, NULL); - DEBUGF(DIR, ("Caching %s for %s\n", - Targ_FmtTime(stb.st_mtime), name)); - Hash_SetValue(entry, (void *)(long)stb.st_mtime); - return (estrdup(name)); - } else { - DEBUGF(DIR, ("failed. Returning NULL\n")); - return (NULL); - } -#endif /* notdef */ -} - -/*- - *----------------------------------------------------------------------- - * Dir_MTime -- - * Find the modification time of the file described by gn along the - * search path dirSearchPath. - * - * Results: - * The modification time or 0 if it doesn't exist - * - * Side Effects: - * The modification time is placed in the node's mtime slot. - * If the node didn't have a path entry before, and Dir_FindFile - * found one for it, the full name is placed in the path slot. - *----------------------------------------------------------------------- - */ -int -Dir_MTime(GNode *gn) -{ - char *fullName; /* the full pathname of name */ - struct stat stb; /* buffer for finding the mod time */ - Hash_Entry *entry; - - if (gn->type & OP_ARCHV) - return (Arch_MTime(gn)); - - else if (gn->path == NULL) - fullName = Path_FindFile(gn->name, &dirSearchPath); - else - fullName = gn->path; - - if (fullName == NULL) - fullName = estrdup(gn->name); - - entry = Hash_FindEntry(&mtimes, fullName); - if (entry != NULL) { - /* - * Only do this once -- the second time folks are checking to - * see if the file was actually updated, so we need to - * actually go to the filesystem. - */ - DEBUGF(DIR, ("Using cached time %s for %s\n", - Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), - fullName)); - stb.st_mtime = (time_t)(long)Hash_GetValue(entry); - Hash_DeleteEntry(&mtimes, entry); - } else if (stat(fullName, &stb) < 0) { - if (gn->type & OP_MEMBER) { - if (fullName != gn->path) - free(fullName); - return (Arch_MemMTime(gn)); - } else { - stb.st_mtime = 0; - } - } - if (fullName && gn->path == NULL) - gn->path = fullName; - - gn->mtime = stb.st_mtime; - return (gn->mtime); -} - -/*- - *----------------------------------------------------------------------- - * Path_AddDir -- - * Add the given name to the end of the given path. - * - * Results: - * none - * - * Side Effects: - * A structure is added to the list and the directory is - * read and hashed. - *----------------------------------------------------------------------- - */ -struct Dir * -Path_AddDir(struct Path *path, const char *name) -{ - Dir *d; /* pointer to new Path structure */ - DIR *dir; /* for reading directory */ - struct PathElement *pe; - struct dirent *dp; /* entry in directory */ - - /* check whether we know this directory */ - TAILQ_FOREACH(d, &openDirectories, link) { - if (strcmp(d->name, name) == 0) { - /* Found it. */ - if (path == NULL) - return (d); - - /* Check whether its already on the path. */ - TAILQ_FOREACH(pe, path, link) { - if (pe->dir == d) - return (d); - } - /* Add it to the path */ - d->refCount += 1; - pe = emalloc(sizeof(*pe)); - pe->dir = d; - TAILQ_INSERT_TAIL(path, pe, link); - return (d); - } - } - - DEBUGF(DIR, ("Caching %s...", name)); - - if ((dir = opendir(name)) == NULL) { - DEBUGF(DIR, (" cannot open\n")); - return (NULL); - } - - d = emalloc(sizeof(*d)); - d->name = estrdup(name); - d->hits = 0; - d->refCount = 1; - Hash_InitTable(&d->files, -1); - - while ((dp = readdir(dir)) != NULL) { -#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ - /* - * The sun directory library doesn't check for - * a 0 inode (0-inode slots just take up space), - * so we have to do it ourselves. - */ - if (dp->d_fileno == 0) - continue; -#endif /* sun && d_ino */ - - /* Skip the '.' and '..' entries by checking - * for them specifically instead of assuming - * readdir() reuturns them in that order when - * first going through a directory. This is - * needed for XFS over NFS filesystems since - * SGI does not guarantee that these are the - * first two entries returned from readdir(). - */ - if (ISDOT(dp->d_name) || ISDOTDOT(dp->d_name)) - continue; - - Hash_CreateEntry(&d->files, dp->d_name, NULL); - } - closedir(dir); - - if (path != NULL) { - /* Add it to the path */ - d->refCount += 1; - pe = emalloc(sizeof(*pe)); - pe->dir = d; - TAILQ_INSERT_TAIL(path, pe, link); - } - - /* Add to list of all directories */ - TAILQ_INSERT_TAIL(&openDirectories, d, link); - - DEBUGF(DIR, ("done\n")); - - return (d); -} - -/** - * Path_Duplicate - * Duplicate a path. Ups the reference count for the directories. - */ -void -Path_Duplicate(struct Path *dst, const struct Path *src) -{ - struct PathElement *ped, *pes; - - TAILQ_FOREACH(pes, src, link) { - ped = emalloc(sizeof(*ped)); - ped->dir = pes->dir; - ped->dir->refCount++; - TAILQ_INSERT_TAIL(dst, ped, link); - } -} - -/** - * Path_MakeFlags - * Make a string by taking all the directories in the given search - * path and preceding them by the given flag. Used by the suffix - * module to create variables for compilers based on suffix search - * paths. - * - * Results: - * The string mentioned above. Note that there is no space between - * the given flag and each directory. The empty string is returned if - * Things don't go well. - */ -char * -Path_MakeFlags(const char *flag, const struct Path *path) -{ - char *str; /* the string which will be returned */ - char *tstr; /* the current directory preceded by 'flag' */ - char *nstr; - const struct PathElement *pe; - - str = estrdup(""); - - TAILQ_FOREACH(pe, path, link) { - tstr = str_concat(flag, '\0', pe->dir->name); - nstr = str_concat(str, ' ', tstr); - free(str); - free(tstr); - str = nstr; - } - - return (str); -} - -/** - * Path_Clear - * - * Destroy a path. This decrements the reference counts of all - * directories of this path and, if a reference count goes 0, - * destroys the directory object. - */ -void -Path_Clear(struct Path *path) -{ - struct PathElement *pe; - - while ((pe = TAILQ_FIRST(path)) != NULL) { - pe->dir->refCount--; - TAILQ_REMOVE(path, pe, link); - if (pe->dir->refCount == 0) { - TAILQ_REMOVE(&openDirectories, pe->dir, link); - Hash_DeleteTable(&pe->dir->files); - free(pe->dir->name); - free(pe->dir); - } - free(pe); - } -} - -/** - * Path_Concat - * - * Concatenate two paths, adding the second to the end of the first. - * Make sure to avoid duplicates. - * - * Side Effects: - * Reference counts for added dirs are upped. - */ -void -Path_Concat(struct Path *path1, const struct Path *path2) -{ - struct PathElement *p1, *p2; - - TAILQ_FOREACH(p2, path2, link) { - TAILQ_FOREACH(p1, path1, link) { - if (p1->dir == p2->dir) - break; - } - if (p1 == NULL) { - p1 = emalloc(sizeof(*p1)); - p1->dir = p2->dir; - p1->dir->refCount++; - TAILQ_INSERT_TAIL(path1, p1, link); - } - } -} - -/********** DEBUG INFO **********/ -void -Dir_PrintDirectories(void) -{ - const Dir *d; - - printf("#*** Directory Cache:\n"); - printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", - hits, misses, nearmisses, bigmisses, - (hits + bigmisses + nearmisses ? - hits * 100 / (hits + bigmisses + nearmisses) : 0)); - printf("# %-20s referenced\thits\n", "directory"); - TAILQ_FOREACH(d, &openDirectories, link) - printf("# %-20s %10d\t%4d\n", d->name, d->refCount, d->hits); -} - -void -Path_Print(const struct Path *path) -{ - const struct PathElement *p; - - TAILQ_FOREACH(p, path, link) - printf("%s ", p->dir->name); -} diff --git a/usr.bin/make/dir.h b/usr.bin/make/dir.h deleted file mode 100644 index d1c29a1a02..0000000000 --- a/usr.bin/make/dir.h +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/dir.h,v 1.18 2005/02/04 07:50:59 harti Exp $ - * $DragonFly: src/usr.bin/make/dir.h,v 1.23 2005/08/07 08:16:52 okumoto Exp $ - */ - -#ifndef dir_h_6002e3b8 -#define dir_h_6002e3b8 - -#include -#include - -struct GNode; -struct Lst; -struct Dir; - -struct PathElement; -TAILQ_HEAD(Path, PathElement); - -void Dir_Init(void); -void Dir_CurObj(const char [], const char []); -bool Dir_HasWildcards(const char *); -int Dir_MTime(struct GNode *); -void Dir_PrintDirectories(void); - -struct Dir *Path_AddDir(struct Path *, const char *); -void Path_Clear(struct Path *); -void Path_Concat(struct Path *, const struct Path *); -void Path_Duplicate(struct Path *, const struct Path *); -void Path_Expand(char *, struct Path *, struct Lst *); -char *Path_FindFile(char *, struct Path *); -char *Path_MakeFlags(const char *, const struct Path *); -void Path_Print(const struct Path *); - -#endif /* dir_h_6002e3b8 */ diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c deleted file mode 100644 index 3dabb0b515..0000000000 --- a/usr.bin/make/for.c +++ /dev/null @@ -1,280 +0,0 @@ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)for.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/for.c,v 1.35 2005/02/10 14:39:05 harti Exp $ - * $DragonFly: src/usr.bin/make/for.c,v 1.47 2005/09/24 07:38:03 okumoto Exp $ - */ - -/*- - * for.c -- - * Functions to handle loops in a makefile. - * - * Interface: - * For_Eval Evaluate the loop in the passed line. - * For_Run Run accumulated loop - * - */ - -#include -#include -#include - -#include "buf.h" -#include "dir.h" -#include "for.h" -#include "globals.h" -#include "lst.h" -#include "make.h" -#include "parse.h" -#include "util.h" -#include "var.h" - -/* - * For statements are of the form: - * - * .for in - * ... - * .endfor - * - * The trick is to look for the matching end inside for for loop - * To do that, we count the current nesting level of the for loops. - * and the .endfor statements, accumulating all the statements between - * the initial .for loop and the matching .endfor; - * then we evaluate the for loop for each variable in the varlist. - */ - -static int forLevel = 0; /* Nesting level */ -static char *forVar; /* Iteration variable */ -static Buffer *forBuf; /* Commands in loop */ -static Lst forLst; /* List of items */ - -/** - * For_For - * Evaluate the for loop in the passed line. The line - * looks like this: - * .for in - * The line pointer points just behind the for. - * - * Results: - * true: Syntax ok. - * false: Syntax error. - */ -bool -For_For(char *line) -{ - char *ptr; - char *wrd; - char *sub; - Buffer *buf; - - ptr = line; - - /* - * Skip space between for and the variable. - */ - for (ptr++; *ptr && isspace((u_char)*ptr); ptr++) - ; - - /* - * Grab the variable - */ - for (wrd = ptr; *ptr && !isspace((u_char)*ptr); ptr++) - ; - - buf = Buf_Init(0); - Buf_AppendRange(buf, wrd, ptr); - forVar = Buf_Data(buf); - - if (Buf_Size(buf) == 0) { - Buf_Destroy(buf, true); - Parse_Error(PARSE_FATAL, "missing variable in for"); - return (false); - } - Buf_Destroy(buf, false); - - /* - * Skip to 'in'. - */ - while (*ptr && isspace((u_char)*ptr)) - ptr++; - - /* - * Grab the `in' - */ - if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace((u_char)ptr[2])) { - free(forVar); - Parse_Error(PARSE_FATAL, "missing `in' in for"); - fprintf(stderr, "%s\n", ptr); - return (false); - } - ptr += 3; - - /* - * Skip to values - */ - while (*ptr && isspace((u_char)*ptr)) - ptr++; - - /* - * Make a list with the remaining words - */ - sub = Buf_Peel(Var_Subst(ptr, VAR_CMD, false)); - for (ptr = sub; *ptr != '\0' && isspace((u_char)*ptr); ptr++) - ; - - Lst_Init(&forLst); - buf = Buf_Init(0); - for (wrd = ptr; *ptr != '\0'; ptr++) { - if (isspace((u_char)*ptr)) { - Buf_AppendRange(buf, wrd, ptr); - Lst_AtFront(&forLst, Buf_Peel(buf)); - - buf = Buf_Init(0); - while (*ptr != '\0' && isspace((u_char)*ptr)) - ptr++; - wrd = ptr--; - } - } - DEBUGF(FOR, ("For: Iterator %s List %s\n", forVar, sub)); - - if (ptr - wrd > 0) { - Buf_AppendRange(buf, wrd, ptr); - Lst_AtFront(&forLst, Buf_Peel(buf)); - } else { - Buf_Destroy(buf, true); - } - free(sub); - - forBuf = Buf_Init(0); - forLevel++; - return (true); -} - -/** - * For_Eval - * Eat a line of the .for body looking for embedded .for loops - * and the .endfor - */ -bool -For_Eval(char *line) -{ - char *ptr; - - ptr = line; - - if (*ptr == '.') { - /* - * Need to check for 'endfor' and 'for' to find the end - * of our loop or to find embedded for loops. - */ - for (ptr++; *ptr != '\0' && isspace((u_char)*ptr); ptr++) - ; - - /* XXX the isspace is wrong */ - if (strncmp(ptr, "endfor", 6) == 0 && - (isspace((u_char)ptr[6]) || ptr[6] == '\0')) { - DEBUGF(FOR, ("For: end for %d\n", forLevel)); - if (forLevel == 0) { - /* should not be here */ - abort(); - } - forLevel--; - - } else if (strncmp(ptr, "for", 3) == 0 && - isspace((u_char)ptr[3])) { - forLevel++; - DEBUGF(FOR, ("For: new loop %d\n", forLevel)); - } - } - - if (forLevel != 0) { - /* - * Still in loop - append the line - */ - Buf_Append(forBuf, line); - Buf_AddByte(forBuf, '\n'); - return (true); - } - - return (false); -} - -/*- - *----------------------------------------------------------------------- - * For_Run -- - * Run the for loop, immitating the actions of an include file - * - * Results: - * None. - * - * Side Effects: - * The values of the variables forLst, forVar and forBuf are freed. - * - *----------------------------------------------------------------------- - */ -void -For_Run(int lineno) -{ - Lst values; /* list of values for the variable */ - char *var; /* the variable's name */ - Buffer *buf; /* the contents of the for loop */ - const char *val; /* current value of loop variable */ - LstNode *ln; - char *str; - - if (forVar == NULL || forBuf == NULL) - return; - - /* copy the global variables to have them free for embedded fors */ - var = forVar; - buf = forBuf; - Lst_Init(&values); - Lst_Concat(&values, &forLst, LST_CONCLINK); - - forVar = NULL; - forBuf = NULL; - - LST_FOREACH(ln, &values) { - val = Lst_Datum(ln); - Var_SetGlobal(var, val); - - DEBUGF(FOR, ("--- %s = %s\n", var, val)); - str = Buf_Peel(Var_SubstOnly(var, Buf_Data(buf), false)); - - Parse_FromString(str, lineno); - Var_Delete(var, VAR_GLOBAL); - } - - free(var); - Lst_Destroy(&values, free); - Buf_Destroy(buf, true); -} diff --git a/usr.bin/make/for.h b/usr.bin/make/for.h deleted file mode 100644 index 01bd9b4366..0000000000 --- a/usr.bin/make/for.h +++ /dev/null @@ -1,50 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/for.h,v 1.6 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef for_h_9d770f33 -#define for_h_9d770f33 - -#include - -bool For_For(char *); -bool For_Eval(char *); -void For_Run(int); - -#endif /* for_h_9d770f33 */ diff --git a/usr.bin/make/globals.h b/usr.bin/make/globals.h deleted file mode 100644 index 11c960db8f..0000000000 --- a/usr.bin/make/globals.h +++ /dev/null @@ -1,97 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/globals.h,v 1.16 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef globals_h_1c1edb96 -#define globals_h_1c1edb96 - -/* - * Global Variables - */ - -#include -#include -#include - -#include "lst.h" - -struct GNode; -struct Path; - -/* The list of directories to search when looking for targets */ -extern struct Path dirSearchPath; - -extern int jobLimit; /* -j argument: maximum number of jobs */ -extern bool jobsRunning; /* True if jobs are running */ -extern bool compatMake; /* True if we are make compatible */ -extern bool ignoreErrors; /* True if should ignore all errors */ -extern bool beSilent; /* True if should print no commands */ -extern bool beVerbose; /* True if should print extra cruft */ -extern bool noExecute; /* True if should execute nothing */ -extern bool allPrecious; /* True if every target is precious */ - -/* True if should continue on unaffected portions of the graph - * when have an error in one portion */ -extern bool keepgoing; - -/* true if targets should just be 'touched'if out of date. Set by the -t flag */ -extern bool touchFlag; - -/* true if should capture the output of subshells by means of pipes. - * Otherwise it is routed to temporary files from which it is retrieved - * when the shell exits */ -extern bool usePipes; - -/* List of specific variables for which the environment should be - * searched before the global context */ -extern Lst envFirstVars; - -extern struct GNode *DEFAULT; /* .DEFAULT rule */ - -/* The time at the start of this whole process */ -extern time_t now; - -extern int debug; - -/* warning flags */ -extern uint32_t warn_cmd; /* positive warning flags on command line */ -extern uint32_t warn_nocmd; /* negative warning flags on command line */ -extern uint32_t warn_flags; /* current warning flags */ - -#endif /* globals_h_1c1edb96 */ diff --git a/usr.bin/make/hash.c b/usr.bin/make/hash.c deleted file mode 100644 index 24c2ac7191..0000000000 --- a/usr.bin/make/hash.c +++ /dev/null @@ -1,418 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)hash.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/hash.c,v 1.24 2005/02/01 10:50:35 harti Exp $ - * $DragonFly: src/usr.bin/make/hash.c,v 1.21 2005/08/04 00:19:04 okumoto Exp $ - */ - -/* hash.c -- - * - * This module contains routines to manipulate a hash table. - * See hash.h for a definition of the structure of the hash - * table. Hash tables grow automatically as the amount of - * information increases. - */ - -#include -#include -#include - -#include "hash.h" -#include "util.h" - -/* - * Forward references to local procedures that are used before they're - * defined: - */ -static void RebuildTable(Hash_Table *); - -/* - * The following defines the ratio of # entries to # buckets - * at which we rebuild the table to make it larger. - */ - -#define rebuildLimit 8 - -/* - *--------------------------------------------------------- - * - * Hash_InitTable -- - * - * This routine just sets up the hash table. - * - * Input: - * t Structure to to hold table. - * numBuckets How many buckets to create for starters. This - * number is rounded up to a power of two. If - * <= 0, a reasonable default is chosen. The - * table will grow in size later as needed. - * - * Results: - * None. - * - * Side Effects: - * Memory is allocated for the initial bucket area. - * - *--------------------------------------------------------- - */ -void -Hash_InitTable(Hash_Table *t, int numBuckets) -{ - int i; - struct Hash_Entry **hp; - - /* - * Round up the size to a power of two. - */ - if (numBuckets <= 0) - i = 16; - else { - for (i = 2; i < numBuckets; i <<= 1) - continue; - } - t->numEntries = 0; - t->size = i; - t->mask = i - 1; - t->bucketPtr = hp = emalloc(sizeof(*hp) * i); - while (--i >= 0) - *hp++ = NULL; -} - -/* - *--------------------------------------------------------- - * - * Hash_DeleteTable -- - * - * This routine removes everything from a hash table - * and frees up the memory space it occupied (except for - * the space in the Hash_Table structure). - * - * Results: - * None. - * - * Side Effects: - * Lots of memory is freed up. - * - *--------------------------------------------------------- - */ -void -Hash_DeleteTable(Hash_Table *t) -{ - struct Hash_Entry **hp, *h, *nexth = NULL; - int i; - - for (hp = t->bucketPtr, i = t->size; --i >= 0;) { - for (h = *hp++; h != NULL; h = nexth) { - nexth = h->next; - free(h); - } - } - free(t->bucketPtr); - - /* - * Set up the hash table to cause memory faults on any future access - * attempts until re-initialization. - */ - t->bucketPtr = NULL; -} - -/* - *--------------------------------------------------------- - * - * Hash_FindEntry -- - * - * Searches a hash table for an entry corresponding to key. - * - * Input: - * t Hash table to search. - * key A hash key. - * - * Results: - * The return value is a pointer to the entry for key, - * if key was present in the table. If key was not - * present, NULL is returned. - * - * Side Effects: - * None. - * - *--------------------------------------------------------- - */ -Hash_Entry * -Hash_FindEntry(const Hash_Table *t, const char *key) -{ - Hash_Entry *e; - unsigned h; - const char *p; - - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; - p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) - if (e->namehash == h && strcmp(e->name, p) == 0) - return (e); - return (NULL); -} - -/* - *--------------------------------------------------------- - * - * Hash_CreateEntry -- - * - * Searches a hash table for an entry corresponding to - * key. If no entry is found, then one is created. - * - * Input: - * t Hash table to search. - * key A hash key. - * newPtr Filled in with true if new entry created, - * FALSE otherwise. - * - * Results: - * The return value is a pointer to the entry. If *newPtr - * isn't NULL, then *newPtr is filled in with true if a - * new entry was created, and false if an entry already existed - * with the given key. - * - * Side Effects: - * Memory may be allocated, and the hash buckets may be modified. - *--------------------------------------------------------- - */ -Hash_Entry * -Hash_CreateEntry(Hash_Table *t, const char *key, bool *newPtr) -{ - Hash_Entry *e; - unsigned int h; - const char *p; - int keylen; - struct Hash_Entry **hp; - - /* - * Hash the key. As a side effect, save the length (strlen) of the - * key in case we need to create the entry. - */ - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; - keylen = p - key; - p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { - if (e->namehash == h && strcmp(e->name, p) == 0) { - if (newPtr != NULL) - *newPtr = false; - return (e); - } - } - - /* - * The desired entry isn't there. Before allocating a new entry, - * expand the table if necessary (and this changes the resulting - * bucket chain). - */ - if (t->numEntries >= rebuildLimit * t->size) - RebuildTable(t); - e = emalloc(sizeof(*e) + keylen); - hp = &t->bucketPtr[h & t->mask]; - e->next = *hp; - *hp = e; - e->clientData = NULL; - e->namehash = h; - strcpy(e->name, p); - t->numEntries++; - - if (newPtr != NULL) - *newPtr = true; - return (e); -} - -/* - *--------------------------------------------------------- - * - * Hash_DeleteEntry -- - * - * Delete the given hash table entry and free memory associated with - * it. - * - * Results: - * None. - * - * Side Effects: - * Hash chain that entry lives in is modified and memory is freed. - * - *--------------------------------------------------------- - */ -void -Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) -{ - Hash_Entry **hp, *p; - - if (e == NULL) - return; - for (hp = &t->bucketPtr[e->namehash & t->mask]; - (p = *hp) != NULL; hp = &p->next) { - if (p == e) { - *hp = p->next; - free(p); - t->numEntries--; - return; - } - } - write(STDERR_FILENO, "bad call to Hash_DeleteEntry\n", 29); - abort(); -} - -/* - *--------------------------------------------------------- - * - * Hash_EnumFirst -- - * This procedure sets things up for a complete search - * of all entries recorded in the hash table. - * - * Input: - * t Table to be searched. - * searchPtr Area in which to keep state about search. - * - * Results: - * The return value is the address of the first entry in - * the hash table, or NULL if the table is empty. - * - * Side Effects: - * The information in searchPtr is initialized so that successive - * calls to Hash_Next will return successive HashEntry's - * from the table. - * - *--------------------------------------------------------- - */ -Hash_Entry * -Hash_EnumFirst(const Hash_Table *t, Hash_Search *searchPtr) -{ - - searchPtr->tablePtr = t; - searchPtr->nextIndex = 0; - searchPtr->hashEntryPtr = NULL; - return (Hash_EnumNext(searchPtr)); -} - -/* - *--------------------------------------------------------- - * - * Hash_EnumNext -- - * This procedure returns successive entries in the hash table. - * - * Input: - * searchPtr Area used to keep state about search. - * - * Results: - * The return value is a pointer to the next HashEntry - * in the table, or NULL when the end of the table is - * reached. - * - * Side Effects: - * The information in searchPtr is modified to advance to the - * next entry. - * - *--------------------------------------------------------- - */ -Hash_Entry * -Hash_EnumNext(Hash_Search *searchPtr) -{ - Hash_Entry *e; - const Hash_Table *t = searchPtr->tablePtr; - - /* - * The hashEntryPtr field points to the most recently returned - * entry, or is NULL if we are starting up. If not NULL, we have - * to start at the next one in the chain. - */ - e = searchPtr->hashEntryPtr; - if (e != NULL) - e = e->next; - /* - * If the chain ran out, or if we are starting up, we need to - * find the next nonempty chain. - */ - while (e == NULL) { - if (searchPtr->nextIndex >= t->size) - return (NULL); - e = t->bucketPtr[searchPtr->nextIndex++]; - } - searchPtr->hashEntryPtr = e; - return (e); -} - -/* - *--------------------------------------------------------- - * - * RebuildTable -- - * This local routine makes a new hash table that - * is larger than the old one. - * - * Results: - * None. - * - * Side Effects: - * The entire hash table is moved, so any bucket numbers - * from the old table are invalid. - * - *--------------------------------------------------------- - */ -static void -RebuildTable(Hash_Table *t) -{ - Hash_Entry *e, *next = NULL, **hp, **xp; - int i, mask; - Hash_Entry **oldhp; - int oldsize; - - oldhp = t->bucketPtr; - oldsize = i = t->size; - i <<= 1; - t->size = i; - t->mask = mask = i - 1; - t->bucketPtr = hp = emalloc(sizeof(*hp) * i); - while (--i >= 0) - *hp++ = NULL; - for (hp = oldhp, i = oldsize; --i >= 0;) { - for (e = *hp++; e != NULL; e = next) { - next = e->next; - xp = &t->bucketPtr[e->namehash & mask]; - e->next = *xp; - *xp = e; - } - } - free(oldhp); -} diff --git a/usr.bin/make/hash.h b/usr.bin/make/hash.h deleted file mode 100644 index 6a42941df1..0000000000 --- a/usr.bin/make/hash.h +++ /dev/null @@ -1,108 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/hash.h,v 1.19 2005/02/01 10:50:35 harti Exp $ - * $DragonFly: src/usr.bin/make/hash.h,v 1.20 2005/08/04 00:21:50 okumoto Exp $ - */ - -#ifndef hash_h_f6312f46 -#define hash_h_f6312f46 - -/* hash.h -- - * - * This file contains definitions used by the hash module, - * which maintains hash tables. - */ - -#include - -/* - * The following defines one entry in the hash table. - */ -typedef struct Hash_Entry { - struct Hash_Entry *next; /* Used to link together all the - * entries associated with the same - * bucket. */ - void *clientData; /* Arbitrary piece of data associated - * with key. */ - unsigned namehash; /* hash value of key */ - char name[1]; /* key string */ -} Hash_Entry; - -typedef struct Hash_Table { - struct Hash_Entry **bucketPtr; /* Pointers to Hash_Entry, one - * for each bucket in the table. */ - int size; /* Actual size of array. */ - int numEntries; /* Number of entries in the table. */ - int mask; /* Used to select bits for hashing. */ -} Hash_Table; - -/* - * The following structure is used by the searching routines - * to record where we are in the search. - */ -typedef struct Hash_Search { - const Hash_Table *tablePtr; /* Table being searched. */ - int nextIndex; /* Next bucket to check (after current).*/ - Hash_Entry *hashEntryPtr; /* Next entry in current bucket */ -} Hash_Search; - -/* - * Macros. - */ - -/* - * void *Hash_GetValue(const Hash_Entry *h) - */ -#define Hash_GetValue(h) ((h)->clientData) - -/* - * Hash_SetValue(Hash_Entry *h, void *val); - */ -#define Hash_SetValue(h, val) ((h)->clientData = (val)) - -void Hash_InitTable(Hash_Table *, int); -void Hash_DeleteTable(Hash_Table *); -Hash_Entry *Hash_FindEntry(const Hash_Table *, const char *); -Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, bool *); -void Hash_DeleteEntry(Hash_Table *, Hash_Entry *); -Hash_Entry *Hash_EnumFirst(const Hash_Table *, Hash_Search *); -Hash_Entry *Hash_EnumNext(Hash_Search *); - -#endif /* hash_h_f6312f46 */ diff --git a/usr.bin/make/hash_tables.c b/usr.bin/make/hash_tables.c deleted file mode 100644 index f2b1212432..0000000000 --- a/usr.bin/make/hash_tables.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * DO NOT EDIT - * $DragonFly: src/usr.bin/make/hash_tables.c,v 1.1 2005/04/29 23:11:49 okumoto Exp $ - * auto-generated from /usr/home/okumoto/Work/make/dfly-src/make/parse.c - * DO NOT EDIT - */ -#include - -#include "hash_tables.h" - -/* - * d=2 - * n=40 - * m=19 - * c=2.09 - * maxlen=1 - * minklen=2 - * maxklen=9 - * minchar=97 - * maxchar=119 - * loop=0 - * numiter=1 - * seed= - */ - -static const signed char directive_g[] = { - 8, 0, 0, 5, 6, -1, 17, 15, 10, 6, - -1, -1, 0, 0, 2, -1, 16, 2, 3, 0, - 7, -1, -1, -1, 0, 14, -1, -1, 11, 7, - -1, -1, 0, -1, 0, 0, 17, 0, -1, 1, -}; - -static const u_char directive_T0[] = { - 26, 14, 19, 35, 10, 34, 18, 27, 1, 17, - 22, 37, 12, 12, 36, 21, 0, 6, 1, 25, - 9, 4, 19, -}; - -static const u_char directive_T1[] = { - 25, 22, 19, 0, 2, 18, 33, 18, 30, 4, - 30, 9, 21, 19, 16, 12, 35, 34, 4, 19, - 9, 33, 16, -}; - - -int -directive_hash(const u_char *key, size_t len) -{ - unsigned f0, f1; - const u_char *kp = key; - - if (len < 2 || len > 9) - return -1; - - for (f0=f1=0; kp < key + len; ++kp) { - if (*kp < 97 || *kp > 119) - return -1; - f0 += directive_T0[-97 + *kp]; - f1 += directive_T1[-97 + *kp]; - } - - f0 %= 40; - f1 %= 40; - - return (directive_g[f0] + directive_g[f1]) % 19; -} -/* - * d=2 - * n=72 - * m=34 - * c=2.09 - * maxlen=1 - * minklen=4 - * maxklen=12 - * minchar=46 - * maxchar=95 - * loop=0 - * numiter=4 - * seed= - */ - -static const signed char keyword_g[] = { - 8, 15, -1, 25, 22, 20, -1, 33, 16, -1, - 21, 31, 0, 0, 0, 29, 30, 8, -1, 0, - -1, 21, -1, 0, -1, -1, -1, -1, -1, 4, - -1, -1, 25, 28, -1, 27, 11, 23, 0, 0, - 24, -1, -1, 0, 3, 0, -1, 24, 0, 0, - -1, 28, 12, -1, 20, 13, -1, 5, -1, 1, - 0, 0, -1, 0, 10, 19, 13, 9, -1, 2, - -1, -1, -}; - -static const u_char keyword_T0[] = { - 32, 10, 54, 61, 2, 35, 62, 50, 52, 53, - 70, 7, 62, 18, 24, 30, 31, 66, 10, 61, - 52, 71, 56, 56, 28, 6, 33, 67, 12, 41, - 39, 45, 51, 21, 34, 53, 56, 40, 47, 52, - 21, 61, 60, 12, 7, 28, 42, 38, 38, 52, -}; - -static const u_char keyword_T1[] = { - 0, 39, 65, 48, 13, 62, 46, 42, 5, 50, - 69, 69, 69, 43, 2, 46, 12, 6, 11, 9, - 24, 10, 25, 64, 68, 13, 57, 55, 17, 33, - 1, 18, 0, 67, 10, 14, 57, 56, 0, 6, - 50, 13, 3, 47, 56, 22, 37, 13, 28, 48, -}; - - -int -keyword_hash(const u_char *key, size_t len) -{ - unsigned f0, f1; - const u_char *kp = key; - - if (len < 4 || len > 12) - return -1; - - for (f0=f1=0; *kp; ++kp) { - if (*kp < 46 || *kp > 95) - return -1; - f0 += keyword_T0[-46 + *kp]; - f1 += keyword_T1[-46 + *kp]; - } - - f0 %= 72; - f1 %= 72; - - return (keyword_g[f0] + keyword_g[f1]) % 34; -} diff --git a/usr.bin/make/hash_tables.h b/usr.bin/make/hash_tables.h deleted file mode 100644 index 626f41bdad..0000000000 --- a/usr.bin/make/hash_tables.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2005 Max Okumoto. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/hash_tables.h,v 1.1 2005/04/29 23:11:49 okumoto Exp $ - */ -#ifndef hash_tables_h_ -#define hash_tables_h_ - -#include - -int directive_hash(const u_char *, size_t); -int keyword_hash(const u_char *, size_t); - -#endif diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c deleted file mode 100644 index 9eb512b374..0000000000 --- a/usr.bin/make/job.c +++ /dev/null @@ -1,3336 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)job.c 8.2 (Berkeley) 3/19/94 - * $FreeBSD: src/usr.bin/make/job.c,v 1.75 2005/02/10 14:32:14 harti Exp $ - */ - -#ifndef OLD_JOKE -#define OLD_JOKE 1 -#endif /* OLD_JOKE */ - -/** - * job.c - * handle the creation etc. of our child processes. - * - * Interface: - * Job_Make Start the creation of the given target. - * - * Job_CatchChildren - * Check for and handle the termination of any children. - * This must be called reasonably frequently to keep the - * whole make going at a decent clip, since job table - * entries aren't removed until their process is caught - * this way. Its single argument is true if the function - * should block waiting for a child to terminate. - * - * Job_CatchOutput Print any output our children have produced. Should - * also be called fairly frequently to keep the user - * informed of what's going on. If no output is waiting, - * it will block for a time given by the SEL_* constants, - * below, or until output is ready. - * - * Job_Init Called to initialize this module. in addition, any - * commands attached to the .BEGIN target are executed - * before this function returns. Hence, the makefile must - * have been parsed before this function is called. - * - * Job_Full Return true if the job table is filled. - * - * Job_Empty Return true if the job table is completely empty. - * - * Job_Finish Perform any final processing which needs doing. This - * includes the execution of any commands which have - * been/were attached to the .END target. It should only - * be called when the job table is empty. - * - * Job_AbortAll Abort all currently running jobs. It doesn't handle - * output or do anything for the jobs, just kills them. - * It should only be called in an emergency, as it were. - * - * JobCheckCommands - * Verify that the commands for a target are ok. Provide - * them if necessary and possible. - * - * JobTouch Update a target without really updating it. - * - * Job_Wait Wait for all currently-running jobs to finish. - * - * compat.c - * The routines in this file implement the full-compatibility - * mode of PMake. Most of the special functionality of PMake - * is available in this mode. Things not supported: - * - different shells. - * - friendly variable substitution. - * - * Interface: - * Compat_Run Initialize things for this module and recreate - * thems as need creatin' - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arch.h" -#include "buf.h" -#include "config.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "job.h" -#include "make.h" -#include "parse.h" -#include "pathnames.h" -#include "proc.h" -#include "shell.h" -#include "str.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -#define TMPPAT "/tmp/makeXXXXXXXXXX" -#define MKLVL_MAXVAL 500 -#define MKLVL_ENVVAR "__MKLVL__" - -/* - * The SEL_ constants determine the maximum amount of time spent in select - * before coming out to see if a child has finished. SEL_SEC is the number of - * seconds and SEL_USEC is the number of micro-seconds - */ -#define SEL_SEC 2 -#define SEL_USEC 0 - -/* - * Job Table definitions. - * - * The job "table" is kept as a linked Lst in 'jobs', with the number of - * active jobs maintained in the 'nJobs' variable. At no time will this - * exceed the value of 'maxJobs', initialized by the Job_Init function. - * - * When a job is finished, the Make_Update function is called on each of the - * parents of the node which was just remade. This takes care of the upward - * traversal of the dependency graph. - */ -#define JOB_BUFSIZE 1024 -typedef struct Job { - struct Shell *shell; - pid_t pid; /* The child's process ID */ - - struct GNode *node; /* The target the child is making */ - - /* - * A LstNode for the first command to be saved after the job completes. - * This is NULL if there was no "..." in the job's commands. - */ - LstNode *tailCmds; - - /* - * An FILE* for writing out the commands. This is only - * used before the job is actually started. - */ - FILE *cmdFILE; - - /* - * A word of flags which determine how the module handles errors, - * echoing, etc. for the job - */ - short flags; /* Flags to control treatment of job */ -#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ -#define JOB_SILENT 0x002 /* no output */ -#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally - * if we can't export it and maxLocal is 0 */ -#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing - * commands */ -#define JOB_FIRST 0x020 /* Job is first job for the node */ -#define JOB_RESTART 0x080 /* Job needs to be completely restarted */ -#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, - * for some reason */ -#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. - * Used to avoid infinite recursion between - * JobFinish and JobRestart */ - - /* union for handling shell's output */ - union { - /* - * This part is used when usePipes is true. - * The output is being caught via a pipe and the descriptors - * of our pipe, an array in which output is line buffered and - * the current position in that buffer are all maintained for - * each job. - */ - struct { - /* - * Input side of pipe associated with - * job's output channel - */ - int op_inPipe; - - /* - * Output side of pipe associated with job's - * output channel - */ - int op_outPipe; - - /* - * Buffer for storing the output of the - * job, line by line - */ - char op_outBuf[JOB_BUFSIZE + 1]; - - /* Current position in op_outBuf */ - int op_curPos; - } o_pipe; - - /* - * If usePipes is false the output is routed to a temporary - * file and all that is kept is the name of the file and the - * descriptor open to the file. - */ - struct { - /* Name of file to which shell output was rerouted */ - char of_outFile[sizeof(TMPPAT)]; - - /* - * Stream open to the output file. Used to funnel all - * from a single job to one file while still allowing - * multiple shell invocations - */ - int of_outFd; - } o_file; - - } output; /* Data for tracking a shell's output */ - - TAILQ_ENTRY(Job) link; /* list link */ -} Job; - -#define outPipe output.o_pipe.op_outPipe -#define inPipe output.o_pipe.op_inPipe -#define outBuf output.o_pipe.op_outBuf -#define curPos output.o_pipe.op_curPos - -#define outFile output.o_file.of_outFile -#define outFd output.o_file.of_outFd - -TAILQ_HEAD(JobList, Job); - -/* - * error handling variables - */ -static int errors = 0; /* number of errors reported */ -static int aborting = 0; /* why is the make aborting? */ -#define ABORT_ERROR 1 /* Because of an error */ -#define ABORT_INTERRUPT 2 /* Because it was interrupted */ -#define ABORT_WAIT 3 /* Waiting for jobs to finish */ - -/* - * post-make command processing. The node postCommands is really just the - * .END target but we keep it around to avoid having to search for it - * all the time. - */ -static GNode *postCommands; - -/* - * The number of commands actually printed for a target. Should this - * number be 0, no shell will be executed. - */ -static int numCommands; - -/* - * Return values from JobStart. - */ -#define JOB_RUNNING 0 /* Job is running */ -#define JOB_ERROR 1 /* Error in starting the job */ -#define JOB_FINISHED 2 /* The job is already finished */ -#define JOB_STOPPED 3 /* The job is stopped */ - -/* - * The maximum number of jobs that may run. This is initialize from the - * -j argument for the leading make and from the FIFO for sub-makes. - */ -static int maxJobs; -static int nJobs; /* The number of children currently running */ - -/* The structures that describe them */ -static struct JobList jobs = TAILQ_HEAD_INITIALIZER(jobs); - -static bool jobFull; /* Flag to tell when the job table is full. It - * is set true when (1) the total number of - * running jobs equals the maximum allowed */ -static fd_set outputs; /* Set of descriptors of pipes connected to - * the output channels of children */ -static GNode *lastNode; /* The node for which output was most recently - * produced. */ -static const char *targFmt; /* Format string to use to head output from a - * job when it's not the most-recent job heard - * from */ - -#define TARG_FMT "--- %s ---\n" /* Default format */ -#define MESSAGE(fp, gn) \ - fprintf(fp, targFmt, gn->name); - -/* - * When JobStart attempts to run a job but isn't allowed to - * or when Job_CatchChildren detects a job that has - * been stopped somehow, the job is placed on the stoppedJobs queue to be run - * when the next job finishes. - * - * Lst of Job structures describing jobs that were stopped due to - * concurrency limits or externally - */ -static struct JobList stoppedJobs = TAILQ_HEAD_INITIALIZER(stoppedJobs); - -static int fifoFd; /* Fd of our job fifo */ -static char fifoName[] = "/tmp/make_fifo_XXXXXXXXX"; -static int fifoMaster; - -#if defined(USE_PGRP) -# if defined(SYSV) -# define KILL(pid, sig) killpg(-(pid), (sig)) -# else -# define KILL(pid, sig) killpg((pid), (sig)) -# endif -#else -# define KILL(pid, sig) kill((pid), (sig)) -#endif - -/* - * Grmpf... There is no way to set bits of the wait structure - * anymore with the stupid W*() macros. I liked the union wait - * stuff much more. So, we devise our own macros... This is - * really ugly, use dramamine sparingly. You have been warned. - */ -#define W_SETMASKED(st, val, fun) \ - { \ - int sh = (int)~0; \ - int mask = fun(sh); \ - \ - for (sh = 0; ((mask >> sh) & 1) == 0; sh++) \ - continue; \ - *(st) = (*(st) & ~mask) | ((val) << sh); \ - } - -#define W_SETTERMSIG(st, val) W_SETMASKED(st, val, WTERMSIG) -#define W_SETEXITSTATUS(st, val) W_SETMASKED(st, val, WEXITSTATUS) - -typedef void AbortProc(const char [], ...) __printflike(1, 2); - -static void JobRestart(Job *); -static int JobStart(GNode *, int, Job *); -static void JobDoOutput(Job *, bool); -static void JobRestartJobs(void); -static int Compat_RunCommand(GNode *, const char [], GNode *); -static void JobPassSig(int); -static void JobTouch(GNode *, bool); -static bool JobCheckCommands(GNode *, AbortProc *); - -static GNode *curTarg = NULL; - -static volatile sig_atomic_t got_SIGINT; -static volatile sig_atomic_t got_SIGHUP; -static volatile sig_atomic_t got_SIGQUIT; -static volatile sig_atomic_t got_SIGTERM; -#if defined(USE_PGRP) -static volatile sig_atomic_t got_SIGTSTP; -static volatile sig_atomic_t got_SIGTTOU; -static volatile sig_atomic_t got_SIGTTIN; -static volatile sig_atomic_t got_SIGWINCH; -#endif - -Shell *commandShell = NULL; - -/** - * In lieu of a good way to prevent every possible looping in make(1), stop - * there from being more than MKLVL_MAXVAL processes forked by make(1), to - * prevent a forkbomb from happening, in a dumb and mechanical way. - * - * Side Effects: - * Creates or modifies environment variable MKLVL_ENVVAR via setenv(). - */ -static void -check_make_level(void) -{ - char *value = getenv(MKLVL_ENVVAR); - int level = (value == NULL) ? 0 : atoi(value); - - if (level < 0) { - errc(2, EAGAIN, "Invalid value for recursion level (%d).", - level); - } else if (level > MKLVL_MAXVAL) { - errc(2, EAGAIN, "Max recursion level (%d) exceeded.", - MKLVL_MAXVAL); - } else { - char new_value[32]; - sprintf(new_value, "%d", level + 1); - if (setenv(MKLVL_ENVVAR, new_value, 1) == -1) - Punt("setenv: %s: can't allocate memory", MKLVL_ENVVAR); - } -} - -/** - * Handle recept of a signal by setting a variable. The handling action is - * defered until the mainline code can safely handle it. - */ -static void -SigCatcher(int sig) -{ - switch (sig) { - case SIGCHLD: - /* do nothing */ - break; - case SIGINT: - got_SIGINT++; - break; - case SIGHUP: - got_SIGHUP++; - break; - case SIGQUIT: - got_SIGQUIT++; - break; - case SIGTERM: - got_SIGTERM++; - break; -#if defined(USE_PGRP) - case SIGTSTP: - got_SIGTSTP++; - break; - case SIGTTOU: - got_SIGTTOU++; - break; - case SIGTTIN: - got_SIGTTIN++; - break; - case SIGWINCH: - got_SIGWINCH++; - break; -#endif - default: - /* unexpected signal */ - break; - } -} - -/** - * - */ -static void -SigHandler(void) -{ - if (got_SIGINT) { - got_SIGINT = 0; - JobPassSig(SIGINT); - } - if (got_SIGHUP) { - got_SIGHUP = 0; - JobPassSig(SIGHUP); - } - if (got_SIGQUIT) { - got_SIGQUIT = 0; - JobPassSig(SIGQUIT); - } - if (got_SIGTERM) { - got_SIGTERM = 0; - JobPassSig(SIGTERM); - } -#if defined(USE_PGRP) - if (got_SIGTSTP) { - got_SIGTSTP = 0; - JobPassSig(SIGTSTP); - } - if (got_SIGTTOU) { - got_SIGTTOU = 0; - JobPassSig(SIGTTOU); - } - if (got_SIGTTIN) { - got_SIGTTIN = 0; - JobPassSig(SIGTTIN); - } - if (got_SIGWINCH) { - got_SIGWINCH = 0; - JobPassSig(SIGWINCH); - } -#endif -} - -static -sig_t -getsignal(int signo) -{ - struct sigaction sa; - if (sigaction(signo, NULL, &sa) < 0) - sa.sa_handler = SIG_IGN; - return(sa.sa_handler); -} - -void -Sig_Init(bool compat) -{ - struct sigaction sa; - - got_SIGINT = 0; - got_SIGHUP = 0; - got_SIGQUIT = 0; - got_SIGTERM = 0; -#if defined(USE_PGRP) - got_SIGTSTP = 0; - got_SIGTTOU = 0; - got_SIGTTIN = 0; - got_SIGWINCH = 0; -#endif - - if (compat == false) { - /* - * Setup handler to catch SIGCHLD so that we get kicked out - * of select() when we need to look at a child. This is only - * known to matter for the -j case (perhaps without -P). - */ - sigemptyset(&sa.sa_mask); - sa.sa_handler = SigCatcher; - sa.sa_flags = SA_NOCLDSTOP; - sigaction(SIGCHLD, &sa, NULL); - } - - /* - * Catch the four signals that POSIX specifies if they aren't - * ignored. - */ - sigemptyset(&sa.sa_mask); - sa.sa_handler = SigCatcher; - sa.sa_flags = 0; - - if (getsignal(SIGINT) != SIG_IGN) { - sigaction(SIGINT, &sa, NULL); - } - if (getsignal(SIGHUP) != SIG_IGN) { - sigaction(SIGHUP, &sa, NULL); - } - if (getsignal(SIGQUIT) != SIG_IGN) { - sigaction(SIGQUIT, &sa, NULL); - } - if (getsignal(SIGTERM) != SIG_IGN) { - sigaction(SIGTERM, &sa, NULL); - } - -#if defined(USE_PGRP) - if (compat == false) { - /* - * There are additional signals that need to be caught and - * passed if either the export system wants to be told - * directly of signals or if we're giving each job its own - * process group (since then it won't get signals from the - * terminal driver as we own the terminal) - */ - if (getsignal(SIGTSTP) != SIG_IGN) { - sigaction(SIGTSTP, &sa, NULL); - } - if (getsignal(SIGTTOU) != SIG_IGN) { - sigaction(SIGTTOU, &sa, NULL); - } - if (getsignal(SIGTTIN) != SIG_IGN) { - sigaction(SIGTTIN, &sa, NULL); - } - if (getsignal(SIGWINCH) != SIG_IGN) { - sigaction(SIGWINCH, &sa, NULL); - } - } -#endif -} - -/** - */ -void -Proc_Init(void) -{ - check_make_level(); - -#ifdef RLIMIT_NOFILE - { - struct rlimit rl; - - /* - * get rid of resource limit on file descriptors - */ - if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { - err(2, "getrlimit"); - } - rl.rlim_cur = rl.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { - err(2, "setrlimit"); - } - } -#endif -} - -/** - * Wait for child process to terminate. - */ -static int -ProcWait(ProcStuff *ps) -{ - /* - * Wait for the process to exit. - */ - for (;;) { - pid_t pid; - - pid = waitpid(ps->child_pid, &ps->child_status, 0); - if (pid == ps->child_pid) { - /* - * We finished waiting for the child. - */ - return (0); - } else { - if (errno == EINTR) { - /* - * Return so we can handle the signal that - * was delivered. - */ - return (-1); - } else { - Fatal("error in wait: %d", pid); - /* NOTREACHED */ - } - } - } -} - -/** - * Execute the list of command associated with the node. - * - * @param save Commands preceeded by "..." are save in this node to be - * executed after the other rules are executed. - */ -static void -Compat_RunCmds(GNode *gn, GNode *save) -{ - Lst *cmds = &gn->commands; - LstNode *ln; - - LST_FOREACH(ln, cmds) { - char *cmd = Lst_Datum(ln); - - if (Compat_RunCommand(gn, cmd, save)) - break; - } -} - -/** - * Pass a signal on to all jobs. - * - * Side Effects: - * We die by the same signal. - */ -static void -JobPassSig(int signo) -{ - Job *job; - - DEBUGF(JOB, ("JobPassSig(%d) called.\n", signo)); - - /* - * Propagate signal to children and in addition, send SIGCONT - * in case any of the children where suspended, so the the - * signal will get delivered. - */ - TAILQ_FOREACH(job, &jobs, link) { - DEBUGF(JOB, ("JobPassSig passing signal %d to child %jd.\n", - signo, (intmax_t)job->pid)); - KILL(job->pid, signo); - KILL(job->pid, SIGCONT); - } - -#if defined(USE_PGRP) - /* - * Why are we even catching these signals? - * SIGTSTP, SIGTTOU, SIGTTIN, and SIGWINCH - */ - switch (signo) { - case SIGTSTP: - case SIGTTOU: - case SIGTTIN: - case SIGWINCH: - return; - default: - break; - } -#endif - - /* - * Deal with proper cleanup based on the signal received. We only run - * the .INTERRUPT target if the signal was in fact an interrupt. - * The other three termination signals are more of a "get out *now*" - * command. - * - */ - aborting = ABORT_INTERRUPT; - - TAILQ_FOREACH(job, &jobs, link) { - if (!Targ_Precious(job->node)) { - char *file = (job->node->path == NULL ? - job->node->name : job->node->path); - - if (!noExecute && eunlink(file) != -1) { - Error("*** %s removed", file); - } - } - } - - if ((signo == SIGINT) && !touchFlag) { - GNode *interrupt; - - interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); - if (interrupt != NULL) { - ignoreErrors = false; - - JobStart(interrupt, JOB_IGNDOTS, NULL); - while (nJobs) { - Job_CatchOutput(0); - Job_CatchChildren(!usePipes); - } - } - } - - /* - * Leave gracefully if SIGQUIT, rather than core dumping. - */ - if (signo == SIGQUIT) { - signo = SIGINT; - } - - DEBUGF(JOB, ("JobPassSig passing signal %d to self.\n", signo)); - - signal(signo, SIG_DFL); - KILL(getpid(), signo); -} - -/** - * JobPrintCommand - * Put out another command for the given job. If the command starts - * with an @ or a - we process it specially. In the former case, - * so long as the -s and -n flags weren't given to make, we stick - * a shell-specific echoOff command in the script. In the latter, - * we ignore errors for the entire job, unless the shell has error - * control. - * If the command is just "..." we take all future commands for this - * job to be commands to be executed once the entire graph has been - * made and return non-zero to signal that the end of the commands - * was reached. These commands are later attached to the postCommands - * node and executed by Job_Finish when all things are done. - * This function is called from JobStart via LST_FOREACH. - * - * Results: - * Always 0, unless the command was "..." - * - * Side Effects: - * If the command begins with a '-' and the shell has no error control, - * the JOB_IGNERR flag is set in the job descriptor. - * If the command is "..." and we're not ignoring such things, - * tailCmds is set to the successor node of the cmd. - * numCommands is incremented if the command is actually printed. - */ -static int -JobPrintCommand(char *cmd, Job *job) -{ - struct Shell *shell = job->shell; - bool noSpecials; /* true if we shouldn't worry about - * inserting special commands into - * the input stream. */ - bool shutUp = false; /* true if we put a no echo command - * into the command file */ - bool errOff = false; /* true if we turned error checking - * off before printing the command - * and need to turn it back on */ - const char *cmdTemplate;/* Template to use when printing the command */ - char *cmdStart; /* Start of expanded command */ - LstNode *cmdNode; /* Node for replacing the command */ - - noSpecials = (noExecute && !(job->node->type & OP_MAKE)); - - if (strcmp(cmd, "...") == 0) { - job->node->type |= OP_SAVE_CMDS; - if ((job->flags & JOB_IGNDOTS) == 0) { - job->tailCmds = - Lst_Succ(Lst_Member(&job->node->commands, cmd)); - return (1); - } - return (0); - } - -#define DBPRINTF(fmt, arg) \ - DEBUGF(JOB, (fmt, arg)); \ - fprintf(job->cmdFILE, fmt, arg); \ - fflush(job->cmdFILE); - - numCommands += 1; - - /* - * For debugging, we replace each command with the result of expanding - * the variables in the command. - */ - cmdNode = Lst_Member(&job->node->commands, cmd); - - cmd = Buf_Peel(Var_Subst(cmd, job->node, false)); - cmdStart = cmd; - - Lst_Replace(cmdNode, cmdStart); - - cmdTemplate = "%s\n"; - - /* - * Check for leading @', -' or +'s to control echoing, error checking, - * and execution on -n. - */ - while (*cmd == '@' || *cmd == '-' || *cmd == '+') { - switch (*cmd) { - - case '@': - shutUp = DEBUG(LOUD) ? false : true; - break; - - case '-': - errOff = true; - break; - - case '+': - if (noSpecials) { - /* - * We're not actually executing anything... - * but this one needs to be - use compat mode - * just for it. - */ - Compat_RunCommand(job->node, cmd, NULL); - return (0); - } - break; - } - cmd++; - } - - while (isspace((unsigned char)*cmd)) - cmd++; - - if (shutUp) { - if (!(job->flags & JOB_SILENT) && !noSpecials && - shell->hasEchoCtl) { - DBPRINTF("%s\n", shell->echoOff); - } else { - shutUp = false; - } - } - - if (errOff) { - if (!(job->flags & JOB_IGNERR) && !noSpecials) { - if (shell->hasErrCtl) { - /* - * We don't want the error-control commands - * showing up either, so we turn off echoing - * while executing them. We could put another - * field in the shell structure to tell - * JobDoOutput to look for this string too, - * but why make it any more complex than - * it already is? - */ - if (!(job->flags & JOB_SILENT) && !shutUp && - shell->hasEchoCtl) { - DBPRINTF("%s\n", shell->echoOff); - DBPRINTF("%s\n", shell->ignErr); - DBPRINTF("%s\n", shell->echoOn); - } else { - DBPRINTF("%s\n", shell->ignErr); - } - } else if (shell->ignErr && - *shell->ignErr != '\0') { - /* - * The shell has no error control, so we need to - * be weird to get it to ignore any errors from - * the command. If echoing is turned on, we turn - * it off and use the errCheck template to echo - * the command. Leave echoing off so the user - * doesn't see the weirdness we go through to - * ignore errors. Set cmdTemplate to use the - * weirdness instead of the simple "%s\n" - * template. - */ - if (!(job->flags & JOB_SILENT) && !shutUp && - shell->hasEchoCtl) { - DBPRINTF("%s\n", shell->echoOff); - DBPRINTF(shell->errCheck, cmd); - shutUp = true; - } - cmdTemplate = shell->ignErr; - /* - * The error ignoration (hee hee) is already - * taken care of by the ignErr template, so - * pretend error checking is still on. - */ - errOff = false; - } else { - errOff = false; - } - } else { - errOff = false; - } - } - - DBPRINTF(cmdTemplate, cmd); - - if (errOff) { - /* - * If echoing is already off, there's no point in issuing the - * echoOff command. Otherwise we issue it and pretend it was on - * for the whole command... - */ - if (!shutUp && !(job->flags & JOB_SILENT) && - shell->hasEchoCtl) { - DBPRINTF("%s\n", shell->echoOff); - shutUp = true; - } - DBPRINTF("%s\n", shell->errCheck); - } - if (shutUp) { - DBPRINTF("%s\n", shell->echoOn); - } - return (0); -} - -/** - * JobClose - * Called to close both input and output pipes when a job is finished. - * - * Side Effects: - * The file descriptors associated with the job are closed. - */ -static void -JobClose(Job *job) -{ - - if (usePipes) { - FD_CLR(job->inPipe, &outputs); - if (job->outPipe != job->inPipe) { - close(job->outPipe); - } - JobDoOutput(job, true); - close(job->inPipe); - } else { - close(job->outFd); - JobDoOutput(job, true); - } -} - -/** - * JobFinish - * Do final processing for the given job including updating - * parents and starting new jobs as available/necessary. Note - * that we pay no attention to the JOB_IGNERR flag here. - * This is because when we're called because of a noexecute flag - * or something, jstat.w_status is 0 and when called from - * Job_CatchChildren, the status is zeroed if it s/b ignored. - * - * Side Effects: - * Some nodes may be put on the toBeMade queue. - * Final commands for the job are placed on postCommands. - * - * If we got an error and are aborting (aborting == ABORT_ERROR) and - * the job list is now empty, we are done for the day. - * If we recognized an error (errors !=0), we set the aborting flag - * to ABORT_ERROR so no more jobs will be started. - */ -static void -JobFinish(Job *job, int *status) -{ - bool done; - LstNode *ln; - - if (WIFEXITED(*status)) { - int job_status = WEXITSTATUS(*status); - - JobClose(job); - /* - * Deal with ignored errors in -B mode. We need to - * print a message telling of the ignored error as - * well as setting status.w_status to 0 so the next - * command gets run. To do this, we set done to be - * true if in -B mode and the job exited non-zero. - */ - if (job_status == 0) { - done = false; - } else { - if (job->flags & JOB_IGNERR) { - done = true; - } else { - /* - * If it exited non-zero and either we're - * doing things our way or we're not ignoring - * errors, the job is finished. Similarly, if - * the shell died because of a signal the job - * is also finished. In these cases, finish - * out the job's output before printing the - * exit status... - */ - done = true; - if (job->cmdFILE != NULL && - job->cmdFILE != stdout) { - fclose(job->cmdFILE); - } - - } - } - } else if (WIFSIGNALED(*status)) { - if (WTERMSIG(*status) == SIGCONT) { - /* - * No need to close things down or anything. - */ - done = false; - } else { - /* - * If it exited non-zero and either we're - * doing things our way or we're not ignoring - * errors, the job is finished. Similarly, if - * the shell died because of a signal the job - * is also finished. In these cases, finish - * out the job's output before printing the - * exit status... - */ - JobClose(job); - if (job->cmdFILE != NULL && - job->cmdFILE != stdout) { - fclose(job->cmdFILE); - } - done = true; - } - } else { - /* - * No need to close things down or anything. - */ - done = false; - } - - if (WIFEXITED(*status)) { - if (done || DEBUG(JOB)) { - FILE *out; - - if (compatMake && - !usePipes && - (job->flags & JOB_IGNERR)) { - /* - * If output is going to a file and this job - * is ignoring errors, arrange to have the - * exit status sent to the output file as - * well. - */ - out = fdopen(job->outFd, "w"); - if (out == NULL) - Punt("Cannot fdopen"); - } else { - out = stdout; - } - - DEBUGF(JOB, ("Process %jd exited.\n", - (intmax_t)job->pid)); - - if (WEXITSTATUS(*status) == 0) { - if (DEBUG(JOB)) { - if (usePipes && job->node != lastNode) { - MESSAGE(out, job->node); - lastNode = job->node; - } - fprintf(out, - "*** Completed successfully\n"); - } - } else { - if (usePipes && job->node != lastNode) { - MESSAGE(out, job->node); - lastNode = job->node; - } - fprintf(out, "*** Error code %d%s\n", - WEXITSTATUS(*status), - (job->flags & JOB_IGNERR) ? - "(ignored)" : ""); - - if (job->flags & JOB_IGNERR) { - *status = 0; - } - } - - fflush(out); - } - } else if (WIFSIGNALED(*status)) { - if (done || DEBUG(JOB) || (WTERMSIG(*status) == SIGCONT)) { - FILE *out; - - if (compatMake && - !usePipes && - (job->flags & JOB_IGNERR)) { - /* - * If output is going to a file and this job - * is ignoring errors, arrange to have the - * exit status sent to the output file as - * well. - */ - out = fdopen(job->outFd, "w"); - if (out == NULL) - Punt("Cannot fdopen"); - } else { - out = stdout; - } - - if (WTERMSIG(*status) == SIGCONT) { - /* - * If the beastie has continued, shift the - * Job from the stopped list to the running - * one (or re-stop it if concurrency is - * exceeded) and go and get another child. - */ - if (job->flags & (JOB_RESUME | JOB_RESTART)) { - if (usePipes && job->node != lastNode) { - MESSAGE(out, job->node); - lastNode = job->node; - } - fprintf(out, "*** Continued\n"); - } - if (!(job->flags & JOB_CONTINUING)) { - DEBUGF(JOB, ("Warning: process %jd was not " - "continuing.\n", (intmax_t) job->pid)); -#ifdef notdef - /* - * We don't really want to restart a - * job from scratch just because it - * continued, especially not without - * killing the continuing process! - * That's why this is ifdef'ed out. - * FD - 9/17/90 - */ - JobRestart(job); -#endif - } - job->flags &= ~JOB_CONTINUING; - TAILQ_INSERT_TAIL(&jobs, job, link); - nJobs += 1; - DEBUGF(JOB, ("Process %jd is continuing locally.\n", - (intmax_t) job->pid)); - if (nJobs == maxJobs) { - jobFull = true; - DEBUGF(JOB, ("Job queue is full.\n")); - } - fflush(out); - return; - - } else { - if (usePipes && job->node != lastNode) { - MESSAGE(out, job->node); - lastNode = job->node; - } - fprintf(out, - "*** Signal %d\n", WTERMSIG(*status)); - fflush(out); - } - } - } else { - /* STOPPED */ - FILE *out; - - if (compatMake && !usePipes && (job->flags & JOB_IGNERR)) { - /* - * If output is going to a file and this job - * is ignoring errors, arrange to have the - * exit status sent to the output file as - * well. - */ - out = fdopen(job->outFd, "w"); - if (out == NULL) - Punt("Cannot fdopen"); - } else { - out = stdout; - } - - DEBUGF(JOB, ("Process %jd stopped.\n", (intmax_t) job->pid)); - if (usePipes && job->node != lastNode) { - MESSAGE(out, job->node); - lastNode = job->node; - } - fprintf(out, "*** Stopped -- signal %d\n", WSTOPSIG(*status)); - job->flags |= JOB_RESUME; - TAILQ_INSERT_TAIL(&stoppedJobs, job, link); - fflush(out); - return; - } - - /* - * Now handle the -B-mode stuff. If the beast still isn't finished, - * try and restart the job on the next command. If JobStart says it's - * ok, it's ok. If there's an error, this puppy is done. - */ - if (compatMake && WIFEXITED(*status) && - Lst_Succ(job->node->compat_command) != NULL) { - switch (JobStart(job->node, job->flags & JOB_IGNDOTS, job)) { - case JOB_RUNNING: - done = false; - break; - case JOB_ERROR: - done = true; - W_SETEXITSTATUS(status, 1); - break; - case JOB_FINISHED: - /* - * If we got back a JOB_FINISHED code, JobStart has - * already called Make_Update and freed the job - * descriptor. We set done to false here to avoid fake - * cycles and double frees. JobStart needs to do the - * update so we can proceed up the graph when given - * the -n flag.. - */ - done = false; - break; - default: - break; - } - } else { - done = true; - } - - if (done && aborting != ABORT_ERROR && - aborting != ABORT_INTERRUPT && *status == 0) { - /* - * As long as we aren't aborting and the job didn't return a - * non-zero status that we shouldn't ignore, we call - * Make_Update to update the parents. In addition, any saved - * commands for the node are placed on the .END target. - */ - for (ln = job->tailCmds; ln != NULL; ln = LST_NEXT(ln)) { - Lst_AtEnd(&postCommands->commands, - Buf_Peel( - Var_Subst(Lst_Datum(ln), job->node, false))); - } - - job->node->made = MADE; - Make_Update(job->node); - free(job); - - } else if (*status != 0) { - errors += 1; - free(job); - } - - JobRestartJobs(); - - /* - * Set aborting if any error. - */ - if (errors && !keepgoing && aborting != ABORT_INTERRUPT) { - /* - * If we found any errors in this batch of children and the -k - * flag wasn't given, we set the aborting flag so no more jobs - * get started. - */ - aborting = ABORT_ERROR; - } - - if (aborting == ABORT_ERROR && Job_Empty()) { - /* - * If we are aborting and the job table is now empty, we finish. - */ - Finish(errors); - } -} - -/** - * JobTouch - * Touch the given target. Called by JobStart when the -t flag was - * given. Prints messages unless told to be silent. - * - * Side Effects: - * The data modification of the file is changed. In addition, if the - * file did not exist, it is created. - */ -static void -JobTouch(GNode *gn, bool silent) -{ - int streamID; /* ID of stream opened to do the touch */ - struct utimbuf times; /* Times for utime() call */ - - if (gn->type & (OP_JOIN | OP_USE | OP_EXEC | OP_OPTIONAL)) { - /* - * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" - * targets and, as such, shouldn't really be created. - */ - return; - } - - if (!silent) { - fprintf(stdout, "touch %s\n", gn->name); - fflush(stdout); - } - - if (noExecute) { - return; - } - - if (gn->type & OP_ARCHV) { - Arch_Touch(gn); - } else if (gn->type & OP_LIB) { - Arch_TouchLib(gn); - } else { - char *file = gn->path ? gn->path : gn->name; - - times.actime = times.modtime = now; - if (utime(file, ×) < 0) { - streamID = open(file, O_RDWR | O_CREAT, 0666); - - if (streamID >= 0) { - char c; - - /* - * Read and write a byte to the file to change - * the modification time, then close the file. - */ - if (read(streamID, &c, 1) == 1) { - lseek(streamID, (off_t)0, SEEK_SET); - write(streamID, &c, 1); - } - - close(streamID); - } else { - fprintf(stdout, "*** couldn't touch %s: %s", - file, strerror(errno)); - fflush(stdout); - } - } - } -} - -/** - * JobCheckCommands - * Make sure the given node has all the commands it needs. - * - * Results: - * true if the commands list is/was ok. - * - * Side Effects: - * The node will have commands from the .DEFAULT rule added to it - * if it needs them. - */ -bool -JobCheckCommands(GNode *gn, AbortProc *abortProc) -{ - const char *msg = "make: don't know how to make"; - - if (!OP_NOP(gn->type)) { - return (true); /* this node does nothing */ - } - - if (!Lst_IsEmpty(&gn->commands)) { - return (true); /* this node has no commands */ - } - - if (gn->type & OP_LIB) { - return (true); - } - - /* - * No commands. Look for .DEFAULT rule from which we might infer - * commands. - */ - if (DEFAULT != NULL && !Lst_IsEmpty(&DEFAULT->commands)) { - /* - * Make only looks for a .DEFAULT if the node was - * never the target of an operator, so that's what we - * do too. If a .DEFAULT was given, we substitute its - * commands for gn's commands and set the IMPSRC - * variable to be the target's name The DEFAULT node - * acts like a transformation rule, in that gn also - * inherits any attributes or sources attached to - * .DEFAULT itself. - */ - Make_HandleUse(DEFAULT, gn); - Var_Set(IMPSRC, Var_Value(TARGET, gn), gn); - return (true); - } - - if (Dir_MTime(gn) != 0) { - return (true); - } - - /* - * The node wasn't the target of an operator we have - * no .DEFAULT rule to go on and the target doesn't - * already exist. There's nothing more we can do for - * this branch. If the -k flag wasn't given, we stop - * in our tracks, otherwise we just don't update - * this node's parents so they never get examined. - */ - if (gn->type & OP_OPTIONAL) { - fprintf(stdout, "%s %s(ignored)\n", msg, gn->name); - fflush(stdout); - return (true); - } - - if (keepgoing) { - fprintf(stdout, "%s %s(continuing)\n", msg, gn->name); - fflush(stdout); - return (false); - } - -#if OLD_JOKE - if (strcmp(gn->name,"love") == 0) - abortProc("Not war."); - else -#endif - abortProc("%s %s. Stop", msg, gn->name); - - return (false); -} - -/** - * JobExec - * Execute the shell for the given job. Called from JobStart and - * JobRestart. - * - * Side Effects: - * A shell is executed, outputs is altered and the Job structure added - * to the job table. - */ -static void -JobExec(Job *job, char **argv) -{ - struct Shell *shell = job->shell; - ProcStuff ps; - - if (DEBUG(JOB)) { - int i; - - DEBUGF(JOB, ("Running %s\n", job->node->name)); - DEBUGF(JOB, ("\tCommand: ")); - for (i = 0; argv[i] != NULL; i++) { - DEBUGF(JOB, ("%s ", argv[i])); - } - DEBUGF(JOB, ("\n")); - } - - /* - * Some jobs produce no output and it's disconcerting to have - * no feedback of their running (since they produce no output, the - * banner with their name in it never appears). This is an attempt to - * provide that feedback, even if nothing follows it. - */ - if (lastNode != job->node && (job->flags & JOB_FIRST) && - !(job->flags & JOB_SILENT)) { - MESSAGE(stdout, job->node); - lastNode = job->node; - } - - ps.in = FILENO(job->cmdFILE); - if (usePipes) { - /* - * Set up the child's output to be routed through the - * pipe we've created for it. - */ - ps.out = job->outPipe; - } else { - /* - * We're capturing output in a file, so we duplicate - * the descriptor to the temporary file into the - * standard output. - */ - ps.out = job->outFd; - } - ps.err = STDERR_FILENO; - - ps.merge_errors = 1; - ps.pgroup = 1; - ps.searchpath = 0; - - ps.argv = argv; - ps.argv_free = 0; - - /* - * Fork. Warning since we are doing vfork() instead of fork(), - * do not allocate memory in the child process! - */ - if ((ps.child_pid = vfork()) == -1) { - Punt("Cannot fork"); - - } else if (ps.child_pid == 0) { - /* - * Child - */ - if (fifoFd >= 0) - close(fifoFd); - - Proc_Exec(&ps, shell); - /* NOTREACHED */ - - } else { - /* - * Parent - */ - job->pid = ps.child_pid; - - if (usePipes && (job->flags & JOB_FIRST)) { - /* - * The first time a job is run for a node, we set the - * current position in the buffer to the beginning and - * mark another stream to watch in the outputs mask. - */ - job->curPos = 0; - FD_SET(job->inPipe, &outputs); - } - - if (job->cmdFILE != NULL && job->cmdFILE != stdout) { - fclose(job->cmdFILE); - job->cmdFILE = NULL; - } - - /* - * Now the job is actually running, add it to the table. - */ - nJobs += 1; - TAILQ_INSERT_TAIL(&jobs, job, link); - if (nJobs == maxJobs) { - jobFull = true; - } - } -} - -/** - * JobMakeArgv - * Create the argv needed to execute the shell for a given job. - */ -static void -JobMakeArgv(Job *job, char **argv) -{ - struct Shell *shell = job->shell; - int argc; - static char args[10]; /* For merged arguments */ - - argv[0] = shell->name; - argc = 1; - - if ((shell->exit && *shell->exit != '-') || - (shell->echo && *shell->echo != '-')) { - /* - * At least one of the flags doesn't have a minus before it, so - * merge them together. Have to do this because the *(&(@*#*&#$# - * Bourne shell thinks its second argument is a file to source. - * Grrrr. Note the ten-character limitation on the combined - * arguments. - */ - sprintf(args, "-%s%s", (job->flags & JOB_IGNERR) ? "" : - shell->exit ? shell->exit : "", - (job->flags & JOB_SILENT) ? "" : - shell->echo ? shell->echo : ""); - - if (args[1]) { - argv[argc] = args; - argc++; - } - } else { - if (!(job->flags & JOB_IGNERR) && shell->exit) { - argv[argc] = shell->exit; - argc++; - } - if (!(job->flags & JOB_SILENT) && shell->echo) { - argv[argc] = shell->echo; - argc++; - } - } - argv[argc] = NULL; -} - -/** - * JobRestart - * Restart a job that stopped for some reason. The job must be neither - * on the jobs nor on the stoppedJobs list. - * - * Side Effects: - * jobFull will be set if the job couldn't be run. - */ -static void -JobRestart(Job *job) -{ - - if (job->flags & JOB_RESTART) { - /* - * Set up the control arguments to the shell. This is based on - * the flags set earlier for this job. If the JOB_IGNERR flag - * is clear, the 'exit' flag of the shell is used to - * cause it to exit upon receiving an error. If the JOB_SILENT - * flag is clear, the 'echo' flag of the shell is used - * to get it to start echoing as soon as it starts - * processing commands. - */ - char *argv[4]; - - JobMakeArgv(job, argv); - - DEBUGF(JOB, ("Restarting %s...", job->node->name)); - if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL)) { - /* - * Not allowed to run -- put it back on the hold - * queue and mark the table full - */ - DEBUGF(JOB, ("holding\n")); - TAILQ_INSERT_HEAD(&stoppedJobs, job, link); - jobFull = true; - DEBUGF(JOB, ("Job queue is full.\n")); - return; - } else { - /* - * Job may be run locally. - */ - DEBUGF(JOB, ("running locally\n")); - } - JobExec(job, argv); - - } else { - /* - * The job has stopped and needs to be restarted. - * Why it stopped, we don't know... - */ - DEBUGF(JOB, ("Resuming %s...", job->node->name)); - if ((nJobs < maxJobs || ((job->flags & JOB_SPECIAL) && - maxJobs == 0)) && nJobs != maxJobs) { - /* - * If we haven't reached the concurrency limit already - * (or the job must be run and maxJobs is 0), it's ok - * to resume it. - */ - bool error; - int status; - - error = (KILL(job->pid, SIGCONT) != 0); - - if (!error) { - /* - * Make sure the user knows we've continued - * the beast and actually put the thing in the - * job table. - */ - job->flags |= JOB_CONTINUING; - status = 0; - W_SETTERMSIG(&status, SIGCONT); - JobFinish(job, &status); - - job->flags &= ~(JOB_RESUME|JOB_CONTINUING); - DEBUGF(JOB, ("done\n")); - } else { - Error("couldn't resume %s: %s", - job->node->name, strerror(errno)); - status = 0; - W_SETEXITSTATUS(&status, 1); - JobFinish(job, &status); - } - } else { - /* - * Job cannot be restarted. Mark the table as full and - * place the job back on the list of stopped jobs. - */ - DEBUGF(JOB, ("table full\n")); - TAILQ_INSERT_HEAD(&stoppedJobs, job, link); - jobFull = true; - DEBUGF(JOB, ("Job queue is full.\n")); - } - } -} - -/** - * JobStart - * Start a target-creation process going for the target described - * by the graph node gn. - * - * Results: - * JOB_ERROR if there was an error in the commands, JOB_FINISHED - * if there isn't actually anything left to do for the job and - * JOB_RUNNING if the job has been started. - * - * Side Effects: - * A new Job node is created and added to the list of running - * jobs. PMake is forked and a child shell created. - */ -static int -JobStart(GNode *gn, int flags, Job *previous) -{ - Job *job; /* new job descriptor */ - char *argv[4]; /* Argument vector to shell */ - bool cmdsOK; /* true if the nodes commands were all right */ - bool noExec; /* Set true if we decide not to run the job */ - int tfd; /* File descriptor for temp file */ - LstNode *ln; - char tfile[sizeof(TMPPAT)]; - - if (previous == NULL) { - job = emalloc(sizeof(Job)); - flags |= JOB_FIRST; - } else { - previous->flags &= ~(JOB_FIRST | JOB_IGNERR | JOB_SILENT); - job = previous; - } - - job->shell = commandShell; - job->node = gn; - job->tailCmds = NULL; - - /* - * Set the initial value of the flags for this job based on the global - * ones and the node's attributes... Any flags supplied by the caller - * are also added to the field. - */ - job->flags = 0; - if (Targ_Ignore(gn)) { - job->flags |= JOB_IGNERR; - } - if (Targ_Silent(gn)) { - job->flags |= JOB_SILENT; - } - job->flags |= flags; - - /* - * Check the commands now so any attributes from .DEFAULT have a chance - * to migrate to the node. - */ - if (!compatMake && (job->flags & JOB_FIRST)) { - cmdsOK = JobCheckCommands(gn, Error); - } else { - cmdsOK = true; - } - - /* - * If the -n flag wasn't given, we open up OUR (not the child's) - * temporary file to stuff commands in it. The thing is rd/wr so we - * don't need to reopen it to feed it to the shell. If the -n flag - * *was* given, we just set the file to be stdout. Cute, huh? - */ - if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { - /* - * We're serious here, but if the commands were bogus, we're - * also dead... - */ - if (!cmdsOK) { - DieHorribly(); - } - - strcpy(tfile, TMPPAT); - if ((tfd = mkstemp(tfile)) == -1) - Punt("Cannot create temp file: %s", strerror(errno)); - job->cmdFILE = fdopen(tfd, "w+"); - eunlink(tfile); - if (job->cmdFILE == NULL) { - close(tfd); - Punt("Could not open %s", tfile); - } - fcntl(FILENO(job->cmdFILE), F_SETFD, 1); - /* - * Send the commands to the command file, flush all its - * buffers then rewind and remove the thing. - */ - noExec = false; - - /* - * Used to be backwards; replace when start doing multiple - * commands per shell. - */ - if (compatMake) { - /* - * Be compatible: If this is the first time for this - * node, verify its commands are ok and open the - * commands list for sequential access by later - * invocations of JobStart. Once that is done, we take - * the next command off the list and print it to the - * command file. If the command was an ellipsis, note - * that there's nothing more to execute. - */ - if (job->flags & JOB_FIRST) - gn->compat_command = Lst_First(&gn->commands); - else - gn->compat_command = - Lst_Succ(gn->compat_command); - - if (gn->compat_command == NULL || - JobPrintCommand(Lst_Datum(gn->compat_command), job)) - noExec = true; - - if (noExec && !(job->flags & JOB_FIRST)) { - /* - * If we're not going to execute anything, the - * job is done and we need to close down the - * various file descriptors we've opened for - * output, then call JobDoOutput to catch the - * final characters or send the file to the - * screen... Note that the i/o streams are only - * open if this isn't the first job. Note also - * that this could not be done in - * Job_CatchChildren b/c it wasn't clear if - * there were more commands to execute or not... - */ - JobClose(job); - } - } else { - /* - * We can do all the commands at once. hooray for sanity - */ - numCommands = 0; - LST_FOREACH(ln, &gn->commands) { - if (JobPrintCommand(Lst_Datum(ln), job)) - break; - } - - /* - * If we didn't print out any commands to the shell - * script, there's not much point in executing the - * shell, is there? - */ - if (numCommands == 0) { - noExec = true; - } - } - - } else if (noExecute) { - /* - * Not executing anything -- just print all the commands to - * stdout in one fell swoop. This will still set up - * job->tailCmds correctly. - */ - if (lastNode != gn) { - MESSAGE(stdout, gn); - lastNode = gn; - } - job->cmdFILE = stdout; - - /* - * Only print the commands if they're ok, but don't die if - * they're not -- just let the user know they're bad and keep - * going. It doesn't do any harm in this case and may do - * some good. - */ - if (cmdsOK) { - LST_FOREACH(ln, &gn->commands) { - if (JobPrintCommand(Lst_Datum(ln), job)) - break; - } - } - /* - * Don't execute the shell, thank you. - */ - noExec = true; - - } else { - /* - * Just touch the target and note that no shell should be - * executed. Set cmdFILE to stdout to make life easier. Check - * the commands, too, but don't die if they're no good -- it - * does no harm to keep working up the graph. - */ - job->cmdFILE = stdout; - JobTouch(gn, job->flags & JOB_SILENT); - noExec = true; - } - - /* - * If we're not supposed to execute a shell, don't. - */ - if (noExec) { - /* - * Unlink and close the command file if we opened one - */ - if (job->cmdFILE != stdout) { - if (job->cmdFILE != NULL) - fclose(job->cmdFILE); - } else { - fflush(stdout); - } - - /* - * We only want to work our way up the graph if we aren't here - * because the commands for the job were no good. - */ - if (cmdsOK) { - if (aborting == 0) { - for (ln = job->tailCmds; ln != NULL; - ln = LST_NEXT(ln)) { - Lst_AtEnd(&postCommands->commands, - Buf_Peel(Var_Subst(Lst_Datum(ln), - job->node, false))); - } - job->node->made = MADE; - Make_Update(job->node); - } - free(job); - return(JOB_FINISHED); - } else { - free(job); - return(JOB_ERROR); - } - } else { - fflush(job->cmdFILE); - } - - /* - * Set up the control arguments to the shell. This is based on the flags - * set earlier for this job. - */ - JobMakeArgv(job, argv); - - /* - * If we're using pipes to catch output, create the pipe by which we'll - * get the shell's output. If we're using files, print out that we're - * starting a job and then set up its temporary-file name. - */ - if (!compatMake || (job->flags & JOB_FIRST)) { - if (usePipes) { - int fd[2]; - - if (pipe(fd) == -1) - Punt("Cannot create pipe: %s", strerror(errno)); - job->inPipe = fd[0]; - job->outPipe = fd[1]; - fcntl(job->inPipe, F_SETFD, 1); - fcntl(job->outPipe, F_SETFD, 1); - } else { - fprintf(stdout, "Remaking `%s'\n", gn->name); - fflush(stdout); - strcpy(job->outFile, TMPPAT); - if ((job->outFd = mkstemp(job->outFile)) == -1) - Punt("cannot create temp file: %s", - strerror(errno)); - fcntl(job->outFd, F_SETFD, 1); - } - } - - if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL) && maxJobs != 0) { - /* - * We've hit the limit of concurrency, so put the job on hold - * until some other job finishes. Note that the special jobs - * (.BEGIN, .INTERRUPT and .END) may be run even when the - * limit has been reached (e.g. when maxJobs == 0). - */ - jobFull = true; - - DEBUGF(JOB, ("Can only run job locally.\n")); - job->flags |= JOB_RESTART; - TAILQ_INSERT_TAIL(&stoppedJobs, job, link); - } else { - if (nJobs >= maxJobs) { - /* - * If we're running this job as a special case - * (see above), at least say the table is full. - */ - jobFull = true; - DEBUGF(JOB, ("Local job queue is full.\n")); - } - JobExec(job, argv); - } - return (JOB_RUNNING); -} - -static char * -JobOutput(Job *job, char *cp, char *endp, int msg) -{ - struct Shell *shell = job->shell; - char *ecp; - - if (shell->noPrint) { - ecp = strstr(cp, shell->noPrint); - while (ecp != NULL) { - if (cp != ecp) { - *ecp = '\0'; - if (msg && job->node != lastNode) { - MESSAGE(stdout, job->node); - lastNode = job->node; - } - /* - * The only way there wouldn't be a newline - * after this line is if it were the last in - * the buffer. However, since the non-printable - * comes after it, there must be a newline, so - * we don't print one. - */ - fprintf(stdout, "%s", cp); - fflush(stdout); - } - cp = ecp + strlen(shell->noPrint); - if (cp != endp) { - /* - * Still more to print, look again after - * skipping the whitespace following the - * non-printable command.... - */ - cp++; - while (*cp == ' ' || *cp == '\t' || - *cp == '\n') { - cp++; - } - ecp = strstr(cp, shell->noPrint); - } else { - return (cp); - } - } - } - return (cp); -} - -/** - * JobDoOutput - * This function is called at different times depending on - * whether the user has specified that output is to be collected - * via pipes or temporary files. In the former case, we are called - * whenever there is something to read on the pipe. We collect more - * output from the given job and store it in the job's outBuf. If - * this makes up a line, we print it tagged by the job's identifier, - * as necessary. - * If output has been collected in a temporary file, we open the - * file and read it line by line, transferring it to our own - * output channel until the file is empty. At which point we - * remove the temporary file. - * In both cases, however, we keep our figurative eye out for the - * 'noPrint' line for the shell from which the output came. If - * we recognize a line, we don't print it. If the command is not - * alone on the line (the character after it is not \0 or \n), we - * do print whatever follows it. - * - * Side Effects: - * curPos may be shifted as may the contents of outBuf. - */ -static void -JobDoOutput(Job *job, bool finish) -{ - bool gotNL = false; /* true if got a newline */ - bool fbuf; /* true if our buffer filled up */ - int nr; /* number of bytes read */ - int i; /* auxiliary index into outBuf */ - int max; /* limit for i (end of current data) */ - int nRead; /* (Temporary) number of bytes read */ - FILE *oFILE; /* Stream pointer to shell's output file */ - char inLine[132]; - - if (usePipes) { - /* - * Read as many bytes as will fit in the buffer. - */ - end_loop: - gotNL = false; - fbuf = false; - - nRead = read(job->inPipe, &job->outBuf[job->curPos], - JOB_BUFSIZE - job->curPos); - /* - * Check for interrupt here too, because the above read may - * block when the child process is stopped. In this case the - * interrupt will unblock it (we don't use SA_RESTART). - */ - SigHandler(); - - if (nRead < 0) { - DEBUGF(JOB, ("JobDoOutput(piperead)")); - nr = 0; - } else { - nr = nRead; - } - - /* - * If we hit the end-of-file (the job is dead), we must flush - * its remaining output, so pretend we read a newline if - * there's any output remaining in the buffer. - * Also clear the 'finish' flag so we stop looping. - */ - if (nr == 0 && job->curPos != 0) { - job->outBuf[job->curPos] = '\n'; - nr = 1; - finish = false; - } else if (nr == 0) { - finish = false; - } - - /* - * Look for the last newline in the bytes we just got. If there - * is one, break out of the loop with 'i' as its index and - * gotNL set true. - */ - max = job->curPos + nr; - for (i = job->curPos + nr - 1; i >= job->curPos; i--) { - if (job->outBuf[i] == '\n') { - gotNL = true; - break; - } else if (job->outBuf[i] == '\0') { - /* - * Why? - */ - job->outBuf[i] = ' '; - } - } - - if (!gotNL) { - job->curPos += nr; - if (job->curPos == JOB_BUFSIZE) { - /* - * If we've run out of buffer space, we have - * no choice but to print the stuff. sigh. - */ - fbuf = true; - i = job->curPos; - } - } - if (gotNL || fbuf) { - /* - * Need to send the output to the screen. Null terminate - * it first, overwriting the newline character if there - * was one. So long as the line isn't one we should - * filter (according to the shell description), we print - * the line, preceded by a target banner if this target - * isn't the same as the one for which we last printed - * something. The rest of the data in the buffer are - * then shifted down to the start of the buffer and - * curPos is set accordingly. - */ - job->outBuf[i] = '\0'; - if (i >= job->curPos) { - char *cp; - - cp = JobOutput(job, job->outBuf, - &job->outBuf[i], false); - - /* - * There's still more in that buffer. This time, - * though, we know there's no newline at the - * end, so we add one of our own free will. - */ - if (*cp != '\0') { - if (job->node != lastNode) { - MESSAGE(stdout, job->node); - lastNode = job->node; - } - fprintf(stdout, "%s%s", cp, - gotNL ? "\n" : ""); - fflush(stdout); - } - } - if (i < max - 1) { - /* shift the remaining characters down */ - memcpy(job->outBuf, &job->outBuf[i + 1], - max - (i + 1)); - job->curPos = max - (i + 1); - - } else { - /* - * We have written everything out, so we just - * start over from the start of the buffer. - * No copying. No nothing. - */ - job->curPos = 0; - } - } - if (finish) { - /* - * If the finish flag is true, we must loop until we hit - * end-of-file on the pipe. This is guaranteed to happen - * eventually since the other end of the pipe is now - * closed (we closed it explicitly and the child has - * exited). When we do get an EOF, finish will be set - * false and we'll fall through and out. - */ - goto end_loop; - } - - } else { - /* - * We've been called to retrieve the output of the job from the - * temporary file where it's been squirreled away. This consists - * of opening the file, reading the output line by line, being - * sure not to print the noPrint line for the shell we used, - * then close and remove the temporary file. Very simple. - * - * Change to read in blocks and do FindSubString type things - * as for pipes? That would allow for "@echo -n..." - */ - oFILE = fopen(job->outFile, "r"); - if (oFILE != NULL) { - fprintf(stdout, "Results of making %s:\n", - job->node->name); - fflush(stdout); - - while (fgets(inLine, sizeof(inLine), oFILE) != NULL) { - char *cp, *endp, *oendp; - - cp = inLine; - oendp = endp = inLine + strlen(inLine); - if (endp[-1] == '\n') { - *--endp = '\0'; - } - cp = JobOutput(job, inLine, endp, false); - - /* - * There's still more in that buffer. This time, - * though, we know there's no newline at the - * end, so we add one of our own free will. - */ - fprintf(stdout, "%s", cp); - fflush(stdout); - if (endp != oendp) { - fprintf(stdout, "\n"); - fflush(stdout); - } - } - fclose(oFILE); - eunlink(job->outFile); - } - } -} - -/** - * Job_CatchChildren - * Handle the exit of a child. Called from Make_Make. - * - * Side Effects: - * The job descriptor is removed from the list of children. - * - * Notes: - * We do waits, blocking or not, according to the wisdom of our - * caller, until there are no more children to report. For each - * job, call JobFinish to finish things off. This will take care of - * putting jobs on the stoppedJobs queue. - */ -void -Job_CatchChildren(bool block) -{ - pid_t pid; /* pid of dead child */ - Job *job; /* job descriptor for dead child */ - int status; /* Exit/termination status */ - - /* - * Don't even bother if we know there's no one around. - */ - if (nJobs == 0) { - return; - } - - for (;;) { - pid = waitpid((pid_t)-1, &status, (block ? 0 : WNOHANG)); - if (pid <= 0) - break; - - DEBUGF(JOB, ("Process %jd exited or stopped.\n", - (intmax_t)pid)); - - TAILQ_FOREACH(job, &jobs, link) { - if (job->pid == pid) - break; - } - - if (job == NULL) { - if (WIFSIGNALED(status) && - (WTERMSIG(status) == SIGCONT)) { - TAILQ_FOREACH(job, &jobs, link) { - if (job->pid == pid) - break; - } - if (job == NULL) { - Error("Resumed child (%jd) " - "not in table", (intmax_t)pid); - continue; - } - TAILQ_REMOVE(&stoppedJobs, job, link); - } else { - Error("Child (%jd) not in table?", - (intmax_t)pid); - continue; - } - } else { - TAILQ_REMOVE(&jobs, job, link); - nJobs -= 1; - if (fifoFd >= 0 && maxJobs > 1) { - write(fifoFd, "+", 1); - maxJobs--; - if (nJobs >= maxJobs) - jobFull = true; - else - jobFull = false; - } else { - DEBUGF(JOB, ("Job queue is no longer full.\n")); - jobFull = false; - } - } - - JobFinish(job, &status); - } - SigHandler(); -} - -/** - * Job_CatchOutput - * Catch the output from our children, if we're using - * pipes do so. Otherwise just block time until we get a - * signal(most likely a SIGCHLD) since there's no point in - * just spinning when there's nothing to do and the reaping - * of a child can wait for a while. - * - * Side Effects: - * Output is read from pipes if we're piping. - */ -void -Job_CatchOutput(int flag) -{ - int nfds; - struct timeval timeout; - fd_set readfds; - Job *job; - - fflush(stdout); - - if (usePipes) { - readfds = outputs; - timeout.tv_sec = SEL_SEC; - timeout.tv_usec = SEL_USEC; - if (flag && jobFull && fifoFd >= 0) - FD_SET(fifoFd, &readfds); - - nfds = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); - if (nfds == 0) { - /* timeout expired */ - SigHandler(); - return; - } else if (nfds < 0) { - /* must be EINTR */ - SigHandler(); - return; - } else { - if (fifoFd >= 0 && FD_ISSET(fifoFd, &readfds)) { - if (--nfds <= 0) - return; - } - job = TAILQ_FIRST(&jobs); - while (nfds != 0 && job != NULL) { - if (FD_ISSET(job->inPipe, &readfds)) { - JobDoOutput(job, false); - nfds--; - } - job = TAILQ_NEXT(job, link); - } - } - } -} - -/** - * Job_Make - * Start the creation of a target. Basically a front-end for - * JobStart used by the Make module. - * - * Side Effects: - * Another job is started. - */ -void -Job_Make(GNode *gn) -{ - - JobStart(gn, 0, NULL); -} - -/** - * Job_Init - * Initialize the process module, given a maximum number of jobs. - * - * Side Effects: - * lists and counters are initialized - */ -void -Job_Init(int maxproc) -{ - GNode *begin; /* node for commands to do at the very start */ - const char *env; - - fifoFd = -1; - env = getenv("MAKE_JOBS_FIFO"); - - if (env == NULL && maxproc > 1) { - /* - * We did not find the environment variable so we are the - * leader. Create the fifo, open it, write one char per - * allowed job into the pipe. - */ - fifoFd = mkfifotemp(fifoName); - if (fifoFd < 0) { - env = NULL; - } else { - fifoMaster = 1; - fcntl(fifoFd, F_SETFL, O_NONBLOCK); - env = fifoName; - if (setenv("MAKE_JOBS_FIFO", env, 1) == -1) - Punt("setenv: MAKE_JOBS_FIFO: can't allocate memory"); - while (maxproc-- > 0) { - write(fifoFd, "+", 1); - } - /* The master make does not get a magic token */ - jobFull = true; - maxJobs = 0; - } - - } else if (env != NULL) { - /* - * We had the environment variable so we are a slave. - * Open fifo and give ourselves a magic token which represents - * the token our parent make has grabbed to start his make - * process. Otherwise the sub-makes would gobble up tokens and - * the proper number of tokens to specify to -j would depend - * on the depth of the tree and the order of execution. - */ - fifoFd = open(env, O_RDWR, 0); - if (fifoFd >= 0) { - fcntl(fifoFd, F_SETFL, O_NONBLOCK); - maxJobs = 1; - jobFull = false; - } - } - if (fifoFd <= 0) { - maxJobs = maxproc; - jobFull = false; - } else { - } - nJobs = 0; - - aborting = 0; - errors = 0; - - lastNode = NULL; - - if ((maxJobs == 1 && fifoFd < 0) || beVerbose == 0) { - /* - * If only one job can run at a time, there's no need for a - * banner, no is there? - */ - targFmt = ""; - } else { - targFmt = TARG_FMT; - } - - begin = Targ_FindNode(".BEGIN", TARG_NOCREATE); - - if (begin != NULL) { - JobStart(begin, JOB_SPECIAL, NULL); - while (nJobs) { - Job_CatchOutput(0); - Job_CatchChildren(!usePipes); - } - } - postCommands = Targ_FindNode(".END", TARG_CREATE); -} - -/** - * Job_Full - * See if the job table is full. It is considered full if it is OR - * if we are in the process of aborting OR if we have - * reached/exceeded our local quota. This prevents any more jobs - * from starting up. - * - * Results: - * true if the job table is full, false otherwise - */ -bool -Job_Full(void) -{ - char c; - int i; - - if (aborting) - return (aborting); - if (fifoFd >= 0 && jobFull) { - i = read(fifoFd, &c, 1); - if (i > 0) { - maxJobs++; - jobFull = false; - } - } - return (jobFull); -} - -/** - * Job_Empty - * See if the job table is empty. Because the local concurrency may - * be set to 0, it is possible for the job table to become empty, - * while the list of stoppedJobs remains non-empty. In such a case, - * we want to restart as many jobs as we can. - * - * Results: - * true if it is. false if it ain't. - */ -bool -Job_Empty(void) -{ - - if (nJobs == 0) { - if (!TAILQ_EMPTY(&stoppedJobs) && !aborting) { - /* - * The job table is obviously not full if it has no - * jobs in it...Try and restart the stopped jobs. - */ - jobFull = false; - JobRestartJobs(); - return (false); - } else { - return (true); - } - } else { - return (false); - } -} - -/** - * Job_Finish - * Do final processing such as the running of the commands - * attached to the .END target. - * - * Results: - * Number of errors reported. - */ -int -Job_Finish(void) -{ - - if (postCommands != NULL && !Lst_IsEmpty(&postCommands->commands)) { - if (errors) { - Error("Errors reported so .END ignored"); - } else { - JobStart(postCommands, JOB_SPECIAL | JOB_IGNDOTS, NULL); - - while (nJobs) { - Job_CatchOutput(0); - Job_CatchChildren(!usePipes); - } - } - } - if (fifoFd >= 0) { - close(fifoFd); - fifoFd = -1; - if (fifoMaster) - unlink(fifoName); - } - return (errors); -} - -/** - * Job_Wait - * Waits for all running jobs to finish and returns. Sets 'aborting' - * to ABORT_WAIT to prevent other jobs from starting. - * - * Side Effects: - * Currently running jobs finish. - */ -void -Job_Wait(void) -{ - - aborting = ABORT_WAIT; - while (nJobs != 0) { - Job_CatchOutput(0); - Job_CatchChildren(!usePipes); - } - aborting = 0; -} - -/** - * Job_AbortAll - * Abort all currently running jobs without handling output or anything. - * This function is to be called only in the event of a major - * error. - * - * Side Effects: - * All children are killed, not just the firstborn - */ -void -Job_AbortAll(void) -{ - Job *job; /* the job descriptor in that element */ - int status; - - aborting = ABORT_ERROR; - - if (nJobs) { - TAILQ_FOREACH(job, &jobs, link) { - /* - * kill the child process with increasingly drastic - * signals to make darn sure it's dead. - */ - KILL(job->pid, SIGINT); - KILL(job->pid, SIGKILL); - } - } - - /* - * Catch as many children as want to report in at first, then give up - */ - while (waitpid((pid_t)-1, &status, WNOHANG) > 0) - ; -} - -/** - * JobRestartJobs - * Tries to restart stopped jobs if there are slots available. - * Note that this tries to restart them regardless of pending errors. - * It's not good to leave stopped jobs lying around! - * - * Side Effects: - * Resumes(and possibly migrates) jobs. - */ -static void -JobRestartJobs(void) -{ - Job *job; - - while (!jobFull && (job = TAILQ_FIRST(&stoppedJobs)) != NULL) { - DEBUGF(JOB, ("Job queue is not full. " - "Restarting a stopped job.\n")); - TAILQ_REMOVE(&stoppedJobs, job, link); - JobRestart(job); - } -} - -/** - * Cmd_Exec - * Execute the command in cmd, and return the output of that command - * in a string. - * - * Results: - * A string containing the output of the command, or the empty string - * If error is not NULL, it contains the reason for the command failure - * Any output sent to stderr in the child process is passed to stderr, - * and not captured in the string. - * - * Side Effects: - * The string must be freed by the caller. - */ -Buffer * -Cmd_Exec(const char *cmd, const char **error) -{ - Shell *shell = commandShell; - int fds[2]; /* Pipe streams */ - Buffer *buf; /* buffer to store the result */ - ssize_t rcnt; - ProcStuff ps; - - *error = NULL; - buf = Buf_Init(0); - - /* - * Open a pipe for fetching its output - */ - if (pipe(fds) == -1) { - *error = "Couldn't create pipe for \"%s\""; - return (buf); - } - - /* Set close-on-exec on read side of pipe. */ - fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC); - - ps.in = STDIN_FILENO; - ps.out = fds[1]; - ps.err = STDERR_FILENO; - - ps.merge_errors = 0; - ps.pgroup = 0; - ps.searchpath = 0; - - /* Set up arguments for shell */ - ps.argv = emalloc(4 * sizeof(char *)); - ps.argv[0] = strdup(shell->name); - ps.argv[1] = strdup("-c"); - ps.argv[2] = strdup(cmd); - ps.argv[3] = NULL; - ps.argv_free = 1; - - /* - * Fork. Warning since we are doing vfork() instead of fork(), - * do not allocate memory in the child process! - */ - if ((ps.child_pid = vfork()) == -1) { - *error = "Couldn't exec \"%s\""; - - } else if (ps.child_pid == 0) { - /* - * Child - */ - Proc_Exec(&ps, shell); - /* NOTREACHED */ - - } else { - free(ps.argv[2]); - free(ps.argv[1]); - free(ps.argv[0]); - free(ps.argv); - - close(fds[1]); /* No need for the writing half of the pipe. */ - - do { - char result[BUFSIZ]; - - rcnt = read(fds[0], result, sizeof(result)); - if (rcnt != -1) - Buf_AddBytes(buf, (size_t)rcnt, result); - } while (rcnt > 0 || (rcnt == -1 && errno == EINTR)); - - if (rcnt == -1) - *error = "Error reading shell's output for \"%s\""; - - /* - * Close the input side of the pipe. - */ - close(fds[0]); - - ProcWait(&ps); - if (ps.child_status) - *error = "\"%s\" returned non-zero status"; - - Buf_StripNewlines(buf); - - } - return (buf); -} - -/** - * Handle interrupts during the creation of the target and remove - * it if it ain't precious. The default handler for the signal is - * reinstalled, and the signal is raised again. - * - * Side Effects: - * The target is removed and the process exits. If the cause was SIGINT - * and .INTERRUPT: exists its commands are run. - */ -static void -CompatInterrupt(void) -{ - GNode *gn; - int signo; - - if (got_SIGINT) { - got_SIGINT = 0; - signo = SIGINT; - - } else if (got_SIGHUP) { - got_SIGHUP = 0; - signo = SIGHUP; - - } else if (got_SIGQUIT) { - got_SIGQUIT = 0; - signo = SIGQUIT; - - } else if (got_SIGTERM) { - got_SIGTERM = 0; - signo = SIGTERM; - - } else { - return; /* no signal delivered */ - } - - if (curTarg != NULL && !Targ_Precious(curTarg)) { - const char *file = Var_Value(TARGET, curTarg); - - if (!noExecute && eunlink(file) != -1) { - printf("*** %s removed\n", file); - } - } - - /* - * Run .INTERRUPT only if hit with interrupt signal - */ - if (signo == SIGINT) { - gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); - if (gn != NULL) { - Compat_RunCmds(gn, NULL); - } - } - - if (signo == SIGQUIT) { - /* - * We do not raise SIGQUIT, since on systems that create core - * files upon receipt of SIGQUIT, the core from make would - * conflict with a core file from the command that was - * running when the SIGQUIT arrived. - * - * This is true even on BSD systems that name the core file - * after the program, since we might be calling make - * recursively. - */ - exit(2); - } - - signal(signo, SIG_DFL); - kill(getpid(), signo); - - /* NOTREACHED */ -} - -/** - * Execute the next command for a target. If the command returns an - * error, the node's made field is set to ERROR and creation stops. - * The node from which the command came is also given. This is used - * to execute the commands in compat mode and when executing commands - * with the '+' flag in non-compat mode. In these modes each command - * line should be executed by its own shell. We do some optimization here: - * if the shell description defines both a string of meta characters and - * a list of builtins and the command line neither contains a meta character - * nor starts with one of the builtins then we execute the command directly - * without invoking a shell. - * - * Results: - * 0 if the command succeeded, 1 if an error occurred. - * - * Side Effects: - * The node's 'made' field may be set to ERROR. - */ -static int -Compat_RunCommand(GNode *gn, const char cmd[], GNode *ENDNode) -{ - Shell *shell = commandShell; - ArgArray aa; - char *cmdStart; /* Start of expanded command */ - bool silent; /* Don't print command */ - bool doit; /* Execute even in -n */ - bool errCheck; /* Check errors */ - int status; /* Description of child's death */ - LstNode *cmdNode; /* Node where current cmd is located */ - char **av; /* Argument vector for thing to exec */ - ProcStuff ps; - char *line; - - cmdNode = Lst_Member(&gn->commands, cmd); - - cmdStart = Buf_Peel(Var_Subst(cmd, gn, false)); - if (cmdStart[0] == '\0') { - free(cmdStart); - Error("%s expands to empty string", cmd); - return (0); - } - - Lst_Replace(cmdNode, cmdStart); - - if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { - if (ENDNode != NULL) { - Lst_AtEnd(&ENDNode->commands, cmdStart); - } - return (0); - } - - if (strcmp(cmdStart, "...") == 0) { - gn->type |= OP_SAVE_CMDS; - return (0); /* any further commands are deferred */ - } - - line = cmdStart; - silent = gn->type & OP_SILENT; - doit = false; - errCheck = !(gn->type & OP_IGNORE); - - while (*line == '@' || *line == '-' || *line == '+') { - switch (*line) { - - case '@': - silent = DEBUG(LOUD) ? false : true; - break; - - case '-': - errCheck = false; - break; - - case '+': - doit = true; - break; - } - line++; - } - - while (isspace((unsigned char)*line)) - line++; - - if (noExecute && doit == false) { - /* Just print out the command */ - printf("%s\n", line); - fflush(stdout); - return (0); - } - - if (silent == false) { - /* - * Print the command before echoing if we're not supposed to - * be quiet for this one. - */ - printf("%s\n", line); - fflush(stdout); - } - - if (shell->meta == NULL || strpbrk(line, shell->meta) == NULL) { - char **p; - char **sh_builtin = shell->builtins.argv + 1; - - /* - * Break the command into words to form an argument - * vector we can execute. - */ - brk_string(&aa, line, true); - av = aa.argv + 1; - - for (p = sh_builtin; *p != NULL; p++) { - if (strcmp(av[0], *p) == 0) { - /* - * This command must be passed by the shell - * for other reasons.. or.. possibly not at - * all. - */ - av = NULL; - break; - } - } - } else { - /* - * We found a "meta" character and need to pass the command - * off to the shell. - */ - av = NULL; - } - - ps.in = STDIN_FILENO; - ps.out = STDOUT_FILENO; - ps.err = STDERR_FILENO; - - ps.merge_errors = 0; - ps.pgroup = 0; - ps.searchpath = 1; - - if (av == NULL) { - /* - * We give the shell the -e flag as well as -c if it's - * supposed to exit when it hits an error. - */ - ps.argv = emalloc(4 * sizeof(char *)); - ps.argv[0] = strdup(shell->path); - ps.argv[1] = strdup(errCheck ? "-ec" : "-c"); - ps.argv[2] = strdup(line); - ps.argv[3] = NULL; - ps.argv_free = 1; - } else { - ps.argv = av; - ps.argv_free = 0; - } - ps.errCheck = errCheck; - - /* - * Fork and execute the single command. If the fork fails, we abort. - * Warning since we are doing vfork() instead of fork(), - * do not allocate memory in the child process! - */ - if ((ps.child_pid = vfork()) == -1) { - Fatal("Could not fork"); - - } else if (ps.child_pid == 0) { - /* - * Child - */ - Proc_Exec(&ps, shell); - /* NOTREACHED */ - - } else { - if (ps.argv_free) { - free(ps.argv[2]); - free(ps.argv[1]); - free(ps.argv[0]); - free(ps.argv); - } else { - ArgArray_Done(&aa); - } - - /* - * we need to print out the command associated with this - * Gnode in Targ_PrintCmd from Targ_PrintGraph when debugging - * at level g2, in main(), Fatal() and DieHorribly(), - * therefore do not free it when debugging. - */ - if (!DEBUG(GRAPH2)) { - free(cmdStart); - } - - /* - * The child is off and running. Now all we can do is wait... - * Block until child has terminated, or a signal is received. - */ - while (ProcWait(&ps) < 0) { - CompatInterrupt(); /* check on the signal */ - } - - /* - * Decode and report the reason child exited, then - * indicate how we handled it. - */ - if (WIFEXITED(ps.child_status)) { - status = WEXITSTATUS(ps.child_status); - if (status == 0) { - return (0); - } else { - printf("*** Error code %d", status); - } - } else if (WIFSTOPPED(ps.child_status)) { - /* can't happen since WUNTRACED isn't set */ - status = WSTOPSIG(ps.child_status); - } else { - status = WTERMSIG(ps.child_status); - printf("*** Signal %d", status); - } - - if (ps.errCheck) { - gn->made = ERROR; - if (keepgoing) { - /* - * Abort the current - * target, but let - * others continue. - */ - printf(" (continuing)\n"); - } - return (status); - } else { - /* - * Continue executing - * commands for this target. - * If we return 0, this will - * happen... - */ - printf(" (ignored)\n"); - return (0); - } - } -} - -/** - * CompatMake - * Make a target, given the parent, to abort if necessary. - * - * Side Effects: - * If an error is detected and not being ignored, the process exits. - */ -static void -CompatMake(GNode *gn, GNode *pgn, GNode *ENDNode, bool queryFlag) -{ - LstNode *ln; - - if (gn->type & OP_USE) { - Make_HandleUse(gn, pgn); - - } else if (gn->made == UNMADE) { - /* - * First mark ourselves to be made, then apply whatever - * transformations the suffix module thinks are necessary. - * Once that's done, we can descend and make all our children. - * If any of them has an error but the -k flag was given, our - * 'make' field will be set false again. This is our signal to - * not attempt to do anything but abort our parent as well. - */ - gn->make = true; - gn->made = BEINGMADE; - Suff_FindDeps(gn); - LST_FOREACH(ln, &gn->children) - CompatMake(Lst_Datum(ln), gn, ENDNode, queryFlag); - if (!gn->make) { - gn->made = ABORTED; - pgn->make = false; - return; - } - - if (Lst_Member(&gn->iParents, pgn) != NULL) { - Var_Set(IMPSRC, Var_Value(TARGET, gn), pgn); - } - - /* - * All the children were made ok. Now cmtime contains the - * modification time of the newest child, we need to find out - * if we exist and when we were modified last. The criteria for - * datedness are defined by the Make_OODate function. - */ - DEBUGF(MAKE, ("Examining %s...", gn->name)); - if (!Make_OODate(gn)) { - gn->made = UPTODATE; - DEBUGF(MAKE, ("up-to-date.\n")); - return; - } else { - DEBUGF(MAKE, ("out-of-date.\n")); - } - - /* - * If the user is just seeing if something is out-of-date, - * exit now to tell him/her "yes". - */ - if (queryFlag) { - exit(1); - } - - /* - * We need to be re-made. We also have to make sure we've got - * a $? variable. To be nice, we also define the $> variable - * using Make_DoAllVar(). - */ - Make_DoAllVar(gn); - - /* - * Alter our type to tell if errors should be ignored or things - * should not be printed so Compat_RunCommand knows what to do. - */ - if (Targ_Ignore(gn)) { - gn->type |= OP_IGNORE; - } - if (Targ_Silent(gn)) { - gn->type |= OP_SILENT; - } - - if (JobCheckCommands(gn, Fatal)) { - /* - * Our commands are ok, but we still have to worry - * about the -t flag... - */ - if (touchFlag) { - JobTouch(gn, gn->type & OP_SILENT); - } else { - curTarg = gn; - Compat_RunCmds(gn, ENDNode); - curTarg = NULL; - } - } else { - gn->made = ERROR; - } - - if (gn->made != ERROR) { - /* - * If the node was made successfully, mark it so, update - * its modification time and timestamp all its parents. - * Note that for .ZEROTIME targets, the timestamping - * isn't done. This is to keep its state from affecting - * that of its parent. - */ - gn->made = MADE; -#ifndef RECHECK - /* - * We can't re-stat the thing, but we can at least take - * care of rules where a target depends on a source that - * actually creates the target, but only if it has - * changed, e.g. - * - * parse.h : parse.o - * - * parse.o : parse.y - * yacc -d parse.y - * cc -c y.tab.c - * mv y.tab.o parse.o - * cmp -s y.tab.h parse.h || mv y.tab.h parse.h - * - * In this case, if the definitions produced by yacc - * haven't changed from before, parse.h won't have been - * updated and gn->mtime will reflect the current - * modification time for parse.h. This is something of a - * kludge, I admit, but it's a useful one.. - * - * XXX: People like to use a rule like - * - * FRC: - * - * To force things that depend on FRC to be made, so we - * have to check for gn->children being empty as well... - */ - if (!Lst_IsEmpty(&gn->commands) || - Lst_IsEmpty(&gn->children)) { - gn->mtime = now; - } -#else - /* - * This is what Make does and it's actually a good - * thing, as it allows rules like - * - * cmp -s y.tab.h parse.h || cp y.tab.h parse.h - * - * to function as intended. Unfortunately, thanks to - * the stateless nature of NFS (and the speed of this - * program), there are times when the modification time - * of a file created on a remote machine will not be - * modified before the stat() implied by the Dir_MTime - * occurs, thus leading us to believe that the file - * is unchanged, wrecking havoc with files that depend - * on this one. - * - * I have decided it is better to make too much than to - * make too little, so this stuff is commented out - * unless you're sure it's ok. - * -- ardeb 1/12/88 - */ - if (noExecute || Dir_MTime(gn) == 0) { - gn->mtime = now; - } - if (gn->cmtime > gn->mtime) - gn->mtime = gn->cmtime; - DEBUGF(MAKE, ("update time: %s\n", - Targ_FmtTime(gn->mtime))); -#endif - if (!(gn->type & OP_EXEC)) { - pgn->childMade = true; - Make_TimeStamp(pgn, gn); - } - - } else if (keepgoing) { - pgn->make = false; - - } else { - printf("\n\nStop in %s.\n", Var_Value(".CURDIR", gn)); - exit(1); - } - } else if (gn->made == ERROR) { - /* - * Already had an error when making this beastie. Tell the - * parent to abort. - */ - pgn->make = false; - } else { - if (Lst_Member(&gn->iParents, pgn) != NULL) { - Var_Set(IMPSRC, Var_Value(TARGET, gn), pgn); - } - switch(gn->made) { - case BEINGMADE: - Error("Graph cycles through %s\n", gn->name); - gn->made = ERROR; - pgn->make = false; - break; - case MADE: - if ((gn->type & OP_EXEC) == 0) { - pgn->childMade = true; - Make_TimeStamp(pgn, gn); - } - break; - case UPTODATE: - if ((gn->type & OP_EXEC) == 0) { - Make_TimeStamp(pgn, gn); - } - break; - default: - break; - } - } -} - -/** - * Start making given a list of target nodes. Returns what the exit - * status of make should be. - * - * @note Obviously some function we call is exiting since the code only - * returns 0. We will fix that bug eventually. - */ -int -Compat_Run(Lst *targs, bool queryFlag) -{ - int error_cnt; /* Number of targets not remade due to errors */ - GNode *deferred; - - deferred = Targ_NewGN("Deferred"); - - /* - * If the user has defined a .BEGIN target, execute the commands - * attached to it. - */ - if (queryFlag == false) { - GNode *gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); - if (gn != NULL) { - Compat_RunCmds(gn, deferred); - if (gn->made == ERROR) { - printf("\n\nStop.\n"); - return (1); /* Failed .BEGIN target */ - } - } - } - - /* - * For each entry in the list of targets to create, call CompatMake on - * it to create the thing. CompatMake will leave the 'made' field of gn - * in one of several states: - * UPTODATE gn was already up-to-date - * MADE gn was recreated successfully - * ERROR An error occurred while gn was being created - * ABORTED gn was not remade because one of its inferiors - * could not be made due to errors. - */ - error_cnt = 0; - while (!Lst_IsEmpty(targs)) { - GNode *gn = Lst_DeQueue(targs); - - CompatMake(gn, gn, deferred, queryFlag); - if (gn->made == UPTODATE) { - printf("`%s' is up to date.\n", gn->name); - } else if (gn->made == ABORTED) { - printf("`%s' not remade because of errors.\n", - gn->name); - error_cnt += 1; - } - } - - if ((error_cnt == 0) && (queryFlag == false)) { - GNode *gn; - - /* - * If the user has deferred commands using "..." run them. - */ - Compat_RunCmds(deferred, NULL); - if (deferred->made == ERROR) { - printf("\n\nStop.\n"); - return (1); /* Failed "deferred" target */ - } - - /* - * If the user has defined a .END target, run its commands. - */ - gn = Targ_FindNode(".END", TARG_NOCREATE); - if (gn != NULL) { - Compat_RunCmds(gn, NULL); - if (gn->made == ERROR) { - printf("\n\nStop.\n"); - return (1); /* Failed .END target */ - } - } - } - - return (0); /* Successful completion */ -} - diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h deleted file mode 100644 index f908dbb4d7..0000000000 --- a/usr.bin/make/job.h +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)job.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/job.h,v 1.30 2005/02/01 10:50:35 harti Exp $ - * $DragonFly: src/usr.bin/make/job.h,v 1.40 2005/09/22 09:13:38 okumoto Exp $ - */ - -#ifndef job_h_4678dfd1 -#define job_h_4678dfd1 - -/*- - * job.h -- - * Definitions pertaining to the running of jobs in parallel mode. - */ - -#include -#include - -struct Buffer; -struct GNode; -struct Lst; - -void Job_CatchChildren(bool); -void Job_CatchOutput(int flag); -void Job_Make(struct GNode *); -void Job_Init(int); -bool Job_Full(void); -bool Job_Empty(void); -int Job_Finish(void); -void Job_Wait(void); -void Job_AbortAll(void); - -void Proc_Init(void); -void Sig_Init(bool); - -extern struct Shell *commandShell; - -struct Buffer *Cmd_Exec(const char *, const char **); - -int Compat_Run(struct Lst *, bool); - -#endif /* job_h_4678dfd1 */ diff --git a/usr.bin/make/lst.c b/usr.bin/make/lst.c deleted file mode 100644 index fa78442bc8..0000000000 --- a/usr.bin/make/lst.c +++ /dev/null @@ -1,345 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/lst.c,v 1.7 2005/07/19 18:12:46 okumoto Exp $ - */ - -/*- - * lst.c -- - * Routines to maintain a linked list of objects. - */ - -#include -#include - -#include "lst.h" -#include "make.h" -#include "util.h" - -/** - * Lst_Append - * Create a new node and add it to the given list after the given node. - * - * Arguments: - * l affected list - * ln node after which to append the datum - * d said datum - * - * Side Effects: - * A new LstNode is created and linked in to the List. The lastPtr - * field of the List will be altered if ln is the last node in the - * list. lastPtr and firstPtr will alter if the list was empty and - * ln was NULL. - */ -void -Lst_Append(Lst *list, LstNode *ln, void *d) -{ - LstNode *nLNode; - - nLNode = emalloc(sizeof(*nLNode)); - nLNode->datum = d; - - if (ln == NULL) { - nLNode->nextPtr = nLNode->prevPtr = NULL; - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = ln; - nLNode->nextPtr = ln->nextPtr; - - ln->nextPtr = nLNode; - if (nLNode->nextPtr != NULL) { - nLNode->nextPtr->prevPtr = nLNode; - } - - if (ln == list->lastPtr) { - list->lastPtr = nLNode; - } - } -} - -/** - * Lst_Concat - * Concatenate two lists. New elements are created to hold the data - * elements, if specified, but the elements themselves are not copied. - * If the elements should be duplicated to avoid confusion with another - * list, the Lst_Duplicate function should be called first. - * - * Arguments: - * list1 The list to which list2 is to be appended - * list2 The list to append to list1 - * flags LST_CONCNEW if LstNode's should be duplicated - * LST_CONCLINK if should just be relinked - * - * Side Effects: - * New elements are created and appended the the first list. - */ -void -Lst_Concat(Lst *list1, Lst *list2, int flags) -{ - LstNode *ln; /* original LstNode */ - LstNode *nln; /* new LstNode */ - LstNode *last; /* the last element in the list. Keeps - * bookkeeping until the end */ - - if (list2->firstPtr == NULL) - return; - - if (flags == LST_CONCLINK) { - /* - * Link the first element of the second list to the last - * element of the first list. If the first list isn't empty, - * we then link the last element of the list to the first - * element of the second list. The last element of the second - * list, if it exists, then becomes the last element of the - * first list. - */ - list2->firstPtr->prevPtr = list1->lastPtr; - if (list1->lastPtr != NULL) - list1->lastPtr->nextPtr = list2->firstPtr; - else - list1->firstPtr = list2->firstPtr; - list1->lastPtr = list2->lastPtr; - - Lst_Init(list2); - } else { - /* - * The loop simply goes through the entire second list creating - * new LstNodes and filling in the nextPtr, and prevPtr to fit - * into list1 and its datum field from the datum field of the - * corresponding element in list2. The 'last' node follows the - * last of the new nodes along until the entire list2 has been - * appended. Only then does the bookkeeping catch up with the - * changes. During the first iteration of the loop, if 'last' - * is NULL, the first list must have been empty so the - * newly-created node is made the first node of the list. - */ - for (last = list1->lastPtr, ln = list2->firstPtr; - ln != NULL; - ln = ln->nextPtr) { - nln = emalloc(sizeof(*nln)); - nln->datum = ln->datum; - if (last != NULL) { - last->nextPtr = nln; - } else { - list1->firstPtr = nln; - } - nln->prevPtr = last; - last = nln; - } - - /* - * Finish bookkeeping. The last new element becomes the last - * element of list one. - */ - list1->lastPtr = last; - last->nextPtr = NULL; - } -} - -/** - * Lst_DeQueue - * Remove and return the datum at the head of the given list. - * - * Results: - * The datum in the node at the head or (ick) NULL if the list - * is empty. - * - * Side Effects: - * The head node is removed from the list. - */ -void * -Lst_DeQueue(Lst *l) -{ - void *rd; - LstNode *tln; - - tln = Lst_First(l); - if (tln == NULL) { - return (NULL); - } - - rd = tln->datum; - Lst_Remove(l, tln); - return (rd); -} - -/** - * Lst_Destroy - * Destroy a list and free all its resources. If the freeProc is - * given, it is called with the datum from each node in turn before - * the node is freed. - * - * Side Effects: - * The given list is freed in its entirety. - */ -void -Lst_Destroy(Lst *list, FreeProc *freeProc) -{ - LstNode *ln; - - if (list->firstPtr == NULL) - return; - - if (freeProc != NOFREE) { - while ((ln = list->firstPtr) != NULL) { - list->firstPtr = ln->nextPtr; - (*freeProc)(ln->datum); - free(ln); - } - } else { - while ((ln = list->firstPtr) != NULL) { - list->firstPtr = ln->nextPtr; - free(ln); - } - } - list->lastPtr = NULL; -} - -/** - * Lst_Duplicate - * Duplicate an entire list. If a function to copy a void * is - * given, the individual client elements will be duplicated as well. - * - * Arguments: - * dst the destination list (initialized) - * src the list to duplicate - * copyProc A function to duplicate each void - */ -void -Lst_Duplicate(Lst *dst, Lst *src, DuplicateProc *copyProc) -{ - LstNode *ln; - - ln = src->firstPtr; - while (ln != NULL) { - if (copyProc != NOCOPY) - Lst_AtEnd(dst, (*copyProc)(ln->datum)); - else - Lst_AtEnd(dst, ln->datum); - ln = ln->nextPtr; - } -} - -/** - * Lst_Insert - * Insert a new node with the given piece of data before the given - * node in the given list. - * - * Parameters: - * l list to manipulate - * ln node before which to insert d - * d datum to be inserted - * - * Side Effects: - * the firstPtr field will be changed if ln is the first node in the - * list. - */ -void -Lst_Insert(Lst *list, LstNode *ln, void *d) -{ - LstNode *nLNode; /* new lnode for d */ - - nLNode = emalloc(sizeof(*nLNode)); - nLNode->datum = d; - - if (ln == NULL) { - nLNode->prevPtr = nLNode->nextPtr = NULL; - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = ln->prevPtr; - nLNode->nextPtr = ln; - - if (nLNode->prevPtr != NULL) { - nLNode->prevPtr->nextPtr = nLNode; - } - ln->prevPtr = nLNode; - - if (ln == list->firstPtr) { - list->firstPtr = nLNode; - } - } -} - -LstNode * -Lst_Member(Lst *list, const void *d) -{ - LstNode *lNode; - - lNode = list->firstPtr; - if (lNode == NULL) { - return (NULL); - } - - do { - if (lNode->datum == d) { - return (lNode); - } - lNode = lNode->nextPtr; - } while (lNode != NULL && lNode != list->firstPtr); - - return (NULL); -} - -/** - * Lst_Remove - * Remove the given node from the given list. - * - * Side Effects: - * The list's firstPtr will be set to NULL if ln is the last - * node on the list. firsPtr and lastPtr will be altered if ln is - * either the first or last node, respectively, on the list. - */ -void -Lst_Remove(Lst *list, LstNode *ln) -{ - /* - * unlink it from the list - */ - if (ln->nextPtr != NULL) - /* unlink from the backward chain */ - ln->nextPtr->prevPtr = ln->prevPtr; - else - /* this was the last element */ - list->lastPtr = ln->prevPtr; - - if (ln->prevPtr != NULL) - /* unlink from the forward chain */ - ln->prevPtr->nextPtr = ln->nextPtr; - else - /* this was the first element */ - list->firstPtr = ln->nextPtr; - - /* - * note that the datum is unmolested. The caller must free it as - * necessary and as expected. - */ - free(ln); -} diff --git a/usr.bin/make/lst.h b/usr.bin/make/lst.h deleted file mode 100644 index 1e1b183c2a..0000000000 --- a/usr.bin/make/lst.h +++ /dev/null @@ -1,178 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/lst.h,v 1.28 2005/02/10 14:25:12 harti Exp $ - * $DragonFly: src/usr.bin/make/lst.h,v 1.32 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef lst_h_38f3ead1 -#define lst_h_38f3ead1 - -/*- - * lst.h -- - * Header for using the list library - */ - -#include - -/* - * Structure of a list node. - */ -struct LstNode { - struct LstNode *prevPtr; /* previous element in list */ - struct LstNode *nextPtr; /* next in list */ - void *datum; /* datum associated with this element */ -}; -typedef struct LstNode LstNode; - -/* - * The list itself - */ -struct Lst { - LstNode *firstPtr; /* first node in list */ - LstNode *lastPtr; /* last node in list */ -}; -typedef struct Lst Lst; - -typedef void *DuplicateProc(void *); -typedef void FreeProc(void *); - -/* - * NOFREE can be used as the freeProc to Lst_Destroy when the elements are - * not to be freed. - * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate. - */ -#define NOFREE ((FreeProc *)NULL) -#define NOCOPY ((DuplicateProc *)NULL) - -#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ -#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ - -/* - * Creation/destruction functions - */ -/* Create a new list */ -#define Lst_Init(LST) do { \ - (LST)->firstPtr = NULL; \ - (LST)->lastPtr = NULL; \ - } while (0) -#define Lst_Initializer(NAME) { NULL, NULL } - -/* Duplicate an existing list */ -void Lst_Duplicate(Lst *, Lst *, DuplicateProc *); - -/* Destroy an old one */ -void Lst_Destroy(Lst *, FreeProc *); - -/* - * Functions to modify a list - */ -/* Insert an element before another */ -void Lst_Insert(Lst *, LstNode *, void *); -/* Insert an element after another */ -void Lst_Append(Lst *, LstNode *, void *); -/* Place an element at the front of a lst. */ -#define Lst_AtFront(LST, D) (Lst_Insert((LST), Lst_First(LST), (D))) -/* Place an element at the end of a lst. */ -#define Lst_AtEnd(LST, D) (Lst_Append((LST), Lst_Last(LST), (D))) -/* Remove an element */ -void Lst_Remove(Lst *, LstNode *); -/* Replace a node with a new value */ -#define Lst_Replace(NODE, D) ((void)((NODE)->datum = (D))) -/* Concatenate two lists */ -void Lst_Concat(Lst *, Lst *, int); - -/* - * Node-specific functions - */ -/* Return first element in list */ -#define Lst_First(LST) ((Lst_Valid(LST) && !Lst_IsEmpty(LST)) \ - ? (LST)->firstPtr : NULL) -/* Return last element in list */ -#define Lst_Last(LST) ((Lst_Valid(LST) && !Lst_IsEmpty(LST)) \ - ? (LST)->lastPtr : NULL) -/* Return successor to given element */ -#define Lst_Succ(NODE) (((NODE) == NULL) ? NULL : (NODE)->nextPtr) -#define LST_NEXT(NODE) ((NODE)->nextPtr) -/* Get datum from LstNode */ -#define Lst_Datum(NODE) ((NODE)->datum) - -/* - * Functions for entire lists - */ - -/* - * See if the given datum is on the list. Returns the LstNode containing - * the datum - */ -LstNode *Lst_Member(Lst *, const void *); - -/* Loop through a list. Note, that you may not delete the list element. */ -#define LST_FOREACH(PTR, LST) \ - for ((PTR) = (LST)->firstPtr; (PTR) != NULL; (PTR) = (PTR)->nextPtr) - -/* - * for using the list as a queue - */ -/* Place an element at tail of queue */ -#define Lst_EnQueue(LST, D) (Lst_Valid(LST) \ - ? Lst_Append((LST), Lst_Last(LST), (D)) \ - : (void)0) -/* Remove an element from head of queue */ -void *Lst_DeQueue(Lst *); - -/* - * LstValid (L) -- - * Return true if the list L is valid - */ -static inline int Lst_Valid(const void *L) { return (L != NULL); } - -/* - * LstNodeValid (LN, L) -- - * Return true if the LstNode LN is valid with respect to L - */ -#define Lst_NodeValid(LN, L) (((LN) == NULL) ? false : true) - -/* - * Lst_IsEmpty(L) -- - * true if the list L is empty. - */ -#define Lst_IsEmpty(L) (!Lst_Valid(L) || (L)->firstPtr == NULL) - -#endif /* lst_h_38f3ead1 */ diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c deleted file mode 100644 index 5294f03675..0000000000 --- a/usr.bin/make/main.c +++ /dev/null @@ -1,1084 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#) Copyright (c) 1988, 1989, 1990, 1993 The Regents of the University of California. All rights reserved. - * @(#)main.c 8.3 (Berkeley) 3/19/94 - * $FreeBSD: src/usr.bin/make/main.c,v 1.118 2005/02/13 13:33:56 harti Exp $ - * $DragonFly: src/usr.bin/make/main.c,v 1.146 2007/01/19 07:23:43 dillon Exp $ - */ - -/* - * main.c - * The main file for this entire program. Exit routines etc - * reside here. - * - * Utility functions defined in this file: - * Main_ParseArgLine - * Takes a line of arguments, breaks them and - * treats them as if they were given when first - * invoked. Used by the parse module to implement - * the .MFLAGS target. - */ - -#ifndef MACHINE -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "arch.h" -#include "buf.h" -#include "config.h" -#include "dir.h" -#include "globals.h" -#include "job.h" -#include "make.h" -#include "parse.h" -#include "pathnames.h" -#include "shell.h" -#include "str.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -extern char **environ; /* XXX what header declares this variable? */ - -/* - * DEFMAXJOBS - * This control the default concurrency. On no occasion will more - * than DEFMAXJOBS targets be created at once. - */ -#define DEFMAXJOBS 1 - -typedef struct CLI { - /** ordered list of makefiles to read */ - Lst makefiles; - - /** list of variables to print */ - Lst variables; - - /** Targets to be made */ - Lst create; - - /** directories to search when looking for includes */ - struct Path parseIncPath; - - /** directories to search when looking for system includes */ - struct Path sysIncPath; - - bool expandVars; /* fully expand printed variables */ - bool builtins; /* -r flag */ - bool forceJobs; /* -j argument given */ - - /** - * -q flag: - * true if we aren't supposed to really make anything, just - * see if the targets are out-of-date - */ - bool queryFlag; -} CLI; - -/* (-E) vars to override from env */ -Lst envFirstVars = Lst_Initializer(envFirstVars); - -bool allPrecious; /* .PRECIOUS given on line by itself */ -bool beSilent; /* -s flag */ -bool beVerbose; /* -v flag */ -bool compatMake; /* -B argument */ -int debug; /* -d flag */ -bool ignoreErrors; /* -i flag */ -int jobLimit; /* -j argument */ -bool jobsRunning; /* true if the jobs might be running */ -bool keepgoing; /* -k flag */ -bool noExecute; /* -n flag */ -bool touchFlag; /* -t flag */ -bool usePipes; /* !-P flag */ -uint32_t warn_cmd; /* command line warning flags */ -uint32_t warn_flags; /* actual warning flags */ -uint32_t warn_nocmd; /* command line no-warning flags */ - -time_t now; /* Time at start of make */ -struct GNode *DEFAULT; /* .DEFAULT node */ - - -/** - * Exit with usage message. - */ -static void -usage(void) -{ - fprintf(stderr, - "usage: make [-BPSXeiknqrstv] [-C directory] [-D variable]\n" - "\t[-d flags] [-E variable] [-f makefile] [-I directory]\n" - "\t[-j max_jobs] [-m directory] [-V variable]\n" - "\t[variable=value] [target ...]\n"); - exit(2); -} - -/** - * MFLAGS_append - * Append a flag with an optional argument to MAKEFLAGS and MFLAGS - */ -static void -MFLAGS_append(const char *flag, char *arg) -{ - char *str; - - Var_Append(".MAKEFLAGS", flag, VAR_GLOBAL); - if (arg != NULL) { - str = MAKEFLAGS_quote(arg); - Var_Append(".MAKEFLAGS", str, VAR_GLOBAL); - free(str); - } - - Var_Append("MFLAGS", flag, VAR_GLOBAL); - if (arg != NULL) { - str = MAKEFLAGS_quote(arg); - Var_Append("MFLAGS", str, VAR_GLOBAL); - free(str); - } -} - -/** - * Open and parse the given makefile. - * - * Results: - * true if ok. false if couldn't open file. - */ -static bool -ReadMakefile(Parser *parser, CLI *cli, const char file[], const char curdir[], const char objdir[]) -{ - char path[MAXPATHLEN]; - FILE *stream; - char *fname; - char *name; - - if (!strcmp(file, "-")) { - Parse_File(parser, cli, "(stdin)", stdin); - Var_SetGlobal("MAKEFILE", ""); - return (true); - } - - if (strcmp(curdir, objdir) == 0 || file[0] == '/') { - strcpy(path, file); - } else { - /* - * we've chdir'd, rebuild the path name - */ - snprintf(path, MAXPATHLEN, "%s/%s", curdir, file); - } -#if THIS_BREAKS_THINGS - /* - * XXX The realpath stuff breaks relative includes - * XXX in some cases. The problem likely is in - * XXX parse.c where it does special things in - * XXX ParseDoInclude if the file is relateive - * XXX or absolute and not a system file. There - * XXX it assumes that if the current file that's - * XXX being included is absolute, that any files - * XXX that it includes shouldn't do the -I path - * XXX stuff, which is inconsistant with historical - * XXX behavior. However, I can't pentrate the mists - * XXX further, so I'm putting this workaround in - * XXX here until such time as the underlying bug - * XXX can be fixed. - */ - if (realpath(path, path) == NULL) { - stream = NULL; - } else { - stream = fopen(path, "r"); - } -#else - stream = fopen(path, "r"); -#endif - if (stream != NULL) { - if (strcmp(file, ".depend") != 0) - Var_SetGlobal("MAKEFILE", file); - Parse_File(parser, cli, path, stream); - fclose(stream); - return (true); - } - - /* look in -I and system include directories. */ - fname = estrdup(file); - name = NULL; - if (name == NULL) - name = Path_FindFile(fname, &cli->parseIncPath); - if (name == NULL) - name = Path_FindFile(fname, &cli->sysIncPath); - - if (name != NULL) { - stream = fopen(name, "r"); - if (stream != NULL) { - /* - * set the MAKEFILE variable desired by System V fans - * -- the placement of the setting here means it gets - * set to the last makefile specified, as it is set - * by SysV make. - */ - if (strcmp(file, ".depend") != 0) - Var_SetGlobal("MAKEFILE", name); - Parse_File(parser, cli, name, stream); - fclose(stream); - return (true); - } - } - - return (false); /* no makefile found */ -} - -/** - * Read in the built-in rules first, followed by the specified - * makefiles or the one of the default makefiles. Finally .depend - * makefile. - */ -static void -ReadInputFiles(Parser *parser, CLI *cli, const char curdir[], const char objdir[]) -{ - if (cli->builtins) { - char defsysmk[] = PATH_DEFSYSMK; /* Path of sys.mk */ - Lst sysMkPath = Lst_Initializer(sysMkPath); - LstNode *ln; - - Path_Expand(defsysmk, &cli->sysIncPath, &sysMkPath); - if (Lst_IsEmpty(&sysMkPath)) - Fatal("make: no system rules (%s).", PATH_DEFSYSMK); - - LST_FOREACH(ln, &sysMkPath) { - char *name = Lst_Datum(ln); - if (!ReadMakefile(parser, cli, name, curdir, objdir)) - Fatal("make: cannot open %s.", name); - } - Lst_Destroy(&sysMkPath, free); - } - - if (!Lst_IsEmpty(&cli->makefiles)) { - LstNode *ln; - - LST_FOREACH(ln, &cli->makefiles) { - char *name = Lst_Datum(ln); - if (!ReadMakefile(parser, cli, name, curdir, objdir)) - Fatal("make: cannot open %s.", name); - } - } else if (ReadMakefile(parser, cli, "BSDmakefile", curdir, objdir)) { - /* read BSDmakefile */ - } else if (ReadMakefile(parser, cli, "makefile", curdir, objdir)) { - /* read makefile */ - } else if (ReadMakefile(parser, cli, "Makefile", curdir, objdir)) { - /* read Makefile */ - } else { - /* No Makefile found */ - } - - ReadMakefile(parser, cli, ".depend", curdir, objdir); -} - -/** - * Main_ParseWarn - * - * Handle argument to warning option. - */ -int -Main_ParseWarn(const char *arg, int iscmd) -{ - int i, neg; - - static const struct { - const char *option; - uint32_t flag; - } options[] = { - { "dirsyntax", WARN_DIRSYNTAX }, - { NULL, 0 } - }; - - neg = 0; - if (arg[0] == 'n' && arg[1] == 'o') { - neg = 1; - arg += 2; - } - - for (i = 0; options[i].option != NULL; i++) - if (strcmp(arg, options[i].option) == 0) - break; - - if (options[i].option == NULL) - /* unknown option */ - return (-1); - - if (iscmd) { - if (!neg) { - warn_cmd |= options[i].flag; - warn_nocmd &= ~options[i].flag; - warn_flags |= options[i].flag; - } else { - warn_nocmd |= options[i].flag; - warn_cmd &= ~options[i].flag; - warn_flags &= ~options[i].flag; - } - } else { - if (!neg) { - warn_flags |= (options[i].flag & ~warn_nocmd); - } else { - warn_flags &= ~(options[i].flag | warn_cmd); - } - } - return (0); -} - -/** - * MainParseArgs - * Parse a given argument vector. Called from main() and from - * Main_ParseArgLine() when the .MAKEFLAGS target is used. - * - * XXX: Deal with command line overriding .MAKEFLAGS in makefile - * - * Side Effects: - * Various global and local flags will be set depending on the flags - * given - */ -static void -MainParseArgs(CLI *cli, int argc, char **argv) -{ - int c; - bool found_dd = false; - -rearg: - optind = 1; /* since we're called more than once */ - optreset = 1; -#define OPTFLAGS "ABC:D:E:I:PSV:Xd:ef:ij:km:nqrstvx:" - for (;;) { - if ((optind < argc) && strcmp(argv[optind], "--") == 0) { - found_dd = true; - } - if ((c = getopt(argc, argv, OPTFLAGS)) == -1) { - break; - } - switch(c) { - - case 'A': - arch_fatal = false; - MFLAGS_append("-A", NULL); - break; - case 'C': - if (chdir(optarg) == -1) - err(1, "chdir %s", optarg); - break; - case 'D': - Var_SetGlobal(optarg, "1"); - MFLAGS_append("-D", optarg); - break; - case 'I': - Path_AddDir(&cli->parseIncPath, optarg); - MFLAGS_append("-I", optarg); - break; - case 'V': - Lst_AtEnd(&cli->variables, estrdup(optarg)); - MFLAGS_append("-V", optarg); - break; - case 'X': - cli->expandVars = false; - break; - case 'B': - compatMake = true; - MFLAGS_append("-B", NULL); - unsetenv("MAKE_JOBS_FIFO"); - break; - case 'P': - usePipes = false; - MFLAGS_append("-P", NULL); - break; - case 'S': - keepgoing = false; - MFLAGS_append("-S", NULL); - break; - case 'd': { - char *modules = optarg; - - for (; *modules; ++modules) - switch (*modules) { - case 'A': - debug = ~0; - break; - case 'a': - debug |= DEBUG_ARCH; - break; - case 'c': - debug |= DEBUG_COND; - break; - case 'd': - debug |= DEBUG_DIR; - break; - case 'f': - debug |= DEBUG_FOR; - break; - case 'g': - if (modules[1] == '1') { - debug |= DEBUG_GRAPH1; - ++modules; - } - else if (modules[1] == '2') { - debug |= DEBUG_GRAPH2; - ++modules; - } - break; - case 'j': - debug |= DEBUG_JOB; - break; - case 'l': - debug |= DEBUG_LOUD; - break; - case 'm': - debug |= DEBUG_MAKE; - break; - case 's': - debug |= DEBUG_SUFF; - break; - case 't': - debug |= DEBUG_TARG; - break; - case 'v': - debug |= DEBUG_VAR; - break; - default: - warnx("illegal argument to d option " - "-- %c", *modules); - usage(); - } - MFLAGS_append("-d", optarg); - break; - } - case 'E': - Lst_AtEnd(&envFirstVars, estrdup(optarg)); - MFLAGS_append("-E", optarg); - break; - case 'e': - checkEnvFirst = true; - MFLAGS_append("-e", NULL); - break; - case 'f': - Lst_AtEnd(&cli->makefiles, estrdup(optarg)); - break; - case 'i': - ignoreErrors = true; - MFLAGS_append("-i", NULL); - break; - case 'j': { - char *endptr; - - cli->forceJobs = true; - jobLimit = strtol(optarg, &endptr, 10); - if (jobLimit <= 0 || *endptr != '\0') { - warnx("illegal number, -j argument -- %s", - optarg); - usage(); - } - MFLAGS_append("-j", optarg); - break; - } - case 'k': - keepgoing = true; - MFLAGS_append("-k", NULL); - break; - case 'm': - Path_AddDir(&cli->sysIncPath, optarg); - MFLAGS_append("-m", optarg); - break; - case 'n': - noExecute = true; - MFLAGS_append("-n", NULL); - break; - case 'q': - cli->queryFlag = true; - /* Kind of nonsensical, wot? */ - MFLAGS_append("-q", NULL); - break; - case 'r': - cli->builtins = false; - MFLAGS_append("-r", NULL); - break; - case 's': - beSilent = true; - MFLAGS_append("-s", NULL); - break; - case 't': - touchFlag = true; - MFLAGS_append("-t", NULL); - break; - case 'v': - beVerbose = true; - MFLAGS_append("-v", NULL); - break; - case 'x': - if (Main_ParseWarn(optarg, 1) != -1) - MFLAGS_append("-x", optarg); - break; - default: - case '?': - usage(); - } - } - argv += optind; - argc -= optind; - - oldVars = true; - - /* - * Parse the rest of the arguments. - * o Check for variable assignments and perform them if so. - * o Check for more flags and restart getopt if so. - * o Anything else is taken to be a target and added - * to the end of the "create" list. - */ - for (; *argv != NULL; ++argv, --argc) { - if (Parse_IsVar(*argv)) { - char *ptr = MAKEFLAGS_quote(*argv); - - Var_Append(".MAKEFLAGS", ptr, VAR_GLOBAL); - Parse_DoVar(*argv, VAR_CMD); - free(ptr); - - } else if ((*argv)[0] == '-') { - if ((*argv)[1] == '\0') { - /* - * (*argv) is a single dash, so we - * just ignore it. - */ - } else if (found_dd) { - /* - * Double dash has been found, ignore - * any more options. But what do we do - * with it? For now treat it like a target. - */ - Lst_AtEnd(&cli->create, estrdup(*argv)); - } else { - /* - * (*argv) is a -flag, so backup argv and - * argc. getopt() expects options to start - * in the 2nd position. - */ - argc++; - argv--; - goto rearg; - } - - } else if ((*argv)[0] == '\0') { - Punt("illegal (null) argument."); - - } else { - Lst_AtEnd(&cli->create, estrdup(*argv)); - } - } -} - -/** - * Main_ParseArgLine - * Used by the parse module when a .MFLAGS or .MAKEFLAGS target - * is encountered and by main() when reading the .MAKEFLAGS envariable. - * Takes a line of arguments and breaks it into its - * component words and passes those words and the number of them to the - * MainParseArgs function. - * The line should have all its leading whitespace removed. - * - * Side Effects: - * Only those that come from the various arguments. - */ -void -Main_ParseArgLine(CLI *cli, const char line[], int mflags) -{ - ArgArray aa; - - if (mflags) { - MAKEFLAGS_break(&aa, line); - } else { - brk_string(&aa, line, true); - } - MainParseArgs(cli, aa.argc, aa.argv); - ArgArray_Done(&aa); -} - -/** - * Try to change the current working directory to path, and return - * the whole path using getcwd(). - * - * @note for amd managed mount points we really should use pawd(1). - */ -static int -CheckDir(const char path[], char newdir[]) -{ - struct stat sb; - - /* - * Check if the path is a directory. If not fail without reporting - * an error. - */ - if (stat(path, &sb) < 0) { - return (0); - } - if (S_ISDIR(sb.st_mode) == 0) { - return (0); - } - - /* - * The path refers to a directory, so we try to change into it. If we - * fail, or we fail to obtain the path from root to the directory, - * then report an error and fail. - */ - if (chdir(path) < 0) { - warn("warning: %s", path); - return (0); - } - if (getcwd(newdir, MAXPATHLEN) == NULL) { - warn("warning: %s", path); - return (0); - } - - /* - * Directory in path is accessable, newdir should now contain the - * path to it. - */ - return (1); -} - -/** - * Determine location of the object directory. - */ -static void -FindObjDir(const char machine[], char curdir[], char objdir[]) -{ - struct stat sa; - char newdir[MAXPATHLEN]; - char mdpath[MAXPATHLEN]; - const char *env; - - /* - * Find a path to where we are... [-C directory] might have changed - * our current directory. - */ - if (getcwd(curdir, MAXPATHLEN) == NULL) - err(2, NULL); - - if (stat(curdir, &sa) == -1) - err(2, "%s", curdir); - - /* - * The object directory location is determined using the - * following order of preference: - * - * 1. MAKEOBJDIRPREFIX`cwd` - * 2. MAKEOBJDIR - * 3. PATH_OBJDIR.${MACHINE} - * 4. PATH_OBJDIR - * 5. PATH_OBJDIRPREFIX`cwd` - * - * If one of the first two fails, use the current directory. - * If the remaining three all fail, use the current directory. - */ - if ((env = getenv("MAKEOBJDIRPREFIX")) != NULL) { - snprintf(mdpath, MAXPATHLEN, "%s%s", env, curdir); - if (CheckDir(mdpath, newdir)) { - strcpy(objdir, newdir); - return; - } - strcpy(objdir, curdir); - return; - } - - if ((env = getenv("MAKEOBJDIR")) != NULL) { - if (CheckDir(env, newdir)) { - strcpy(objdir, newdir); - return; - } - strcpy(objdir, curdir); - return; - } - - snprintf(mdpath, MAXPATHLEN, "%s.%s", PATH_OBJDIR, machine); - if (CheckDir(mdpath, newdir)) { - strcpy(objdir, newdir); - return; - } - - if (CheckDir(PATH_OBJDIR, newdir)) { - strcpy(objdir, newdir); - return; - } - - snprintf(mdpath, MAXPATHLEN, "%s%s", PATH_OBJDIRPREFIX, curdir); - if (CheckDir(mdpath, newdir)) { - strcpy(objdir, newdir); - return; - } - - strcpy(objdir, curdir); -} - -/** - * Initialize various make variables. - * MAKE also gets this name, for compatibility - * .MAKEFLAGS gets set to the empty string just in case. - * MFLAGS also gets initialized empty, for compatibility. - */ -static void -InitVariables(const char progname[]) -{ - const char *machine_platform; - const char *machine_arch; - const char *machine; - char buf[256]; - size_t bufsiz; - - Var_SetGlobal("MAKE", progname); - Var_SetGlobal(".MAKEFLAGS", ""); - Var_SetGlobal("MFLAGS", ""); - - Var_SetGlobal(".DIRECTIVE_MAKEENV", "YES"); - Var_SetGlobal(".ST_EXPORTVAR", "YES"); -#ifdef MAKE_VERSION - Var_SetGlobal("MAKE_VERSION", MAKE_VERSION); -#endif - - /* - * The make program defines MACHINE_PLATFORM, MACHINE and MACHINE_ARCH. - * These parameters are taken from the running system but can be - * overridden by environment variables. - * - * MACHINE_PLATFORM - * - This is the platform, e.g. "vkernel", "pc32", - * and so forth. - * - * MACHINE - This is the machine architecture and in most - * cases is the same as the cpu architecture. - * - * MACHINE_ARCH - This is the cpu architecture, for example "i386". - * Several different platforms may use the same - * cpu architecture. - * - * In most, but not all cases, MACHINE == MACHINE_ARCH. - * - * PLATFORM distinguishes differences between, say, a virtual kernel - * build and a real kernel build. - */ - if ((machine_platform = getenv("MACHINE_PLATFORM")) == NULL) { - bufsiz = sizeof(buf); - if (sysctlbyname("hw.platform", buf, &bufsiz, NULL, 0) < 0) - machine_platform = "unknown"; - else - machine_platform = strdup(buf); - } - - if ((machine = getenv("MACHINE")) == NULL) { - bufsiz = sizeof(buf); - if (sysctlbyname("hw.machine", buf, &bufsiz, NULL, 0) < 0) - machine = "unknown"; - else - machine = strdup(buf); - } - - if ((machine_arch = getenv("MACHINE_ARCH")) == NULL) { - bufsiz = sizeof(buf); - if (sysctlbyname("hw.machine_arch", buf, &bufsiz, NULL, 0) < 0) - machine_arch = "unknown"; - else - machine_arch = strdup(buf); - } - - Var_SetGlobal("MACHINE_PLATFORM", machine_platform); - Var_SetGlobal("MACHINE", machine); - Var_SetGlobal("MACHINE_ARCH", machine_arch); -} - -/** - * Build targets given in the command line or if none were given - * use the main target determined by the parsing module. - */ -static int -BuildStuff(CLI *cli) -{ - int status; - Lst targs = Lst_Initializer(targs); - - if (Lst_IsEmpty(&cli->create)) - Parse_MainName(&targs); - else - Targ_FindList(&targs, &cli->create, TARG_CREATE); - - /* Traverse the graph, checking on all the targets */ - if (compatMake) { - Sig_Init(true); - status = Compat_Run(&targs, cli->queryFlag); - } else { - Sig_Init(false); - status = Make_Run(&targs, cli->queryFlag); - } - - Lst_Destroy(&targs, NOFREE); - return (status); -} - -/** - * main - * The main function, for obvious reasons. Initializes variables - * and a few modules, then parses the arguments give it in the - * environment and on the command line. Reads the system makefile - * followed by either Makefile, makefile or the file given by the - * -f argument. Sets the .MAKEFLAGS PMake variable based on all the - * flags it has received by then uses either the Make or the Compat - * module to create the initial list of targets. - * - * Results: - * If -q was given, exits -1 if anything was out-of-date. Else it exits - * 0. - * - * Side Effects: - * The program exits when done. Targets are created. etc. etc. etc. - */ -int -main(int argc, char **argv) -{ - CLI cli; - Parser parser; - Shell *shell; - int status; /* exit status */ - char curdir[MAXPATHLEN]; /* startup directory */ - char objdir[MAXPATHLEN]; /* where we chdir'ed to */ - const char *make_flags; - - /*------------------------------------------------------------* - * This section initializes stuff that require no input. - *------------------------------------------------------------*/ - /* - * Initialize program globals. - */ - beSilent = false; /* Print commands as executed */ - ignoreErrors = false; /* Pay attention to non-zero returns */ - noExecute = false; /* Execute all commands */ - keepgoing = false; /* Stop on error */ - allPrecious = false; /* Remove targets when interrupted */ - touchFlag = false; /* Actually update targets */ - usePipes = true; /* Catch child output in pipes */ - debug = 0; /* No debug verbosity, please. */ - jobsRunning = false; - - jobLimit = DEFMAXJOBS; - compatMake = false; /* No compat mode */ - - /* - * Initialize program flags. - */ - Lst_Init(&cli.makefiles); - Lst_Init(&cli.variables); - Lst_Init(&cli.create); - TAILQ_INIT(&cli.parseIncPath); - TAILQ_INIT(&cli.sysIncPath); - - cli.expandVars = true; - cli.builtins = true; /* Read the built-in rules */ - cli.queryFlag = false; - cli.forceJobs = false; - - shell = Shell_Match(DEFSHELLNAME); - - /* - * Initialize the various modules. - */ - Proc_Init(); - commandShell = shell; - Targ_Init(); - Suff_Init(); - Dir_Init(); - - /*------------------------------------------------------------* - * This section initializes stuff that depend on things - * in the enviornment, command line, or a input file. - *------------------------------------------------------------*/ - Var_Init(environ); - - InitVariables(argv[0]); - - /* - * First snag things out of the MAKEFLAGS environment - * variable. Then parse the command line arguments. - */ - if ((make_flags = getenv("MAKEFLAGS")) != NULL) { - Main_ParseArgLine(&cli, make_flags, 1); - } - MainParseArgs(&cli, argc, argv); - - FindObjDir(Var_Value("MACHINE", VAR_GLOBAL), curdir, objdir); - Var_SetGlobal(".CURDIR", curdir); - Var_SetGlobal(".OBJDIR", objdir); - - /* - * Set up the .TARGETS variable to contain the list of targets to be - * created. If none specified, make the variable empty -- the parser - * will fill the thing in with the default or .MAIN target. - */ - if (Lst_IsEmpty(&cli.create)) { - Var_SetGlobal(".TARGETS", ""); - } else { - LstNode *ln; - - LST_FOREACH(ln, &cli.create) { - char *name = Lst_Datum(ln); - - Var_Append(".TARGETS", name, VAR_GLOBAL); - } - } - - Dir_CurObj(curdir, objdir); - - /* - * If no user-supplied system path was given (through the -m option) - * add the directories from the DEFSYSPATH (more than one may be given - * as dir1:...:dirn) to the system include path. - */ - if (TAILQ_EMPTY(&cli.sysIncPath)) { - char syspath[] = PATH_DEFSYSPATH; - char *start = syspath; - char *cp; - - while ((cp = strsep(&start, ":")) != NULL) { - Path_AddDir(&cli.sysIncPath, cp); - } - } - - if (getenv("MAKE_JOBS_FIFO") != NULL) - cli.forceJobs = true; - - /* - * Be compatible if user did not specify -j and did not explicitly - * turned compatibility on - */ - if (compatMake == false && cli.forceJobs == false) - compatMake = true; - - DEFAULT = NULL; - time(&now); - - parser.create = &cli.create; - parser.parseIncPath = &cli.parseIncPath; - parser.sysIncPath = &cli.sysIncPath; - - ReadInputFiles(&parser, &cli, curdir, objdir); - - /*------------------------------------------------------------* - * We are finished processing inputs. - *------------------------------------------------------------*/ - - /* Install all the flags into the MAKE envariable. */ - { - const char *p; - - p = Var_Value(".MAKEFLAGS", VAR_GLOBAL); - if (p != NULL && *p != '\0') { - if (setenv("MAKEFLAGS", p, 1) == -1) - Punt("setenv: MAKEFLAGS: can't allocate memory"); - } - } - - /* - * For compatibility, look at the directories in the VPATH variable - * and add them to the search path, if the variable is defined. The - * variable's value is in the same format as the PATH envariable, i.e. - * ::... - */ - if (Var_Value("VPATH", VAR_CMD) != NULL) { - Buffer *buf = Var_Subst("${VPATH}", VAR_CMD, false); - char *start = Buf_Data(buf); - char *cp; - - while ((cp = strsep(&start, ":")) != NULL) { - Path_AddDir(&dirSearchPath, cp); - } - - Buf_Destroy(buf, true); - } - - /* - * Now that all search paths have been read for suffixes et al, it's - * time to add the default search path to their lists... - */ - Suff_DoPaths(); - - /* print the initial graph, if the user requested it */ - if (DEBUG(GRAPH1)) - Targ_PrintGraph(1); - - if (Lst_IsEmpty(&cli.variables)) { - status = BuildStuff(&cli); - } else { - /* Print the values of variables requested by the user. */ - Var_Print(&cli.variables, cli.expandVars); - - /* - * XXX - * This should be a "don't care", we do not check - * the status of any files. It might make sense to - * modify Var_Print() to indicate that one of the - * requested variables did not exist, and use that - * as the return status. - * XXX - */ - status = cli.queryFlag ? 1 : 0; - } - - /* print the graph now it's been processed if the user requested it */ - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - -#if 0 - TAILQ_DESTROY(&cli.sysIncPath); - TAILQ_DESTROY(&cli.parseIncPath); -#endif - Lst_Destroy(&cli.create, free); - Lst_Destroy(&cli.variables, free); - Lst_Destroy(&cli.makefiles, free); - - return (status); -} - diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 deleted file mode 100644 index 9916419623..0000000000 --- a/usr.bin/make/make.1 +++ /dev/null @@ -1,1567 +0,0 @@ -.\" Copyright (c) 1990, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 -.\" $FreeBSD: src/usr.bin/make/make.1,v 1.29.2.15 2002/12/17 19:01:18 seanc Exp $ -.\" $DragonFly: src/usr.bin/make/make.1,v 1.32 2008/06/23 06:56:25 swildner Exp $ -.\" -.Dd November 14, 2009 -.Dt MAKE 1 -.Os -.Sh NAME -.Nm make -.Nd maintain program dependencies -.Sh SYNOPSIS -.Nm -.Op Fl ABPSXeiknqrstv -.Op Fl C Ar directory -.Op Fl D Ar variable -.Op Fl d Ar flags -.Op Fl E Ar variable -.Op Fl f Ar makefile -.Op Fl I Ar directory -.Bk -words -.Op Fl j Ar max_jobs -.Op Fl m Ar directory -.Ek -.Op Fl V Ar variable -.Op Fl x Ar warning_options -.Op Ar variable Ns No = Ns Ar value -.Op Ar target ... -.Sh DESCRIPTION -The -.Nm -utility is a program designed to simplify the maintenance of other programs. -Its input is a list of specifications -describing dependency relationships between the generation of -files and programs. -The first of -.Pa BSDmakefile , -.Pa makefile -and -.Pa Makefile -that can be found in either the current directory or a special object directory -(see -.Va .OBJDIR ) -will be read for this list of specifications. -If the file -.Pa .depend -can be found, it is also read (see -.Xr mkdep 1 ) . -.Pp -This manual page is intended as a reference document only. -For a more thorough introduction to -.Nm -and makefiles, please refer to -.%T "Make \- A Tutorial" . -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl A -Make archive errors non-fatal, causing -.Nm -to just skip the remainder -or all of the archive and continue after printing a message. -.It Fl B -Try to be backwards compatible by executing a single shell per command and -by executing the commands to make the sources of a dependency line in sequence. -This is turned on by default unless -.Fl j -is used. -.It Fl C Ar directory -Change to -.Ar directory -before reading the makefiles or doing anything else. -If multiple -.Fl C -options are specified, each is interpreted relative to the previous one: -.Fl C Pa / Fl C Pa etc -is equivalent to -.Fl C Pa /etc . -.It Fl D Ar variable -Define -.Ar variable -to be 1, in the global context. -.It Fl d Ar flags -Turn on debugging, and specify which portions of -.Nm -are to print debugging information. -Argument -.Ar flags -is one or more of the following: -.Bl -tag -width Ds -.It Ar A -Print all possible debugging information; -equivalent to specifying all of the debugging flags. -.It Ar a -Print debugging information about archive searching and caching. -.It Ar c -Print debugging information about conditional evaluation. -.It Ar d -Print debugging information about directory searching and caching. -.It Ar f -Print debugging information about the execution of for loops. -.It Ar "g1" -Print the input graph before making anything. -.It Ar "g2" -Print the input graph after making everything, or before exiting -on error. -.It Ar j -Print debugging information about running multiple shells. -.It Ar l -Print commands in Makefiles regardless of whether or not they are prefixed -by @ or other "quiet" flags. -Also known as "loud" behavior. -.It Ar m -Print debugging information about making targets, including modification -dates. -.It Ar s -Print debugging information about suffix-transformation rules. -.It Ar t -Print debugging information about target list maintenance. -.It Ar v -Print debugging information about variable assignment. -.El -.It Fl E Ar variable -Specify a variable whose environment value (if any) will override -macro assignments within makefiles. -.It Fl e -Specify that environment values override macro assignments within -makefiles for all variables. -.It Fl f Ar makefile -Specify a makefile to read instead of the default -.Pa makefile -and -.Pa Makefile . -If -.Ar makefile -is -.Sq Fl , -standard input is read. -Multiple makefiles may be specified, and are read in the order specified. -.It Fl I Ar directory -Specify a directory in which to search for makefiles and included makefiles. -The system makefile directory (or directories, see the -.Fl m -option) is automatically included as part of this list. -.It Fl i -Ignore non-zero exit of shell commands in the makefile. -Equivalent to specifying -.Sq Ic \- -before each command line in the makefile. -.It Fl j Ar max_jobs -Specify the maximum number of jobs that -.Nm -may have running at any one time. -Turns compatibility mode off, unless the -.Ar B -flag is also specified. -.It Fl k -Continue processing after errors are encountered, but only on those targets -that do not depend on the target whose creation caused the error. -.It Fl m Ar directory -Specify a directory in which to search for -.Pa sys.mk -and makefiles included via the <...> style. -Multiple directories can be added to form a search path. -This path will override the default system include path: -.Pa /usr/share/mk . -Furthermore, the system include path will be appended to the search path used -for "..."-style inclusions (see the -.Fl I -option). -.It Fl n -Display the commands that would have been executed, but do not actually -execute them. -.It Fl P -Collate the output of a given job and display it only when the job finishes, -instead of mixing the output of parallel jobs together. -This option has no effect unless -.Fl j -is used too. -.It Fl q -Do not execute any commands, but exit 0 if the specified targets are -up-to-date and 1, otherwise. -.It Fl r -Do not use the built-in rules specified in the system makefile. -.It Fl S -Stop processing when an error is encountered. -Default behaviour. -This is needed to negate the -.Fl k -option during recursive builds. -.It Fl s -Do not echo any commands as they are executed. -Equivalent to specifying -.Sq Ic @ -before each command line in the makefile. -.It Fl t -Rather than re-building a target as specified in the makefile, create it -or update its modification time to make it appear up-to-date. -.It Fl V Ar variable -Print -.Nm Ns 's -idea of the value of -.Ar variable , -in the global context. -Do not build any targets. -Multiple instances of this option may be specified; -the variables will be printed one per line, -with a blank line for each null or undefined variable. -.It Fl v -Be extra verbose. -For multi-job makes, this will cause file banners to be generated. -.It Fl X -When using the -.Fl V -option to print the values of variables, -do not recursively expand the values. -.It Ar variable Ns No = Ns Ar value -Set the value of the variable -.Ar variable -to -.Ar value . -.It Fl x Ar warning_options -Specify extended warning options. -This option may be specified several times. -A -.Ar warning_option -can be prefixed with -.Dq Li no -in which case the warning is switched off. -The currently available options are: -.Bl -tag -width indent -.It Li dirsyntax -Warn if anything except blanks and comments follows an -.Ic .endif -or -.Ic .else -directive. -.El -.Pp -See also the -.Ic .WARN -special target. -.El -.Pp -There are seven different types of lines in a makefile: file dependency -specifications, shell commands, variable assignments, include statements, -conditional directives, for loops, and comments. -.Pp -In general, lines may be continued from one line to the next by ending -them with a backslash -.Pq Ql \e . -The trailing newline character and initial whitespace on the following -line are compressed into a single space. -.Sh FILE DEPENDENCY SPECIFICATIONS -Dependency lines consist of one or more targets, an operator, and zero -or more sources. -This creates a relationship where the targets -.Dq depend -on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: -.Bl -tag -width flag -.It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. -.It Ic \&! -Targets are always re-created, but not until all sources have been -examined and re-created as necessary. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. -.It Ic \&:: -If no sources are specified, the target is always re-created. -Otherwise, a target is considered out-of-date if any of its sources has -been modified more recently than the target. -Sources for a target do not accumulate over dependency lines when this -operator is used. -The target will not be removed if -.Nm -is interrupted. -.El -.Pp -Targets and sources may contain the shell wildcard expressions -.Ql \&? , -.Ql * , -.Ql [] -and -.Ql {} . -The expressions -.Ql \&? , -.Ql * -and -.Ql [] -may only be used as part of the final -component of the target or source, and must be used to describe existing -files. -The expression -.Ql {} -need not necessarily be used to describe existing files. -Expansion is in directory order, not alphabetically as done in the shell. -.Sh SHELL COMMANDS -Each target may have associated with it a series of shell commands, normally -used to create the target. -Each of the commands in this script -.Em must -be preceded by a tab. -While any target may appear on a dependency line, only one of these -dependencies may be followed by a creation script, unless the -.Sq Ic :: -operator is used. -.Pp -If the first characters of the command line are -.Sq Ic @ , -.Sq Ic \- , -and/or -.Sq Ic + , -the command is treated specially. -A -.Sq Ic @ -causes the command not to be echoed before it is executed. -A -.Sq Ic \- -causes any non-zero exit status of the command line to be ignored. -A -.Sq Ic + -causes the command to be executed even if -.Fl n -is specified on the command line. -.Sh VARIABLE ASSIGNMENTS -Variables in -.Nm -are much like variables in the shell, and, by tradition, -consist of all upper-case letters. -The five operators that can be used to assign values to variables are as -follows: -.Bl -tag -width Ds -.It Ic \&= -Assign the value to the variable. -Any previous value is overridden. -.It Ic \&+= -Append the value to the current value of the variable. -.It Ic \&?= -Assign the value to the variable if it is not already defined. -.It Ic \&:= -Assign with expansion, i.e., expand the value before assigning it -to the variable. -Normally, expansion is not done until the variable is referenced. -.It Ic \&!= -Expand the value and pass it to the shell for execution and assign -the result to the variable. -Any newlines in the result are replaced with spaces. -.El -.Pp -Any whitespace before the assigned -.Ar value -is removed; if the value is being appended, a single space is inserted -between the previous contents of the variable and the appended value. -.Pp -Variables are expanded by surrounding the variable name with either -curly braces -.Pq Ql {} -or parentheses -.Pq Ql () -and preceding it with -a dollar sign -.Pq Ql \&$ . -If the variable name contains only a single letter, the surrounding -braces or parentheses are not required. -This shorter form is not recommended. -.Pp -Variable substitution occurs at two distinct times, depending on where -the variable is being used. -Variables in dependency lines are expanded as the line is read. -Variables in shell commands are expanded when the shell command is -executed. -.Pp -The four different classes of variables (in order of increasing precedence) -are: -.Bl -tag -width Ds -.It Environment variables -Variables defined as part of -.Nm Ns 's -environment. -.It Global variables -Variables defined in the makefile or in included makefiles. -.It Command line variables -Variables defined as part of the command line and variables -obtained from the -.Ev MAKEFLAGS -environment variable or the -.Ic .MAKEFLAGS -target. -.It Local variables -Variables that are defined specific to a certain target. -The seven local variables are as follows: -.Bl -tag -width ".ARCHIVE" -.It Va .ALLSRC -The list of all sources for this target; also known as -.Sq Va \&> . -.It Va .ARCHIVE -The name of the archive file; also known as -.Sq Va \&! . -.It Va .IMPSRC -The name/path of the source from which the target is to be transformed -(the -.Dq implied -source); also known as -.Sq Va \&< . -.It Va .MEMBER -The name of the archive member; also known as -.Sq Va \&% . -.It Va .OODATE -The list of sources for this target that were deemed out-of-date; also -known as -.Sq Va \&? . -.It Va .PREFIX -The file prefix of the file, containing only the file portion, no suffix -or preceding directory components; also known as -.Sq Va * . -.It Va .TARGET -The name of the target; also known as -.Sq Va @ . -.El -.Pp -The shorter forms -.Sq Va @ , -.Sq Va \&! , -.Sq Va \&< , -.Sq Va \&% , -.Sq Va \&? , -.Sq Va \&> , -and -.Sq Va * -are permitted for backward -compatibility and are not recommended. -The six variables -.Sq Va @F , -.Sq Va @D , -.Sq Va -.It Ic \&.include Ar \*qfile\*q -Include the specified makefile. -Variables between the angle brackets -or double quotes are expanded to form the file name. -If angle brackets -are used, the included makefile is expected to be in the system -makefile directory. -If double quotes are used, the including -makefile's directory and any directories specified using the -.Fl I -option are searched before the system -makefile directory. -.It Ic .undef Ar variable -Un-define the specified global variable. -Only global variables may be un-defined. -.It Ic \&.makeenv Ar variable -Set the environment flag for a preexisting global variable. The current -and future contents of the variable will be exported to the environment. -.It Ic .error Ar message -Terminate processing of the makefile immediately. -The filename of the -makefile, the line on which the error was encountered and the specified -message are printed to the standard error output and -.Nm -terminates with exit code 1. -Variables in the message are expanded. -.It Ic .warning Ar message -Emit a warning message. -The filename of the makefile, -the line on which the warning was encountered, -and the specified message are printed to the standard error output. -Variables in the message are expanded. -.El -.Pp -Conditionals are used to determine which parts of the Makefile -to process. -They are used similarly to the conditionals supported -by the C pre-processor. -The following conditionals are supported: -.Bl -tag -width Ds -.It Xo -.Ic .if -.Oo \&! Oc Ns Ar expression -.Op Ar operator expression ... -.Xc -Test the value of an expression. -.It Xo -.Ic .ifdef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -Test the value of a variable. -.It Xo -.Ic .ifndef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -Test the value of a variable. -.It Xo -.Ic .ifmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -Test the target being built. -.It Xo -.Ic .ifnmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -Test the target being built. -.It Ic .else -Reverse the sense of the last conditional. -.It Xo -.Ic .elif -.Oo \&! Oc Ns Ar expression -.Op Ar operator expression ... -.Xc -A combination of -.Ic .else -followed by -.Ic .if . -.It Xo -.Ic .elifdef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -A combination of -.Ic .else -followed by -.Ic .ifdef . -.It Xo -.Ic .elifndef -.Oo \&! Oc Ns Ar variable -.Op Ar operator variable ... -.Xc -A combination of -.Ic .else -followed by -.Ic .ifndef . -.It Xo -.Ic .elifmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -A combination of -.Ic .else -followed by -.Ic .ifmake . -.It Xo -.Ic .elifnmake -.Oo \&! Oc Ns Ar target -.Op Ar operator target ... -.Xc -A combination of -.Ic .else -followed by -.Ic .ifnmake . -.It Ic .endif -End the body of the conditional. -.El -.Pp -The -.Ar operator -may be any one of the following: -.Bl -tag -width "Cm XX" -.It Cm || -logical -.Tn OR -.It Cm && -Logical -.Tn AND ; -of higher precedence than -.Sq Ic || . -.El -.Pp -As in C, -.Nm -will only evaluate a conditional as far as is necessary to determine -its value. -Parentheses may be used to change the order of evaluation. -The boolean operator -.Sq Ic !\& -may be used to logically negate an entire -conditional. -It is of higher precedence than -.Sq Ic && . -.Pp -The value of -.Ar expression -may be any of the following: -.Bl -tag -width Ic -.It Ic defined -Takes a variable name as an argument and evaluates to true if the variable -has been defined. -.It Ic make -Takes a target name as an argument and evaluates to true if the target -was specified as part of -.Nm Ns 's -command line or was declared the default target (either implicitly or -explicitly, see -.Va .MAIN ) -before the line containing the conditional. -.It Ic empty -Takes a variable, with possible modifiers, and evaluates to true if -the expansion of the variable would result in an empty string. -.It Ic exists -Takes a file name as an argument and evaluates to true if the file exists. -The file is searched for on the system search path (see -.Va .PATH ) . -.It Ic target -Takes a target name as an argument and evaluates to true if the target -has been defined. -.El -.Pp -An -.Ar expression -may also be an arithmetic or string comparison, with the left-hand side -being a variable expansion. -Variable expansion is -performed on both sides of the comparison, after which the integral -values are compared. -A value is interpreted as hexadecimal if it is -preceded by 0x, otherwise it is decimal; octal numbers are not supported. -The standard C relational operators are all supported. -If after -variable expansion, either the left or right hand side of a -.Sq Ic == -or -.Sq Ic != -operator is not an integral value, then -string comparison is performed between the expanded -variables. -If no relational operator is given, it is assumed that the expanded -variable is being compared against 0. -.Pp -When -.Nm -is evaluating one of these conditional expressions, and it encounters -a word it does not recognize, either the -.Dq make -or -.Dq defined -expression is applied to it, depending on the form of the conditional. -If the form is -.Ic .if , -.Ic .ifdef -or -.Ic .ifndef , -the -.Dq defined -expression is applied. -Similarly, if the form is -.Ic .ifmake -or -.Ic .ifnmake , -the -.Dq make -expression is applied. -.Pp -If the conditional evaluates to true the parsing of the makefile continues -as before. -If it evaluates to false, the following lines are skipped. -In both cases this continues until a -.Ic .else -or -.Ic .endif -is found. -.Pp -For loops are typically used to apply a set of rules to a list of files. -The syntax of a for loop is: -.Pp -.Bl -tag -width indent -compact -.It Ic .for Ar variable Ic in Ar expression -.It -.It Ic .endfor -.El -.Pp -After the for -.Ar expression -is evaluated, it is split into words. -The -iteration -.Ar variable -is successively set to each word, and substituted in the -.Ic make-rules -inside the body of the for loop. -.Sh COMMENTS -Comments begin with a hash -.Pq Ql # -character, anywhere but in a shell -command line, and continue to the end of the line. -.Sh SPECIAL SOURCES -.Bl -tag -width Ic -.It Ic .IGNORE -Ignore any errors from the commands associated with this target, exactly -as if they all were preceded by a dash -.Pq Ql \- . -.It Ic .MAKE -Execute the commands associated with this target even if the -.Fl n -or -.Fl t -options were specified. -Normally used to mark recursive -.Nm Ns 's . -.It Ic .NOTMAIN -Normally -.Nm -selects the first target it encounters as the default target to be built -if no target was specified. -This source prevents this target from being selected. -.It Ic .OPTIONAL -If a target is marked with this attribute and -.Nm -cannot figure out how to create it, it will ignore this fact and assume -the file is not needed or already exists. -.It Ic .PRECIOUS -When -.Nm -is interrupted, it removes any partially made targets. -This source prevents the target from being removed. -.It Ic .SILENT -Do not echo any of the commands associated with this target, exactly -as if they all were preceded by an at sign -.Pq Ql @ . -.It Ic .USE -Turn the target into -.Nm Ns 's -version of a macro. -When the target is used as a source for another target, the other target -acquires the commands, sources, and attributes (except for -.Ic .USE ) -of the -source. -If the target already has commands, the -.Ic .USE -target's commands are appended -to them. -.It Ic .WAIT -If special -.Ic .WAIT -source is appears in a dependency line, the sources that precede it are -made before the sources that succeed it in the line. -Loops are not being -detected and targets that form loops will be silently ignored. -.El -.Sh "SPECIAL TARGETS" -Special targets may not be included with other targets, i.e., they must be -the only target specified. -.Bl -tag -width Ic -.It Ic .BEGIN -Any command lines attached to this target are executed before anything -else is done. -.It Ic .DEFAULT -This is sort of a -.Ic .USE -rule for any target (that was used only as a -source) that -.Nm -cannot figure out any other way to create. -Only the shell script is used. -The -.Ic .IMPSRC -variable of a target that inherits -.Ic .DEFAULT Ns 's -commands is set -to the target's own name. -.It Ic .END -Any command lines attached to this target are executed after everything -else is done. -.It Ic .IGNORE -Mark each of the sources with the -.Ic .IGNORE -attribute. -If no sources are specified, this is the equivalent of specifying the -.Fl i -option. -.It Ic .INCLUDES -A list of suffixes that indicate files that can be included in a source -file. -The suffix must have already been declared with -.Ic .SUFFIXES ; -any suffix so declared will have the directories on its search path (see -.Ic .PATH ) -placed in the -.Va .INCLUDES -special variable, each preceded by a -.Fl I -flag. -.It Ic .INTERRUPT -If -.Nm -is interrupted, the commands for this target will be executed. -.It Ic .LIBS -This does for libraries what -.Ic .INCLUDES -does for include files, except that the flag used is -.Fl L . -.It Ic .MAIN -If no target is specified when -.Nm -is invoked, this target will be built. -This is always set, either -explicitly, or implicitly when -.Nm -selects the default target, to give the user a way to refer to the default -target on the command line. -.It Ic .MAKEFLAGS -This target provides a way to specify flags for -.Nm -when the makefile is used. -The flags are as if typed to the shell, though the -.Fl f -option will have -no effect. -Flags (except for -.Fl f ) -and variable assignments specified as the source -for this target are also appended to the -.Va .MAKEFLAGS -internal variable. -Please note the difference between this target and the -.Va .MAKEFLAGS -internal variable: specifying an option or variable -assignment as the source for this target will affect -.Em both -the current makefile and all processes that -.Nm -executes. -.It Ic .MFLAGS -Same as above, for backward compatibility. -.\" XXX: NOT YET!!!! -.\" .It Ic .NOTPARALLEL -.\" The named targets are executed in non parallel mode. If no targets are -.\" specified, then all targets are executed in non parallel mode. -.It Ic .NOTPARALLEL -Disable parallel mode. -.It Ic .NO_PARALLEL -Same as above, for compatibility with other -.Nm pmake -variants. -.It Ic .ORDER -The named targets are made in sequence. -.\" XXX: NOT YET!!!! -.\" .It Ic .PARALLEL -.\" The named targets are executed in parallel mode. If no targets are -.\" specified, then all targets are executed in parallel mode. -.It Ic .PATH -The sources are directories which are to be searched for files not -found in the current directory. -If no sources are specified, any previously specified directories are -deleted. -Where possible, use of -.Ic .PATH -is preferred over use of the -.Va VPATH -variable. -.It Ic .PATH\fIsuffix\fR -The sources are directories which are to be searched for suffixed files -not found in the current directory. -The -.Nm -utility -first searches the suffixed search path, before reverting to the default -path if the file is not found there. -This form is required for -.Ic .LIBS -and -.Ic .INCLUDES -to work. -.It Ic .PHONY -Apply the -.Ic .PHONY -attribute to any specified sources. -Targets with this attribute are always -considered to be out of date. -.It Ic .PRECIOUS -Apply the -.Ic .PRECIOUS -attribute to any specified sources. -If no sources are specified, the -.Ic .PRECIOUS -attribute is applied to every -target in the file. -.It Ic .SHELL -Select another shell. -The sources of this target have the format -.Ar key Ns = Ns Ar value . -The -.Ar key -is one of: -.Bl -tag -width ".Va hasErrCtl" -.It Va path -Specify the path to the new shell. -.It Va name -Specify the name of the new shell. -This may be either one of the three builtin shells (see below) or any -other name. -.It Va quiet -Specify the shell command to turn echoing off. -.It Va echo -Specify the shell command to turn echoing on. -.It Va filter -Usually shells print the echo off command before turning echoing off. -This is the exact string that will be printed by the shell and is used -to filter the shell output to remove the echo off command. -.It Va echoFlag -The shell option that turns echoing on. -.It Va errFlag -The shell option to turn on error checking. -If error checking is on, the shell should exit if a command returns -a non-zero status. -.It Va hasErrCtl -True if the shell has error control. -.It Va check -If -.Va hasErrCtl -is true then this is the shell command to turn error checking on. -If -.Va hasErrCtl -is false then this is a command template to echo commands for which error -checking is disabled. -The template must contain a -.Ql %s . -.It Va ignore -If -.Va hasErrCtl -is true, this is the shell command to turn error checking off. -If -.Va hasErrCtl -is false, this is a command template to execute a command so that errors -are ignored. -The template must contain a -.Ql %s . -.It Va meta -This is a string of meta characters of the shell. -.It Va builtins -This is a string holding all the shell's builtin commands separated by blanks. -The -.Va meta -and -.Va builtins -strings are used in compat mode. -When a command line contains neither a meta -character nor starts with a shell builtin, it is executed directly without -invoking a shell. -When one of these strings (or both) is empty all commands are executed -through a shell. -.It Va unsetenv -If true, remove the -.Ev ENV -environment variable before executing any command. -This is useful for the Korn-shell -.Pq Nm ksh . -.El -.Pp -Values that are strings must be surrounded by double quotes. -Boolean values are specified as -.Ql T -or -.Ql Y -(in either case) to mean true. -Any other value is taken to mean false. -.Pp -There are several uses of the -.Ic .SHELL -target: -.Bl -bullet -.It -Selecting one of the builtin shells. -This is done by just specifying the name of the shell with the -.Va name -keyword. -It is also possible to modify the parameters of the builtin shell by just -specifying other keywords (except for -.Va path ) . -.It -Using another executable for one of the builtin shells. -This is done by specifying the path to the executable with the -.Va path -keyword. -If the last component is the same as the name of the builtin shell, no -name needs to be specified; if it is different, the name must be given: -.Bd -literal -offset indent -\&.SHELL: path="/usr/local/bin/sh" -.Ed -.Pp -selects the builtin shell -.Dq Li sh -but will execute it from -.Pa /usr/local/bin/sh . -Like in the previous case, it is possible to modify parameters of the builtin -shell by just specifying them. -.It -Using an entirely different shell. -This is done by specifying all keywords. -.El -.Pp -The builtin shells are -.Dq Li sh , -.Dq Li csh -and -.Dq Li ksh . -Because -.Dx -has no -.Nm ksh -in -.Pa /bin , -it is unwise to specify -.Va name Ns = Ns Qq Li ksh -without also specifying a path. -.It Ic .SILENT -Apply the -.Ic .SILENT -attribute to any specified sources. -If no sources are specified, the -.Ic .SILENT -attribute is applied to every -command in the file. -.It Ic .SUFFIXES -Each source specifies a suffix to -.Nm . -If no sources are specified, any previous specified suffices are deleted. -.It Ic .WARN -Each source specifies a warning flag as previously described for the -.Fl x -command line option. -Warning flags specified on the command line take precedence over flags -specified in the makefile. -Also, command line warning flags are pushed to sub-makes through the -.Ev MAKEFLAGS -environment variables so that a warning flag specified on the command -line will influence all sub-makes. -Several flags can be specified on a single -.Ic .WARN -target by separating them with blanks. -.El -.Sh ENVIRONMENT -The -.Nm -utility uses the following environment variables, if they exist: -.Ev MACHINE , -.Ev MAKE , -.Ev MAKEFLAGS , -.Ev MAKEOBJDIR , -and -.Ev MAKEOBJDIRPREFIX . -.Sh FILES -.Bl -tag -width ".Pa /usr/share/mk" -compact -.It Pa .depend -list of dependencies -.It Pa Makefile -list of dependencies -.It Pa makefile -list of dependencies -.It obj -object directory -.It Pa sys.mk -system makefile (processed before any other file, including -.Pa makefile -and -.Pa Makefile ) -.It Pa /usr/share/mk -system makefile directory -.\" .It /usr/share/doc/psd/12.make -.\" PMake tutorial -.It Pa /usr/obj -default -.Ev MAKEOBJDIRPREFIX -directory. -.El -.Sh EXAMPLES -List all included makefiles in order visited: -.Pp -.Dl "make -V .MAKEFILE_LIST | tr \e\ \e\en" -.Sh COMPATIBILITY -Older versions of -.Nm -used -.Ev MAKE -instead of -.Ev MAKEFLAGS . -This was removed for POSIX compatibility. -The internal variable -.Va MAKE -is set to the same value as -.Va .MAKE ; -support for this may be removed in the future. -.Pp -Most of the more esoteric features of -.Nm -should probably be avoided for greater compatibility. -.Sh SEE ALSO -.Xr mkdep 1 , -.Xr make.conf 5 -.Rs -.%A "Adam de Boor" -.%B "4.4BSD Programmer's Supplementary Documents (PSD)" -.%T "PMake - A Tutorial" -.Re -.\" in -.\" .Pa /usr/share/doc/psd/12.make -.Sh HISTORY -A -.Nm -command appeared in PWB -.Ux . -.Sh BUGS -The determination of -.Va .OBJDIR -is contorted to the point of absurdity. -.Pp -In the presence of several -.Ic .MAIN -special targets, -.Nm -silently ignores all but the first. -.Pp -.Va .TARGETS -is not set to the default target when -.Nm -is invoked without a target name and no -.Ic .MAIN -special target exists. -.Pp -The evaluation of -.Ar expression -in a test is very simple-minded. -Currently, the only form that works is -.Ql .if ${VAR} op something -For instance, you should write tests as -.Ql .if ${VAR} == "string" -not the other way around, which would give you an error. -.Pp -For loops are expanded before tests, so a fragment such as: -.Bd -literal -offset indent -\&.for ARCH in ${SHARED_ARCHS} -\&.if ${ARCH} == ${MACHINE} - ... -\&.endif -\&.endfor -.Ed -.Pp -will not work, and should be rewritten as: -.Bd -literal -offset indent -\&.for ARCH in ${SHARED_ARCHS} -\&.if ${MACHINE} == ${ARCH} - ... -\&.endif -\&.endfor -.Ed -.Pp -The parsing code is broken with respect to handling a semicolon -after a colon, so a fragment like this will fail: -.Bd -literal -offset indent -HDRS= foo.h bar.h - -all: -\&.for h in ${HDRS:S;^;${.CURDIR}/;} - ... -\&.endfor -.Ed -.Pp -A trailing backslash in a variable value defined on the command line causes -the delimiting space in the -.Ev MAKEFLAGS -environment variable to be preceded by that backslash. -That causes a submake to not treat that space as a word delimiter. -Fixing this requires a larger rewrite of the code handling command line -macros and assignments to -.Va .MAKEFLAGS . diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c deleted file mode 100644 index 23866ecc5c..0000000000 --- a/usr.bin/make/make.c +++ /dev/null @@ -1,819 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)make.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.bin/make/make.c,v 1.33 2005/02/04 12:38:57 harti Exp $ - * $DragonFly: src/usr.bin/make/make.c,v 1.33 2005/09/24 07:27:26 okumoto Exp $ - */ - -/* - * make.c - * The functions which perform the examination of targets and - * their suitability for creation - * - * Interface: - * Make_Run Initialize things for the module and recreate - * whatever needs recreating. Returns true if - * work was (or would have been) done and false - * otherwise. - * - * Make_Update Update all parents of a given child. Performs - * various bookkeeping chores like the updating - * of the cmtime field of the parent, filling - * of the IMPSRC context variable, etc. It will - * place the parent on the toBeMade queue if it should be. - * - * Make_TimeStamp Function to set the parent's cmtime field - * based on a child's modification time. - * - * Make_DoAllVar Set up the various local variables for a - * target, including the .ALLSRC variable, making - * sure that any variable that needs to exist - * at the very least has the empty value. - * - * Make_OODate Determine if a target is out-of-date. - * - * Make_HandleUse See if a child is a .USE node for a parent - * and perform the .USE actions if so. - */ - -#include - -#include "arch.h" -#include "config.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "job.h" -#include "make.h" -#include "parse.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/* The current fringe of the graph. These are nodes which await examination - * by MakeOODate. It is added to by Make_Update and subtracted from by - * MakeStartJobs */ -static Lst toBeMade = Lst_Initializer(toBeMade); - -/* - * Number of nodes to be processed. If this is non-zero when Job_Empty() - * returns true, there's a cycle in the graph. - */ -static int numNodes; - -/** - * Make_TimeStamp - * Set the cmtime field of a parent node based on the mtime stamp in its - * child. Called from MakeOODate via LST_FOREACH. - * - * Results: - * Always returns 0. - * - * Side Effects: - * The cmtime of the parent node will be changed if the mtime - * field of the child is greater than it. - */ -int -Make_TimeStamp(GNode *pgn, GNode *cgn) -{ - - if (cgn->mtime > pgn->cmtime) { - pgn->cmtime = cgn->mtime; - } - return (0); -} - -/** - * Make_OODate - * See if a given node is out of date with respect to its sources. - * Used by Make_Run when deciding which nodes to place on the - * toBeMade queue initially and by Make_Update to screen out USE and - * EXEC nodes. In the latter case, however, any other sort of node - * must be considered out-of-date since at least one of its children - * will have been recreated. - * - * Results: - * true if the node is out of date. false otherwise. - * - * Side Effects: - * The mtime field of the node and the cmtime field of its parents - * will/may be changed. - */ -bool -Make_OODate(GNode *gn) -{ - bool oodate; - LstNode *ln; - - /* - * Certain types of targets needn't even be sought as their datedness - * doesn't depend on their modification time... - */ - if ((gn->type & (OP_JOIN | OP_USE | OP_EXEC)) == 0) { - Dir_MTime(gn); - if (gn->mtime != 0) { - DEBUGF(MAKE, ("modified %s...", - Targ_FmtTime(gn->mtime))); - } else { - DEBUGF(MAKE, ("non-existent...")); - } - } - - /* - * A target is remade in one of the following circumstances: - * its modification time is smaller than that of its youngest child - * and it would actually be run (has commands or type OP_NOP) - * it's the object of a force operator - * it has no children, was on the lhs of an operator and doesn't - * exist already. - * - * Libraries are only considered out-of-date if the archive module says - * they are. - * - * These weird rules are brought to you by Backward-Compatibility and - * the strange people who wrote 'Make'. - */ - if (gn->type & OP_USE) { - /* - * If the node is a USE node it is *never* out of date - * no matter *what*. - */ - DEBUGF(MAKE, (".USE node...")); - oodate = false; - - } else if (gn->type & OP_LIB) { - DEBUGF(MAKE, ("library...")); - - /* - * always out of date if no children and :: target - */ - oodate = Arch_LibOODate(gn) || - ((gn->cmtime == 0) && (gn->type & OP_DOUBLEDEP)); - - } else if (gn->type & OP_JOIN) { - /* - * A target with the .JOIN attribute is only considered - * out-of-date if any of its children was out-of-date. - */ - DEBUGF(MAKE, (".JOIN node...")); - oodate = gn->childMade; - - } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { - /* - * A node which is the object of the force (!) operator or - * which has the .EXEC attribute is always considered - * out-of-date. - */ - if (gn->type & OP_FORCE) { - DEBUGF(MAKE, ("! operator...")); - } else if (gn->type & OP_PHONY) { - DEBUGF(MAKE, (".PHONY node...")); - } else { - DEBUGF(MAKE, (".EXEC node...")); - } - oodate = true; - - } else if ((gn->mtime < gn->cmtime) || - ((gn->cmtime == 0) && - ((gn->mtime==0) || (gn->type & OP_DOUBLEDEP)))) { - /* - * A node whose modification time is less than that of its - * youngest child or that has no children (cmtime == 0) and - * either doesn't exist (mtime == 0) or was the object of a - * :: operator is out-of-date. Why? Because that's the way - * Make does it. - */ - if (gn->mtime < gn->cmtime) { - DEBUGF(MAKE, ("modified before source...")); - } else if (gn->mtime == 0) { - DEBUGF(MAKE, ("non-existent and no sources...")); - } else { - DEBUGF(MAKE, (":: operator and no sources...")); - } - oodate = true; - } else - oodate = false; - - /* - * If the target isn't out-of-date, the parents need to know its - * modification time. Note that targets that appear to be out-of-date - * but aren't, because they have no commands and aren't of type OP_NOP, - * have their mtime stay below their children's mtime to keep parents - * from thinking they're out-of-date. - */ - if (!oodate) { - LST_FOREACH(ln, &gn->parents) - if (Make_TimeStamp(Lst_Datum(ln), gn)) - break; - } - - return (oodate); -} - -/** - * Make_HandleUse - * Function called by Make_Run and SuffApplyTransform on the downward - * pass to handle .USE and transformation nodes. A callback function - * for LST_FOREACH, it implements the .USE and transformation - * functionality by copying the node's commands, type flags - * and children to the parent node. Should be called before the - * children are enqueued to be looked at. - * - * A .USE node is much like an explicit transformation rule, except - * its commands are always added to the target node, even if the - * target already has commands. - * - * Results: - * returns 0. - * - * Side Effects: - * Children and commands may be added to the parent and the parent's - * type may be changed. - */ -int -Make_HandleUse(GNode *cgn, GNode *pgn) -{ - GNode *gn; /* A child of the .USE node */ - LstNode *ln; /* An element in the children list */ - - if (cgn->type & (OP_USE | OP_TRANSFORM)) { - if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) { - /* - * .USE or transformation and target has no commands -- - * append the child's commands to the parent. - */ - Lst_Concat(&pgn->commands, &cgn->commands, LST_CONCNEW); - } - - for (ln = Lst_First(&cgn->children); ln != NULL; - ln = Lst_Succ(ln)) { - gn = Lst_Datum(ln); - - if (Lst_Member(&pgn->children, gn) == NULL) { - Lst_AtEnd(&pgn->children, gn); - Lst_AtEnd(&gn->parents, pgn); - pgn->unmade += 1; - } - } - - pgn->type |= cgn->type & ~(OP_OPMASK | OP_USE | OP_TRANSFORM); - - /* - * This child node is now "made", so we decrement the count of - * unmade children in the parent... We also remove the child - * from the parent's list to accurately reflect the number of - * decent children the parent has. This is used by Make_Run to - * decide whether to queue the parent or examine its children... - */ - if (cgn->type & OP_USE) { - pgn->unmade--; - } - } - return (0); -} - -/** - * Make_Update - * Perform update on the parents of a node. Used by JobFinish once - * a node has been dealt with and by MakeStartJobs if it finds an - * up-to-date node. - * - * Results: - * Always returns 0 - * - * Side Effects: - * The unmade field of pgn is decremented and pgn may be placed on - * the toBeMade queue if this field becomes 0. - * - * If the child was made, the parent's childMade field will be set true - * and its cmtime set to now. - * - * If the child wasn't made, the cmtime field of the parent will be - * altered if the child's mtime is big enough. - * - * Finally, if the child is the implied source for the parent, the - * parent's IMPSRC variable is set appropriately. - */ -void -Make_Update(GNode *cgn) -{ - GNode *pgn; /* the parent node */ - const char *cname; /* the child's name */ - LstNode *ln; /* Element in parents and iParents lists */ - const char *cpref; - - cname = Var_Value(TARGET, cgn); - - /* - * If the child was actually made, see what its modification time is - * now -- some rules won't actually update the file. If the file still - * doesn't exist, make its mtime now. - */ - if (cgn->made != UPTODATE) { -#ifndef RECHECK - /* - * We can't re-stat the thing, but we can at least take care - * of rules where a target depends on a source that actually - * creates the target, but only if it has changed, e.g. - * - * parse.h : parse.o - * - * parse.o : parse.y - * yacc -d parse.y - * cc -c y.tab.c - * mv y.tab.o parse.o - * cmp -s y.tab.h parse.h || mv y.tab.h parse.h - * - * In this case, if the definitions produced by yacc haven't - * changed from before, parse.h won't have been updated and - * cgn->mtime will reflect the current modification time for - * parse.h. This is something of a kludge, I admit, but it's a - * useful one.. - * XXX: People like to use a rule like - * - * FRC: - * - * To force things that depend on FRC to be made, so we have to - * check for gn->children being empty as well... - */ - if (!Lst_IsEmpty(&cgn->commands) || - Lst_IsEmpty(&cgn->children)) { - cgn->mtime = now; - } - #else - /* - * This is what Make does and it's actually a good thing, as it - * allows rules like - * - * cmp -s y.tab.h parse.h || cp y.tab.h parse.h - * - * to function as intended. Unfortunately, thanks to the - * stateless nature of NFS (by which I mean the loose coupling - * of two clients using the same file from a common server), - * there are times when the modification time of a file created - * on a remote machine will not be modified before the local - * stat() implied by the Dir_MTime occurs, thus leading us to - * believe that the file is unchanged, wreaking havoc with - * files that depend on this one. - * - * I have decided it is better to make too much than to make too - * little, so this stuff is commented out unless you're sure - * it's ok. - * -- ardeb 1/12/88 - */ - /* - * Christos, 4/9/92: If we are saving commands pretend that - * the target is made now. Otherwise archives with ... rules - * don't work! - */ - if (noExecute || (cgn->type & OP_SAVE_CMDS) || - Dir_MTime(cgn) == 0) { - cgn->mtime = now; - } - DEBUGF(MAKE, ("update time: %s\n", Targ_FmtTime(cgn->mtime))); -#endif - } - - for (ln = Lst_First(&cgn->parents); ln != NULL; ln = Lst_Succ(ln)) { - pgn = Lst_Datum(ln); - if (pgn->make) { - pgn->unmade -= 1; - - if (!(cgn->type & (OP_EXEC | OP_USE))) { - if (cgn->made == MADE) { - pgn->childMade = true; - if (pgn->cmtime < cgn->mtime) { - pgn->cmtime = cgn->mtime; - } - } else { - Make_TimeStamp(pgn, cgn); - } - } - if (pgn->unmade == 0) { - /* - * Queue the node up -- any unmade predecessors - * will be dealt with in MakeStartJobs. - */ - Lst_EnQueue(&toBeMade, pgn); - } else if (pgn->unmade < 0) { - Error("Graph cycles through %s", pgn->name); - } - } - } - - /* - * Deal with successor nodes. If any is marked for making and has an - * unmade count of 0, has not been made and isn't in the examination - * queue, it means we need to place it in the queue as it restrained - * itself before. - */ - for (ln = Lst_First(&cgn->successors); ln != NULL; ln = Lst_Succ(ln)) { - GNode *succ = Lst_Datum(ln); - - if (succ->make && succ->unmade == 0 && succ->made == UNMADE && - Lst_Member(&toBeMade, succ) == NULL) { - Lst_EnQueue(&toBeMade, succ); - } - } - - /* - * Set the .PREFIX and .IMPSRC variables for all the implied parents - * of this node. - */ - cpref = Var_Value(PREFIX, cgn); - for (ln = Lst_First(&cgn->iParents); ln != NULL; ln = Lst_Succ(ln)) { - pgn = Lst_Datum(ln); - if (pgn->make) { - Var_Set(IMPSRC, cname, pgn); - Var_Set(PREFIX, cpref, pgn); - } - } -} - -/** - * Make_DoAllVar - * Set up the ALLSRC and OODATE variables. Sad to say, it must be - * done separately, rather than while traversing the graph. This is - * because Make defined OODATE to contain all sources whose modification - * times were later than that of the target, *not* those sources that - * were out-of-date. Since in both compatibility and native modes, - * the modification time of the parent isn't found until the child - * has been dealt with, we have to wait until now to fill in the - * variable. As for ALLSRC, the ordering is important and not - * guaranteed when in native mode, so it must be set here, too. - * - * Side Effects: - * The ALLSRC and OODATE variables of the given node is filled in. - * If the node is a .JOIN node, its TARGET variable will be set to - * match its ALLSRC variable. - */ -void -Make_DoAllVar(GNode *gn) -{ - LstNode *ln; - - LST_FOREACH(ln, &gn->children) { - /* - * Add the child's name to the ALLSRC and OODATE variables of - * the given node. The child is added only if it has not been - * given the .EXEC, .USE or .INVISIBLE attributes. .EXEC and - * .USE children are very rarely going to be files, so... - * - * A child is added to the OODATE variable if its modification - * time is later than that of its parent, as defined by Make, - * except if the parent is a .JOIN node. In that case, it is - * only added to the OODATE variable if it was actually made - * (since .JOIN nodes don't have modification times, the - * comparison is rather unfair...). - */ - GNode *cgn = Lst_Datum(ln); - - if ((cgn->type & (OP_EXEC | OP_USE | OP_INVISIBLE)) == 0) { - const char *child; - - if (OP_NOP(cgn->type)) { - /* - * this node is only source; use the specific - * pathname for it - */ - child = cgn->path ? cgn->path : cgn->name; - } else { - child = Var_Value(TARGET, cgn); - } - - Var_Append(ALLSRC, child, gn); - if (gn->type & OP_JOIN) { - if (cgn->made == MADE) { - Var_Append(OODATE, child, gn); - } - } else if (gn->mtime < cgn->mtime || - (cgn->mtime >= now && cgn->made == MADE)) { - /* - * It goes in the OODATE variable if the parent - * is younger than the child or if the child has - * been modified more recently than the start of - * the make. This is to keep pmake from getting - * confused if something else updates the parent - * after the make starts (shouldn't happen, I - * know, but sometimes it does). In such a case, - * if we've updated the kid, the parent is - * likely to have a modification time later than - * that of the kid and anything that relies on - * the OODATE variable will be hosed. - * - * XXX: This will cause all made children to - * go in the OODATE variable, even if they're - * not touched, if RECHECK isn't defined, since - * cgn->mtime is set to now in Make_Update. - * According to some people, this is good... - */ - Var_Append(OODATE, child, gn); - } - } - } - - if (Var_Value(OODATE, gn) == NULL) { - Var_Set(OODATE, "", gn); - } - if (Var_Value(ALLSRC, gn) == NULL) { - Var_Set(ALLSRC, "", gn); - } - - if (gn->type & OP_JOIN) { - Var_Set(TARGET, Var_Value(ALLSRC, gn), gn); - } -} - -/** - * MakeStartJobs - * Start as many jobs as possible. - * - * Results: - * If the query flag was given to pmake, no job will be started, - * but as soon as an out-of-date target is found, this function - * returns 1. At all other times, this function returns 0. - * - * Side Effects: - * Nodes are removed from the toBeMade queue and job table slots - * are filled. - */ -static int -MakeStartJobs(bool queryFlag) -{ - GNode *gn; - - while (!Lst_IsEmpty(&toBeMade) && !Job_Full()) { - gn = Lst_DeQueue(&toBeMade); - DEBUGF(MAKE, ("Examining %s...", gn->name)); - - /* - * Make sure any and all predecessors that are going to be made, - * have been. - */ - if (!Lst_IsEmpty(&gn->preds)) { - LstNode *ln; - - for (ln = Lst_First(&gn->preds); ln != NULL; - ln = Lst_Succ(ln)){ - GNode *pgn = Lst_Datum(ln); - - if (pgn->make && pgn->made == UNMADE) { - DEBUGF(MAKE, ("predecessor %s not made " - "yet.\n", pgn->name)); - break; - } - } - /* - * If ln isn't NULL, there's a predecessor as yet - * unmade, so we just drop this node on the floor. - * When the node in question has been made, it will - * notice this node as being ready to make but as yet - * unmade and will place the node on the queue. - */ - if (ln != NULL) { - continue; - } - } - - numNodes--; - if (Make_OODate(gn)) { - DEBUGF(MAKE, ("out-of-date\n")); - if (queryFlag) { - return (1); /* target was not up-to-date */ - } - Make_DoAllVar(gn); - Job_Make(gn); - } else { - DEBUGF(MAKE, ("up-to-date\n")); - gn->made = UPTODATE; - if (gn->type & OP_JOIN) { - /* - * Even for an up-to-date .JOIN node, we need - * it to have its context variables so - * references to it get the correct value for - * .TARGET when building up the context - * variables of its parent(s)... - */ - Make_DoAllVar(gn); - } - - Make_Update(gn); - } - } - return (0); /* Successful completion */ -} - -/** - * MakePrintStatus - * Print the status of a top-level node, viz. it being up-to-date - * already or not created due to an error in a lower level. - * Callback function for Make_Run via LST_FOREACH. If gn->unmade is - * nonzero and that is meant to imply a cycle in the graph, then - * cycle is true. - * - * Side Effects: - * A message may be printed. - */ -static void -MakePrintStatus(GNode *gn, bool cycle) -{ - LstNode *ln; - - if (gn->made == UPTODATE) { - printf("`%s' is up to date.\n", gn->name); - - } else if (gn->unmade != 0) { - if (cycle) { - /* - * If printing cycles and came to one that has unmade - * children, print out the cycle by recursing on its - * children. Note a cycle like: - * a : b - * b : c - * c : b - * will cause this to erroneously complain about a - * being in the cycle, but this is a good approximation. - */ - if (gn->made == CYCLE) { - Error("Graph cycles through `%s'", gn->name); - gn->made = ENDCYCLE; - LST_FOREACH(ln, &gn->children) - MakePrintStatus(Lst_Datum(ln), true); - gn->made = UNMADE; - } else if (gn->made != ENDCYCLE) { - gn->made = CYCLE; - LST_FOREACH(ln, &gn->children) - MakePrintStatus(Lst_Datum(ln), true); - } - } else { - printf("`%s' not remade because of errors.\n", - gn->name); - } - } -} - -/** - * Make_Run - * Initialize the nodes to remake and the list of nodes which are - * ready to be made by doing a breadth-first traversal of the graph - * starting from the nodes in the given list. Once this traversal - * is finished, all the 'leaves' of the graph are in the toBeMade - * queue. - * Using this queue and the Job module, work back up the graph, - * calling on MakeStartJobs to keep the job table as full as - * possible. - * - * Results: - * Exit status of make. - * - * Side Effects: - * The make field of all nodes involved in the creation of the given - * targets is set to 1. The toBeMade list is set to contain all the - * 'leaves' of these subgraphs. - */ -bool -Make_Run(Lst *targs, bool queryFlag) -{ - GNode *gn; /* a temporary pointer */ - GNode *cgn; - Lst examine; /* List of targets to examine */ - int errors; /* Number of errors the Job module reports */ - LstNode *ln; - - /* - * Initialize job module before traversing - * the graph, now that any .BEGIN and .END - * targets have been read. This is done - * only if the -q flag wasn't given (to - * prevent the .BEGIN from being executed - * should it exist). - */ - if (!queryFlag) { - Job_Init(jobLimit); - jobsRunning = true; - } - - Lst_Init(&examine); - Lst_Duplicate(&examine, targs, NOCOPY); - numNodes = 0; - - /* - * Make an initial downward pass over the graph, marking nodes to be - * made as we go down. We call Suff_FindDeps to find where a node is and - * to get some children for it if it has none and also has no commands. - * If the node is a leaf, we stick it on the toBeMade queue to - * be looked at in a minute, otherwise we add its children to our queue - * and go on about our business. - */ - while (!Lst_IsEmpty(&examine)) { - gn = Lst_DeQueue(&examine); - - if (!gn->make) { - gn->make = true; - numNodes++; - - /* - * Apply any .USE rules before looking for implicit - * dependencies to make sure everything has commands - * that should... - */ - LST_FOREACH(ln, &gn->children) - if (Make_HandleUse(Lst_Datum(ln), gn)) - break; - - Suff_FindDeps(gn); - - if (gn->unmade != 0) { - LST_FOREACH(ln, &gn->children) { - cgn = Lst_Datum(ln); - if (!cgn->make && !(cgn->type & OP_USE)) - Lst_EnQueue(&examine, cgn); - } - } else { - Lst_EnQueue(&toBeMade, gn); - } - } - } - - if (queryFlag) { - /* - * We wouldn't do any work unless we could start some jobs in - * the next loop... (we won't actually start any, of course, - * this is just to see if any of the targets was out of date) - */ - return MakeStartJobs(true); - } - - /* - * Initialization. At the moment, no jobs are running and - * until some get started, nothing will happen since the - * remaining upward traversal of the graph is performed by the - * routines in job.c upon the finishing of a job. So we fill - * the Job table as much as we can before going into our loop. - */ - MakeStartJobs(false); - - /* - * Main Loop: The idea here is that the ending of jobs will take - * care of the maintenance of data structures and the waiting for output - * will cause us to be idle most of the time while our children run as - * much as possible. Because the job table is kept as full as possible, - * the only time when it will be empty is when all the jobs which need - * running have been run, so that is the end condition of this loop. - * Note that the Job module will exit if there were any errors unless - * the keepgoing flag was given. - */ - while (!Job_Empty()) { - Job_CatchOutput(!Lst_IsEmpty(&toBeMade)); - Job_CatchChildren(!usePipes); - MakeStartJobs(false); - } - - errors = Job_Finish(); - - /* - * Print the final status of each target. E.g. if it wasn't made - * because some inferior reported an error. - */ - errors = ((errors == 0) && (numNodes != 0)); - LST_FOREACH(ln, targs) - MakePrintStatus(Lst_Datum(ln), errors); - - return 0; /* Successful completion */ -} diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h deleted file mode 100644 index a95899d487..0000000000 --- a/usr.bin/make/make.h +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * from: @(#)make.h 8.3 (Berkeley) 6/13/95 - * $FreeBSD: src/usr.bin/make/make.h,v 1.29 2005/02/01 10:50:36 harti Exp $ - * $DragonFly: src/usr.bin/make/make.h,v 1.40 2005/08/23 21:03:02 okumoto Exp $ - */ - -#ifndef make_h_a91074b9 -#define make_h_a91074b9 - -/** - * make.h - * The global definitions for make - */ - -#include - -/* buildworld needs this on FreeBSD */ -#ifndef __arysize -#define __arysize(ary) (sizeof(ary)/sizeof((ary)[0])) -#endif - -struct Buffer; -struct CLI; -struct GNode; -struct Lst; -struct Shell; - -/* - * Warning flags - */ -enum { - WARN_DIRSYNTAX = 0x0001 /* syntax errors in directives */ -}; - -int Make_TimeStamp(struct GNode *, struct GNode *); -bool Make_OODate(struct GNode *); -int Make_HandleUse(struct GNode *, struct GNode *); -void Make_Update(struct GNode *); -void Make_DoAllVar(struct GNode *); -bool Make_Run(struct Lst *, bool); -void Main_ParseArgLine(struct CLI *, const char [], int); -int Main_ParseWarn(const char *, int); - -#endif /* make_h_a91074b9 */ diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c deleted file mode 100644 index aa5bafb495..0000000000 --- a/usr.bin/make/parse.c +++ /dev/null @@ -1,2513 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)parse.c 8.3 (Berkeley) 3/19/94 - * $FreeBSD: src/usr.bin/make/parse.c,v 1.75 2005/02/07 11:27:47 harti Exp $ - * $DragonFly: src/usr.bin/make/parse.c,v 1.99 2005/11/13 10:55:36 corecode Exp $ - */ - -/*- - * parse.c -- - * Functions to parse a makefile. - * - * Most important structures are kept in Lsts. Directories for - * the #include "..." function are kept in the 'parseIncPath' Lst, while - * those for the #include <...> are kept in the 'sysIncPath' Lst. The - * targets currently being defined are kept in the 'targets' Lst. - * - * Interface: - * - * Parse_File Function used to parse a makefile. It must - * be given the name of the file, which should - * already have been opened, and a function - * to call to read a character from the file. - * - * Parse_IsVar Returns true if the given line is a - * variable assignment. Used by MainParseArgs - * to determine if an argument is a target - * or a variable assignment. Used internally - * for pretty much the same thing... - * - * Parse_Error Function called when an error occurs in - * parsing. Used by the variable and - * conditional modules. - * - * Parse_MainName Returns a Lst of the main target to create. - */ - -#include -#include -#include -#include -#include -#include - -#include "arch.h" -#include "buf.h" -#include "cond.h" -#include "config.h" -#include "dir.h" -#include "for.h" -#include "globals.h" -#include "GNode.h" -#include "hash_tables.h" -#include "job.h" -#include "make.h" -#include "parse.h" -#include "pathnames.h" -#include "shell.h" -#include "str.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/* - * These values are returned by ParsePopInput to tell Parse_File whether to - * CONTINUE parsing, i.e. it had only reached the end of an include file, - * or if it's DONE. - */ -#define CONTINUE 1 -#define DONE 0 - -/* targets we're working on */ -static Lst targets = Lst_Initializer(targets); - -/* true if currently in a dependency line or its commands */ -static bool inLine; - -static int fatals = 0; - -/* - * The main target to create. This is the first target on the - * first dependency line in the first makefile. - */ -static GNode *mainNode; - -/* - * Definitions for handling #include specifications - */ -struct IFile { - char *fname; /* name of previous file */ - int lineno; /* saved line number */ - FILE *F; /* the open stream */ - char *str; /* the string when parsing a string */ - char *ptr; /* the current pointer when parsing a string */ - TAILQ_ENTRY(IFile) link;/* stack the files */ -}; - -/* stack of IFiles generated by * #includes */ -static TAILQ_HEAD(, IFile) includes = TAILQ_HEAD_INITIALIZER(includes); - -/* access current file */ -#define CURFILE (TAILQ_FIRST(&includes)) - -/* - * specType contains the SPECial TYPE of the current target. It is - * Not if the target is unspecial. If it *is* special, however, the children - * are linked as children of the parent but not vice versa. This variable is - * set in ParseDoDependency - */ -typedef enum { - Begin, /* .BEGIN */ - Default, /* .DEFAULT */ - End, /* .END */ - ExportVar, /* .EXPORTVAR */ - Ignore, /* .IGNORE */ - Includes, /* .INCLUDES */ - Interrupt, /* .INTERRUPT */ - Libs, /* .LIBS */ - MFlags, /* .MFLAGS or .MAKEFLAGS */ - Main, /* .MAIN and we don't have anyth. user-spec. to make */ - Not, /* Not special */ - NotParallel, /* .NOTPARALELL */ - Null, /* .NULL */ - Order, /* .ORDER */ - Parallel, /* .PARALLEL */ - ExPath, /* .PATH */ - Phony, /* .PHONY */ - Posix, /* .POSIX */ - Precious, /* .PRECIOUS */ - ExShell, /* .SHELL */ - Silent, /* .SILENT */ - SingleShell, /* .SINGLESHELL */ - Suffixes, /* .SUFFIXES */ - Wait, /* .WAIT */ - Warn, /* .WARN */ - Attribute /* Generic attribute */ -} ParseSpecial; - -static ParseSpecial specType; -static int waiting; - -/* - * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER - * seen, then set to each successive source on the line. - */ -static GNode *predecessor; - -/* - * The parseKeywords table is searched using binary search when deciding - * if a target or source is special. The 'spec' field is the ParseSpecial - * type of the keyword ("Not" if the keyword isn't special as a target) while - * the 'op' field is the operator to apply to the list of targets if the - * keyword is used as a source ("0" if the keyword isn't special as a source) - */ -static const struct keyword { - const char *name; /* Name of keyword */ - ParseSpecial spec; /* Type when used as a target */ - int op; /* Operator when used as a source */ -} parseKeywords[] = { - /* KEYWORD-START-TAG */ - { ".BEGIN", Begin, 0 }, - { ".DEFAULT", Default, 0 }, - { ".END", End, 0 }, - { ".EXEC", Attribute, OP_EXEC }, - { ".EXPORTVAR", ExportVar, 0 }, - { ".IGNORE", Ignore, OP_IGNORE }, - { ".INCLUDES", Includes, 0 }, - { ".INTERRUPT", Interrupt, 0 }, - { ".INVISIBLE", Attribute, OP_INVISIBLE }, - { ".JOIN", Attribute, OP_JOIN }, - { ".LIBS", Libs, 0 }, - { ".MAIN", Main, 0 }, - { ".MAKE", Attribute, OP_MAKE }, - { ".MAKEFLAGS", MFlags, 0 }, - { ".MFLAGS", MFlags, 0 }, - { ".NOTMAIN", Attribute, OP_NOTMAIN }, - { ".NOTPARALLEL", NotParallel, 0 }, - { ".NO_PARALLEL", NotParallel, 0 }, - { ".NULL", Null, 0 }, - { ".OPTIONAL", Attribute, OP_OPTIONAL }, - { ".ORDER", Order, 0 }, - { ".PARALLEL", Parallel, 0 }, - { ".PATH", ExPath, 0 }, - { ".PHONY", Phony, OP_PHONY }, - { ".POSIX", Posix, 0 }, - { ".PRECIOUS", Precious, OP_PRECIOUS }, - { ".RECURSIVE", Attribute, OP_MAKE }, - { ".SHELL", ExShell, 0 }, - { ".SILENT", Silent, OP_SILENT }, - { ".SINGLESHELL", SingleShell, 0 }, - { ".SUFFIXES", Suffixes, 0 }, - { ".USE", Attribute, OP_USE }, - { ".WAIT", Wait, 0 }, - { ".WARN", Warn, 0 }, - /* KEYWORD-END-TAG */ -}; -#define NKEYWORDS (sizeof(parseKeywords) / sizeof(parseKeywords[0])) - -static DirectiveHandler parse_include; -static DirectiveHandler parse_message; -static DirectiveHandler parse_makeenv; -static DirectiveHandler parse_undef; -static DirectiveHandler parse_for; -static DirectiveHandler parse_endfor; - -static const struct directive { - const char *name; - int code; - bool skip_flag; /* execute even when skipped */ - DirectiveHandler *func; -} directives[] = { - /* DIRECTIVES-START-TAG */ - { "elif", COND_ELIF, true, Cond_If }, - { "elifdef", COND_ELIFDEF, true, Cond_If }, - { "elifmake", COND_ELIFMAKE, true, Cond_If }, - { "elifndef", COND_ELIFNDEF, true, Cond_If }, - { "elifnmake", COND_ELIFNMAKE, true, Cond_If }, - { "else", COND_ELSE, true, Cond_Else }, - { "endfor", 0, false, parse_endfor }, - { "endif", COND_ENDIF, true, Cond_Endif }, - { "error", 1, false, parse_message }, - { "for", 0, false, parse_for }, - { "if", COND_IF, true, Cond_If }, - { "ifdef", COND_IFDEF, true, Cond_If }, - { "ifmake", COND_IFMAKE, true, Cond_If }, - { "ifndef", COND_IFNDEF, true, Cond_If }, - { "ifnmake", COND_IFNMAKE, true, Cond_If }, - { "include", 0, false, parse_include }, - { "makeenv", 0, false, parse_makeenv }, - { "undef", 0, false, parse_undef }, - { "warning", 0, false, parse_message }, - /* DIRECTIVES-END-TAG */ -}; -#define NDIRECTS (sizeof(directives) / sizeof(directives[0])) - -/*- - * ParseFindKeyword - * Look in the table of keywords for one matching the given string. - * - * Results: - * The pointer to keyword table entry or NULL. - */ -static const struct keyword * -ParseFindKeyword(const char *str) -{ - int kw; - - kw = keyword_hash((const unsigned char *)str, strlen(str)); - if (kw < 0 || kw >= (int)NKEYWORDS || - strcmp(str, parseKeywords[kw].name) != 0) - return (NULL); - return (&parseKeywords[kw]); -} - -/*- - * Parse_Error -- - * Error message abort function for parsing. Prints out the context - * of the error (line number and file) as well as the message with - * two optional arguments. - * - * Results: - * None - * - * Side Effects: - * "fatals" is incremented if the level is PARSE_FATAL. - */ -/* VARARGS */ -void -Parse_Error(int type, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (CURFILE != NULL) - fprintf(stderr, "\"%s\", line %d: ", - CURFILE->fname, CURFILE->lineno); - if (type == PARSE_WARNING) - fprintf(stderr, "warning: "); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); - fflush(stderr); - if (type == PARSE_FATAL) - fatals += 1; -} - -/** - * ParsePushInput - * - * Push a new input source onto the input stack. If ptr is NULL - * the fullname is used to fopen the file. If it is not NULL, - * ptr is assumed to point to the string to be parsed. If opening the - * file fails, the fullname is freed. - */ -static void -ParsePushInput(char *fullname, FILE *fp, char *ptr, int lineno) -{ - struct IFile *nf; - - nf = emalloc(sizeof(*nf)); - nf->fname = fullname; - nf->lineno = lineno; - - if (ptr == NULL) { - /* the input source is a file */ - if ((nf->F = fp) == NULL) { - nf->F = fopen(fullname, "r"); - if (nf->F == NULL) { - Parse_Error(PARSE_FATAL, "Cannot open %s", - fullname); - free(fullname); - free(nf); - return; - } - } - nf->str = nf->ptr = NULL; - Var_Append(".MAKEFILE_LIST", fullname, VAR_GLOBAL); - } else { - nf->str = nf->ptr = ptr; - nf->F = NULL; - } - TAILQ_INSERT_HEAD(&includes, nf, link); -} - -/** - * ParsePopInput - * Called when EOF is reached in the current file. If we were reading - * an include file, the includes stack is popped and things set up - * to go back to reading the previous file at the previous location. - * - * Results: - * CONTINUE if there's more to do. DONE if not. - * - * Side Effects: - * The old curFile.F is closed, but only if it was not a top-level - * file. The includes list is shortened. curFile.lineno, curFile.F, - * and curFile.fname are changed if CONTINUE is returned. - */ -static int -ParsePopInput(void) -{ - struct IFile *ifile; /* the state on the top of the includes stack */ - - assert(!TAILQ_EMPTY(&includes)); - - ifile = TAILQ_FIRST(&includes); - TAILQ_REMOVE(&includes, ifile, link); - - free(ifile->fname); - if (ifile->F != NULL) { - /* Don't close the top-level file */ - if (!TAILQ_EMPTY(&includes)) - fclose(ifile->F); - Var_Append(".MAKEFILE_LIST", "..", VAR_GLOBAL); - } - if (ifile->str != NULL) { - free(ifile->str); - } - free(ifile); - - return (TAILQ_EMPTY(&includes) ? DONE : CONTINUE); -} - -/** - * parse_warn - * Parse the .WARN pseudo-target. - */ -static void -parse_warn(const char line[]) -{ - ArgArray aa; - int i; - - brk_string(&aa, line, true); - - for (i = 1; i < aa.argc; i++) - Main_ParseWarn(aa.argv[i], 0); -} - -/*- - *--------------------------------------------------------------------- - * ParseLinkSrc -- - * Link the parent nodes to their new child. Used by - * ParseDoDependency. If the specType isn't 'Not', the parent - * isn't linked as a parent of the child. - * - * Side Effects: - * New elements are added to the parents lists of cgn and the - * children list of cgn. the unmade field of pgn is updated - * to reflect the additional child. - *--------------------------------------------------------------------- - */ -static void -ParseLinkSrc(Lst *parents, GNode *cgn) -{ - LstNode *ln; - GNode *pgn; - - LST_FOREACH(ln, parents) { - pgn = Lst_Datum(ln); - if (Lst_Member(&pgn->children, cgn) == NULL) { - Lst_AtEnd(&pgn->children, cgn); - if (specType == Not) { - Lst_AtEnd(&cgn->parents, pgn); - } - pgn->unmade += 1; - } - } -} - -/*- - *--------------------------------------------------------------------- - * ParseDoOp -- - * Apply the parsed operator to all target nodes. Used in - * ParseDoDependency once all targets have been found and their - * operator parsed. If the previous and new operators are incompatible, - * a major error is taken. - * - * Side Effects: - * The type field of the node is altered to reflect any new bits in - * the op. - *--------------------------------------------------------------------- - */ -static void -ParseDoOp(int op) -{ - GNode *cohort; - LstNode *ln; - GNode *gn; - - LST_FOREACH(ln, &targets) { - gn = Lst_Datum(ln); - - /* - * If the dependency mask of the operator and the node don't - * match and the node has actually had an operator applied to - * it before, and the operator actually has some dependency - * information in it, complain. - */ - if ((op & OP_OPMASK) != (gn->type & OP_OPMASK) && - !OP_NOP(gn->type) && !OP_NOP(op)) { - Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", - gn->name); - return; - } - - if (op == OP_DOUBLEDEP && - (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { - /* - * If the node was the object of a :: operator, we need - * to create a new instance of it for the children and - * commands on this dependency line. The new instance - * is placed on the 'cohorts' list of the initial one - * (note the initial one is not on its own cohorts list) - * and the new instance is linked to all parents of the - * initial instance. - */ - cohort = Targ_NewGN(gn->name); - - /* - * Duplicate links to parents so graph traversal is - * simple. Perhaps some type bits should be duplicated? - * - * Make the cohort invisible as well to avoid - * duplicating it into other variables. True, parents - * of this target won't tend to do anything with their - * local variables, but better safe than sorry. - */ - ParseLinkSrc(&gn->parents, cohort); - cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; - Lst_AtEnd(&gn->cohorts, cohort); - - /* - * Replace the node in the targets list with the - * new copy - */ - Lst_Replace(ln, cohort); - gn = cohort; - } - /* - * We don't want to nuke any previous flags (whatever they were) - * so we just OR the new operator into the old - */ - gn->type |= op; - } -} - -/*- - *--------------------------------------------------------------------- - * ParseDoSrc -- - * Given the name of a source, figure out if it is an attribute - * and apply it to the targets if it is. Else decide if there is - * some attribute which should be applied *to* the source because - * of some special target and apply it if so. Otherwise, make the - * source be a child of the targets in the list 'targets' - * - * Results: - * None - * - * Side Effects: - * Operator bits may be added to the list of targets or to the source. - * The targets may have a new source added to their lists of children. - *--------------------------------------------------------------------- - */ -static void -ParseDoSrc(Parser *parser, int tOp, char *src, Lst *allsrc) -{ - GNode *gn = NULL; - const struct keyword *kw; - - if (src[0] == '.' && isupper ((unsigned char)src[1])) { - if ((kw = ParseFindKeyword(src)) != NULL) { - if (kw->op != 0) { - ParseDoOp(kw->op); - return; - } - if (kw->spec == Wait) { - waiting++; - return; - } - } - } - - switch (specType) { - case Main: - /* - * If we have noted the existence of a .MAIN, it means we need - * to add the sources of said target to the list of things - * to create. The string 'src' is likely to be free, so we - * must make a new copy of it. Note that this will only be - * invoked if the user didn't specify a target on the command - * line. This is to allow #ifmake's to succeed, or something... - */ - Lst_AtEnd(parser->create, estrdup(src)); - /* - * Add the name to the .TARGETS variable as well, so the user - * can employ that, if desired. - */ - Var_Append(".TARGETS", src, VAR_GLOBAL); - return; - - case Order: - /* - * Create proper predecessor/successor links between the - * previous source and the current one. - */ - gn = Targ_FindNode(src, TARG_CREATE); - if (predecessor != NULL) { - Lst_AtEnd(&predecessor->successors, gn); - Lst_AtEnd(&gn->preds, predecessor); - } - /* - * The current source now becomes the predecessor for the next - * one. - */ - predecessor = gn; - break; - - default: - /* - * If the source is not an attribute, we need to find/create - * a node for it. After that we can apply any operator to it - * from a special target or link it to its parents, as - * appropriate. - * - * In the case of a source that was the object of a :: operator, - * the attribute is applied to all of its instances (as kept in - * the 'cohorts' list of the node) or all the cohorts are linked - * to all the targets. - */ - gn = Targ_FindNode(src, TARG_CREATE); - if (tOp) { - gn->type |= tOp; - } else { - ParseLinkSrc(&targets, gn); - } - if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { - GNode *cohort; - LstNode *ln; - - for (ln = Lst_First(&gn->cohorts); ln != NULL; - ln = Lst_Succ(ln)) { - cohort = Lst_Datum(ln); - if (tOp) { - cohort->type |= tOp; - } else { - ParseLinkSrc(&targets, cohort); - } - } - } - break; - } - - gn->order = waiting; - Lst_AtEnd(allsrc, gn); - if (waiting) { - LstNode *ln; - GNode *p; - - /* - * Check if GNodes needs to be synchronized. - * This has to be when two nodes are on different sides of a - * .WAIT directive. - */ - LST_FOREACH(ln, allsrc) { - p = Lst_Datum(ln); - - if (p->order >= gn->order) - break; - /* - * XXX: This can cause loops, and loops can cause - * unmade targets, but checking is tedious, and the - * debugging output can show the problem - */ - Lst_AtEnd(&p->successors, gn); - Lst_AtEnd(&gn->preds, p); - } - } -} - - -/*- - *--------------------------------------------------------------------- - * ParseDoDependency -- - * Parse the dependency line in line. - * - * Results: - * None - * - * Side Effects: - * The nodes of the sources are linked as children to the nodes of the - * targets. Some nodes may be created. - * - * We parse a dependency line by first extracting words from the line and - * finding nodes in the list of all targets with that name. This is done - * until a character is encountered which is an operator character. Currently - * these are only ! and :. At this point the operator is parsed and the - * pointer into the line advanced until the first source is encountered. - * The parsed operator is applied to each node in the 'targets' list, - * which is where the nodes found for the targets are kept, by means of - * the ParseDoOp function. - * The sources are read in much the same way as the targets were except - * that now they are expanded using the wildcarding scheme of the C-Shell - * and all instances of the resulting words in the list of all targets - * are found. Each of the resulting nodes is then linked to each of the - * targets as one of its children. - * Certain targets are handled specially. These are the ones detailed - * by the specType variable. - * The storing of transformation rules is also taken care of here. - * A target is recognized as a transformation rule by calling - * Suff_IsTransform. If it is a transformation rule, its node is gotten - * from the suffix module via Suff_AddTransform rather than the standard - * Targ_FindNode in the target module. - *--------------------------------------------------------------------- - */ -static void -ParseDoDependency(Parser *parser, struct CLI *cli, char line[]) -{ - char *cp; /* our current position */ - GNode *gn; /* a general purpose temporary node */ - int op; /* the operator on the line */ - char savec; /* a place to save a character */ - Lst paths; /* Search paths to alter when parsing .PATH targets */ - int tOp; /* operator from special target */ - LstNode *ln; - const struct keyword *kw; - - tOp = 0; - - specType = Not; - waiting = 0; - Lst_Init(&paths); - - do { - for (cp = line; - *cp && !isspace((unsigned char)*cp) && *cp != OPEN_PAREN; - cp++) { - if (*cp == '$') { - /* - * Must be a dynamic source (would have been - * expanded otherwise), so call the Var module - * to parse the puppy so we can safely advance - * beyond it...There should be no errors in this - * as they would have been discovered in the - * initial Var_Subst and we wouldn't be here. - */ - size_t length = 0; - bool freeIt; - char *result; - - result = Var_Parse(cp, VAR_CMD, true, - &length, &freeIt); - - if (freeIt) { - free(result); - } - cp += length - 1; - - } else if (*cp == '!' || *cp == ':') { - /* - * We don't want to end a word on ':' or '!' if - * there is a better match later on in the - * string (greedy matching). - * This allows the user to have targets like: - * fie::fi:fo: fum - * foo::bar: - * where "fie::fi:fo" and "foo::bar" are the - * targets. In real life this is used for perl5 - * library man pages where "::" separates an - * object from its class. Ie: - * "File::Spec::Unix". This behaviour is also - * consistent with other versions of make. - */ - char *p = cp + 1; - - if (*cp == ':' && *p == ':') - p++; - - /* Found the best match already. */ - if (*p == '\0' || isspace((unsigned char)*p)) - break; - - p += strcspn(p, "!:"); - - /* No better match later on... */ - if (*p == '\0') - break; - } - continue; - } - if (*cp == OPEN_PAREN) { - /* - * Archives must be handled specially to make sure the - * OP_ARCHV flag is set in their 'type' field, for one - * thing, and because things like "archive(file1.o - * file2.o file3.o)" are permissible. Arch_ParseArchive - * will set 'line' to be the first non-blank after the - * archive-spec. It creates/finds nodes for the members - * and places them on the given list, returning true - * if all went well and false if there was an error in - * the specification. On error, line should remain - * untouched. - */ - if (!Arch_ParseArchive(&line, &targets, VAR_CMD)) { - Parse_Error(PARSE_FATAL, - "Error in archive specification: \"%s\"", - line); - return; - } else { - cp = line; - continue; - } - } - savec = *cp; - - if (!*cp) { - /* - * Ending a dependency line without an operator is a * Bozo no-no. As a heuristic, this is also often - * triggered by undetected conflicts from cvs/rcs - * merges. - */ - if (strncmp(line, "<<<<<<", 6) == 0 || - strncmp(line, "======", 6) == 0 || - strncmp(line, ">>>>>>", 6) == 0) { - Parse_Error(PARSE_FATAL, "Makefile appears to " - "contain unresolved cvs/rcs/??? merge " - "conflicts"); - } else - Parse_Error(PARSE_FATAL, "Need an operator"); - return; - } - *cp = '\0'; - /* - * Have a word in line. See if it's a special target and set - * specType to match it. - */ - if (*line == '.' && isupper((unsigned char)line[1])) { - /* - * See if the target is a special target that must have - * it or its sources handled specially. - */ - if ((kw = ParseFindKeyword(line)) != NULL) { - if (specType == ExPath && kw->spec != ExPath) { - Parse_Error(PARSE_FATAL, - "Mismatched special targets"); - return; - } - - specType = kw->spec; - tOp = kw->op; - - /* - * Certain special targets have special - * semantics: - * .PATH Have to set the dirSearchPath - * variable too - * .MAIN Its sources are only used if - * nothing has been specified to - * create. - * .DEFAULT Need to create a node to hang - * commands on, but we don't want - * it in the graph, nor do we want - * it to be the Main Target, so we - * create it, set OP_NOTMAIN and - * add it to the list, setting - * DEFAULT to the new node for - * later use. We claim the node is - * A transformation rule to make - * life easier later, when we'll - * use Make_HandleUse to actually - * apply the .DEFAULT commands. - * .PHONY The list of targets - * .BEGIN - * .END - * .INTERRUPT Are not to be considered the - * main target. - * .NOTPARALLEL Make only one target at a time. - * .SINGLESHELL Create a shell for each - * command. - * .ORDER Must set initial predecessor - * to NULL - */ - switch (specType) { - case ExPath: - Lst_AtEnd(&paths, &dirSearchPath); - break; - case Main: - if (!Lst_IsEmpty(parser->create)) { - specType = Not; - } - break; - case Begin: - case End: - case Interrupt: - gn = Targ_FindNode(line, TARG_CREATE); - gn->type |= OP_NOTMAIN; - Lst_AtEnd(&targets, gn); - break; - case Default: - gn = Targ_NewGN(".DEFAULT"); - gn->type |= (OP_NOTMAIN|OP_TRANSFORM); - Lst_AtEnd(&targets, gn); - DEFAULT = gn; - break; - case NotParallel: - jobLimit = 1; - break; - case SingleShell: - compatMake = 1; - break; - case Order: - predecessor = NULL; - break; - default: - break; - } - - } else if (strncmp(line, ".PATH", 5) == 0) { - /* - * .PATH has to be handled specially. - * Call on the suffix module to give us a path - * to modify. - */ - struct Path *path; - - specType = ExPath; - path = Suff_GetPath(&line[5]); - if (path == NULL) { - Parse_Error(PARSE_FATAL, "Suffix '%s' " - "not defined (yet)", &line[5]); - return; - } else - Lst_AtEnd(&paths, path); - } - } - - /* - * Have word in line. Get or create its node and stick it at - * the end of the targets list - */ - if (specType == Not && *line != '\0') { - - /* target names to be found and added to targets list */ - Lst curTargs = Lst_Initializer(curTargs); - - if (Dir_HasWildcards(line)) { - /* - * Targets are to be sought only in the current - * directory, so create an empty path for the - * thing. Note we need to use Path_Clear in the - * destruction of the path as the Dir module - * could have added a directory to the path... - */ - struct Path emptyPath = - TAILQ_HEAD_INITIALIZER(emptyPath); - - Path_Expand(line, &emptyPath, &curTargs); - Path_Clear(&emptyPath); - - } else { - /* - * No wildcards, but we want to avoid code - * duplication, so create a list with the word - * on it. - */ - Lst_AtEnd(&curTargs, line); - } - - while (!Lst_IsEmpty(&curTargs)) { - char *targName = Lst_DeQueue(&curTargs); - - if (!Suff_IsTransform (targName)) { - gn = Targ_FindNode(targName, - TARG_CREATE); - } else { - gn = Suff_AddTransform(targName); - } - - Lst_AtEnd(&targets, gn); - } - } else if (specType == ExPath && *line != '.' && *line != '\0'){ - Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", - line); - } - - *cp = savec; - /* - * If it is a special type and not .PATH, it's the only - * target we allow on this line... - */ - if (specType != Not && specType != ExPath) { - bool warnFlag = false; - - while (*cp != '!' && *cp != ':' && *cp) { - if (*cp != ' ' && *cp != '\t') { - warnFlag = true; - } - cp++; - } - if (warnFlag) { - Parse_Error(PARSE_WARNING, - "Extra target ignored"); - } - } else { - while (*cp && isspace((unsigned char)*cp)) { - cp++; - } - } - line = cp; - } while (*line != '!' && *line != ':' && *line); - - if (!Lst_IsEmpty(&targets)) { - switch (specType) { - default: - Parse_Error(PARSE_WARNING, "Special and mundane " - "targets don't mix. Mundane ones ignored"); - break; - case Default: - case Begin: - case End: - case Interrupt: - /* - * These four create nodes on which to hang commands, so - * targets shouldn't be empty... - */ - case Not: - /* - * Nothing special here -- targets can be empty if it - * wants. - */ - break; - } - } - - /* - * Have now parsed all the target names. Must parse the operator next. - * The result is left in op. - */ - if (*cp == '!') { - op = OP_FORCE; - } else if (*cp == ':') { - if (cp[1] == ':') { - op = OP_DOUBLEDEP; - cp++; - } else { - op = OP_DEPENDS; - } - } else { - Parse_Error(PARSE_FATAL, "Missing dependency operator"); - return; - } - - cp++; /* Advance beyond operator */ - - ParseDoOp(op); - - /* - * Get to the first source - */ - while (*cp && isspace((unsigned char)*cp)) { - cp++; - } - line = cp; - - /* - * Several special targets take different actions if present with no - * sources: - * a .SUFFIXES line with no sources clears out all old suffixes - * a .PRECIOUS line makes all targets precious - * a .IGNORE line ignores errors for all targets - * a .SILENT line creates silence when making all targets - * a .PATH removes all directories from the search path(s). - */ - if (!*line) { - switch (specType) { - case Suffixes: - Suff_ClearSuffixes(); - break; - case Precious: - allPrecious = true; - break; - case Ignore: - ignoreErrors = true; - break; - case Silent: - beSilent = true; - break; - case ExPath: - LST_FOREACH(ln, &paths) - Path_Clear(Lst_Datum(ln)); - break; - case Posix: - Var_SetGlobal("%POSIX", "1003.2"); - break; - default: - break; - } - - } else if (specType == MFlags) { - /* - * Call on functions in main.c to deal with these arguments and - * set the initial character to a null-character so the loop to - * get sources won't get anything - */ - Main_ParseArgLine(cli, line, 0); - *line = '\0'; - - } else if (specType == Warn) { - parse_warn(line); - *line = '\0'; - - } else if (specType == ExShell) { - Shell *shell; - - if ((shell = Shell_Parse(line)) == NULL) { - Parse_Error(PARSE_FATAL, - "improper shell specification"); - return; - } - commandShell = shell; - *line = '\0'; - - } else if (specType == NotParallel || specType == SingleShell) { - *line = '\0'; - } - - /* - * NOW GO FOR THE SOURCES - */ - if (specType == Suffixes || specType == ExPath || - specType == Includes || specType == Libs || - specType == Null) { - while (*line) { - /* - * If the target was one that doesn't take files as its - * sources but takes something like suffixes, we take - * each space-separated word on the line as a something - * and deal with it accordingly. - * - * If the target was .SUFFIXES, we take each source as - * a suffix and add it to the list of suffixes - * maintained by the Suff module. - * - * If the target was a .PATH, we add the source as a - * directory to search on the search path. - * - * If it was .INCLUDES, the source is taken to be the - * suffix of files which will be #included and whose - * search path should be present in the .INCLUDES - * variable. - * - * If it was .LIBS, the source is taken to be the - * suffix of files which are considered libraries and - * whose search path should be present in the .LIBS - * variable. - * - * If it was .NULL, the source is the suffix to use - * when a file has no valid suffix. - */ - char savech; - while (*cp && !isspace((unsigned char)*cp)) { - cp++; - } - savech = *cp; - *cp = '\0'; - switch (specType) { - case Suffixes: - Suff_AddSuffix(line); - break; - case ExPath: - LST_FOREACH(ln, &paths) - Path_AddDir(Lst_Datum(ln), line); - break; - case Includes: - Suff_AddInclude(line); - break; - case Libs: - Suff_AddLib(line); - break; - case Null: - Suff_SetNull(line); - break; - default: - break; - } - *cp = savech; - if (savech != '\0') { - cp++; - } - while (*cp && isspace((unsigned char)*cp)) { - cp++; - } - line = cp; - } - Lst_Destroy(&paths, NOFREE); - - } else if (specType == ExportVar) { - Var_SetEnv(line, VAR_GLOBAL); - - } else { - /* list of sources in order */ - Lst curSrcs = Lst_Initializer(curSrc); - - while (*line) { - /* - * The targets take real sources, so we must beware of - * archive specifications (i.e. things with left - * parentheses in them) and handle them accordingly. - */ - while (*cp && !isspace((unsigned char)*cp)) { - if (*cp == OPEN_PAREN && cp > line && cp[-1] != '$') { - /* - * Only stop for a left parenthesis if - * it isn't at the start of a word - * (that'll be for variable changes - * later) and isn't preceded by a dollar - * sign (a dynamic source). - */ - break; - } else { - cp++; - } - } - - if (*cp == OPEN_PAREN) { - GNode *gnp; - - /* list of archive source names after exp. */ - Lst sources = Lst_Initializer(sources); - - if (!Arch_ParseArchive(&line, &sources, - VAR_CMD)) { - Parse_Error(PARSE_FATAL, "Error in " - "source archive spec \"%s\"", line); - return; - } - - while (!Lst_IsEmpty(&sources)) { - gnp = Lst_DeQueue(&sources); - ParseDoSrc(parser, tOp, gnp->name, &curSrcs); - } - cp = line; - } else { - if (*cp) { - *cp = '\0'; - cp += 1; - } - - ParseDoSrc(parser, tOp, line, &curSrcs); - } - while (*cp && isspace((unsigned char)*cp)) { - cp++; - } - line = cp; - } - Lst_Destroy(&curSrcs, NOFREE); - } - - if (mainNode == NULL) { - /* - * If we have yet to decide on a main target to make, in the - * absence of any user input, we want the first target on - * the first dependency line that is actually a real target - * (i.e. isn't a .USE or .EXEC rule) to be made. - */ - LST_FOREACH(ln, &targets) { - gn = Lst_Datum(ln); - if ((gn->type & (OP_NOTMAIN | OP_USE | - OP_EXEC | OP_TRANSFORM)) == 0) { - mainNode = gn; - Targ_SetMain(gn); - break; - } - } - } -} - -/*- - *--------------------------------------------------------------------- - * Parse_IsVar -- - * Return true if the passed line is a variable assignment. A variable - * assignment consists of a single word followed by optional whitespace - * followed by either a += or an = operator. - * This function is used both by the Parse_File function and main when - * parsing the command-line arguments. - * - * Results: - * true if it is. false if it ain't - * - * Side Effects: - * none - *--------------------------------------------------------------------- - */ -bool -Parse_IsVar(char *line) -{ - bool wasSpace = false; /* set true if found a space */ - bool haveName = false; /* Set true if have a variable name */ - - int level = 0; -#define ISEQOPERATOR(c) \ - ((c) == '+' || (c) == ':' || (c) == '?' || (c) == '!') - - /* - * Skip to variable name - */ - for (; *line == ' ' || *line == '\t'; line++) - continue; - - for (; *line != '=' || level != 0; line++) { - switch (*line) { - case '\0': - /* - * end-of-line -- can't be a variable assignment. - */ - return (false); - - case ' ': - case '\t': - /* - * there can be as much white space as desired so long - * as there is only one word before the operator - */ - wasSpace = true; - break; - - case OPEN_PAREN: - case OPEN_BRACE: - level++; - break; - - case CLOSE_BRACE: - case CLOSE_PAREN: - level--; - break; - - default: - if (wasSpace && haveName) { - if (ISEQOPERATOR(*line)) { - /* - * We must have a finished word - */ - if (level != 0) - return (false); - - /* - * When an = operator [+?!:] is found, - * the next character must be an = or - * it ain't a valid assignment. - */ - if (line[1] == '=') - return (haveName); -#ifdef SUNSHCMD - /* - * This is a shell command - */ - if (strncmp(line, ":sh", 3) == 0) - return (haveName); -#endif - } - /* - * This is the start of another word, so not - * assignment. - */ - return (false); - - } else { - haveName = true; - wasSpace = false; - } - break; - } - } - - return (haveName); -} - -/*- - *--------------------------------------------------------------------- - * Parse_DoVar -- - * Take the variable assignment in the passed line and do it in the - * global context. - * - * Note: There is a lexical ambiguity with assignment modifier characters - * in variable names. This routine interprets the character before the = - * as a modifier. Therefore, an assignment like - * C++=/usr/bin/CC - * is interpreted as "C+ +=" instead of "C++ =". - * - * Results: - * none - * - * Side Effects: - * the variable structure of the given variable name is altered in the - * global context. - *--------------------------------------------------------------------- - */ -void -Parse_DoVar(char *line, GNode *ctxt) -{ - char *cp; /* pointer into line */ - enum { - VAR_SUBST, - VAR_APPEND, - VAR_SHELL, - VAR_NORMAL - } type; /* Type of assignment */ - char *opc; /* ptr to operator character to - * null-terminate the variable name */ - - /* - * Skip to variable name - */ - while (*line == ' ' || *line == '\t') { - line++; - } - - /* - * Skip to operator character, nulling out whitespace as we go - */ - for (cp = line + 1; *cp != '='; cp++) { - if (isspace((unsigned char)*cp)) { - *cp = '\0'; - } - } - opc = cp - 1; /* operator is the previous character */ - *cp++ = '\0'; /* nuke the = */ - - /* - * Check operator type - */ - switch (*opc) { - case '+': - type = VAR_APPEND; - *opc = '\0'; - break; - - case '?': - /* - * If the variable already has a value, we don't do anything. - */ - *opc = '\0'; - if (Var_Value(line, ctxt) != NULL) { - return; - } else { - type = VAR_NORMAL; - } - break; - - case ':': - type = VAR_SUBST; - *opc = '\0'; - break; - - case '!': - type = VAR_SHELL; - *opc = '\0'; - break; - - default: -#ifdef SUNSHCMD - while (*opc != ':') { - if (opc == line) - break; - else - --opc; - } - - if (strncmp(opc, ":sh", 3) == 0) { - type = VAR_SHELL; - *opc = '\0'; - break; - } -#endif - type = VAR_NORMAL; - break; - } - - while (isspace((unsigned char)*cp)) { - cp++; - } - - if (type == VAR_APPEND) { - Var_Append(line, cp, ctxt); - - } else if (type == VAR_SUBST) { - /* - * Allow variables in the old value to be undefined, but leave - * their invocation alone -- this is done by forcing oldVars - * to be false. - * XXX: This can cause recursive variables, but that's not - * hard to do, and this allows someone to do something like - * - * CFLAGS = $(.INCLUDES) - * CFLAGS := -I.. $(CFLAGS) - * - * And not get an error. - */ - bool oldOldVars = oldVars; - - oldVars = false; - - /* - * make sure that we set the variable the first time to nothing - * so that it gets substituted! - */ - if (Var_Value(line, ctxt) == NULL) - Var_Set(line, "", ctxt); - - cp = Buf_Peel(Var_Subst(cp, ctxt, false)); - - oldVars = oldOldVars; - - Var_Set(line, cp, ctxt); - free(cp); - - } else if (type == VAR_SHELL) { - /* - * true if the command needs to be freed, i.e. - * if any variable expansion was performed - */ - bool freeCmd = false; - Buffer *buf; - const char *error; - - if (strchr(cp, '$') != NULL) { - /* - * There's a dollar sign in the command, so perform - * variable expansion on the whole thing. The - * resulting string will need freeing when we're done, - * so set freeCmd to true. - */ - cp = Buf_Peel(Var_Subst(cp, VAR_CMD, true)); - freeCmd = true; - } - - buf = Cmd_Exec(cp, &error); - Var_Set(line, Buf_Data(buf), ctxt); - Buf_Destroy(buf, true); - - if (error) - Parse_Error(PARSE_WARNING, error, cp); - - if (freeCmd) - free(cp); - - } else { - /* - * Normal assignment -- just do it. - */ - Var_Set(line, cp, ctxt); - } -} - -/*- - *----------------------------------------------------------------------- - * ParseHasCommands -- - * Callback procedure for Parse_File when destroying the list of - * targets on the last dependency line. Marks a target as already - * having commands if it does, to keep from having shell commands - * on multiple dependency lines. - * - * Results: - * None - * - * Side Effects: - * OP_HAS_COMMANDS may be set for the target. - * - *----------------------------------------------------------------------- - */ -static void -ParseHasCommands(void *gnp) -{ - GNode *gn = gnp; - - if (!Lst_IsEmpty(&gn->commands)) { - gn->type |= OP_HAS_COMMANDS; - } -} - -/*- - *--------------------------------------------------------------------- - * Parse_FromString -- - * Start Parsing from the given string - * - * Results: - * None - * - * Side Effects: - * A structure is added to the includes Lst and readProc, curFile.lineno, - * curFile.fname and curFile.F are altered for the new file - *--------------------------------------------------------------------- - */ -void -Parse_FromString(char *str, int lineno) -{ - - DEBUGF(FOR, ("%s\n---- at line %d\n", str, lineno)); - - ParsePushInput(estrdup(CURFILE->fname), NULL, str, lineno); -} - -#ifdef SYSVINCLUDE -/*- - *--------------------------------------------------------------------- - * ParseTraditionalInclude -- - * Push to another file. - * - * The input is the line minus the "include". The file name is - * the string following the "include". - * - * Results: - * None - * - * Side Effects: - * A structure is added to the includes Lst and readProc, curFile.lineno, - * curFile.fname and curFile.F are altered for the new file - *--------------------------------------------------------------------- - */ -static void -ParseTraditionalInclude(Parser *parser, char *file) -{ - char *fullname; /* full pathname of file */ - char *cp; /* current position in file spec */ - - /* - * Skip over whitespace - */ - while (*file == ' ' || *file == '\t') { - file++; - } - - if (*file == '\0') { - Parse_Error(PARSE_FATAL, "Filename missing from \"include\""); - return; - } - - /* - * Skip to end of line or next whitespace - */ - for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) { - continue; - } - - *cp = '\0'; - - /* - * Substitute for any variables in the file name before trying to - * find the thing. - */ - file = Buf_Peel(Var_Subst(file, VAR_CMD, false)); - - /* - * Now we know the file's name, we attempt to find the durn thing. - * Search for it first on the -I search path, then on the .PATH - * search path, if not found in a -I directory. - */ - fullname = Path_FindFile(file, parser->parseIncPath); - if (fullname == NULL) { - fullname = Path_FindFile(file, &dirSearchPath); - } - - if (fullname == NULL) { - /* - * Still haven't found the makefile. Look for it on the system - * path as a last resort. - */ - fullname = Path_FindFile(file, parser->sysIncPath); - } - - if (fullname == NULL) { - Parse_Error(PARSE_FATAL, "Could not find %s", file); - /* XXXHB free(file) */ - return; - } - - /* XXXHB free(file) */ - - /* - * We set up the name of the file to be the absolute - * name of the include file so error messages refer to the right - * place. - */ - ParsePushInput(fullname, NULL, NULL, 0); -} -#endif - -/*- - *--------------------------------------------------------------------- - * ParseReadc -- - * Read a character from the current file - * - * Results: - * The character that was read - * - * Side Effects: - *--------------------------------------------------------------------- - */ -static int -ParseReadc(void) -{ - - if (CURFILE->F != NULL) - return (fgetc(CURFILE->F)); - - if (CURFILE->str != NULL && *CURFILE->ptr != '\0') - return (*CURFILE->ptr++); - - return (EOF); -} - - -/*- - *--------------------------------------------------------------------- - * ParseUnreadc -- - * Put back a character to the current file - * - * Results: - * None. - * - * Side Effects: - *--------------------------------------------------------------------- - */ -static void -ParseUnreadc(int c) -{ - - if (CURFILE->F != NULL) { - ungetc(c, CURFILE->F); - return; - } - if (CURFILE->str != NULL) { - *--(CURFILE->ptr) = c; - return; - } -} - -/* ParseSkipLine(): - * Grab the next line unless it begins with a dot (`.') and we're told to - * ignore such lines. - */ -static char * -ParseSkipLine(int skip, int keep_newline) -{ - char *line; - int c, lastc; - Buffer *buf; - - buf = Buf_Init(MAKE_BSIZE); - - do { - Buf_Clear(buf); - lastc = '\0'; - - while (((c = ParseReadc()) != '\n' || lastc == '\\') - && c != EOF) { - if (skip && c == '#' && lastc != '\\') { - /* - * let a comment be terminated even by an - * escaped \n. This is consistent to comment - * handling in ParseReadLine - */ - while ((c = ParseReadc()) != '\n' && c != EOF) - ; - break; - } - if (c == '\n') { - if (keep_newline) - Buf_AddByte(buf, c); - else - Buf_ReplaceLastByte(buf, ' '); - CURFILE->lineno++; - - while ((c = ParseReadc()) == ' ' || c == '\t') - continue; - - if (c == EOF) - break; - } - - Buf_AddByte(buf, c); - lastc = c; - } - - if (c == EOF) { - Parse_Error(PARSE_FATAL, - "Unclosed conditional/for loop"); - Buf_Destroy(buf, true); - return (NULL); - } - - CURFILE->lineno++; - Buf_AddByte(buf, '\0'); - line = Buf_Data(buf); - } while (skip == 1 && line[0] != '.'); - - Buf_Destroy(buf, false); - return (line); -} - -/*- - *--------------------------------------------------------------------- - * ParseReadLine -- - * Read an entire line from the input file. Called only by Parse_File. - * To facilitate escaped newlines and what have you, a character is - * buffered in 'lastc', which is '\0' when no characters have been - * read. When we break out of the loop, c holds the terminating - * character and lastc holds a character that should be added to - * the line (unless we don't read anything but a terminator). - * - * Results: - * A line w/o its newline - * - * Side Effects: - * Only those associated with reading a character - *--------------------------------------------------------------------- - */ -static char * -ParseReadLine(void) -{ - Buffer *buf; /* Buffer for current line */ - int c; /* the current character */ - int lastc; /* The most-recent character */ - bool semiNL; /* treat semi-colons as newlines */ - bool ignDepOp; /* true if should ignore dependency operators - * for the purposes of setting semiNL */ - bool ignComment; /* true if should ignore comments (in a - * shell command */ - char *line; /* Result */ - char *ep; /* to strip trailing blanks */ - - again: - semiNL = false; - ignDepOp = false; - ignComment = false; - - lastc = '\0'; - - /* - * Handle tab at the beginning of the line. A leading tab (shell - * command) forces us to ignore comments and dependency operators and - * treat semi-colons as semi-colons (by leaving semiNL false). - * This also discards completely blank lines. - */ - for (;;) { - c = ParseReadc(); - if (c == EOF) { - if (ParsePopInput() == DONE) { - /* End of all inputs - return NULL */ - return (NULL); - } - continue; - } - - if (c == '\t') { - ignComment = ignDepOp = true; - lastc = c; - break; - } - if (c != '\n') { - ParseUnreadc(c); - break; - } - CURFILE->lineno++; - } - - buf = Buf_Init(MAKE_BSIZE); - - while (((c = ParseReadc()) != '\n' || lastc == '\\') && c != EOF) { - test_char: - switch (c) { - case '\n': - /* - * Escaped newline: read characters until a - * non-space or an unescaped newline and - * replace them all by a single space. This is - * done by storing the space over the backslash - * and dropping through with the next nonspace. - * If it is a semi-colon and semiNL is true, - * it will be recognized as a newline in the - * code below this... - */ - CURFILE->lineno++; - lastc = ' '; - while ((c = ParseReadc()) == ' ' || c == '\t') { - continue; - } - if (c == EOF || c == '\n') { - goto line_read; - } else { - /* - * Check for comments, semiNL's, etc. -- - * easier than ParseUnreadc(c); - * continue; - */ - goto test_char; - } - /*NOTREACHED*/ - break; - - case ';': - /* - * Semi-colon: Need to see if it should be - * interpreted as a newline - */ - if (semiNL) { - /* - * To make sure the command that may - * be following this semi-colon begins - * with a tab, we push one back into the - * input stream. This will overwrite the - * semi-colon in the buffer. If there is - * no command following, this does no - * harm, since the newline remains in - * the buffer and the - * whole line is ignored. - */ - ParseUnreadc('\t'); - goto line_read; - } - break; - case '=': - if (!semiNL) { - /* - * Haven't seen a dependency operator - * before this, so this must be a - * variable assignment -- don't pay - * attention to dependency operators - * after this. - */ - ignDepOp = true; - } else if (lastc == ':' || lastc == '!') { - /* - * Well, we've seen a dependency - * operator already, but it was the - * previous character, so this is really - * just an expanded variable assignment. - * Revert semi-colons to being just - * semi-colons again and ignore any more - * dependency operators. - * - * XXX: Note that a line like - * "foo : a:=b" will blow up, but who'd - * write a line like that anyway? - */ - ignDepOp = true; - semiNL = false; - } - break; - case '#': - if (!ignComment) { - if (lastc != '\\') { - /* - * If the character is a hash - * mark and it isn't escaped - * (or we're being compatible), - * the thing is a comment. - * Skip to the end of the line. - */ - do { - c = ParseReadc(); - } while (c != '\n' && c != EOF); - goto line_read; - } else { - /* - * Don't add the backslash. - * Just let the # get copied - * over. - */ - lastc = c; - continue; - } - } - break; - - case ':': - case '!': - if (!ignDepOp) { - /* - * A semi-colon is recognized as a - * newline only on dependency lines. - * Dependency lines are lines with a - * colon or an exclamation point. - * Ergo... - */ - semiNL = true; - } - break; - - default: - break; - } - /* - * Copy in the previous character (there may be none if this - * was the first character) and save this one in - * lastc. - */ - if (lastc != '\0') - Buf_AddByte(buf, lastc); - lastc = c; - } - line_read: - CURFILE->lineno++; - - if (lastc != '\0') { - Buf_AddByte(buf, lastc); - } - Buf_AddByte(buf, '\0'); - line = Buf_Peel(buf); - - /* - * Strip trailing blanks and tabs from the line. - * Do not strip a blank or tab that is preceded by - * a '\' - */ - ep = line; - while (*ep) - ++ep; - while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) { - if (ep > line + 1 && ep[-2] == '\\') - break; - --ep; - } - *ep = 0; - - if (line[0] == '\0') { - /* empty line - just ignore */ - free(line); - goto again; - } - - return (line); -} - -/*- - *----------------------------------------------------------------------- - * ParseFinishLine -- - * Handle the end of a dependency group. - * - * Results: - * Nothing. - * - * Side Effects: - * inLine set false. 'targets' list destroyed. - * - *----------------------------------------------------------------------- - */ -static void -ParseFinishLine(void) -{ - const LstNode *ln; - - if (inLine) { - LST_FOREACH(ln, &targets) { - if (((const GNode *)Lst_Datum(ln))->type & OP_TRANSFORM) - Suff_EndTransform(Lst_Datum(ln)); - } - Lst_Destroy(&targets, ParseHasCommands); - inLine = false; - } -} - -/** - * parse_include - * Parse an .include directive and push the file onto the input stack. - * The input is the line minus the .include. A file spec is a string - * enclosed in <> or "". The former is looked for only in sysIncPath. - * The latter in . and the directories specified by -I command line - * options - */ -static void -parse_include(Parser *parser __unused, char *file, int code __unused, int lineno __unused) -{ - char *fullname; /* full pathname of file */ - char endc; /* the character which ends the file spec */ - char *cp; /* current position in file spec */ - bool isSystem; /* true if makefile is a system makefile */ - char *prefEnd, *Fname; - char *newName; - - /* - * Skip to delimiter character so we know where to look - */ - while (*file == ' ' || *file == '\t') { - file++; - } - - if (*file != '"' && *file != '<') { - Parse_Error(PARSE_FATAL, - ".include filename must be delimited by '\"' or '<'"); - return; - } - - /* - * Set the search path on which to find the include file based on the - * characters which bracket its name. Angle-brackets imply it's - * a system Makefile while double-quotes imply it's a user makefile - */ - if (*file == '<') { - isSystem = true; - endc = '>'; - } else { - isSystem = false; - endc = '"'; - } - - /* - * Skip to matching delimiter - */ - for (cp = ++file; *cp != endc; cp++) { - if (*cp == '\0') { - Parse_Error(PARSE_FATAL, - "Unclosed .include filename. '%c' expected", endc); - return; - } - } - *cp = '\0'; - - /* - * Substitute for any variables in the file name before trying to - * find the thing. - */ - file = Buf_Peel(Var_Subst(file, VAR_CMD, false)); - - /* - * Now we know the file's name and its search path, we attempt to - * find the durn thing. A return of NULL indicates the file don't - * exist. - */ - if (!isSystem) { - /* - * Include files contained in double-quotes are first searched - * for relative to the including file's location. We don't want - * to cd there, of course, so we just tack on the old file's - * leading path components and call Dir_FindFile to see if - * we can locate the beast. - */ - - /* Make a temporary copy of this, to be safe. */ - Fname = estrdup(CURFILE->fname); - - prefEnd = strrchr(Fname, '/'); - if (prefEnd != NULL) { - *prefEnd = '\0'; - if (file[0] == '/') - newName = estrdup(file); - else - newName = str_concat(Fname, '/', file); - fullname = Path_FindFile(newName, parser->parseIncPath); - if (fullname == NULL) { - fullname = Path_FindFile(newName, - &dirSearchPath); - } - free(newName); - *prefEnd = '/'; - } else { - fullname = NULL; - } - free(Fname); - } else { - fullname = NULL; - } - - if (fullname == NULL) { - /* - * System makefile or makefile wasn't found in same directory as - * included makefile. Search for it first on the -I search path, - * then on the .PATH search path, if not found in a -I - * directory. - * XXX: Suffix specific? - */ - fullname = Path_FindFile(file, parser->parseIncPath); - if (fullname == NULL) { - fullname = Path_FindFile(file, &dirSearchPath); - } - } - - if (fullname == NULL) { - /* - * Still haven't found the makefile. Look for it on the system - * path as a last resort. - */ - fullname = Path_FindFile(file, parser->sysIncPath); - } - - if (fullname == NULL) { - *cp = endc; - Parse_Error(PARSE_FATAL, "Could not find %s", file); - free(file); - return; - } - free(file); - - /* - * We set up the name of the file to be the absolute - * name of the include file so error messages refer to the right - * place. - */ - ParsePushInput(fullname, NULL, NULL, 0); -} - -/** - * parse_message - * Parse a .warning or .error directive - * - * The input is the line minus the ".error"/".warning". We substitute - * variables, print the message and exit(1) (for .error) or just print - * a warning if the directive is malformed. - */ -static void -parse_message(Parser *parser __unused, char *line, int iserror, int lineno __unused) -{ - - if (!isspace((u_char)*line)) { - Parse_Error(PARSE_WARNING, "invalid syntax: .%s%s", - iserror ? "error" : "warning", line); - return; - } - - while (isspace((u_char)*line)) - line++; - - line = Buf_Peel(Var_Subst(line, VAR_GLOBAL, false)); - Parse_Error(iserror ? PARSE_FATAL : PARSE_WARNING, "%s", line); - free(line); - - if (iserror) { - /* Terminate immediately. */ - exit(1); - } -} - -/** - * parse_undef - * Parse an .undef directive. - */ -static void -parse_undef(Parser *parser __unused, char *line, int code __unused, int lineno __unused) -{ - char *cp; - - while (isspace((u_char)*line)) - line++; - - for (cp = line; !isspace((u_char)*cp) && *cp != '\0'; cp++) { - ; - } - *cp = '\0'; - - cp = Buf_Peel(Var_Subst(line, VAR_CMD, false)); - Var_Delete(cp, VAR_GLOBAL); - free(cp); -} - -/** - * parse_makeenv - * Parse an .makeenv directive. - */ -static void -parse_makeenv(Parser *parser __unused, char *line, int code __unused, int lineno __unused) -{ - char *cp; - - while (isspace((u_char)*line)) - line++; - - for (cp = line; !isspace((u_char)*cp) && *cp != '\0'; cp++) { - ; - } - *cp = '\0'; - - cp = Buf_Peel(Var_Subst(line, VAR_CMD, false)); - Var_SetEnv(cp, VAR_GLOBAL); - free(cp); -} - -/** - * parse_for - * Parse a .for directive. - */ -static void -parse_for(Parser *parser __unused, char *line, int code __unused, int lineno) -{ - - if (!For_For(line)) { - /* syntax error */ - return; - } - line = NULL; - - /* - * Skip after the matching endfor. - */ - do { - free(line); - line = ParseSkipLine(0, 1); - if (line == NULL) { - Parse_Error(PARSE_FATAL, - "Unexpected end of file in for loop.\n"); - return; - } - } while (For_Eval(line)); - free(line); - - /* execute */ - For_Run(lineno); -} - -/** - * parse_endfor - * Parse endfor. This may only happen if there was no matching .for. - */ -static void -parse_endfor(Parser *parser __unused, char *line __unused, int code __unused, int lineno __unused) -{ - - Parse_Error(PARSE_FATAL, "for-less endfor"); -} - -/** - * parse_directive - * Got a line starting with a '.'. Check if this is a directive - * and parse it. - * - * return: - * true if line was a directive, false otherwise. - */ -static bool -parse_directive(Parser *parser, char *line) -{ - char *start; - char *cp; - int dir; - - /* - * Get the keyword: - * .[[:space:]]*\([[:alpha:]][[:alnum:]_]*\).* - * \1 is the keyword. - */ - for (start = line; isspace((u_char)*start); start++) { - ; - } - - if (!isalpha((u_char)*start)) { - return (false); - } - - cp = start + 1; - while (isalnum((u_char)*cp) || *cp == '_') { - cp++; - } - - dir = directive_hash((const unsigned char *)start, cp - start); - if (dir < 0 || dir >= (int)NDIRECTS || - (size_t)(cp - start) != strlen(directives[dir].name) || - strncmp(start, directives[dir].name, cp - start) != 0) { - /* not actually matched */ - return (false); - } - - if (!skipLine || directives[dir].skip_flag) - (*directives[dir].func)(parser, cp, directives[dir].code, - CURFILE->lineno); - return (true); -} - -/** - * Parse a file into its component parts, incorporating it into the - * current dependency graph. This is the main function and controls - * almost every other function in this module - * - * Results: - * None - * - * Side Effects: - * Loads. Nodes are added to the list of all targets, nodes and links - * are added to the dependency graph. etc. etc. etc. - */ -void -Parse_File(Parser *parser, struct CLI *cli, const char name[], FILE *stream) -{ - char *cp; /* pointer into the line */ - char *line; /* the line we're working on */ - - inLine = false; - fatals = 0; - - ParsePushInput(estrdup(name), stream, NULL, 0); - - while ((line = ParseReadLine()) != NULL) { - if (*line == '.' && parse_directive(parser, line + 1)) { - /* directive consumed */ - goto nextLine; - } - if (skipLine || *line == '#') { - /* Skipping .if block or comment. */ - goto nextLine; - } - - if (*line == '\t') { - /* - * If a line starts with a tab, it can only - * hope to be a creation command. - */ - for (cp = line + 1; isspace((unsigned char)*cp); cp++) { - continue; - } - if (*cp) { - if (inLine) { - LstNode *ln; - GNode *gn; - - /* - * So long as it's not a blank - * line and we're actually in a - * dependency spec, add the - * command to the list of - * commands of all targets in - * the dependency spec. - */ - LST_FOREACH(ln, &targets) { - gn = Lst_Datum(ln); - - /* - * if target already - * supplied, ignore - * commands - */ - if (!(gn->type & OP_HAS_COMMANDS)) - Lst_AtEnd(&gn->commands, cp); - else - Parse_Error(PARSE_WARNING, "duplicate script " - "for target \"%s\" ignored", gn->name); - } - continue; - } else { - Parse_Error(PARSE_FATAL, - "Unassociated shell command \"%s\"", - cp); - } - } -#ifdef SYSVINCLUDE - } else if (strncmp(line, "include", 7) == 0 && - isspace((unsigned char)line[7]) && - strchr(line, ':') == NULL) { - /* - * It's an S3/S5-style "include". - */ - ParseTraditionalInclude(parser, line + 7); - goto nextLine; -#endif - } else if (Parse_IsVar(line)) { - ParseFinishLine(); - Parse_DoVar(line, VAR_GLOBAL); - - } else { - /* - * We now know it's a dependency line so it - * needs to have all variables expanded before - * being parsed. Tell the variable module to - * complain if some variable is undefined... - * To make life easier on novices, if the line - * is indented we first make sure the line has - * a dependency operator in it. If it doesn't - * have an operator and we're in a dependency - * line's script, we assume it's actually a - * shell command and add it to the current - * list of targets. XXX this comment seems wrong. - */ - cp = line; - if (isspace((unsigned char)line[0])) { - while (*cp != '\0' && - isspace((unsigned char)*cp)) { - cp++; - } - if (*cp == '\0') { - goto nextLine; - } - } - - ParseFinishLine(); - - cp = Buf_Peel(Var_Subst(line, VAR_CMD, true)); - - free(line); - line = cp; - - /* - * Need a non-circular list for the target nodes - */ - Lst_Destroy(&targets, NOFREE); - inLine = true; - - ParseDoDependency(parser, cli, line); - } - - nextLine: - free(line); - } - - ParseFinishLine(); - - /* - * Make sure conditionals are clean - */ - Cond_End(parser, NULL, 0, 0); - - if (fatals) - errx(1, "fatal errors encountered -- cannot continue"); -} - -/*- - *----------------------------------------------------------------------- - * Parse_MainName -- - * Return a Lst of the main target to create for main()'s sake. If - * no such target exists, we Punt with an obnoxious error message. - * - * Results: - * A Lst of the single node to create. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -void -Parse_MainName(Lst *listmain) -{ - - if (mainNode == NULL) { - Punt("no target to make."); - /*NOTREACHED*/ - } else if (mainNode->type & OP_DOUBLEDEP) { - Lst_AtEnd(listmain, mainNode); - Lst_Concat(listmain, &mainNode->cohorts, LST_CONCNEW); - } else - Lst_AtEnd(listmain, mainNode); -} diff --git a/usr.bin/make/parse.h b/usr.bin/make/parse.h deleted file mode 100644 index df00890533..0000000000 --- a/usr.bin/make/parse.h +++ /dev/null @@ -1,102 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/parse.h,v 1.16 2005/09/17 11:07:23 okumoto Exp $ - */ - -#ifndef parse_h_470eeb9a -#define parse_h_470eeb9a - -#include -#include - -struct CLI; -struct GNode; -struct Lst; - -typedef struct Parser { - /** - * The list of target names specified on the command line. - * Used to resolve #if make(...) statements - */ - struct Lst *create; - - /** The list of dirs to search when looking for includes. (readonly) */ - struct Path *parseIncPath; - - /** The system include path. (readonly) */ - struct Path *sysIncPath; -} Parser; - -typedef void DirectiveHandler(Parser *, char *, int, int); - -/* - * Error levels for parsing. PARSE_FATAL means the process cannot continue - * once the makefile has been parsed. PARSE_WARNING means it can. Passed - * as the first argument to Parse_Error. - */ -#define PARSE_WARNING 2 -#define PARSE_FATAL 1 - -/* - * Definitions for the "local" variables. Used only for clarity. - */ -#define TARGET "@" /* Target of dependency */ -#define OODATE "?" /* All out-of-date sources */ -#define ALLSRC ">" /* All sources */ -#define IMPSRC "<" /* Source implied by transformation */ -#define PREFIX "*" /* Common prefix */ -#define ARCHIVE "!" /* Archive in "archive(member)" syntax */ -#define MEMBER "%" /* Member in "archive(member)" syntax */ - -#define FTARGET "@F" /* file part of TARGET */ -#define DTARGET "@D" /* directory part of TARGET */ -#define FIMPSRC " -#include -#include -#include -#include - -#include "proc.h" -#include "shell.h" -#include "util.h" - -/** - * Replace the current process. - */ -void -Proc_Exec(const ProcStuff *ps, struct Shell *shell) -{ - - if (ps->in != STDIN_FILENO) { - /* - * Redirect the child's stdin to the input fd - * and reset it to the beginning (again). - */ - if (dup2(ps->in, STDIN_FILENO) == -1) - Punt("Cannot dup2: %s", strerror(errno)); - lseek(STDIN_FILENO, (off_t)0, SEEK_SET); - } - - if (ps->out != STDOUT_FILENO) { - /* - * Redirect the child's stdout to the output fd. - */ - if (dup2(ps->out, STDOUT_FILENO) == -1) - Punt("Cannot dup2: %s", strerror(errno)); - close(ps->out); - } - - if (ps->err != STDERR_FILENO) { - /* - * Redirect the child's stderr to the err fd. - */ - if (dup2(ps->err, STDERR_FILENO) == -1) - Punt("Cannot dup2: %s", strerror(errno)); - close(ps->err); - } - - if (ps->merge_errors) { - /* - * Send stderr to parent process too. - */ - if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) - Punt("Cannot dup2: %s", strerror(errno)); - } - - if (shell->unsetenv) { - /* for the benfit of ksh */ - unsetenv("ENV"); - } - - /* - * The file descriptors for stdin, stdout, or stderr might - * have been marked close-on-exec. Clear the flag on all - * of them. - */ - fcntl(STDIN_FILENO, F_SETFD, - fcntl(STDIN_FILENO, F_GETFD) & (~FD_CLOEXEC)); - fcntl(STDOUT_FILENO, F_SETFD, - fcntl(STDOUT_FILENO, F_GETFD) & (~FD_CLOEXEC)); - fcntl(STDERR_FILENO, F_SETFD, - fcntl(STDERR_FILENO, F_GETFD) & (~FD_CLOEXEC)); - - if (ps->pgroup) { -#if defined(USE_PGRP) - /* - * Become a process group leader, so we can kill it and all - * its descendants in one fell swoop, by killing its process - * family, but not commit suicide. - */ -#if defined(SYSV) - setsid(); -#else - setpgid(0, getpid()); -#endif -#endif /* USE_PGRP */ - } - - if (ps->searchpath) { - execvp(ps->argv[0], ps->argv); - - write(STDERR_FILENO, ps->argv[0], strlen(ps->argv[0])); - write(STDERR_FILENO, ":", 1); - write(STDERR_FILENO, strerror(errno), strlen(strerror(errno))); - write(STDERR_FILENO, "\n", 1); - } else { - execv(shell->path, ps->argv); - - write(STDERR_FILENO, - "Could not execute shell\n", - sizeof("Could not execute shell")); - } - - /* - * Since we are the child process, exit without flushing buffers. - */ - _exit(1); -} diff --git a/usr.bin/make/proc.h b/usr.bin/make/proc.h deleted file mode 100644 index 353d0371c5..0000000000 --- a/usr.bin/make/proc.h +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * Copyright (C) 2005 Max Okumoto. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/proc.h,v 1.4 2005/07/13 20:41:43 okumoto Exp $ - */ - -#ifndef proc_h_458845848 -#define proc_h_458845848 - -struct Shell; - -/** - * Information used to create a new process. - */ -typedef struct ProcStuff { - int in; /* stdin for new process */ - int out; /* stdout for new process */ - int err; /* stderr for new process */ - - int merge_errors; /* true if stderr is redirected to stdin */ - int pgroup; /* true if new process a process leader */ - int searchpath; /* true if binary should be found via $PATH */ - - char **argv; - int argv_free; /* release argv after use */ - int errCheck; - - pid_t child_pid; - int child_status; /* returned by waitpid() */ -} ProcStuff; - -void Proc_Exec(const ProcStuff *, struct Shell *) __dead2; - -#endif /* proc_h_458845848 */ diff --git a/usr.bin/make/shell.c b/usr.bin/make/shell.c deleted file mode 100644 index 6471b21df8..0000000000 --- a/usr.bin/make/shell.c +++ /dev/null @@ -1,452 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/shell.c,v 1.25 2005/09/22 09:13:38 okumoto Exp $ - */ - -#include -#include - -#include "make.h" -#include "parse.h" -#include "pathnames.h" -#include "shell.h" -#include "str.h" -#include "util.h" - -/** - * Helper function for sorting the builtin list alphabetically. - */ -static int -sort_builtins(const void *p1, const void *p2) -{ - return (strcmp(*(const char* const*)p1, *(const char* const*)p2)); -} - -/** - * Free a shell structure and all associated strings. - */ -void -Shell_Destroy(Shell *sh) -{ - if (sh != NULL) { - free(sh->name); - free(sh->path); - free(sh->echoOff); - free(sh->echoOn); - free(sh->noPrint); - free(sh->errCheck); - free(sh->ignErr); - free(sh->echo); - free(sh->exit); - ArgArray_Done(&sh->builtins); - free(sh->meta); - free(sh); - } -} - -/** - * Dump a shell specification to stderr. - */ -void -Shell_Dump(const Shell *sh) -{ - int i; - - fprintf(stderr, "Shell %p:\n", sh); - fprintf(stderr, " name='%s' path='%s'\n", sh->name, sh->path); - fprintf(stderr, " hasEchoCtl=%d echoOff='%s' echoOn='%s'\n", - sh->hasEchoCtl, sh->echoOff, sh->echoOn); - fprintf(stderr, " noPrint='%s'\n", sh->noPrint); - fprintf(stderr, " hasErrCtl=%d errCheck='%s' ignErr='%s'\n", - sh->hasErrCtl, sh->errCheck, sh->ignErr); - fprintf(stderr, " echo='%s' exit='%s'\n", sh->echo, sh->exit); - fprintf(stderr, " builtins=%d\n", sh->builtins.argc - 1); - for (i = 1; i < sh->builtins.argc; i++) - fprintf(stderr, " '%s'", sh->builtins.argv[i]); - fprintf(stderr, "\n meta='%s'\n", sh->meta); - fprintf(stderr, " unsetenv=%d\n", sh->unsetenv); -} - -/** - * Parse a shell specification line and return the new Shell structure. - * In case of an error a message is printed and NULL is returned. - * - * Notes: - * A shell specification consists of a .SHELL target, with dependency - * operator, followed by a series of blank-separated words. Double - * quotes can be used to use blanks in words. A backslash escapes - * anything (most notably a double-quote and a space) and - * provides the functionality it does in C. Each word consists of - * keyword and value separated by an equal sign. There should be no - * unnecessary spaces in the word. The keywords are as follows: - * name Name of shell. - * path Location of shell. Overrides "name" if given - * quiet Command to turn off echoing. - * echo Command to turn echoing on - * filter Result of turning off echoing that shouldn't be - * printed. - * echoFlag Flag to turn echoing on at the start - * errFlag Flag to turn error checking on at the start - * hasErrCtl True if shell has error checking control - * check Command to turn on error checking if hasErrCtl - * is true or template of command to echo a command - * for which error checking is off if hasErrCtl is - * false. - * ignore Command to turn off error checking if hasErrCtl - * is true or template of command to execute a - * command so as to ignore any errors it returns if - * hasErrCtl is false. - * builtins A space separated list of builtins. If one - * of these builtins is detected when make wants - * to execute a command line, the command line is - * handed to the shell. Otherwise make may try to - * execute the command directly. If this list is empty - * it is assumed, that the command must always be - * handed over to the shell. - * meta The shell meta characters. If this is not specified - * or empty, commands are alway passed to the shell. - * Otherwise they are not passed when they contain - * neither a meta character nor a builtin command. - */ -static Shell * -ShellParseSpec(const char spec[], bool *fullSpec) -{ - ArgArray aa; - Shell *sh; - char *eq; - char *keyw; - int arg; - - *fullSpec = false; - - sh = emalloc(sizeof(*sh)); - memset(sh, 0, sizeof(*sh)); - ArgArray_Init(&sh->builtins); - - /* - * Parse the specification by keyword but skip the first word - */ - brk_string(&aa, spec, true); - - for (arg = 1; arg < aa.argc; arg++) { - /* - * Split keyword and value - */ - keyw = aa.argv[arg]; - if ((eq = strchr(keyw, '=')) == NULL) { - Parse_Error(PARSE_FATAL, "missing '=' in shell " - "specification keyword '%s'", keyw); - ArgArray_Done(&aa); - Shell_Destroy(sh); - return (NULL); - } - *eq++ = '\0'; - - if (strcmp(keyw, "path") == 0) { - free(sh->path); - sh->path = estrdup(eq); - } else if (strcmp(keyw, "name") == 0) { - free(sh->name); - sh->name = estrdup(eq); - } else if (strcmp(keyw, "quiet") == 0) { - free(sh->echoOff); - sh->echoOff = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "echo") == 0) { - free(sh->echoOn); - sh->echoOn = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "filter") == 0) { - free(sh->noPrint); - sh->noPrint = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "echoFlag") == 0) { - free(sh->echo); - sh->echo = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "errFlag") == 0) { - free(sh->exit); - sh->exit = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "hasErrCtl") == 0) { - sh->hasErrCtl = ( - *eq == 'Y' || *eq == 'y' || - *eq == 'T' || *eq == 't'); - *fullSpec = true; - } else if (strcmp(keyw, "check") == 0) { - free(sh->errCheck); - sh->errCheck = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "ignore") == 0) { - free(sh->ignErr); - sh->ignErr = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "builtins") == 0) { - ArgArray_Done(&sh->builtins); - brk_string(&sh->builtins, eq, true); - qsort(sh->builtins.argv + 1, sh->builtins.argc - 1, - sizeof(char *), sort_builtins); - *fullSpec = true; - } else if (strcmp(keyw, "meta") == 0) { - free(sh->meta); - sh->meta = estrdup(eq); - *fullSpec = true; - } else if (strcmp(keyw, "unsetenv") == 0) { - sh->unsetenv = ( - *eq == 'Y' || *eq == 'y' || - *eq == 'T' || *eq == 't'); - *fullSpec = true; - } else { - Parse_Error(PARSE_FATAL, "unknown keyword in shell " - "specification '%s'", keyw); - ArgArray_Done(&aa); - Shell_Destroy(sh); - return (NULL); - } - } - ArgArray_Done(&aa); - - /* - * Some checks (could be more) - */ - if (*fullSpec) { - if ((sh->echoOn != NULL) ^ (sh->echoOff != NULL)) { - Parse_Error(PARSE_FATAL, "Shell must have either both " - "echoOff and echoOn or none of them"); - Shell_Destroy(sh); - return (NULL); - } - - if (sh->echoOn != NULL && sh->echoOff != NULL) - sh->hasEchoCtl = true; - } - - return (sh); -} - -/** - * Find a matching shell in 'shells' given its final component. - * - * Descriptions for various shells. What the list of builtins should contain - * is debatable: either all builtins or only those which may specified on - * a single line without use of meta-characters. For correct makefiles that - * contain only correct command lines there is no difference. But if a command - * line, for example, is: 'if -foo bar' and there is an executable named 'if' - * in the path, the first possibility would execute that 'if' while in the - * second case the shell would give an error. Histerically only a small - * subset of the builtins and no reserved words where given in the list which - * corresponds roughly to the first variant. So go with this but add missing - * words. - * - * @result - * A pointer to a Shell structure, or NULL if no shell with - * the given name is found. - */ -Shell * -Shell_Match(const char name[]) -{ - Shell *shell; - const char *shellDir = PATH_DEFSHELLDIR; - - shell = emalloc(sizeof(Shell)); - - if (strcmp(name, "csh") == 0) { - /* - * CSH description. The csh can do echo control by playing - * with the setting of the 'echo' shell variable. Sadly, - * however, it is unable to do error control nicely. - */ - shell->name = strdup(name); - shell->path = str_concat(shellDir, '/', name); - shell->hasEchoCtl = true; - shell->echoOff = strdup("unset verbose"); - shell->echoOn = strdup("set verbose"); - shell->noPrint = strdup("unset verbose"); - shell->hasErrCtl = false; - shell->errCheck = strdup("echo \"%s\"\n"); - shell->ignErr = strdup("csh -c \"%s || exit 0\""); - shell->echo = strdup("v"); - shell->exit = strdup("e"); - shell->meta = strdup("#=|^(){};&<>*?[]:$`\\@\n"); - brk_string(&shell->builtins, - "alias cd eval exec exit read set ulimit unalias " - "umask unset wait", true); - shell->unsetenv = false; - - } else if (strcmp(name, "sh") == 0) { - /* - * SH description. Echo control is also possible and, under - * sun UNIX anyway, one can even control error checking. - */ - - shell->name = strdup(name); - shell->path = str_concat(shellDir, '/', name); - shell->hasEchoCtl = true; - shell->echoOff = strdup("set -"); - shell->echoOn = strdup("set -v"); - shell->noPrint = strdup("set -"); -#ifdef OLDBOURNESHELL - shell->hasErrCtl = false; - shell->errCheck = strdup("echo \"%s\"\n"); - shell->ignErr = strdup("sh -c '%s || exit 0'\n"); -#else - shell->hasErrCtl = true; - shell->errCheck = strdup("set -e"); - shell->ignErr = strdup("set +e"); -#endif - shell->echo = strdup("v"); - shell->exit = strdup("e"); - shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n"); - brk_string(&shell->builtins, - "alias cd eval exec exit read set ulimit unalias " - "umask unset wait", true); - shell->unsetenv = false; - - } else if (strcmp(name, "ksh") == 0) { - /* - * KSH description. The Korn shell has a superset of - * the Bourne shell's functionality. There are probably - * builtins missing here. - */ - shell->name = strdup(name); - shell->path = str_concat(shellDir, '/', name); - shell->hasEchoCtl = true; - shell->echoOff = strdup("set -"); - shell->echoOn = strdup("set -v"); - shell->noPrint = strdup("set -"); - shell->hasErrCtl = true; - shell->errCheck = strdup("set -e"); - shell->ignErr = strdup("set +e"); - shell->echo = strdup("v"); - shell->exit = strdup("e"); - shell->meta = strdup("#=|^(){};&<>*?[]:$`\\\n"); - brk_string(&shell->builtins, - "alias cd eval exec exit read set ulimit unalias " - "umask unset wait", true); - shell->unsetenv = true; - - } else { - free(shell); - shell = NULL; - } - - return (shell); -} - -/** - * Given the line following a .SHELL target, parse it as a shell - * specification. - * - * Results: - * A pointer to a Shell structure, or NULL if no the spec was invalid. - */ -Shell * -Shell_Parse(const char line[]) -{ - bool fullSpec; - Shell *sh; - - /* parse the specification */ - if ((sh = ShellParseSpec(line, &fullSpec)) == NULL) - return (NULL); - - if (sh->path == NULL) { - Shell *match; - /* - * If no path was given, the user wants one of the pre-defined - * shells, yes? So we find the one s/he wants with the help of - * ShellMatch and set things up the right way. - */ - if (sh->name == NULL) { - Parse_Error(PARSE_FATAL, - "Neither path nor name specified"); - Shell_Destroy(sh); - return (NULL); - } - if (fullSpec) { - Parse_Error(PARSE_FATAL, "No path specified"); - Shell_Destroy(sh); - return (NULL); - } - if ((match = Shell_Match(sh->name)) == NULL) { - Parse_Error(PARSE_FATAL, "%s: no matching shell", - sh->name); - Shell_Destroy(sh); - return (NULL); - } - - Shell_Destroy(sh); - return (match); - - } else { - Shell *match; - - /* - * The user provided a path. If s/he gave nothing else - * (fullSpec is false), try and find a matching shell in the - * ones we know of. Else we just take the specification at its - * word and copy it to a new location. In either case, we need - * to record the path the user gave for the shell. - */ - if (sh->name == NULL) { - /* get the base name as the name */ - if ((sh->name = strrchr(sh->path, '/')) == NULL) { - sh->name = estrdup(sh->path); - } else { - sh->name = estrdup(sh->name + 1); - } - } - if (fullSpec) { - return (sh); - } - if ((match = Shell_Match(sh->name)) == NULL) { - Parse_Error(PARSE_FATAL, - "%s: no matching shell", sh->name); - Shell_Destroy(sh); - return (NULL); - } - - free(match->path); - match->path = sh->path; - sh->path = NULL; - - Shell_Destroy(sh); - return (match); - } -} diff --git a/usr.bin/make/shell.h b/usr.bin/make/shell.h deleted file mode 100644 index a2a5b0b40d..0000000000 --- a/usr.bin/make/shell.h +++ /dev/null @@ -1,105 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1988, 1989 by Adam de Boor - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/shell.h,v 1.17 2005/09/22 09:13:38 okumoto Exp $ - */ - -#ifndef shell_h_6002e3b8 -#define shell_h_6002e3b8 - -#include - -#include "str.h" - -/** - * Shell Specifications: - * - * Some special stuff goes on if a shell doesn't have error control. In such - * a case, errCheck becomes a printf template for echoing the command, - * should echoing be on and ignErr becomes another printf template for - * executing the command while ignoring the return status. If either of these - * strings is empty when hasErrCtl is false, the command will be executed - * anyway as is and if it causes an error, so be it. - */ -typedef struct Shell { - /* - * the name of the shell. For Bourne and C shells, this is used - * only to find the shell description when used as the single - * source of a .SHELL target. For user-defined shells, this is - * the full path of the shell. - */ - char *name; - char *path; /* full path to the shell */ - - /* True if both echoOff and echoOn defined */ - bool hasEchoCtl; - - char *echoOff; /* command to turn off echo */ - char *echoOn; /* command to turn it back on */ - - /* - * What the shell prints, when given the echo-off command. - * This line will not be printed when received from the shell. - * This is usually the command which was executed to turn off echoing. - */ - char *noPrint; - - /* set if can control error checking for individual commands */ - bool hasErrCtl; - - /* string to turn error checking on */ - char *errCheck; - - /* string to turn off error checking */ - char *ignErr; - - char *echo; /* command line flag: echo commands */ - char *exit; /* command line flag: exit on error */ - - ArgArray builtins; /* ordered list of shell builtins */ - char *meta; /* shell meta characters */ - - bool unsetenv; /* unsetenv("ENV") before exec */ -} Shell; - -Shell *Shell_Match(const char []); -Shell *Shell_Parse(const char []); -void Shell_Destroy(Shell *); -void Shell_Dump(const struct Shell *); - -#endif /* shell_h_6002e3b8 */ diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c deleted file mode 100644 index e037fe043c..0000000000 --- a/usr.bin/make/str.c +++ /dev/null @@ -1,552 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)str.c 5.8 (Berkeley) 6/1/90 - * $FreeBSD: src/usr.bin/make/str.c,v 1.40 2005/02/07 07:54:23 harti Exp $ - * $DragonFly: src/usr.bin/make/str.c,v 1.39 2005/08/05 22:42:12 okumoto Exp $ - */ - -#include -#include -#include - -#include "buf.h" -#include "globals.h" -#include "str.h" -#include "util.h" - -/** - * Initialize the argument array object. The array is initially - * eight positions, and will be expaned as necessary. The first - * position is set to NULL since everything ignores it. We allocate - * (size + 1) since we need space for the terminating NULL. The - * buffer is set to NULL, since no common buffer is allocated yet. - */ -void -ArgArray_Init(ArgArray *aa) -{ - - aa->size = 8; - aa->argv = emalloc((aa->size + 1) * sizeof(char *)); - aa->argc = 0; - aa->argv[aa->argc++] = NULL; - aa->len = 0; - aa->buffer = NULL; -} - -/** - * Cleanup the memory allocated for in the argument array object. - */ -void -ArgArray_Done(ArgArray *aa) -{ - - if (aa->buffer == NULL) { - int i; - /* args are individually allocated */ - for (i = 0; i < aa->argc; ++i) { - if (aa->argv[i]) { - free(aa->argv[i]); - aa->argv[i] = NULL; - } - } - } else { - /* args are part of a single allocation */ - free(aa->buffer); - aa->buffer = NULL; - } - free(aa->argv); - aa->argv = NULL; - aa->argc = 0; - aa->size = 0; -} - -/** - * Concatenate the two strings, possibily inserting a character between them. - * - * @returns - * the resulting string in allocated space. - */ -char * -str_concat(const char s1[], char c, const char s2[]) -{ - int len1, len2; - char *result; - - /* get the length of both strings */ - len1 = strlen(s1); - len2 = strlen(s2); - - /* allocate length plus separator plus EOS */ - result = emalloc(len1 + len2 + 2); - - /* copy first string into place */ - memcpy(result, s1, len1); - - /* add separator character */ - if (c != '\0') { - result[len1] = c; - ++len1; - } - - /* copy second string plus EOS into place */ - memcpy(result + len1, s2, len2 + 1); - - return (result); -} - -/** - * Fracture a string into an array of words (as delineated by tabs or - * spaces) taking quotation marks into account. Leading tabs/spaces - * are ignored. - */ -void -brk_string(ArgArray *aa, const char str[], bool expand) -{ - char inquote; - char *start; - char *arg; - - /* skip leading space chars. */ - for (; *str == ' ' || *str == '\t'; ++str) - continue; - - ArgArray_Init(aa); - aa->buffer = estrdup(str); - - arg = aa->buffer; - start = arg; - inquote = '\0'; - - /* - * copy the string; at the same time, parse backslashes, - * quotes and build the argument list. - */ - for (;;) { - switch (str[0]) { - case '"': - case '\'': - if (inquote == '\0') { - inquote = str[0]; - if (expand) - break; - if (start == NULL) - start = arg; - } else if (inquote == str[0]) { - inquote = '\0'; - /* Don't miss "" or '' */ - if (start == NULL) - start = arg; - if (expand) - break; - } else { - /* other type of quote found */ - if (start == NULL) - start = arg; - } - *arg++ = str[0]; - break; - case ' ': - case '\t': - case '\n': - if (inquote) { - if (start == NULL) - start = arg; - *arg++ = str[0]; - break; - } - if (start == NULL) - break; - /* FALLTHROUGH */ - case '\0': - /* - * end of a token -- make sure there's enough argv - * space and save off a pointer. - */ - if (aa->argc == aa->size) { - aa->size *= 2; /* ramp up fast */ - aa->argv = erealloc(aa->argv, - (aa->size + 1) * sizeof(char *)); - } - - *arg++ = '\0'; - if (start == NULL) { - aa->argv[aa->argc] = start; - return; - } - if (str[0] == '\n' || str[0] == '\0') { - aa->argv[aa->argc++] = start; - aa->argv[aa->argc] = NULL; - return; - } else { - aa->argv[aa->argc++] = start; - start = NULL; - break; - } - case '\\': - if (start == NULL) - start = arg; - if (expand) { - switch (str[1]) { - case '\0': - case '\n': - /* hmmm; fix it up as best we can */ - *arg++ = '\\'; - break; - case 'b': - *arg++ = '\b'; - ++str; - break; - case 'f': - *arg++ = '\f'; - ++str; - break; - case 'n': - *arg++ = '\n'; - ++str; - break; - case 'r': - *arg++ = '\r'; - ++str; - break; - case 't': - *arg++ = '\t'; - ++str; - break; - default: - *arg++ = str[1]; - ++str; - break; - } - } else { - *arg++ = str[0]; - ++str; - *arg++ = str[0]; - } - break; - default: - if (start == NULL) - start = arg; - *arg++ = str[0]; - break; - } - ++str; - } -} - -/* - * Quote a string for appending it to MAKEFLAGS. According to Posix the - * kind of quoting here is implementation-defined. This quoting must ensure - * that the parsing of MAKEFLAGS's contents in a sub-shell yields the same - * options, option arguments and macro definitions as in the calling make. - * We simply quote all blanks, which according to Posix are space and tab - * in the POSIX locale. Don't use isblank because in that case makes with - * different locale settings could not communicate. We must also quote - * backslashes obviously. - */ -char * -MAKEFLAGS_quote(const char *str) -{ - char *ret, *q; - const char *p; - - /* assume worst case - everything has to be quoted */ - ret = emalloc(strlen(str) * 2 + 1); - - p = str; - q = ret; - while (*p != '\0') { - switch (*p) { - - case ' ': - case '\t': - *q++ = '\\'; - break; - - default: - break; - } - *q++ = *p++; - } - *q++ = '\0'; - return (ret); -} - -void -MAKEFLAGS_break(ArgArray *aa, const char str[]) -{ - char *arg; - char *start; - - ArgArray_Init(aa); - aa->buffer = strdup(str); - - arg = aa->buffer; - start = NULL; - - for (;;) { - switch (str[0]) { - case ' ': - case '\t': - /* word separator */ - if (start == NULL) { - /* not in a word */ - str++; - continue; - } - /* FALLTHRU */ - case '\0': - if (aa->argc == aa->size) { - aa->size *= 2; - aa->argv = erealloc(aa->argv, - (aa->size + 1) * sizeof(char *)); - } - - *arg++ = '\0'; - if (start == NULL) { - aa->argv[aa->argc] = start; - return; - } - if (str[0] == '\0') { - aa->argv[aa->argc++] = start; - aa->argv[aa->argc] = NULL; - return; - } else { - aa->argv[aa->argc++] = start; - start = NULL; - str++; - continue; - } - - case '\\': - if (str[1] == ' ' || str[1] == '\t') - str++; - break; - - default: - break; - } - if (start == NULL) - start = arg; - *arg++ = *str++; - } -} - -/* - * Str_Match -- - * - * See if a particular string matches a particular pattern. - * - * Results: Non-zero is returned if string matches pattern, 0 otherwise. The - * matching operation permits the following special characters in the - * pattern: *?\[] (see the man page for details on what these mean). - * - * Side effects: None. - */ -int -Str_Match(const char *string, const char *pattern) -{ - char c2; - - for (;;) { - /* - * See if we're at the end of both the pattern and the - * string. If, we succeeded. If we're at the end of the - * pattern but not at the end of the string, we failed. - */ - if (*pattern == 0) - return (!*string); - if (*string == 0 && *pattern != '*') - return (0); - /* - * Check for a "*" as the next pattern character. It matches - * any substring. We handle this by calling ourselves - * recursively for each postfix of string, until either we - * match or we reach the end of the string. - */ - if (*pattern == '*') { - pattern += 1; - if (*pattern == 0) - return (1); - while (*string != 0) { - if (Str_Match(string, pattern)) - return (1); - ++string; - } - return (0); - } - /* - * Check for a "?" as the next pattern character. It matches - * any single character. - */ - if (*pattern == '?') - goto thisCharOK; - /* - * Check for a "[" as the next pattern character. It is - * followed by a list of characters that are acceptable, or - * by a range (two characters separated by "-"). - */ - if (*pattern == '[') { - ++pattern; - for (;;) { - if ((*pattern == ']') || (*pattern == 0)) - return (0); - if (*pattern == *string) - break; - if (pattern[1] == '-') { - c2 = pattern[2]; - if (c2 == 0) - return (0); - if ((*pattern <= *string) && - (c2 >= *string)) - break; - if ((*pattern >= *string) && - (c2 <= *string)) - break; - pattern += 2; - } - ++pattern; - } - while ((*pattern != ']') && (*pattern != 0)) - ++pattern; - goto thisCharOK; - } - /* - * If the next pattern character is '/', just strip off the - * '/' so we do exact matching on the character that follows. - */ - if (*pattern == '\\') { - ++pattern; - if (*pattern == 0) - return (0); - } - /* - * There's no special character. Just make sure that the - * next characters of each string match. - */ - if (*pattern != *string) - return (0); -thisCharOK: ++pattern; - ++string; - } -} - - -/** - * Str_SYSVMatch - * Check word against pattern for a match (% is wild), - * - * Results: - * Returns the beginning position of a match or null. The number - * of characters matched is returned in len. - */ -const char * -Str_SYSVMatch(const char *word, const char *pattern, int *len) -{ - const char *m, *p, *w; - - p = pattern; - w = word; - - if (*w == '\0') { - /* Zero-length word cannot be matched against */ - *len = 0; - return (NULL); - } - - if (*p == '\0') { - /* Null pattern is the whole string */ - *len = strlen(w); - return (w); - } - - if ((m = strchr(p, '%')) != NULL) { - /* check that the prefix matches */ - for (; p != m && *w && *w == *p; w++, p++) - continue; - - if (p != m) - return (NULL); /* No match */ - - if (*++p == '\0') { - /* No more pattern, return the rest of the string */ - *len = strlen(w); - return (w); - } - } - - m = w; - - /* Find a matching tail */ - do - if (strcmp(p, w) == 0) { - *len = w - m; - return (m); - } - while (*w++ != '\0'); - - return (NULL); -} - - -/** - * Str_SYSVSubst - * Substitute '%' on the pattern with len characters from src. - * If the pattern does not contain a '%' prepend len characters - * from src. - * - * Side Effects: - * Places result on buf - */ -void -Str_SYSVSubst(Buffer *buf, const char *pat, const char *src, int len) -{ - const char *m; - - if ((m = strchr(pat, '%')) != NULL) { - /* Copy the prefix */ - Buf_AppendRange(buf, pat, m); - /* skip the % */ - pat = m + 1; - } - - /* Copy the pattern */ - Buf_AddBytes(buf, len, src); - - /* append the rest */ - Buf_Append(buf, pat); -} diff --git a/usr.bin/make/str.h b/usr.bin/make/str.h deleted file mode 100644 index 576602a82a..0000000000 --- a/usr.bin/make/str.h +++ /dev/null @@ -1,71 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/str.h,v 1.11 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef str_h_44db59e6 -#define str_h_44db59e6 - -#include - -struct Buffer; - -/** - * An array of c-strings. The pointers stored in argv, point to - * strings stored in buffer. - */ -typedef struct ArgArray { - int size; /* size of argv array */ - int argc; /* strings referenced in argv */ - char **argv; /* array of string pointers */ - size_t len; /* size of buffer */ - char *buffer; /* data buffer */ -} ArgArray; - -void ArgArray_Init(ArgArray *); -void ArgArray_Done(ArgArray *); - -char *str_concat(const char *, char, const char *); -void brk_string(ArgArray *, const char [], bool); -char *MAKEFLAGS_quote(const char *); -void MAKEFLAGS_break(ArgArray *, const char []); -int Str_Match(const char *, const char *); -const char *Str_SYSVMatch(const char *, const char *, int *); -void Str_SYSVSubst(struct Buffer *, const char *, const char *, int); - -#endif /* str_h_44db59e6 */ diff --git a/usr.bin/make/suff.c b/usr.bin/make/suff.c deleted file mode 100644 index 4f10690bb5..0000000000 --- a/usr.bin/make/suff.c +++ /dev/null @@ -1,2203 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)suff.c 8.4 (Berkeley) 3/21/94 - * $FreeBSD: src/usr.bin/make/suff.c,v 1.43 2005/02/04 13:23:39 harti Exp $ - * $DragonFly: src/usr.bin/make/suff.c,v 1.66 2005/09/24 07:37:01 okumoto Exp $ - */ - -/*- - * suff.c -- - * Functions to maintain suffix lists and find implicit dependents - * using suffix transformation rules - * - * Interface: - * Suff_Init Initialize all things to do with suffixes. - * - * Suff_DoPaths This function is used to make life easier - * when searching for a file according to its - * suffix. It takes the global search path, - * as defined using the .PATH: target, and appends - * its directories to the path of each of the - * defined suffixes, as specified using - * .PATH: targets. In addition, all - * directories given for suffixes labeled as - * include files or libraries, using the .INCLUDES - * or .LIBS targets, are played with using - * Dir_MakeFlags to create the .INCLUDES and - * .LIBS global variables. - * - * Suff_ClearSuffixes Clear out all the suffixes and defined - * transformations. - * - * Suff_IsTransform Return true if the passed string is the lhs - * of a transformation rule. - * - * Suff_AddSuffix Add the passed string as another known suffix. - * - * Suff_GetPath Return the search path for the given suffix. - * - * Suff_AddInclude Mark the given suffix as denoting an include - * file. - * - * Suff_AddLib Mark the given suffix as denoting a library. - * - * Suff_AddTransform Add another transformation to the suffix - * graph. Returns GNode suitable for framing, I - * mean, tacking commands, attributes, etc. on. - * - * Suff_SetNull Define the suffix to consider the suffix of - * any file that doesn't have a known one. - * - * Suff_FindDeps Find implicit sources for and the location of - * a target based on its suffix. Returns the - * bottom-most node added to the graph or NULL - * if the target had no implicit sources. - */ - -#include -#include -#include -#include - -#include "arch.h" -#include "buf.h" -#include "config.h" -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "lst.h" -#include "make.h" -#include "parse.h" -#include "str.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/** - * The suffix used to denote libraries and is used by the Suff module - * to find the search path on which to seek any -l targets. - */ -#define LIBSUFF ".a" - -/* Lst of suffixes */ -static Lst sufflist = Lst_Initializer(sufflist); - -/* Lst of suffixes to be cleaned */ -static Lst suffClean = Lst_Initializer(suffClean); - -/* Lst of sources */ -static Lst srclist = Lst_Initializer(srclist); - -/* Lst of transformation rules */ -static Lst transforms = Lst_Initializer(transforms); - -/* Counter for assigning suffix numbers */ -static int sNum = 0; - -/* - * Structure describing an individual suffix. - */ -typedef struct Suff { - char *name; /* The suffix itself */ - int nameLen; /* Length of the suffix */ - short flags; /* Type of suffix */ -#define SUFF_INCLUDE 0x01 /* One which is #include'd */ -#define SUFF_LIBRARY 0x02 /* One which contains a library */ -#define SUFF_NULL 0x04 /* The empty suffix */ - struct Path searchPath; /* Path for files with this suffix */ - int sNum; /* The suffix number */ - int refCount; /* Reference count of list membership */ - Lst parents; /* Suffixes we have a transformation to */ - Lst children; /* Suffixes we have a transformation from */ - Lst ref; /* List of lists this suffix is referenced */ -} Suff; - -/* - * Structure used in the search for implied sources. - */ -typedef struct Src { - char *file; /* The file to look for */ - char *pref; /* Prefix from which file was formed */ - Suff *suff; /* The suffix on the file */ - struct Src *parent; /* The Src for which this is a source */ - GNode *node; /* The node describing the file */ - int children; /* Count of existing children (so we don't free - * this thing too early or never nuke it) */ -#ifdef DEBUG_SRC - Lst cp; /* Debug; children list */ -#endif -} Src; - -/* The NULL suffix for this run */ -static Suff *suffNull; - -/* The empty suffix required for POSIX single-suffix transformation rules */ -static Suff *emptySuff; - -static void SuffFindDeps(GNode *, Lst *); - - -/*- - *----------------------------------------------------------------------- - * SuffSuffIsSuffix -- - * See if suff is a suffix of str. - * - * Results: - * NULL if it ain't, pointer to character in str before suffix if - * it is. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static char * -SuffSuffIsSuffix(const Suff *s, char *str) -{ - const char *p1; /* Pointer into suffix name */ - char *p2; /* Pointer into string being examined */ - size_t len; - - len = strlen(str); - p1 = s->name + s->nameLen; - p2 = str + len; - - while (p1 >= s->name && len > 0 && *p1 == *p2) { - p1--; - p2--; - len--; - } - - return (p1 == s->name - 1 ? p2 : NULL); -} - -/*- - *----------------------------------------------------------------------- - * SuffSuffFind -- - * Find a suffix given its name. - * - * Results: - * The suffix or NULL. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static Suff * -SuffSuffFind(const char *s) -{ - LstNode *ln; - - LST_FOREACH(ln, &sufflist) { - if (strcmp(s, ((const Suff *)Lst_Datum(ln))->name) == 0) - return (Lst_Datum(ln)); - } - return (NULL); -} - -/*- - *----------------------------------------------------------------------- - * SuffTransFind - * Find a transform. - * - * Results: - * transform or NULL. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static GNode * -SuffTransFind(const char *name) -{ - LstNode *ln; - - LST_FOREACH(ln, &transforms) { - if (strcmp(name, ((const GNode *)Lst_Datum(ln))->name) == 0) - return (Lst_Datum(ln)); - } - return (NULL); -} - - /*********** Maintenance Functions ************/ - -#if 0 -/* - * Keep this function for now until it is clear why a .SUFFIXES: doesn't - * actually delete the suffixes but just puts them on the suffClean list. - */ -/*- - *----------------------------------------------------------------------- - * SuffFree -- - * Free up all memory associated with the given suffix structure. - * - * Results: - * none - * - * Side Effects: - * the suffix entry is detroyed - *----------------------------------------------------------------------- - */ -static void -SuffFree(void *sp) -{ - Suff *s = sp; - - if (s == suffNull) - suffNull = NULL; - - if (s == emptySuff) - emptySuff = NULL; - - Lst_Destroy(&s->ref, NOFREE); - Lst_Destroy(&s->children, NOFREE); - Lst_Destroy(&s->parents, NOFREE); - Lst_Destroy(&s->searchPath, Dir_Destroy); - - free(s->name); - free(s); -} -#endif - -/*- - *----------------------------------------------------------------------- - * SuffRemove -- - * Remove the suffix into the list - * - * Results: - * None - * - * Side Effects: - * The reference count for the suffix is decremented - *----------------------------------------------------------------------- - */ -static void -SuffRemove(Lst *l, Suff *s) -{ - LstNode *ln = Lst_Member(l, s); - - if (ln != NULL) { - Lst_Remove(l, ln); - s->refCount--; - } -} - -/*- - *----------------------------------------------------------------------- - * SuffInsert -- - * Insert the suffix into the list keeping the list ordered by suffix - * numbers. - * - * Results: - * None - * - * Side Effects: - * The reference count of the suffix is incremented - *----------------------------------------------------------------------- - */ -static void -SuffInsert(Lst *l, Suff *s) -{ - LstNode *ln; /* current element in l we're examining */ - Suff *s2; /* the suffix descriptor in this element */ - - s2 = NULL; - for (ln = Lst_First(l); ln != NULL; ln = Lst_Succ(ln)) { - s2 = Lst_Datum(ln); - if (s2->sNum >= s->sNum) - break; - } - if (s2 == NULL) { - DEBUGF(SUFF, ("inserting an empty list?...")); - } - - DEBUGF(SUFF, ("inserting %s(%d)...", s->name, s->sNum)); - if (ln == NULL) { - DEBUGF(SUFF, ("at end of list\n")); - Lst_AtEnd(l, s); - s->refCount++; - Lst_AtEnd(&s->ref, l); - } else if (s2->sNum != s->sNum) { - DEBUGF(SUFF, ("before %s(%d)\n", s2->name, s2->sNum)); - Lst_Insert(l, ln, s); - s->refCount++; - Lst_AtEnd(&s->ref, l); - } else { - DEBUGF(SUFF, ("already there\n")); - } -} - -/*- - *----------------------------------------------------------------------- - * Suff_ClearSuffixes -- - * This is gross. Nuke the list of suffixes but keep all transformation - * rules around. The transformation graph is destroyed in this process, - * but we leave the list of rules so when a new graph is formed the rules - * will remain. - * This function is called from the parse module when a - * .SUFFIXES:\n line is encountered. - * - * Results: - * none - * - * Side Effects: - * the sufflist and its graph nodes are destroyed - *----------------------------------------------------------------------- - */ -void -Suff_ClearSuffixes(void) -{ - - Lst_Concat(&suffClean, &sufflist, LST_CONCLINK); - - sNum = 1; - suffNull = emptySuff; - /* - * Clear suffNull's children list (the other suffixes are built new, but - * suffNull is used as is). - * NOFREE is used because all suffixes are are on the suffClean list. - * suffNull should not have parents. - */ - Lst_Destroy(&suffNull->children, NOFREE); -} - -/*- - *----------------------------------------------------------------------- - * SuffParseTransform -- - * Parse a transformation string to find its two component suffixes. - * - * Results: - * true if the string is a valid transformation and false otherwise. - * - * Side Effects: - * The passed pointers are overwritten. - * - *----------------------------------------------------------------------- - */ -static bool -SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) -{ - LstNode *srcLn; /* element in suffix list of trans source*/ - Suff *src; /* Source of transformation */ - char *str2; /* Extra pointer (maybe target suffix) */ - LstNode *singleLn; /* element in suffix list of any suffix - * that exactly matches str */ - Suff *single = NULL; /* Source of possible transformation to - * null suffix */ - - singleLn = NULL; - - /* - * Loop looking first for a suffix that matches the start of the - * string and then for one that exactly matches the rest of it. If - * we can find two that meet these criteria, we've successfully - * parsed the string. - */ - srcLn = Lst_First(&sufflist); - for (;;) { - /* advance to next possible suffix */ - while (srcLn != NULL) { - src = Lst_Datum(srcLn); - if (strncmp(str, src->name, strlen(src->name)) == 0) - break; - srcLn = LST_NEXT(srcLn); - } - - if (srcLn == NULL) { - /* - * Ran out of source suffixes -- no such rule - */ - if (singleLn != NULL) { - /* - * Not so fast Mr. Smith! There was a suffix - * that encompassed the entire string, so we - * assume it was a transformation to the null - * suffix (thank you POSIX). We still prefer to - * find a double rule over a singleton, hence we - * leave this check until the end. - * - * XXX: Use emptySuff over suffNull? - */ - *srcPtr = single; - *targPtr = suffNull; - return (true); - } - return (false); - } - str2 = str + src->nameLen; - if (*str2 == '\0') { - single = src; - singleLn = srcLn; - } else { - - *targPtr = SuffSuffFind(str2); - if (*targPtr != NULL) { - *srcPtr = src; - return (true); - } - } - /* next one */ - srcLn = LST_NEXT(srcLn); - } -} - -/*- - *----------------------------------------------------------------------- - * Suff_IsTransform -- - * Return true if the given string is a transformation rule - * - * - * Results: - * true if the string is a concatenation of two known suffixes. - * false otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -bool -Suff_IsTransform(char *str) -{ - Suff *src, *targ; - - return (SuffParseTransform(str, &src, &targ)); -} - -/*- - *----------------------------------------------------------------------- - * Suff_AddTransform -- - * Add the transformation rule described by the line to the - * list of rules and place the transformation itself in the graph - * - * Results: - * The node created for the transformation in the transforms list - * - * Side Effects: - * The node is placed on the end of the transforms Lst and links are - * made between the two suffixes mentioned in the target name - *----------------------------------------------------------------------- - */ -GNode * -Suff_AddTransform(char *line) -{ - GNode *gn; /* GNode of transformation rule */ - Suff *s; /* source suffix */ - Suff *t; /* target suffix */ - - gn = SuffTransFind(line); - if (gn == NULL) { - /* - * Make a new graph node for the transformation. - * It will be filled in by the Parse module. - */ - gn = Targ_NewGN(line); - Lst_AtEnd(&transforms, gn); - } else { - /* - * New specification for transformation rule. Just nuke the - * old list of commands so they can be filled in again... - * We don't actually free the commands themselves, because a - * given command can be attached to several different - * transformations. - */ - Lst_Destroy(&gn->commands, NOFREE); - Lst_Destroy(&gn->children, NOFREE); - } - - gn->type = OP_TRANSFORM; - - SuffParseTransform(line, &s, &t); - - /* - * link the two together in the proper relationship and order - */ - DEBUGF(SUFF, ("defining transformation from `%s' to `%s'\n", - s->name, t->name)); - SuffInsert(&t->children, s); - SuffInsert(&s->parents, t); - - return (gn); -} - -/*- - *----------------------------------------------------------------------- - * Suff_EndTransform -- - * Handle the finish of a transformation definition, removing the - * transformation from the graph if it has neither commands nor - * sources. This is called from the Parse module at the end of - * a dependency block. - * - * Side Effects: - * If the node has no commands or children, the children and parents - * lists of the affected suffices are altered. - * - *----------------------------------------------------------------------- - */ -void -Suff_EndTransform(const GNode *gn) -{ - Suff *s, *t; - - if (!Lst_IsEmpty(&gn->commands) || !Lst_IsEmpty(&gn->children)) { - DEBUGF(SUFF, ("transformation %s complete\n", gn->name)); - return; - } - - /* - * SuffParseTransform() may fail for special rules which are not - * actual transformation rules (e.g., .DEFAULT). - */ - if (!SuffParseTransform(gn->name, &s, &t)) - return; - - DEBUGF(SUFF, ("deleting transformation from `%s' to `%s'\n", - s->name, t->name)); - - /* - * Remove the source from the target's children list. We check - * for a NULL return to handle a beanhead saying something like - * .c.o .c.o: - * - * We'll be called twice when the next target is seen, but .c - * and .o are only linked once... - */ - SuffRemove(&t->children, s); - - /* - * Remove the target from the source's parents list - */ - SuffRemove(&s->parents, t); -} - -/*- - *----------------------------------------------------------------------- - * SuffRebuildGraph -- - * Called from Suff_AddSuffix via LST_FOREACH to search through the - * list of existing transformation rules and rebuild the transformation - * graph when it has been destroyed by Suff_ClearSuffixes. If the - * given rule is a transformation involving this suffix and another, - * existing suffix, the proper relationship is established between - * the two. - * - * Side Effects: - * The appropriate links will be made between this suffix and - * others if transformation rules exist for it. - * - *----------------------------------------------------------------------- - */ -static void -SuffRebuildGraph(const GNode *transform, Suff *s) -{ - char *cp; - Suff *s2 = NULL; - - /* - * First see if it is a transformation from this suffix. - */ - if (strncmp(transform->name, s->name, strlen(s->name)) == 0) { - cp = transform->name + strlen(s->name); - - if (cp[0] == '\0') /* null rule */ - s2 = suffNull; - else - s2 = SuffSuffFind(cp); - if (s2 != NULL) { - /* - * Found target. Link in and return, since it can't be - * anything else. - */ - SuffInsert(&s2->children, s); - SuffInsert(&s->parents, s2); - return; - } - } - - /* - * Not from, maybe to? - */ - cp = SuffSuffIsSuffix(s, transform->name); - if (cp != NULL) { - /* - * Null-terminate the source suffix in order to find it. - */ - cp[1] = '\0'; - s2 = SuffSuffFind(transform->name); - - /* - * Replace the start of the target suffix - */ - cp[1] = s->name[0]; - if (s2 != NULL) { - /* - * Found it -- establish the proper relationship - */ - SuffInsert(&s->children, s2); - SuffInsert(&s2->parents, s); - } - } -} - -/*- - *----------------------------------------------------------------------- - * Suff_AddSuffix -- - * Add the suffix in string to the end of the list of known suffixes. - * Should we restructure the suffix graph? Make doesn't... - * - * Results: - * None - * - * Side Effects: - * A GNode is created for the suffix and a Suff structure is created and - * added to the suffixes list unless the suffix was already known. - *----------------------------------------------------------------------- - */ -void -Suff_AddSuffix(char *str) -{ - Suff *s; /* new suffix descriptor */ - LstNode *ln; - - if (SuffSuffFind(str) != NULL) - /* - * Already known - */ - return; - - s = emalloc(sizeof(Suff)); - - s->name = estrdup(str); - s->nameLen = strlen(s->name); - TAILQ_INIT(&s->searchPath); - Lst_Init(&s->children); - Lst_Init(&s->parents); - Lst_Init(&s->ref); - s->sNum = sNum++; - s->flags = 0; - s->refCount = 0; - - Lst_AtEnd(&sufflist, s); - - /* - * Look for any existing transformations from or to this suffix. - * XXX: Only do this after a Suff_ClearSuffixes? - */ - LST_FOREACH(ln, &transforms) - SuffRebuildGraph(Lst_Datum(ln), s); -} - -/*- - *----------------------------------------------------------------------- - * Suff_GetPath -- - * Return the search path for the given suffix, if it's defined. - * - * Results: - * The searchPath for the desired suffix or NULL if the suffix isn't - * defined. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -struct Path * -Suff_GetPath(char *sname) -{ - Suff *s; - - s = SuffSuffFind(sname); - if (s == NULL) - return (NULL); - return (&s->searchPath); -} - -/*- - *----------------------------------------------------------------------- - * Suff_DoPaths -- - * Extend the search paths for all suffixes to include the default - * search path. - * - * Results: - * None. - * - * Side Effects: - * The searchPath field of all the suffixes is extended by the - * directories in dirSearchPath. If paths were specified for the - * ".h" suffix, the directories are stuffed into a global variable - * called ".INCLUDES" with each directory preceded by a -I. The same - * is done for the ".a" suffix, except the variable is called - * ".LIBS" and the flag is -L. - *----------------------------------------------------------------------- - */ -void -Suff_DoPaths(void) -{ - Suff *s; - LstNode *ln; - char *ptr; - struct Path inIncludes; /* Cumulative .INCLUDES path */ - struct Path inLibs; /* Cumulative .LIBS path */ - - TAILQ_INIT(&inIncludes); - TAILQ_INIT(&inLibs); - - for (ln = Lst_First(&sufflist); ln != NULL; ln = Lst_Succ(ln)) { - s = Lst_Datum(ln); -#ifdef INCLUDES - if (s->flags & SUFF_INCLUDE) { - Path_Concat(&inIncludes, &s->searchPath); - } -#endif /* INCLUDES */ -#ifdef LIBRARIES - if (s->flags & SUFF_LIBRARY) { - Path_Concat(&inLibs, &s->searchPath); - } -#endif /* LIBRARIES */ - Path_Concat(&s->searchPath, &dirSearchPath); - } - - ptr = Path_MakeFlags("-I", &inIncludes); - Var_SetGlobal(".INCLUDES", ptr); - free(ptr); - - ptr = Path_MakeFlags("-L", &inLibs); - Var_SetGlobal(".LIBS", ptr); - free(ptr); - - Path_Clear(&inIncludes); - Path_Clear(&inLibs); -} - -/*- - *----------------------------------------------------------------------- - * Suff_AddInclude -- - * Add the given suffix as a type of file which gets included. - * Called from the parse module when a .INCLUDES line is parsed. - * The suffix must have already been defined. - * - * Results: - * None. - * - * Side Effects: - * The SUFF_INCLUDE bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- - */ -void -Suff_AddInclude(char *sname) -{ - Suff *s; - - if ((s = SuffSuffFind(sname)) != NULL) - s->flags |= SUFF_INCLUDE; -} - -/*- - *----------------------------------------------------------------------- - * Suff_AddLib -- - * Add the given suffix as a type of file which is a library. - * Called from the parse module when parsing a .LIBS line. The - * suffix must have been defined via .SUFFIXES before this is - * called. - * - * Results: - * None. - * - * Side Effects: - * The SUFF_LIBRARY bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- - */ -void -Suff_AddLib(char *sname) -{ - Suff *s; - - if ((s = SuffSuffFind(sname)) != NULL) - s->flags |= SUFF_LIBRARY; -} - -/* - * Create a new Src structure - */ -static Src * -SuffSrcCreate(char *file, char *prefix, Suff *suff, Src *parent, GNode *node) -{ - Src *s; - - s = emalloc(sizeof(*s)); - s->file = file; - s->pref = prefix; - s->suff = suff; - s->parent = parent; - s->node = node; - s->children = 0; - -#ifdef DEBUG_SRC - Lst_Init(&s->cp); -#endif - - return (s); -} - - /********** Implicit Source Search Functions *********/ - -/*- - *----------------------------------------------------------------------- - * SuffAddLevel -- - * Add all the children of targ as Src structures to the given list: - * Add a suffix as a Src structure to the given list with its parent - * being the given Src structure. If the suffix is the null suffix, - * the prefix is used unaltered as the file name in the Src structure. - * - * Results: - * None - * - * Side Effects: - * Lots of structures are created and added to the list - *----------------------------------------------------------------------- - */ -static void -SuffAddLevel(Lst *l, Src *targ) -{ - LstNode *ln; - Suff *suff; - Src *s2; -#ifdef DEBUG_SRC - const LstNode *ln1; -#endif - - LST_FOREACH(ln, &targ->suff->children) { - suff = Lst_Datum(ln); - - if ((suff->flags & SUFF_NULL) && *suff->name != '\0') { - /* - * If the suffix has been marked as the NULL suffix, - * also create a Src structure for a file with no suffix - * attached. Two birds, and all that... - */ - s2 = SuffSrcCreate(estrdup(targ->pref), targ->pref, - suff, targ, NULL); - suff->refCount++; - targ->children += 1; - Lst_AtEnd(l, s2); -#ifdef DEBUG_SRC - Lst_AtEnd(&targ->cp, s2); - printf("1 add %p %p to %p:", targ, s2, l); - LST_FOREACH(ln1, l) - printf("%p ", (const void *)Lst_Datum(ln1)); - printf("\n"); -#endif - } - s2 = SuffSrcCreate( - str_concat(targ->pref, '\0', suff->name), - targ->pref, suff, targ, NULL); - suff->refCount++; - targ->children += 1; - Lst_AtEnd(l, s2); -#ifdef DEBUG_SRC - Lst_AtEnd(&targ->cp, s2); - printf("2 add %p %p to %p:", targ, s2, l); - LST_FOREACH(ln1, l) - printf("%p ", (const void *)Lst_Datum(ln1)); - printf("\n"); -#endif - } -} - -/*- - *---------------------------------------------------------------------- - * SuffRemoveSrc -- - * Free all src structures in list that don't have a reference count - * XXX this actually frees only the first of these. - * - * Results: - * True if a src was removed - * - * Side Effects: - * The memory is free'd. - *---------------------------------------------------------------------- - */ -static int -SuffRemoveSrc(Lst *l) -{ - LstNode *ln, *ln1; - Src *s; - int t = 0; - -#ifdef DEBUG_SRC - printf("cleaning %lx: ", (unsigned long) l); - LST_FOREACH(ln, l) - printf("%p ", (const void *)Lst_Datum(ln)); - printf("\n"); -#endif - - for (ln = Lst_First(l); ln != NULL; ln = ln1) { - ln1 = Lst_Succ(ln); - - s = (Src *)Lst_Datum(ln); - if (s->children == 0) { - free(s->file); - if (!s->parent) - free(s->pref); - else { -#ifdef DEBUG_SRC - LstNode *ln = Lst_Member(&s->parent->cp, s); - if (ln != NULL) - Lst_Remove(&s->parent->cp, ln); -#endif - --s->parent->children; - } -#ifdef DEBUG_SRC - printf("free: [l=%p] p=%p %d\n", l, s, s->children); - Lst_Destroy(&s->cp, NOFREE); -#endif - Lst_Remove(l, ln); - free(s); - t |= 1; - return (true); - } -#ifdef DEBUG_SRC - else { - const LstNode *tln; - - printf("keep: [l=%p] p=%p %d: ", l, s, s->children); - LST_FOREACH(tln, &s->cp) - printf("%p ", (const void *)Lst_Datum(tln)); - printf("\n"); - } -#endif - } - - return (t); -} - -/*- - *----------------------------------------------------------------------- - * SuffFindThem -- - * Find the first existing file/target in the list srcs - * - * Results: - * The lowest structure in the chain of transformations - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static Src * -SuffFindThem(Lst *srcs, Lst *slst) -{ - Src *s; /* current Src */ - Src *rs; /* returned Src */ - char *ptr; - - rs = NULL; - - while (!Lst_IsEmpty (srcs)) { - s = Lst_DeQueue(srcs); - - DEBUGF(SUFF, ("\ttrying %s...", s->file)); - - /* - * A file is considered to exist if either a node exists in the - * graph for it or the file actually exists. - */ - if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) { -#ifdef DEBUG_SRC - printf("remove %p from %p\n", s, srcs); -#endif - rs = s; - break; - } - - if ((ptr = Path_FindFile(s->file, - &s->suff->searchPath)) != NULL) { - rs = s; -#ifdef DEBUG_SRC - printf("remove %p from %p\n", s, srcs); -#endif - free(ptr); - break; - } - - DEBUGF(SUFF, ("not there\n")); - - SuffAddLevel(srcs, s); - Lst_AtEnd(slst, s); - } - - if (rs) { - DEBUGF(SUFF, ("got it\n")); - } - return (rs); -} - -/*- - *----------------------------------------------------------------------- - * SuffFindCmds -- - * See if any of the children of the target in the Src structure is - * one from which the target can be transformed. If there is one, - * a Src structure is put together for it and returned. - * - * Results: - * The Src structure of the "winning" child, or NULL if no such beast. - * - * Side Effects: - * A Src structure may be allocated. - * - *----------------------------------------------------------------------- - */ -static Src * -SuffFindCmds(Src *targ, Lst *slst) -{ - LstNode *ln; /* General-purpose list node */ - GNode *t; /* Target GNode */ - GNode *s; /* Source GNode */ - int prefLen;/* The length of the defined prefix */ - Suff *suff; /* Suffix on matching beastie */ - Src *ret; /* Return value */ - char *cp; - - t = targ->node; - prefLen = strlen(targ->pref); - - for (ln = Lst_First(&t->children); ln != NULL; ln = Lst_Succ(ln)) { - s = Lst_Datum(ln); - - cp = strrchr(s->name, '/'); - if (cp == NULL) { - cp = s->name; - } else { - cp++; - } - if (strncmp(cp, targ->pref, prefLen) == 0) { - /* - * The node matches the prefix ok, see if it has - * a known suffix. - */ - suff = SuffSuffFind(&cp[prefLen]); - if (suff != NULL) { - /* - * It even has a known suffix, see if there's - * a transformation defined between the node's - * suffix and the target's suffix. - * - * XXX: Handle multi-stage transformations - * here, too. - */ - if (Lst_Member(&suff->parents, - targ->suff) != NULL) { - /* - * Hot Damn! Create a new Src structure - * to describe this transformation - * (making sure to duplicate the - * source node's name so Suff_FindDeps - * can free it again (ick)), and return - * the new structure. - */ - ret = SuffSrcCreate(estrdup(s->name), - targ->pref, suff, targ, s); - suff->refCount++; - targ->children += 1; -#ifdef DEBUG_SRC - printf("3 add %p %p\n", &targ, ret); - Lst_AtEnd(&targ->cp, ret); -#endif - Lst_AtEnd(slst, ret); - DEBUGF(SUFF, ("\tusing existing source " - "%s\n", s->name)); - return (ret); - } - } - } - } - return (NULL); -} - -/*- - * The child node contains variable references. Expand them and return - * a list of expansions. - */ -static void -SuffExpandVariables(GNode *parent, GNode *child, Lst *members) -{ - Buffer *buf; - char *cp; - char *start; - - Lst_Init(members); - - DEBUGF(SUFF, ("Expanding \"%s\"...", child->name)); - buf = Var_Subst(child->name, parent, true); - cp = Buf_Data(buf); - - if (child->type & OP_ARCHV) { - /* - * Node was an archive(member) target, so we - * want to call on the Arch module to find the - * nodes for us, expanding variables in the - * parent's context. - */ - Arch_ParseArchive(&cp, members, parent); - Buf_Destroy(buf, true); - return; - } - /* - * Break the result into a vector of strings whose nodes we can find, - * then add those nodes to the members list. - */ - for (start = cp; *start == ' ' || *start == '\t'; start++) - ; - - for (cp = start; *cp != '\0'; cp++) { - if (*cp == ' ' || *cp == '\t') { - /* - * White-space -- terminate element, find the node, - * add it, skip any further spaces. - */ - *cp++ = '\0'; - Lst_AtEnd(members, Targ_FindNode(start, TARG_CREATE)); - - while (*cp == ' ' || *cp == '\t') { - cp++; - } - /* - * Adjust cp for increment at - * start of loop, but set start - * to first non-space. - */ - start = cp--; - - } else if (*cp == '$') { - /* - * Start of a variable spec -- contact variable module - * to find the end so we can skip over it. - */ - char *junk; - size_t len = 0; - bool doFree; - - junk = Var_Parse(cp, parent, true, &len, &doFree); - if (junk != var_Error) { - cp += len - 1; - } - if (doFree) { - free(junk); - } - - } else if (*cp == '\\' && *cp != '\0') { - /* - * Escaped something -- skip over it - */ - cp++; - } - } - - if (cp != start) { - /* - * Stuff left over -- add it to the - * list too - */ - Lst_AtEnd(members, Targ_FindNode(start, TARG_CREATE)); - } - - Buf_Destroy(buf, true); -} - -/*- - * The child node contains wildcards. Expand them and return a list of - * expansions. - */ -static void -SuffExpandWildcards(GNode *child, Lst *members) -{ - char *cp; - Lst exp; /* List of expansions */ - LstNode *ln; - struct Path *path; /* Search path along which to expand */ - - Lst_Init(members); - - /* - * Find a path along which to expand the word. - * - * If the word has a known suffix, use that path. - * If it has no known suffix and we're allowed to use the null - * suffix, use its path. - * Else use the default system search path. - */ - LST_FOREACH(ln, &sufflist) { - if (SuffSuffIsSuffix(Lst_Datum(ln), child->name) != NULL) - break; - } - - DEBUGF(SUFF, ("Wildcard expanding \"%s\"...", child->name)); - - if (ln != NULL) { - Suff *s = Lst_Datum(ln); - - DEBUGF(SUFF, ("suffix is \"%s\"...", s->name)); - path = &s->searchPath; - } else { - /* - * Use default search path - */ - path = &dirSearchPath; - } - - /* - * Expand the word along the chosen path - */ - Lst_Init(&exp); - Path_Expand(child->name, path, &exp); - - while (!Lst_IsEmpty(&exp)) { - /* - * Fetch next expansion off the list and find its GNode - */ - cp = Lst_DeQueue(&exp); - - DEBUGF(SUFF, ("%s...", cp)); - Lst_AtEnd(members, Targ_FindNode(cp, TARG_CREATE)); - } -} - -/*- - *----------------------------------------------------------------------- - * SuffExpandChildren -- - * Expand the names of any children of a given node that contain - * variable invocations or file wildcards into actual targets. - * - * Results: - * == 0 (continue) - * - * Side Effects: - * The expanded node is removed from the parent's list of children, - * and the parent's unmade counter is decremented, but other nodes - * may be added. - * - *----------------------------------------------------------------------- - */ -static void -SuffExpandChildren(GNode *parent, LstNode *current) -{ - GNode *cchild; /* current child */ - GNode *gn; - LstNode *prev; /* node after which to append new source */ - Lst members; /* expanded nodes */ - - if (current == NULL) { - /* start from begin of parent's children list */ - current = Lst_First(&parent->children); - } - - while (current != NULL) { - cchild = Lst_Datum(current); - - /* - * First do variable expansion -- this takes precedence over - * wildcard expansion. If the result contains wildcards, they'll - * be gotten to later since the resulting words are tacked - * instead of the current child onto the children list. - * - * XXXHB what if cchild contains lib.a(t1.o t2.o t3.o) but - * no $? - */ - if (strchr(cchild->name, '$') != NULL) { - SuffExpandVariables(parent, cchild, &members); - - } else if (Dir_HasWildcards(cchild->name)) { - SuffExpandWildcards(cchild, &members); - - } else { - /* nothing special just advance to next child */ - current = LST_NEXT(current); - continue; - } - - /* - * New nodes effectively take the place of the child, - * so place them after the child - */ - prev = current; - - /* - * Add all new elements to the parent node if they aren't - * already children of it. - */ - while(!Lst_IsEmpty(&members)) { - gn = Lst_DeQueue(&members); - - DEBUGF(SUFF, ("%s...", gn->name)); - if (Lst_Member(&parent->children, gn) == NULL) { - Lst_Append(&parent->children, prev, gn); - prev = Lst_Succ(prev); - Lst_AtEnd(&gn->parents, parent); - parent->unmade++; - } - } - - /* - * Now the source is expanded, remove it from the list - * of children to keep it from being processed. - * Advance to the next child. - */ - prev = current; - current = LST_NEXT(current); - - parent->unmade--; - Lst_Remove(&parent->children, prev); - DEBUGF(SUFF, ("\n")); - } -} - -/*- - *----------------------------------------------------------------------- - * SuffApplyTransform -- - * Apply a transformation rule, given the source and target nodes - * and suffixes. - * - * Results: - * true if successful, false if not. - * - * Side Effects: - * The source and target are linked and the commands from the - * transformation are added to the target node's commands list. - * All attributes but OP_DEPMASK and OP_TRANSFORM are applied - * to the target. The target also inherits all the sources for - * the transformation rule. - * - *----------------------------------------------------------------------- - */ -static bool -SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) -{ - LstNode *ln; /* General node */ - char *tname; /* Name of transformation rule */ - GNode *gn; /* Node for same */ - - if (Lst_Member(&tGn->children, sGn) == NULL) { - /* - * Not already linked, so form the proper links between the - * target and source. - */ - Lst_AtEnd(&tGn->children, sGn); - Lst_AtEnd(&sGn->parents, tGn); - tGn->unmade += 1; - } - - if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) { - /* - * When a :: node is used as the implied source of a node, - * we have to link all its cohorts in as sources as well. Only - * the initial sGn gets the target in its iParents list, however - * as that will be sufficient to get the .IMPSRC variable set - * for tGn - */ - for (ln = Lst_First(&sGn->cohorts); ln != NULL; - ln = Lst_Succ(ln)) { - gn = Lst_Datum(ln); - - if (Lst_Member(&tGn->children, gn) == NULL) { - /* - * Not already linked, so form the proper - * links between the target and source. - */ - Lst_AtEnd(&tGn->children, gn); - Lst_AtEnd(&gn->parents, tGn); - tGn->unmade += 1; - } - } - } - /* - * Locate the transformation rule itself - */ - tname = str_concat(s->name, '\0', t->name); - gn = SuffTransFind(tname); - free(tname); - - if (gn == NULL) { - /* - * Not really such a transformation rule (can happen when we're - * called to link an OP_MEMBER and OP_ARCHV node), so return - * false. - */ - return (false); - } - - DEBUGF(SUFF, ("\tapplying %s -> %s to \"%s\"\n", - s->name, t->name, tGn->name)); - - /* - * Record last child for expansion purposes - */ - ln = Lst_Last(&tGn->children); - - /* - * Pass the buck to Make_HandleUse to apply the rule - */ - Make_HandleUse(gn, tGn); - - /* - * Deal with wildcards and variables in any acquired sources - */ - ln = Lst_Succ(ln); - if (ln != NULL) { - SuffExpandChildren(tGn, ln); - } - - /* - * Keep track of another parent to which this beast is transformed so - * the .IMPSRC variable can be set correctly for the parent. - */ - Lst_AtEnd(&sGn->iParents, tGn); - - return (true); -} - - -/*- - *----------------------------------------------------------------------- - * SuffFindArchiveDeps -- - * Locate dependencies for an OP_ARCHV node. - * - * Results: - * None - * - * Side Effects: - * Same as Suff_FindDeps - * - *----------------------------------------------------------------------- - */ -static void -SuffFindArchiveDeps(GNode *gn, Lst *slst) -{ - char *eoarch; /* End of archive portion */ - char *eoname; /* End of member portion */ - char *name; /* Start of member's name */ - GNode *mem; /* Node for member */ - Suff *ms; /* Suffix descriptor for member */ - - /* - * The node is an archive(member) pair. so we must find a - * suffix for both of them. - */ - eoarch = strchr(gn->name, OPEN_PAREN); - eoname = strchr(eoarch, CLOSE_PAREN); - - *eoname = '\0'; /* Nuke parentheses during suffix search */ - *eoarch = '\0'; /* So a suffix can be found */ - - name = eoarch + 1; - - /* - * To simplify things, call Suff_FindDeps recursively on the member now, - * so we can simply compare the member's .PREFIX and .TARGET variables - * to locate its suffix. This allows us to figure out the suffix to - * use for the archive without having to do a quadratic search over the - * suffix list, backtracking for each one... - */ - mem = Targ_FindNode(name, TARG_CREATE); - SuffFindDeps(mem, slst); - - /* - * Create the link between the two nodes right off - */ - if (Lst_Member(&gn->children, mem) == NULL) { - Lst_AtEnd(&gn->children, mem); - Lst_AtEnd(&mem->parents, gn); - gn->unmade += 1; - } - - /* - * Copy in the variables from the member node to this one. - */ - Var_Set(PREFIX, Var_Value(PREFIX, mem), gn); - Var_Set(TARGET, Var_Value(TARGET, mem), gn); - - ms = mem->suffix; - if (ms == NULL) { - /* - * Didn't know what it was -- use .NULL suffix if not in - * make mode - */ - DEBUGF(SUFF, ("using null suffix\n")); - ms = suffNull; - } - - /* - * Set the other two local variables required for this target. - */ - Var_Set(MEMBER, name, gn); - Var_Set(ARCHIVE, gn->name, gn); - - if (ms != NULL) { - /* - * Member has a known suffix, so look for a transformation rule - * from it to a possible suffix of the archive. Rather than - * searching through the entire list, we just look at suffixes - * to which the member's suffix may be transformed... - */ - LstNode *ln; - - /* - * Use first matching suffix... - */ - LST_FOREACH(ln, &ms->parents) { - if (SuffSuffIsSuffix(Lst_Datum(ln), gn->name) != NULL) - break; - } - - if (ln != NULL) { - /* - * Got one -- apply it - */ - if (!SuffApplyTransform(gn, mem, Lst_Datum(ln), ms)) { - DEBUGF(SUFF, ("\tNo transformation from " - "%s -> %s\n", ms->name, - ((Suff *)Lst_Datum(ln))->name)); - } - } - } - - /* - * Replace the opening and closing parens now we've no need - * of the separate pieces. - */ - *eoarch = OPEN_PAREN; - *eoname = CLOSE_PAREN; - - /* - * Pretend gn appeared to the left of a dependency operator so - * the user needn't provide a transformation from the member to the - * archive. - */ - if (OP_NOP(gn->type)) { - gn->type |= OP_DEPENDS; - } - - /* - * Flag the member as such so we remember to look in the archive for - * its modification time. - */ - mem->type |= OP_MEMBER; -} - -/*- - *----------------------------------------------------------------------- - * SuffFindNormalDeps -- - * Locate implicit dependencies for regular targets. - * - * Results: - * None. - * - * Side Effects: - * Same as Suff_FindDeps... - * - *----------------------------------------------------------------------- - */ -static void -SuffFindNormalDeps(GNode *gn, Lst *slst) -{ - char *eoname; /* End of name */ - char *sopref; /* Start of prefix */ - LstNode *ln; /* Next suffix node to check */ - Lst srcs; /* List of sources at which to look */ - Lst targs; /* List of targets to which things can be - * transformed. They all have the same file, - * but different suff and pref fields */ - Src *bottom; /* Start of found transformation path */ - Src *src; /* General Src pointer */ - char *pref; /* Prefix to use */ - Src *targ; /* General Src target pointer */ - - eoname = gn->name + strlen(gn->name); - sopref = gn->name; - - /* - * Begin at the beginning... - */ - ln = Lst_First(&sufflist); - Lst_Init(&srcs); - Lst_Init(&targs); - - /* - * We're caught in a catch-22 here. On the one hand, we want to use any - * transformation implied by the target's sources, but we can't examine - * the sources until we've expanded any variables/wildcards they may - * hold, and we can't do that until we've set up the target's local - * variables and we can't do that until we know what the proper suffix - * for the target is (in case there are two suffixes one of which is a - * suffix of the other) and we can't know that until we've found its - * implied source, which we may not want to use if there's an existing - * source that implies a different transformation. - * - * In an attempt to get around this, which may not work all the time, - * but should work most of the time, we look for implied sources first, - * checking transformations to all possible suffixes of the target, - * use what we find to set the target's local variables, expand the - * children, then look for any overriding transformations they imply. - * Should we find one, we discard the one we found before. - */ - - while (ln != NULL) { - /* - * Look for next possible suffix... - */ - while (ln != NULL) { - if (SuffSuffIsSuffix(Lst_Datum(ln), gn->name) != NULL) - break; - ln = LST_NEXT(ln); - } - - if (ln != NULL) { - int prefLen; /* Length of the prefix */ - Src *target; - - /* - * Allocate a Src structure to which things can be - * transformed - */ - target = SuffSrcCreate(estrdup(gn->name), NULL, - Lst_Datum(ln), NULL, gn); - target->suff->refCount++; - - /* - * Allocate room for the prefix, whose end is found - * by subtracting the length of the suffix from - * the end of the name. - */ - prefLen = (eoname - target->suff->nameLen) - sopref; - assert(prefLen >= 0); - target->pref = emalloc(prefLen + 1); - memcpy(target->pref, sopref, prefLen); - target->pref[prefLen] = '\0'; - - /* - * Add nodes from which the target can be made - */ - SuffAddLevel(&srcs, target); - - /* - * Record the target so we can nuke it - */ - Lst_AtEnd(&targs, target); - - /* - * Search from this suffix's successor... - */ - ln = Lst_Succ(ln); - } - } - - /* - * Handle target of unknown suffix... - */ - if (Lst_IsEmpty(&targs) && suffNull != NULL) { - DEBUGF(SUFF, ("\tNo known suffix on %s. Using .NULL suffix\n", - gn->name)); - - targ = SuffSrcCreate(estrdup(gn->name), estrdup(sopref), - suffNull, NULL, gn); - targ->suff->refCount++; - - /* - * Only use the default suffix rules if we don't have commands - * or dependencies defined for this gnode - */ - if (Lst_IsEmpty(&gn->commands) && Lst_IsEmpty(&gn->children)) - SuffAddLevel(&srcs, targ); - else { - DEBUGF(SUFF, ("not ")); - } - - DEBUGF(SUFF, ("adding suffix rules\n")); - - Lst_AtEnd(&targs, targ); - } - - /* - * Using the list of possible sources built up from the target - * suffix(es), try and find an existing file/target that matches. - */ - bottom = SuffFindThem(&srcs, slst); - - if (bottom == NULL) { - /* - * No known transformations -- use the first suffix found for - * setting the local variables. - */ - if (!Lst_IsEmpty(&targs)) { - targ = Lst_Datum(Lst_First(&targs)); - } else { - targ = NULL; - } - } else { - /* - * Work up the transformation path to find the suffix of the - * target to which the transformation was made. - */ - for (targ = bottom; targ->parent != NULL; targ = targ->parent) - continue; - } - - /* - * The .TARGET variable we always set to be the name at this point, - * since it's only set to the path if the thing is only a source and - * if it's only a source, it doesn't matter what we put here as far - * as expanding sources is concerned, since it has none... - */ - Var_Set(TARGET, gn->name, gn); - - pref = (targ != NULL) ? targ->pref : gn->name; - Var_Set(PREFIX, pref, gn); - - /* - * Now we've got the important local variables set, expand any sources - * that still contain variables or wildcards in their names. - */ - SuffExpandChildren(gn, NULL); - - if (targ == NULL) { - DEBUGF(SUFF, ("\tNo valid suffix on %s\n", gn->name)); - - sfnd_abort: - /* - * Deal with finding the thing on the default search path if the - * node is only a source (not on the lhs of a dependency - * operator or [XXX] it has neither children or commands). - */ - if (OP_NOP(gn->type) || (Lst_IsEmpty(&gn->children) && - Lst_IsEmpty(&gn->commands))) { - gn->path = Path_FindFile(gn->name, - (targ == NULL ? &dirSearchPath : - &targ->suff->searchPath)); - if (gn->path != NULL) { - char *ptr; - Var_Set(TARGET, gn->path, gn); - - if (targ != NULL) { - /* - * Suffix known for the thing -- trim - * the suffix off the path to form the - * proper .PREFIX variable. - */ - int savep = strlen(gn->path) - - targ->suff->nameLen; - char savec; - - if (gn->suffix) - gn->suffix->refCount--; - gn->suffix = targ->suff; - gn->suffix->refCount++; - - savec = gn->path[savep]; - gn->path[savep] = '\0'; - - if ((ptr = strrchr(gn->path, '/')) != NULL) - ptr++; - else - ptr = gn->path; - - Var_Set(PREFIX, ptr, gn); - - gn->path[savep] = savec; - } else { - /* - * The .PREFIX gets the full path if - * the target has no known suffix. - */ - if (gn->suffix) - gn->suffix->refCount--; - gn->suffix = NULL; - - if ((ptr = strrchr(gn->path, '/')) != NULL) - ptr++; - else - ptr = gn->path; - - Var_Set(PREFIX, ptr, gn); - } - } - } else { - /* - * Not appropriate to search for the thing -- set the - * path to be the name so Dir_MTime won't go - * grovelling for it. - */ - if (gn->suffix) - gn->suffix->refCount--; - gn->suffix = (targ == NULL) ? NULL : targ->suff; - if (gn->suffix) - gn->suffix->refCount++; - free(gn->path); - gn->path = estrdup(gn->name); - } - - goto sfnd_return; - } - - /* - * If the suffix indicates that the target is a library, mark that in - * the node's type field. - */ - if (targ->suff->flags & SUFF_LIBRARY) { - gn->type |= OP_LIB; - } - - /* - * Check for overriding transformation rule implied by sources - */ - if (!Lst_IsEmpty(&gn->children)) { - src = SuffFindCmds(targ, slst); - - if (src != NULL) { - /* - * Free up all the Src structures in the - * transformation path up to, but not including, - * the parent node. - */ - while (bottom && bottom->parent != NULL) { - if (Lst_Member(slst, bottom) == NULL) { - Lst_AtEnd(slst, bottom); - } - bottom = bottom->parent; - } - bottom = src; - } - } - - if (bottom == NULL) { - /* - * No idea from where it can come -- return now. - */ - goto sfnd_abort; - } - - /* - * We now have a list of Src structures headed by 'bottom' and linked - * via their 'parent' pointers. What we do next is create links between - * source and target nodes (which may or may not have been created) - * and set the necessary local variables in each target. The - * commands for each target are set from the commands of the - * transformation rule used to get from the src suffix to the targ - * suffix. Note that this causes the commands list of the original - * node, gn, to be replaced by the commands of the final - * transformation rule. Also, the unmade field of gn is incremented. - * Etc. - */ - if (bottom->node == NULL) { - bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); - } - - for (src = bottom; src->parent != NULL; src = src->parent) { - targ = src->parent; - - if (src->node->suffix) - src->node->suffix->refCount--; - src->node->suffix = src->suff; - src->node->suffix->refCount++; - - if (targ->node == NULL) { - targ->node = Targ_FindNode(targ->file, TARG_CREATE); - } - - SuffApplyTransform(targ->node, src->node, - targ->suff, src->suff); - - if (targ->node != gn) { - /* - * Finish off the dependency-search process for any - * nodes between bottom and gn (no point in questing - * around the filesystem for their implicit source - * when it's already known). Note that the node can't - * have any sources that need expanding, since - * SuffFindThem will stop on an existing - * node, so all we need to do is set the standard and - * System V variables. - */ - targ->node->type |= OP_DEPS_FOUND; - - Var_Set(PREFIX, targ->pref, targ->node); - Var_Set(TARGET, targ->node->name, targ->node); - } - } - - if (gn->suffix) - gn->suffix->refCount--; - gn->suffix = src->suff; - gn->suffix->refCount++; - - /* - * So Dir_MTime doesn't go questing for it... - */ - free(gn->path); - gn->path = estrdup(gn->name); - - /* - * Nuke the transformation path and the Src structures left over in the - * two lists. - */ - sfnd_return: - if (bottom) - if (Lst_Member(slst, bottom) == NULL) - Lst_AtEnd(slst, bottom); - - while (SuffRemoveSrc(&srcs) || SuffRemoveSrc(&targs)) - continue; - - Lst_Concat(slst, &srcs, LST_CONCLINK); - Lst_Concat(slst, &targs, LST_CONCLINK); -} - -/*- - *----------------------------------------------------------------------- - * Suff_FindDeps -- - * Find implicit sources for the target described by the graph node - * gn - * - * Results: - * Nothing. - * - * Side Effects: - * Nodes are added to the graph below the passed-in node. The nodes - * are marked to have their IMPSRC variable filled in. The - * PREFIX variable is set for the given node and all its - * implied children. - * - * Notes: - * The path found by this target is the shortest path in the - * transformation graph, which may pass through non-existent targets, - * to an existing target. The search continues on all paths from the - * root suffix until a file is found. I.e. if there's a path - * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but - * the .c and .l files don't, the search will branch out in - * all directions from .o and again from all the nodes on the - * next level until the .l,v node is encountered. - * - *----------------------------------------------------------------------- - */ -void -Suff_FindDeps(GNode *gn) -{ - - SuffFindDeps(gn, &srclist); - while (SuffRemoveSrc(&srclist)) - continue; -} - - -static void -SuffFindDeps(GNode *gn, Lst *slst) -{ - - if (gn->type & OP_DEPS_FOUND) { - /* - * If dependencies already found, no need to do it again... - */ - return; - } else { - gn->type |= OP_DEPS_FOUND; - } - - DEBUGF(SUFF, ("SuffFindDeps (%s)\n", gn->name)); - - if (gn->type & OP_ARCHV) { - SuffFindArchiveDeps(gn, slst); - - } else if (gn->type & OP_LIB) { - /* - * If the node is a library, it is the arch module's job to find - * it and set the TARGET variable accordingly. We merely provide - * the search path, assuming all libraries end in ".a" (if the - * suffix hasn't been defined, there's nothing we can do for it, - * so we just set the TARGET variable to the node's name in order - * to give it a value). - */ - Suff *s; - - s = SuffSuffFind(LIBSUFF); - if (gn->suffix) - gn->suffix->refCount--; - if (s != NULL) { - gn->suffix = s; - gn->suffix->refCount++; - Arch_FindLib(gn, &s->searchPath); - } else { - gn->suffix = NULL; - Var_Set(TARGET, gn->name, gn); - } - - /* - * Because a library (-lfoo) target doesn't follow the standard - * filesystem conventions, we don't set the regular variables for - * the thing. .PREFIX is simply made empty... - */ - Var_Set(PREFIX, "", gn); - - } else { - SuffFindNormalDeps(gn, slst); - } -} - -/*- - *----------------------------------------------------------------------- - * Suff_SetNull -- - * Define which suffix is the null suffix. - * - * Results: - * None. - * - * Side Effects: - * 'suffNull' is altered. - * - * Notes: - * Need to handle the changing of the null suffix gracefully so the - * old transformation rules don't just go away. - * - *----------------------------------------------------------------------- - */ -void -Suff_SetNull(char *name) -{ - Suff *s; - - if ((s = SuffSuffFind(name)) == NULL) { - Parse_Error(PARSE_WARNING, "Desired null suffix %s " - "not defined.", name); - return; - } - - if (suffNull != NULL) { - suffNull->flags &= ~SUFF_NULL; - } - s->flags |= SUFF_NULL; - - /* - * XXX: Here's where the transformation mangling - * would take place - */ - suffNull = s; -} - -/*- - *----------------------------------------------------------------------- - * Suff_Init -- - * Initialize suffixes module - * - * Results: - * None - * - * Side Effects: - * Many - *----------------------------------------------------------------------- - */ -void -Suff_Init(void) -{ - - sNum = 0; - /* - * Create null suffix for single-suffix rules (POSIX). The thing doesn't - * actually go on the suffix list or everyone will think that's its - * suffix. - */ - emptySuff = suffNull = emalloc(sizeof(Suff)); - - suffNull->name = estrdup(""); - suffNull->nameLen = 0; - TAILQ_INIT(&suffNull->searchPath); - Path_Concat(&suffNull->searchPath, &dirSearchPath); - Lst_Init(&suffNull->children); - Lst_Init(&suffNull->parents); - Lst_Init(&suffNull->ref); - suffNull->sNum = sNum++; - suffNull->flags = SUFF_NULL; - suffNull->refCount = 1; -} - -/********************* DEBUGGING FUNCTIONS **********************/ - -void -Suff_PrintAll(void) -{ - const LstNode *ln; - const LstNode *tln; - const GNode *gn; - const Suff *s; - - static const struct flag2str suff_flags[] = { - { SUFF_INCLUDE, "INCLUDE" }, - { SUFF_LIBRARY, "LIBRARY" }, - { SUFF_NULL, "NULL" }, - { 0, NULL } - }; - - printf("#*** Suffixes:\n"); - LST_FOREACH(ln, &sufflist) { - s = Lst_Datum(ln); - printf("# `%s' [%d] ", s->name, s->refCount); - - if (s->flags != 0) { - printf(" "); - print_flags(stdout, suff_flags, s->flags, 1); - } - - printf("\n#\tTo: "); - LST_FOREACH(tln, &s->parents) - printf("`%s' ", ((const Suff *)Lst_Datum(tln))->name); - - printf("\n#\tFrom: "); - LST_FOREACH(tln, &s->children) - printf("`%s' ", ((const Suff *)Lst_Datum(tln))->name); - - printf("\n#\tSearch Path: "); - Path_Print(&s->searchPath); - - printf("\n"); - } - - printf("#*** Transformations:\n"); - LST_FOREACH(ln, &transforms) { - gn = Lst_Datum(ln); - printf("%-16s: ", gn->name); - Targ_PrintType(gn->type); - printf("\n"); - LST_FOREACH(tln, &gn->commands) - printf("\t%s\n", (const char *)Lst_Datum(tln)); - printf("\n"); - } -} diff --git a/usr.bin/make/suff.h b/usr.bin/make/suff.h deleted file mode 100644 index a8921ec9c5..0000000000 --- a/usr.bin/make/suff.h +++ /dev/null @@ -1,63 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/suff.h,v 1.6 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef suff_h_2d5a821c -#define suff_h_2d5a821c - -#include - -struct GNode; -struct Path; - -void Suff_ClearSuffixes(void); -bool Suff_IsTransform(char *); -struct GNode *Suff_AddTransform(char *); -void Suff_EndTransform(const struct GNode *); -void Suff_AddSuffix(char *); -struct Path *Suff_GetPath(char *); -void Suff_DoPaths(void); -void Suff_AddInclude(char *); -void Suff_AddLib(char *); -void Suff_FindDeps(struct GNode *); -void Suff_SetNull(char *); -void Suff_Init(void); -void Suff_PrintAll(void); - -#endif /* suff_h_2d5a821c */ diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c deleted file mode 100644 index a01a0cfe0b..0000000000 --- a/usr.bin/make/targ.c +++ /dev/null @@ -1,471 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)targ.c 8.2 (Berkeley) 3/19/94 - * $FreeBSD: src/usr.bin/make/targ.c,v 1.37 2005/02/04 12:38:57 harti Exp $ - * $DragonFly: src/usr.bin/make/targ.c,v 1.31 2005/07/29 22:48:41 okumoto Exp $ - */ - -/* - * Functions for maintaining the Lst allTargets. Target nodes are - * kept in two structures: a Lst, maintained by the list library, and a - * hash table, maintained by the hash library. - * - * Interface: - * Targ_Init Initialization procedure. - * - * Targ_NewGN Create a new GNode for the passed target (string). - * The node is *not* placed in the hash table, though all - * its fields are initialized. - * - * Targ_FindNode Find the node for a given target, creating and storing - * it if it doesn't exist and the flags are right - * (TARG_CREATE) - * - * Targ_FindList Given a list of names, find nodes for all of them. If a - * name doesn't exist and the TARG_NOCREATE flag was given, - * an error message is printed. Else, if a name doesn't - * exist, its node is created. - * - * Targ_Ignore Return true if errors should be ignored when creating - * the given target. - * - * Targ_Silent Return true if we should be silent when creating the - * given target. - * - * Targ_Precious Return true if the target is precious and should not - * be removed if we are interrupted. - * - * Debugging: - * Targ_PrintGraph Print out the entire graphm all variables and statistics - * for the directory cache. Should print something for - * suffixes, too, but... - */ - -#include -#include - -#include "dir.h" -#include "globals.h" -#include "GNode.h" -#include "hash.h" -#include "make.h" -#include "suff.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/* the list of all targets found so far */ -static Lst allTargets = Lst_Initializer(allTargets); - -static Hash_Table targets; /* a hash table of same */ - -#define HTSIZE 191 /* initial size of hash table */ - -/** - * Targ_Init - * Initialize this module - * - * Side Effects: - * The allTargets list and the targets hash table are initialized - */ -void -Targ_Init(void) -{ - - Hash_InitTable(&targets, HTSIZE); -} - -/** - * Targ_NewGN - * Create and initialize a new graph node - * - * Results: - * An initialized graph node with the name field filled with a copy - * of the passed name - * - * Side Effects: - * The gnode is added to the list of all gnodes. - */ -GNode * -Targ_NewGN(const char *name) -{ - GNode *gn; - - gn = emalloc(sizeof(GNode)); - gn->name = estrdup(name); - gn->path = NULL; - if (name[0] == '-' && name[1] == 'l') { - gn->type = OP_LIB; - } else { - gn->type = 0; - } - gn->unmade = 0; - gn->make = false; - gn->made = UNMADE; - gn->childMade = false; - gn->order = 0; - gn->mtime = gn->cmtime = 0; - Lst_Init(&gn->iParents); - Lst_Init(&gn->cohorts); - Lst_Init(&gn->parents); - Lst_Init(&gn->children); - Lst_Init(&gn->successors); - Lst_Init(&gn->preds); - Lst_Init(&gn->context); - Lst_Init(&gn->commands); - gn->suffix = NULL; - - return (gn); -} - -/** - * Targ_FindNode - * Find a node in the list using the given name for matching - * - * Results: - * The node in the list if it was. If it wasn't, return NULL of - * flags was TARG_NOCREATE or the newly created and initialized node - * if it was TARG_CREATE - * - * Side Effects: - * Sometimes a node is created and added to the list - */ -GNode * -Targ_FindNode(const char *name, int flags) -{ - GNode *gn; /* node in that element */ - Hash_Entry *he; /* New or used hash entry for node */ - bool isNew; /* Set true if Hash_CreateEntry had to create */ - /* an entry for the node */ - - if (flags & TARG_CREATE) { - he = Hash_CreateEntry(&targets, name, &isNew); - if (isNew) { - gn = Targ_NewGN(name); - Hash_SetValue(he, gn); - Lst_AtEnd(&allTargets, gn); - } - } else { - he = Hash_FindEntry(&targets, name); - } - - if (he == NULL) { - return (NULL); - } else { - return (Hash_GetValue(he)); - } -} - -/** - * Targ_FindList - * Make a complete list of GNodes from the given list of names - * - * Results: - * A complete list of graph nodes corresponding to all instances of all - * the names in names. - * - * Side Effects: - * If flags is TARG_CREATE, nodes will be created for all names in - * names which do not yet have graph nodes. If flags is TARG_NOCREATE, - * an error message will be printed for each name which can't be found. - */ -void -Targ_FindList(Lst *nodes, Lst *names, int flags) -{ - LstNode *ln; /* name list element */ - GNode *gn; /* node in tLn */ - char *name; - - for (ln = Lst_First(names); ln != NULL; ln = Lst_Succ(ln)) { - name = Lst_Datum(ln); - gn = Targ_FindNode(name, flags); - if (gn != NULL) { - /* - * Note: Lst_AtEnd must come before the Lst_Concat so - * the nodes are added to the list in the order in which - * they were encountered in the makefile. - */ - Lst_AtEnd(nodes, gn); - if (gn->type & OP_DOUBLEDEP) { - Lst_Concat(nodes, &gn->cohorts, LST_CONCNEW); - } - - } else if (flags == TARG_NOCREATE) { - Error("\"%s\" -- target unknown.", name); - } - } -} - -/** - * Targ_Ignore - * Return true if should ignore errors when creating gn - * - * Results: - * true if should ignore errors - */ -bool -Targ_Ignore(GNode *gn) -{ - - if (ignoreErrors || (gn->type & OP_IGNORE)) { - return (true); - } else { - return (false); - } -} - -/** - * Targ_Silent - * Return true if be silent when creating gn - * - * Results: - * true if should be silent - */ -bool -Targ_Silent(GNode *gn) -{ - - if (beSilent || (gn->type & OP_SILENT)) { - return (true); - } else { - return (false); - } -} - -/** - * Targ_Precious - * See if the given target is precious - * - * Results: - * true if it is precious. false otherwise - */ -bool -Targ_Precious(GNode *gn) -{ - - if (allPrecious || (gn->type & (OP_PRECIOUS | OP_DOUBLEDEP))) { - return (true); - } else { - return (false); - } -} - -static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ - -/** - * Targ_SetMain - * Set our idea of the main target we'll be creating. Used for - * debugging output. - * - * Side Effects: - * "mainTarg" is set to the main target's node. - */ -void -Targ_SetMain(GNode *gn) -{ - - mainTarg = gn; -} - -/** - * Targ_FmtTime - * Format a modification time in some reasonable way and return it. - * - * Results: - * The time reformatted. - * - * Side Effects: - * The time is placed in a static area, so it is overwritten - * with each call. - */ -char * -Targ_FmtTime(time_t modtime) -{ - struct tm *parts; - static char buf[128]; - - parts = localtime(&modtime); - - strftime(buf, sizeof(buf), "%H:%M:%S %b %d, %Y", parts); - buf[sizeof(buf) - 1] = '\0'; - return (buf); -} - -/** - * Targ_PrintType - * Print out a type field giving only those attributes the user can - * set. - */ -void -Targ_PrintType(int type) -{ - static const struct flag2str type2str[] = { - { OP_OPTIONAL, ".OPTIONAL" }, - { OP_USE, ".USE" }, - { OP_EXEC, ".EXEC" }, - { OP_IGNORE, ".IGNORE" }, - { OP_PRECIOUS, ".PRECIOUS" }, - { OP_SILENT, ".SILENT" }, - { OP_MAKE, ".MAKE" }, - { OP_JOIN, ".JOIN" }, - { OP_INVISIBLE, ".INVISIBLE" }, - { OP_NOTMAIN, ".NOTMAIN" }, - { OP_PHONY, ".PHONY" }, - { OP_LIB, ".LIB" }, - { OP_MEMBER, ".MEMBER" }, - { OP_ARCHV, ".ARCHV" }, - { 0, NULL } - }; - - type &= ~OP_OPMASK; - if (!DEBUG(TARG)) - type &= ~(OP_ARCHV | OP_LIB | OP_MEMBER); - print_flags(stdout, type2str, type, 0); -} - -/** - * TargPrintNode - * print the contents of a node - */ -static int -TargPrintNode(const GNode *gn, int pass) -{ - const LstNode *tln; - - if (!OP_NOP(gn->type)) { - printf("#\n"); - if (gn == mainTarg) { - printf("# *** MAIN TARGET ***\n"); - } - if (pass == 2) { - if (gn->unmade) { - printf("# %d unmade children\n", gn->unmade); - } else { - printf("# No unmade children\n"); - } - if (!(gn->type & (OP_JOIN | OP_USE | OP_EXEC))) { - if (gn->mtime != 0) { - printf("# last modified %s: %s\n", - Targ_FmtTime(gn->mtime), - gn->made == UNMADE ? "unmade" : - gn->made == MADE ? "made" : - gn->made == UPTODATE ? "up-to-date": - "error when made"); - } else if (gn->made != UNMADE) { - printf("# non-existent (maybe): %s\n", - gn->made == MADE ? "made" : - gn->made == UPTODATE ? "up-to-date": - gn->made == ERROR ? "error when made" : "aborted"); - } else { - printf("# unmade\n"); - } - } - if (!Lst_IsEmpty(&gn->iParents)) { - printf("# implicit parents: "); - LST_FOREACH(tln, &gn->iParents) - printf("%s ", ((const GNode *) - Lst_Datum(tln))->name); - printf("\n"); - } - } - if (!Lst_IsEmpty(&gn->parents)) { - printf("# parents: "); - LST_FOREACH(tln, &gn->parents) - printf("%s ", ((const GNode *) - Lst_Datum(tln))->name); - printf("\n"); - } - - printf("%-16s", gn->name); - switch (gn->type & OP_OPMASK) { - case OP_DEPENDS: - printf(": "); - break; - case OP_FORCE: - printf("! "); - break; - case OP_DOUBLEDEP: - printf(":: "); - break; - default: - break; - } - Targ_PrintType(gn->type); - LST_FOREACH(tln, &gn->children) - printf("%s ", ((const GNode *)Lst_Datum(tln))->name); - printf("\n"); - LST_FOREACH(tln, &gn->commands) - printf("\t%s\n", (const char *)Lst_Datum(tln)); - printf("\n\n"); - if (gn->type & OP_DOUBLEDEP) { - LST_FOREACH(tln, &gn->cohorts) - TargPrintNode((const GNode *)Lst_Datum(tln), - pass); - } - } - return (0); -} - -/** - * Targ_PrintGraph - * Print the entire graph. - */ -void -Targ_PrintGraph(int pass) -{ - const GNode *gn; - const LstNode *tln; - - printf("#*** Input graph:\n"); - LST_FOREACH(tln, &allTargets) - TargPrintNode((const GNode *)Lst_Datum(tln), pass); - printf("\n\n"); - - printf("#\n# Files that are only sources:\n"); - LST_FOREACH(tln, &allTargets) { - gn = Lst_Datum(tln); - if (OP_NOP(gn->type)) - printf("#\t%s [%s]\n", gn->name, - gn->path ? gn->path : gn->name); - } - Var_Dump(); - printf("\n"); - Dir_PrintDirectories(); - printf("\n"); - Suff_PrintAll(); -} diff --git a/usr.bin/make/targ.h b/usr.bin/make/targ.h deleted file mode 100644 index 0538265c4f..0000000000 --- a/usr.bin/make/targ.h +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/targ.h,v 1.5 2005/08/03 19:48:44 okumoto Exp $ - */ - -#ifndef targ_h_6ded1830 -#define targ_h_6ded1830 - -#include -#include - -/* - * The TARG_ constants are used when calling the Targ_FindNode and - * Targ_FindList functions in targ.c. They simply tell the functions what to - * do if the desired node(s) is (are) not found. If the TARG_CREATE constant - * is given, a new, empty node will be created for the target, placed in the - * table of all targets and its address returned. If TARG_NOCREATE is given, - * a NULL pointer will be returned. - */ -#define TARG_CREATE 0x01 /* create node if not found */ -#define TARG_NOCREATE 0x00 /* don't create it */ - -struct GNode; -struct Lst; - -void Targ_Init(void); -struct GNode *Targ_NewGN(const char *); -struct GNode *Targ_FindNode(const char *, int); -void Targ_FindList(struct Lst *, struct Lst *, int); -bool Targ_Ignore(struct GNode *); -bool Targ_Silent(struct GNode *); -bool Targ_Precious(struct GNode *); -void Targ_SetMain(struct GNode *); -int Targ_PrintCmd(void *, void *); -char *Targ_FmtTime(time_t); -void Targ_PrintType(int); -void Targ_PrintGraph(int); - -#endif /* targ_h_6ded1830 */ diff --git a/usr.bin/make/tests/README b/usr.bin/make/tests/README deleted file mode 100644 index 4fe506d7e0..0000000000 --- a/usr.bin/make/tests/README +++ /dev/null @@ -1,45 +0,0 @@ -$DragonFly: src/usr.bin/make/tests/README,v 1.3 2005/02/26 11:58:04 okumoto Exp $ - -This directory contains regresion tests for make(1). - -The tests are invoked via the test.sh script. - % test.sh [-v] [-m path_to_make_binary] command - - clean - Remove the results and the other temp files. - - compare - Check if results of the test match the expected - output from stdout, stderr, and the status. - - desc - print description of test - - diff - Output the diffs from the tests and the expected - stdout, stderr, and the status files. - - run - Invoke test, compare, and clean in sequence. - - test - Invoke the test code - - update - Copy the output of the last test run as the expected - output from stdout, stderr, and the status. - -Example: - % sh test.sh -m `pwd`/../obj/make run - -Directory layout ----------------- -common.sh - common code -test.sh - top level test script. -basic/test.sh - 2nd level test script. -variables/test.sh - 2nd level test script. - -basic/ - t0/test.sh - regression test - t1/test.sh - regression test - t2/test.sh - regression test -variables/ - t0/test.sh - regression test - t1/test.sh - regression test - -Each test directory should contain a test.sh script -and the expected output files. - diff --git a/usr.bin/make/tests/basic/t0/expected.status b/usr.bin/make/tests/basic/t0/expected.status deleted file mode 100644 index 0cfbf08886..0000000000 --- a/usr.bin/make/tests/basic/t0/expected.status +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/usr.bin/make/tests/basic/t0/expected.stderr b/usr.bin/make/tests/basic/t0/expected.stderr deleted file mode 100644 index 90280c8298..0000000000 --- a/usr.bin/make/tests/basic/t0/expected.stderr +++ /dev/null @@ -1 +0,0 @@ -make: no target to make. diff --git a/usr.bin/make/tests/basic/t0/expected.stdout b/usr.bin/make/tests/basic/t0/expected.stdout deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/basic/t0/test.sh b/usr.bin/make/tests/basic/t0/test.sh deleted file mode 100644 index d24a45cc74..0000000000 --- a/usr.bin/make/tests/basic/t0/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/basic/t0/test.sh,v 1.6 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cp /dev/null $WORK_BASE/Makefile -} - -desc_test() -{ - echo "A empty Makefile file." -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/basic/t1/expected.status b/usr.bin/make/tests/basic/t1/expected.status deleted file mode 100644 index 573541ac97..0000000000 --- a/usr.bin/make/tests/basic/t1/expected.status +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/usr.bin/make/tests/basic/t1/expected.stderr b/usr.bin/make/tests/basic/t1/expected.stderr deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/basic/t1/expected.stdout b/usr.bin/make/tests/basic/t1/expected.stdout deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/basic/t1/test.sh b/usr.bin/make/tests/basic/t1/test.sh deleted file mode 100644 index d00f11e9c8..0000000000 --- a/usr.bin/make/tests/basic/t1/test.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/basic/t1/test.sh,v 1.6 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cat > $WORK_BASE/Makefile << _EOF_ -all: -_EOF_ -} - -desc_test() -{ - echo "A Makefile file with only a 'all:' file dependency specification." -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/basic/t2/expected.status b/usr.bin/make/tests/basic/t2/expected.status deleted file mode 100644 index 573541ac97..0000000000 --- a/usr.bin/make/tests/basic/t2/expected.status +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/usr.bin/make/tests/basic/t2/expected.stderr b/usr.bin/make/tests/basic/t2/expected.stderr deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/basic/t2/expected.stdout b/usr.bin/make/tests/basic/t2/expected.stdout deleted file mode 100644 index e618540e38..0000000000 --- a/usr.bin/make/tests/basic/t2/expected.stdout +++ /dev/null @@ -1,2 +0,0 @@ -echo hello -hello diff --git a/usr.bin/make/tests/basic/t2/test.sh b/usr.bin/make/tests/basic/t2/test.sh deleted file mode 100644 index 8c282d20ea..0000000000 --- a/usr.bin/make/tests/basic/t2/test.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/basic/t2/test.sh,v 1.6 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cat > $WORK_BASE/Makefile << _EOF_ -all: - echo hello -_EOF_ -} - -desc_test() -{ - echo "A Makefile file with only a 'all:' file dependency specification, and shell command." -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/basic/t3/expected.status b/usr.bin/make/tests/basic/t3/expected.status deleted file mode 100644 index 0cfbf08886..0000000000 --- a/usr.bin/make/tests/basic/t3/expected.status +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/usr.bin/make/tests/basic/t3/expected.stderr b/usr.bin/make/tests/basic/t3/expected.stderr deleted file mode 100644 index 90280c8298..0000000000 --- a/usr.bin/make/tests/basic/t3/expected.stderr +++ /dev/null @@ -1 +0,0 @@ -make: no target to make. diff --git a/usr.bin/make/tests/basic/t3/expected.stdout b/usr.bin/make/tests/basic/t3/expected.stdout deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/basic/t3/test.sh b/usr.bin/make/tests/basic/t3/test.sh deleted file mode 100644 index a238d4d803..0000000000 --- a/usr.bin/make/tests/basic/t3/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/basic/t3/test.sh,v 1.2 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - rm -f $WORK_BASE/Makefile -} - -desc_test() -{ - echo "No Makefile file." -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/basic/test.sh b/usr.bin/make/tests/basic/test.sh deleted file mode 100644 index af75d28cca..0000000000 --- a/usr.bin/make/tests/basic/test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/basic/test.sh,v 1.4 2005/02/25 12:28:13 okumoto Exp $ - -. ../common.sh - -DIR="t?" - -eval_cmd $1 diff --git a/usr.bin/make/tests/common.sh b/usr.bin/make/tests/common.sh deleted file mode 100644 index fc91533fd9..0000000000 --- a/usr.bin/make/tests/common.sh +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh -# -# Common code used run regression tests for usr.bin/make. -# -# $DragonFly: src/usr.bin/make/tests/common.sh,v 1.8 2005/03/02 10:55:37 okumoto Exp $ - -# -# Output usage messsage. -# -print_usage() -{ - echo "Usage: $0 command" - echo " clean - remove temp files" - echo " compare - compare result of test to expected" - echo " desc - print description of test" - echo " diff - print diffs between results and expected" - echo " run - run the {test, compare, clean}" - echo " test - run test case" - echo " update - update the expected with current results" -} - -# -# Check if the test result is the same as the expected result. -# -# $1 Input file -# -hack_cmp() -{ - local EXPECTED RESULT - EXPECTED="expected.$1" - RESULT=$WORK_BASE/$1 - - if [ -f $EXPECTED ]; then - diff -q $EXPECTED $RESULT 1> /dev/null 2> /dev/null - return $? - else - return 1 # FAIL - fi -} - -# -# Check if the test result is the same as the expected result. -# -# $1 Input file -# -hack_diff() -{ - local EXPECTED RESULT - EXPECTED="expected.$1" - RESULT=$WORK_BASE/$1 - - echo diff $EXPECTED $RESULT - if [ -f $EXPECTED ]; then - diff $EXPECTED $RESULT - return $? - else - return 1 # FAIL - fi -} - -# -# Default run_test() function. It should be replace by the -# user specified regression test. -# -# Both the variables SRC_BASE WORK_BASE are available. -# -setup_test() -{ - echo "Missing setup_test() function in $SRC_BASE/test.sh" -} - -# -# Default run_test() function. It can be replace by the -# user specified regression test. -# -# Both the variables SRC_BASE WORK_BASE are available. -# -# Note: this function executes from a subshell. -# -run_test() -( - cd $WORK_BASE; - $MAKE_PROG 1> stdout 2> stderr - echo $? > status -) - -# -# Execute cmd in subdirectory. -# -eval_subdir_cmd() -{ - local SRC_BASE WORK_BASE - - if [ ! -d $1 ]; then - echo "Test directory '$1' missing in directory '$SRC_BASE'" - return - fi - - if [ ! -f $1/test.sh ]; then - echo "Test script missing in directory '$SRC_BASE/$1'" - return - fi - - SRC_BASE=${SRC_BASE}/$1 - WORK_BASE=${WORK_BASE}/$1 - (cd $1; sh ./test.sh $2) -} - -# -# Note: Uses global variable $DIR which might be assigned by -# the script which sourced this file. -# -eval_cmd() -{ - if [ "${DIR}" ]; then - # - # When there are subdirectories defined, recurse - # down into them if the cmd is valid. - # - case $1 in - clean|compare|desc|diff|run|test|update|run_output) - for d in $DIR; do - eval_subdir_cmd $d $1 - done - ;; - *) - print_usage; - ;; - esac - else - # - # - # - case $1 in - clean) - rm -f Makefile - rm -f stdout - rm -f stderr - rm -f status - ;; - compare) - hack_cmp stdout || FAIL="stdout $FAIL" - hack_cmp stderr || FAIL="stderr $FAIL" - hack_cmp status || FAIL="status $FAIL" - - if [ ! -z "$FAIL" ]; then - FAIL=`echo $FAIL` - echo "$SRC_BASE: Test failed {$FAIL}" - fi - ;; - desc) - echo -n "$SRC_BASE: " - desc_test - ;; - diff) - sh $0 test - echo "------------------------" - echo "- $SRC_BASE" - echo "------------------------" - hack_diff stdout - hack_diff stderr - hack_diff status - ;; - run) - sh $0 test - sh $0 compare - sh $0 clean - ;; - run_output) - sh $0 test - sh $0 compare - echo "------------------------" - echo "- stdout" - echo "------------------------" - cat $WORK_BASE/stdout - echo "------------------------" - echo "- stderr" - echo "------------------------" - cat $WORK_BASE/stderr - echo "------------------------" - echo "- status" - echo "------------------------" - echo -n "status =" - cat $WORK_BASE/status - sh $0 clean - ;; - test) - [ -d $WORK_BASE ] || mkdir -p $WORK_BASE - setup_test - run_test - ;; - update) - sh $0 test - cp $WORK_BASE/stdout "."$SRC_BASE/expected.stdout - cp $WORK_BASE/stderr "."$SRC_BASE/expected.stderr - cp $WORK_BASE/status "."$SRC_BASE/expected.status - ;; - *) - print_usage - ;; - esac - fi -} - -# -# Parse command line arguments. -# -args=`getopt m:w:v $*` -if [ $? != 0 ]; then - echo 'Usage: ...' - exit 2 -fi -set -- $args -for i; do - case "$i" in - -m) - MAKE_PROG="$2" - shift - shift - ;; - -w) - WORK_BASE="$2" - shift - shift - ;; - -v) - VERBOSE=1 - shift - ;; - --) - shift - break - ;; - esac -done - -SRC_BASE=${SRC_BASE:-""} -WORK_BASE=${WORK_BASE:-"/tmp/$USER.make.test"} -MAKE_PROG=${MAKE_PROG:-/usr/bin/make} - -export MAKE_PROG -export VERBOSE -export SRC_BASE -export WORK_BASE diff --git a/usr.bin/make/tests/test.sh b/usr.bin/make/tests/test.sh deleted file mode 100644 index 46b3f7be11..0000000000 --- a/usr.bin/make/tests/test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/test.sh,v 1.4 2005/02/25 12:28:13 okumoto Exp $ - -. ./common.sh - -DIR="basic variables" - -eval_cmd $1 diff --git a/usr.bin/make/tests/variables/t0/expected.status b/usr.bin/make/tests/variables/t0/expected.status deleted file mode 100644 index 573541ac97..0000000000 --- a/usr.bin/make/tests/variables/t0/expected.status +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/usr.bin/make/tests/variables/t0/expected.stderr b/usr.bin/make/tests/variables/t0/expected.stderr deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/variables/t0/expected.stdout b/usr.bin/make/tests/variables/t0/expected.stdout deleted file mode 100644 index dfe03b893a..0000000000 --- a/usr.bin/make/tests/variables/t0/expected.stdout +++ /dev/null @@ -1,31 +0,0 @@ -echo 0 -0 -echo 1 -1 -echo 0 -0 -# The following are soo broken why no syntax error? -echo - -echo - -echo - -echo - -echo - -echo - -echo - -echo - -echo - -echo - -echo - -echo - diff --git a/usr.bin/make/tests/variables/t0/test.sh b/usr.bin/make/tests/variables/t0/test.sh deleted file mode 100644 index ef757a5985..0000000000 --- a/usr.bin/make/tests/variables/t0/test.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/variables/t0/test.sh,v 1.6 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cat > $WORK_BASE/Makefile << "_EOF_" -A = 0 -AV = 1 -all: - echo $A - echo ${AV} - echo ${A} - # The following are soo broken why no syntax error? - echo $( - echo $) - echo ${ - echo ${A - echo ${A) - echo ${A){ - echo ${AV - echo ${AV) - echo ${AV){ - echo ${AV{ - echo ${A{ - echo $} -_EOF_ -} - -desc_test() -{ - echo "Variable expansion." -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/variables/t1/expected.status b/usr.bin/make/tests/variables/t1/expected.status deleted file mode 100644 index 573541ac97..0000000000 --- a/usr.bin/make/tests/variables/t1/expected.status +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/usr.bin/make/tests/variables/t1/expected.stderr b/usr.bin/make/tests/variables/t1/expected.stderr deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/variables/t1/expected.stdout b/usr.bin/make/tests/variables/t1/expected.stdout deleted file mode 100644 index e00d5a7f7c..0000000000 --- a/usr.bin/make/tests/variables/t1/expected.stdout +++ /dev/null @@ -1,7 +0,0 @@ -all files: main.c globals.h util.c util.h map.c map.h parser.y lexer.l cmdman.1 format.5 -cfiles: main.c util.c map.c -hfiles: globals.h util.h map.h -grammer and lexer: parser.y lexer.l -man page: cmdman.1 format.5 -utility files: util.c util.h -m files: main.c map.c map.h diff --git a/usr.bin/make/tests/variables/t1/test.sh b/usr.bin/make/tests/variables/t1/test.sh deleted file mode 100644 index 4c99ac275c..0000000000 --- a/usr.bin/make/tests/variables/t1/test.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/variables/t1/test.sh,v 1.7 2005/03/01 22:42:28 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cat > $WORK_BASE/Makefile << "_EOF_" -FILES = \ - main.c globals.h \ - util.c util.h \ - map.c map.h \ - parser.y lexer.l \ - cmdman.1 format.5 -all: - @echo "all files: ${FILES}" - @echo "cfiles: ${FILES:M*.c}" - @echo "hfiles: ${FILES:M*.h}" - @echo "grammer and lexer: ${FILES:M*.[ly]}" - @echo "man page: ${FILES:M*.[1-9]}" - @echo "utility files: ${FILES:Mutil.?}" - @echo "m files: ${FILES:Mm*}" -_EOF_ -} - -desc_test() -{ - echo "Variable expansion with M modifier" -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/variables/t2/expected.status b/usr.bin/make/tests/variables/t2/expected.status deleted file mode 100644 index 573541ac97..0000000000 --- a/usr.bin/make/tests/variables/t2/expected.status +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/usr.bin/make/tests/variables/t2/expected.stderr b/usr.bin/make/tests/variables/t2/expected.stderr deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/usr.bin/make/tests/variables/t2/expected.stdout b/usr.bin/make/tests/variables/t2/expected.stdout deleted file mode 100644 index 3df88fb372..0000000000 --- a/usr.bin/make/tests/variables/t2/expected.stdout +++ /dev/null @@ -1,14 +0,0 @@ -Old: main.c globals.h util.c util.h map.c map.h parser.y lexer.l cmdman.1 format.5 -New: main.c globals.h util.c util.h Map.c Map.h parser.y lexer.l cmdman.1 format.5 -New: main_wrapper.c globals.h util.c util.h map.c map.h parser.y lexer.l cmdman.1 format.5 -Integer Integer Integer Integer -Integer Integer Integer Integer -FOOFOOFOO BBB CCC -AAA BARBARBAR CCC -AAA BBB BAZBAZBAZ -FOOAA BBB CCC -AAA BARBB CCC -AAA BBB BAZCC -AAFOO BBB CCC -AAA BBBAR CCC -AAA BBB CCBAZ diff --git a/usr.bin/make/tests/variables/t2/test.sh b/usr.bin/make/tests/variables/t2/test.sh deleted file mode 100644 index 5c41d5d21a..0000000000 --- a/usr.bin/make/tests/variables/t2/test.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/variables/t2/test.sh,v 1.2 2005/03/03 19:13:00 okumoto Exp $ - -. ../../common.sh - -setup_test() -{ - cat > $WORK_BASE/Makefile << "_EOF_" -FILES = \ - main.c globals.h \ - util.c util.h \ - map.c map.h \ - parser.y lexer.l \ - cmdman.1 format.5 -ORANGE = Variable Variable Variable Variable -APPLE = AAA BBB CCC -all: - @echo "Old: ${FILES}" - @echo "New: ${FILES:S/map/Map/}" - @echo "New: ${FILES:S/main/&_wrapper/}" - - @echo "${ORANGE:S/Variable/Integer/g}" - @echo "${ORANGE:S/Variable/Integer/}" - - @echo "${APPLE:S/A/FOO/g}" - @echo "${APPLE:S/B/BAR/g}" - @echo "${APPLE:S/C/BAZ/g}" - - @echo "${APPLE:S/^A/FOO/g}" - @echo "${APPLE:S/^B/BAR/g}" - @echo "${APPLE:S/^C/BAZ/g}" - - @echo "${APPLE:S/A$/FOO/g}" - @echo "${APPLE:S/B$/BAR/g}" - @echo "${APPLE:S/C$/BAZ/g}" -_EOF_ -} - -desc_test() -{ - echo "Variable expansion with M modifier" -} - -eval_cmd $1 diff --git a/usr.bin/make/tests/variables/test.sh b/usr.bin/make/tests/variables/test.sh deleted file mode 100644 index b4747186a9..0000000000 --- a/usr.bin/make/tests/variables/test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# $DragonFly: src/usr.bin/make/tests/variables/test.sh,v 1.4 2005/02/25 12:28:13 okumoto Exp $ - -. ../common.sh - -DIR="t?" - -eval_cmd $1 diff --git a/usr.bin/make/util.c b/usr.bin/make/util.c deleted file mode 100644 index 6c3f7c9b80..0000000000 --- a/usr.bin/make/util.c +++ /dev/null @@ -1,393 +0,0 @@ -/*- - * Copyright (c) 2002 Juli Mallett. All rights reserved. - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: src/usr.bin/make/util.c,v 1.16 2005/02/04 13:23:39 harti Exp $ - * $DragonFly: src/usr.bin/make/util.c,v 1.25 2006/07/27 00:41:10 corecode Exp $ - */ - -/*- - * util.c -- - * General utilitarian routines for make(1). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "globals.h" -#include "job.h" -#include "targ.h" -#include "util.h" - -/*- - * Debug -- - * Print a debugging message given its format. - * - * Results: - * None. - * - * Side Effects: - * The message is printed. - */ -/* VARARGS */ -void -Debug(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fflush(stderr); -} - -/*- - * Print a debugging message given its format and append the current - * errno description. Terminate with a newline. - */ -/* VARARGS */ -void -DebugM(const char *fmt, ...) -{ - va_list ap; - int e = errno; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": %s\n", strerror(e)); - va_end(ap); - fflush(stderr); -} - -/*- - * Error -- - * Print an error message given its format. - * - * Results: - * None. - * - * Side Effects: - * The message is printed. - */ -/* VARARGS */ -void -Error(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); - fflush(stderr); -} - -/*- - * Fatal -- - * Produce a Fatal error message. If jobs are running, waits for them - * to finish. - * - * Results: - * None - * - * Side Effects: - * The program exits - */ -/* VARARGS */ -void -Fatal(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (jobsRunning) - Job_Wait(); - - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); - fflush(stderr); - - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - exit(2); /* Not 1 so -q can distinguish error */ -} - -/* - * Punt -- - * Major exception once jobs are being created. Kills all jobs, prints - * a message and exits. - * - * Results: - * None - * - * Side Effects: - * All children are killed indiscriminately and the program Lib_Exits - */ -/* VARARGS */ -void -Punt(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "make: "); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); - fflush(stderr); - - DieHorribly(); -} - -/*- - * DieHorribly -- - * Exit without giving a message. - * - * Results: - * None - * - * Side Effects: - * A big one... - */ -void -DieHorribly(void) -{ - if (jobsRunning) - Job_AbortAll(); - if (DEBUG(GRAPH2)) - Targ_PrintGraph(2); - exit(2); /* Not 1, so -q can distinguish error */ -} - -/* - * Finish -- - * Called when aborting due to errors in child shell to signal - * abnormal exit, with the number of errors encountered in Make_Make. - * - * Results: - * None - * - * Side Effects: - * The program exits - */ -void -Finish(int errors) -{ - - Fatal("%d error%s", errors, errors == 1 ? "" : "s"); -} - -/* - * emalloc -- - * malloc, but die on error. - */ -void * -emalloc(size_t size) -{ - void *p; - - if ((p = malloc(size)) == NULL) - err(2, "malloc(%zd)", size); - return (p); -} - -/* - * estrdup -- - * strdup, but die on error. - */ -char * -estrdup(const char *str) -{ - char *p; - - if ((p = strdup(str)) == NULL) - err(2, "strdup(%s)", str); - return (p); -} - -/* - * erealloc -- - * realloc, but die on error. - */ -void * -erealloc(void *ptr, size_t size) -{ - char *p; - - if ((p = realloc(ptr, size)) == NULL) - err(2, "realloc(%zd)", size); - return (p); -} - -/* - * enunlink -- - * Remove a file carefully, avoiding directories. - */ -int -eunlink(const char *file) -{ - struct stat st; - - if (lstat(file, &st) == -1) - return (-1); - - if (S_ISDIR(st.st_mode)) { - errno = EISDIR; - return (-1); - } - return (unlink(file)); -} - -/* - * Convert a flag word to a printable thing and print it - */ -void -print_flags(FILE *fp, const struct flag2str *tab, u_int flags, int par) -{ - int first = 1; - - if (par) - fprintf(fp, "("); - while (tab->str != NULL) { - if (flags & tab->flag) { - if (!first) - fprintf(fp, par ? "|" : " "); - first = 0; - fprintf(fp, "%s", tab->str); - } - tab++; - } - if (par) - fprintf(fp, ")"); -} - -/** - * Create a fifo file with a uniq filename, and returns a file - * descriptor to that fifo. - */ -int -mkfifotemp(char *template) -{ - char *start; - char *pathend; - char *ptr; - const char padchar[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - - if (template[0] == '\0') { - errno = EINVAL; /* bad input string */ - return (-1); - } - - /* Find end of template string. */ - pathend = strchr(template, '\0'); - ptr = pathend - 1; - - /* - * Starting from the end of the template replace spaces with 'X' in - * them with random characters until there are no more 'X'. - */ - while (ptr >= template && *ptr == 'X') { - uint32_t rand_num = arc4random() % (sizeof(padchar) - 1); - *ptr-- = padchar[rand_num]; - } - start = ptr + 1; - - /* Check the target directory. */ - for (; ptr > template; --ptr) { - if (*ptr == '/') { - struct stat sbuf; - - *ptr = '\0'; - if (stat(template, &sbuf) != 0) - return (-1); - - if (!S_ISDIR(sbuf.st_mode)) { - errno = ENOTDIR; - return (-1); - } - *ptr = '/'; - break; - } - } - - for (;;) { - if (mkfifo(template, 0600) == 0) { - int fd; - - if ((fd = open(template, O_RDWR, 0600)) < 0) { - unlink(template); - return (-1); - } else { - return (fd); - } - } else { - if (errno != EEXIST) { - return (-1); - } - } - - /* - * If we have a collision, cycle through the space of - * filenames. - */ - for (ptr = start;;) { - char *pad; - - if (*ptr == '\0' || ptr == pathend) - return (-1); - - pad = strchr(padchar, *ptr); - if (pad == NULL || *++pad == '\0') { - *ptr++ = padchar[0]; - } else { - *ptr++ = *pad; - break; - } - } - } - /*NOTREACHED*/ -} - diff --git a/usr.bin/make/util.h b/usr.bin/make/util.h deleted file mode 100644 index 77df79537e..0000000000 --- a/usr.bin/make/util.h +++ /dev/null @@ -1,118 +0,0 @@ -/*- - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $DragonFly: src/usr.bin/make/util.h,v 1.16 2005/09/24 07:37:01 okumoto Exp $ - */ - -#ifndef util_h_b7020fdb -#define util_h_b7020fdb - -#include -#include - -#define CONCAT(a,b) a##b - -#define OPEN_PAREN '(' -#define CLOSE_PAREN ')' -#define OPEN_BRACE '{' -#define CLOSE_BRACE '}' - -struct flag2str { - u_int flag; - const char *str; -}; - -/* - * debug control: - * There is one bit per module. It is up to the module what debug - * information to print. - */ -#define DEBUG_ARCH 0x0001 -#define DEBUG_COND 0x0002 -#define DEBUG_DIR 0x0004 -#define DEBUG_GRAPH1 0x0008 -#define DEBUG_GRAPH2 0x0010 -#define DEBUG_JOB 0x0020 -#define DEBUG_MAKE 0x0040 -#define DEBUG_SUFF 0x0080 -#define DEBUG_TARG 0x0100 -#define DEBUG_VAR 0x0200 -#define DEBUG_FOR 0x0400 -#define DEBUG_LOUD 0x0800 - -#define DEBUG(module) (debug & CONCAT(DEBUG_,module)) -#define DEBUGF(module,args) \ -do { \ - if (DEBUG(module)) { \ - Debug args ; \ - } \ -} while (0) -#define DEBUGM(module, args) do { \ - if (DEBUG(module)) { \ - DebugM args; \ - } \ - } while (0) - -#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/'))) -#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1]))) - -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -/* - * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file - * is a char! So when we go above 127 we turn negative! - */ -#define FILENO(a) ((unsigned)fileno(a)) - -void Debug(const char *, ...) __printflike(1, 2); -void DebugM(const char *, ...) __printflike(1, 2); -void Error(const char *, ...) __printflike(1, 2); -void Fatal(const char *, ...) __dead2 __printflike(1, 2); -void Punt(const char *, ...) __dead2 __printflike(1, 2); -void DieHorribly(void) __dead2; -void Finish(int) __dead2; -char *estrdup(const char *); -void *emalloc(size_t); -void *erealloc(void *, size_t); -int eunlink(const char *); -void print_flags(FILE *, const struct flag2str *, u_int, int); -int mkfifotemp(char *); - - -#endif /* util_h_b7020fdb */ diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c deleted file mode 100644 index 945a17dbc4..0000000000 --- a/usr.bin/make/var.c +++ /dev/null @@ -1,2532 +0,0 @@ -/*- - * Copyright (c) 2002 Juli Mallett. - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)var.c 8.3 (Berkeley) 3/19/94 - * $FreeBSD: src/usr.bin/make/var.c,v 1.83 2005/02/11 10:49:01 harti Exp $ - * $DragonFly: src/usr.bin/make/var.c,v 1.224 2005/09/24 07:37:38 okumoto Exp $ - */ - -/** - * var.c -- - * Variable-handling functions - * - * Interface: - * Var_Set Set the value of a variable in the given - * context. The variable is created if it doesn't - * yet exist. The value and variable name need not - * be preserved. - * - * Var_Append Append more characters to an existing variable - * in the given context. The variable needn't - * exist already -- it will be created if it doesn't. - * A space is placed between the old value and the - * new one. - * - * Var_Value Return the value of a variable in a context or - * NULL if the variable is undefined. - * - * Var_Subst Substitute named variable, or all variables if - * NULL in a string using - * the given context as the top-most one. If the - * third argument is non-zero, Parse_Error is - * called if any variables are undefined. - * - * Var_Parse Parse a variable expansion from a string and - * return the result and the number of characters - * consumed. - * - * Var_Delete Delete a variable in a context. - * - * Var_Init Initialize this module. - * - * Debugging: - * Var_Dump Print out all variables defined in the given - * context. - * - * XXX: There's a lot of duplication in these functions. - */ - -#include -#include -#include -#include -#include -#include - -#include "buf.h" -#include "config.h" -#include "globals.h" -#include "GNode.h" -#include "job.h" -#include "lst.h" -#include "make.h" -#include "parse.h" -#include "str.h" -#include "targ.h" -#include "util.h" -#include "var.h" - -/** - * - */ -typedef struct VarParser { - const char *const input; /* pointer to input string */ - const char *ptr; /* current parser pos in input str */ - GNode *ctxt; - bool err; - bool execute; -} VarParser; - -typedef struct Var { - char *name; /* the variable's name */ - struct Buffer *val; /* its value */ - int flags; /* miscellaneous status flags */ - -#define VAR_IN_USE 1 /* Variable's value currently being used. - * Used to avoid recursion */ - -#define VAR_JUNK 4 /* Variable is a junk variable that - * should be destroyed when done with - * it. Used by Var_Parse for undefined, - * modified variables */ - -#define VAR_TO_ENV 8 /* Place variable in environment */ -} Var; - -typedef struct { - struct Buffer *lhs; /* String to match */ - struct Buffer *rhs; /* Replacement string (w/ &'s removed) */ - - regex_t re; - int nsub; - regmatch_t *matches; - - int flags; -#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ -#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ -#define VAR_SUB_MATCHED 0x04 /* There was a match */ -#define VAR_MATCH_START 0x08 /* Match at start of word */ -#define VAR_MATCH_END 0x10 /* Match at end of word */ -} VarPattern; - -typedef bool VarModifyProc(const char [], bool, struct Buffer *, void *); - -static char *VarParse(VarParser *, bool *); - -/* - * This is a harmless return value for Var_Parse that can be used by Var_Subst - * to determine if there was an error in parsing -- easier than returning - * a flag, as things outside this module don't give a hoot. - */ -char var_Error[] = ""; - -/* - * Similar to var_Error, but returned when the 'err' flag for Var_Parse is - * set false. Why not just use a constant? Well, gcc likes to condense - * identical string instances... - */ -static char varNoError[] = ""; - -/* - * Internally, variables are contained in four different contexts. - * 1) the environment. They may not be changed. If an environment - * variable is appended-to, the result is placed in the global - * context. - * 2) the global context. Variables set in the Makefile are located in - * the global context. It is the penultimate context searched when - * substituting. - * 3) the command-line context. All variables set on the command line - * are placed in this context. They are UNALTERABLE once placed here. - * 4) the local context. Each target has associated with it a context - * list. On this list are located the structures describing such - * local variables as $(@) and $(*) - * The four contexts are searched in the reverse order from which they are - * listed. - */ -static GNode *VAR_ENV; /* variables from the environment */ -GNode *VAR_GLOBAL; /* variables from the makefile */ -GNode *VAR_CMD; /* variables defined on the command-line */ - -bool oldVars; /* variable substitution style */ -bool checkEnvFirst; /* -e flag */ - -/** - * Create a Var object. - * - * @param name Name of variable. - * @param value Value of variable. - * @param flags Flags set on variable. - */ -static Var * -VarCreate(const char name[], const char value[], int flags) -{ - Var *v; - - v = emalloc(sizeof(Var)); - v->name = estrdup(name); - v->val = Buf_Init(0); - v->flags = flags; - - if (value != NULL) { - Buf_Append(v->val, value); - } - return (v); -} - -/** - * Destroy a Var object. - * - * @param v Object to destroy. - * @param f true if internal buffer in Buffer object is to be - * removed. - */ -static void -VarDestroy(Var *v, bool f) -{ - - Buf_Destroy(v->val, f); - free(v->name); - free(v); -} - -/** - * Remove the tail of the given word and place the result in the given - * buffer. - * - * Results: - * true if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The trimmed word is added to the buffer. - */ -static bool -VarHead(const char word[], bool addSpace, Buffer *buf, void *dummy __unused) -{ - char *slash; - - slash = strrchr(word, '/'); - if (slash != NULL) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - Buf_AppendRange(buf, word, slash); - } else { - /* - * If no directory part, give . (q.v. the POSIX standard) - */ - if (addSpace) { - Buf_Append(buf, " ."); - } else { - Buf_AddByte(buf, '.'); - } - } - return (true); -} - -/** - * Remove the head of the given word and place the result in the given - * buffer. - * - * Results: - * true if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The trimmed word is added to the buffer. - */ -static bool -VarTail(const char word[], bool addSpace, Buffer *buf, void *dummy __unused) -{ - const char *slash; - - if (addSpace) { - Buf_AddByte(buf, ' '); - } - - slash = strrchr(word, '/'); - if (slash != NULL) { - slash++; - Buf_Append(buf, slash); - } else { - Buf_Append(buf, word); - } - return (true); -} - -/** - * Place the suffix of the given word in the given buffer. - * - * Results: - * true if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The suffix from the word is placed in the buffer. - */ -static bool -VarSuffix(const char word[], bool addSpace, Buffer *buf, void *dummy __unused) -{ - const char *dot; - - dot = strrchr(word, '.'); - if (dot != NULL) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - dot++; - Buf_Append(buf, dot); - addSpace = true; - } - return (addSpace); -} - -/** - * Remove the suffix of the given word and place the result in the - * buffer. - * - * Results: - * true if characters were added to the buffer (a space needs to be - * added to the buffer before the next word). - * - * Side Effects: - * The trimmed word is added to the buffer. - */ -static bool -VarRoot(const char word[], bool addSpace, Buffer *buf, void *dummy __unused) -{ - char *dot; - - if (addSpace) { - Buf_AddByte(buf, ' '); - } - - dot = strrchr(word, '.'); - if (dot != NULL) { - Buf_AppendRange(buf, word, dot); - } else { - Buf_Append(buf, word); - } - return (true); -} - -/** - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the :M modifier. - * A space will be added if requested. A pattern is supplied - * which the word must match. - * - * Results: - * true if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - */ -static bool -VarMatch(const char word[], bool addSpace, Buffer *buf, void *pattern) -{ - - if (Str_Match(word, pattern)) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - addSpace = true; - Buf_Append(buf, word); - } - return (addSpace); -} - -/** - * Place the word in the buffer if it matches the given pattern. - * Callback function for VarModify to implement the System V % - * modifiers. A space is added if requested. - * - * Results: - * true if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - */ -static bool -VarSYSVMatch(const char word[], bool addSpace, Buffer *buf, void *patp) -{ - int len; - const char *ptr; - VarPattern *pat = (VarPattern *)patp; - - if (addSpace) - Buf_AddByte(buf, ' '); - - addSpace = true; - - if ((ptr = Str_SYSVMatch(word, Buf_Data(pat->lhs), &len)) != NULL) - Str_SYSVSubst(buf, Buf_Data(pat->rhs), ptr, len); - else - Buf_Append(buf, word); - - return (addSpace); -} - -/** - * Place the word in the buffer if it doesn't match the given pattern. - * Callback function for VarModify to implement the :N modifier. A - * space is added if requested. - * - * Results: - * true if a space should be placed in the buffer before the next - * word. - * - * Side Effects: - * The word may be copied to the buffer. - */ -static bool -VarNoMatch(const char word[], bool addSpace, Buffer *buf, void *pattern) -{ - - if (!Str_Match(word, pattern)) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - addSpace = true; - Buf_Append(buf, word); - } - return (addSpace); -} - -/** - * Perform a string-substitution on the given word, placing the - * result in the passed buffer. A space is added if requested. - * - * Results: - * true if a space is needed before more characters are added. - */ -static bool -VarSubstitute(const char word[], bool addSpace, Buffer *buf, void *patternp) -{ - size_t wordLen; /* Length of word */ - const char *cp; /* General pointer */ - VarPattern *pattern = patternp; - - wordLen = strlen(word); - if (1) { /* substitute in each word of the variable */ - /* - * Break substitution down into simple anchored cases - * and if none of them fits, perform the general substitution - * case. - */ - if ((pattern->flags & VAR_MATCH_START) && - (strncmp(word, Buf_Data(pattern->lhs), - Buf_Size(pattern->lhs)) == 0)) { - /* - * Anchored at start and beginning of word matches - * pattern. - */ - if ((pattern->flags & VAR_MATCH_END) && - (wordLen == Buf_Size(pattern->lhs))) { - /* - * Also anchored at end and matches to the end - * (word is same length as pattern) add space - * and rhs only if rhs is non-null. - */ - if (Buf_Size(pattern->rhs) != 0) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - addSpace = true; - Buf_AppendBuf(buf, pattern->rhs); - } - - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Doesn't match to end -- copy word wholesale - */ - goto nosub; - - } else { - /* - * Matches at start but need to copy in - * trailing characters. - */ - if ((Buf_Size(pattern->rhs) + wordLen - - Buf_Size(pattern->lhs)) != 0) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - addSpace = true; - } - Buf_AppendBuf(buf, pattern->rhs); - Buf_AddBytes(buf, wordLen - - Buf_Size(pattern->lhs), - (word + Buf_Size(pattern->lhs))); - } - - } else if (pattern->flags & VAR_MATCH_START) { - /* - * Had to match at start of word and didn't -- copy - * whole word. - */ - goto nosub; - - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Anchored at end, Find only place match could occur - * (leftLen characters from the end of the word) and - * see if it does. Note that because the $ will be - * left at the end of the lhs, we have to use strncmp. - */ - cp = word + (wordLen - Buf_Size(pattern->lhs)); - if ((cp >= word) && (strncmp(cp, Buf_Data(pattern->lhs), - Buf_Size(pattern->lhs)) == 0)) { - /* - * Match found. If we will place characters in - * the buffer, add a space before hand as - * indicated by addSpace, then stuff in the - * initial, unmatched part of the word followed - * by the right-hand-side. - */ - if ((cp - word) + Buf_Size(pattern->rhs) != 0) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - addSpace = true; - } - Buf_AppendRange(buf, word, cp); - Buf_AppendBuf(buf, pattern->rhs); - - } else { - /* - * Had to match at end and didn't. Copy entire - * word. - */ - goto nosub; - } - } else { - /* - * Pattern is unanchored: search for the pattern in the - * word using strstr(3), copying unmatched portions and - * the right-hand-side for each match found, handling - * non-global substitutions correctly, etc. When the - * loop is done, any remaining part of the word (word - * and wordLen are adjusted accordingly through the - * loop) is copied straight into the buffer. - * addSpace is set false as soon as a space is added - * to the buffer. - */ - bool done; - size_t origSize; - - done = false; - origSize = Buf_Size(buf); - while (!done) { - cp = strstr(word, Buf_Data(pattern->lhs)); - if (cp != NULL) { - if (addSpace && (((cp - word) + - Buf_Size(pattern->rhs)) != 0)) { - Buf_AddByte(buf, ' '); - addSpace = false; - } - Buf_AppendRange(buf, word, cp); - Buf_AppendBuf(buf, pattern->rhs); - wordLen -= (cp - word) + - Buf_Size(pattern->lhs); - word = cp + Buf_Size(pattern->lhs); - if (wordLen == 0 || (pattern->flags & - VAR_SUB_GLOBAL) == 0) { - done = true; - } - } else { - done = true; - } - } - if (wordLen != 0) { - if (addSpace) { - Buf_AddByte(buf, ' '); - } - Buf_AddBytes(buf, wordLen, word); - } - - /* - * If added characters to the buffer, need to add a - * space before we add any more. If we didn't add any, - * just return the previous value of addSpace. - */ - return ((Buf_Size(buf) != origSize) || addSpace); - } - /* - * Common code for anchored substitutions: - * addSpace was set true if characters were added to the buffer. - */ - return (addSpace); - } - nosub: - if (addSpace) { - Buf_AddByte(buf, ' '); - } - Buf_AddBytes(buf, wordLen, word); - return (true); -} - -/** - * Print the error caused by a regcomp or regexec call. - * - * Side Effects: - * An error gets printed. - */ -static void -VarREError(int err, regex_t *pat, const char str[]) -{ - char *errbuf; - int errlen; - - errlen = regerror(err, pat, 0, 0); - errbuf = emalloc(errlen); - regerror(err, pat, errbuf, errlen); - Error("%s: %s", str, errbuf); - free(errbuf); -} - - -/** - * Perform a regex substitution on the given word, placing the - * result in the passed buffer. A space is added if requested. - * - * Results: - * true if a space is needed before more characters are added. - */ -static bool -VarRESubstitute(const char word[], bool addSpace, Buffer *buf, void *patternp) -{ - VarPattern *pat; - int xrv; - const char *wp; - char *rp; - int added; - int flags = 0; - -#define MAYBE_ADD_SPACE() \ - if (addSpace && !added) \ - Buf_AddByte(buf, ' '); \ - added = 1 - - added = 0; - wp = word; - pat = patternp; - - if ((pat->flags & (VAR_SUB_ONE | VAR_SUB_MATCHED)) == - (VAR_SUB_ONE | VAR_SUB_MATCHED)) { - xrv = REG_NOMATCH; - } else { - tryagain: - xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); - } - - switch (xrv) { - case 0: - pat->flags |= VAR_SUB_MATCHED; - if (pat->matches[0].rm_so > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, pat->matches[0].rm_so, wp); - } - - for (rp = Buf_Data(pat->rhs); *rp; rp++) { - if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, rp[1]); - rp++; - - } else if ((*rp == '&') || - ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { - int n; - const char *subbuf; - int sublen; - char errstr[3]; - - if (*rp == '&') { - n = 0; - errstr[0] = '&'; - errstr[1] = '\0'; - } else { - n = rp[1] - '0'; - errstr[0] = '\\'; - errstr[1] = rp[1]; - errstr[2] = '\0'; - rp++; - } - - if (n > pat->nsub) { - Error("No subexpression %s", - &errstr[0]); - subbuf = ""; - sublen = 0; - - } else if ((pat->matches[n].rm_so == -1) && - (pat->matches[n].rm_eo == -1)) { - Error("No match for subexpression %s", - &errstr[0]); - subbuf = ""; - sublen = 0; - - } else { - subbuf = wp + pat->matches[n].rm_so; - sublen = pat->matches[n].rm_eo - - pat->matches[n].rm_so; - } - - if (sublen > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, sublen, subbuf); - } - } else { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *rp); - } - } - wp += pat->matches[0].rm_eo; - if (pat->flags & VAR_SUB_GLOBAL) { - flags |= REG_NOTBOL; - if (pat->matches[0].rm_so == 0 && - pat->matches[0].rm_eo == 0) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *wp); - wp++; - } - if (*wp) - goto tryagain; - } - if (*wp) { - MAYBE_ADD_SPACE(); - Buf_Append(buf, wp); - } - break; - - default: - VarREError(xrv, &pat->re, "Unexpected regex error"); - /* fall through */ - - case REG_NOMATCH: - if (*wp) { - MAYBE_ADD_SPACE(); - Buf_Append(buf, wp); - } - break; - } - return (addSpace || added); -} - -/** - * Find a variable in a variable list. - */ -static Var * -VarLookup(Lst *vlist, const char name[]) -{ - LstNode *ln; - - LST_FOREACH(ln, vlist) - if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) - return (Lst_Datum(ln)); - return (NULL); -} - -/** - * Expand a variable name's embedded variables in the given context. - * - * Results: - * The contents of name, possibly expanded. - */ -static char * -VarPossiblyExpand(const char name[], GNode *ctxt) -{ - Buffer *buf; - - if (strchr(name, '$') != NULL) { - buf = Var_Subst(name, ctxt, 0); - return (Buf_Peel(buf)); - } else { - return estrdup(name); - } -} - -/** - * If the variable name begins with a '.', it could very well be - * one of the local ones. We check the name against all the local - * variables and substitute the short version in for 'name' if it - * matches one of them. - */ -static const char * -VarLocal(const char name[]) -{ - if (name[0] == '.') { - switch (name[1]) { - case 'A': - if (!strcmp(name, ".ALLSRC")) - return (ALLSRC); - if (!strcmp(name, ".ARCHIVE")) - return (ARCHIVE); - break; - case 'I': - if (!strcmp(name, ".IMPSRC")) - return (IMPSRC); - break; - case 'M': - if (!strcmp(name, ".MEMBER")) - return (MEMBER); - break; - case 'O': - if (!strcmp(name, ".OODATE")) - return (OODATE); - break; - case 'P': - if (!strcmp(name, ".PREFIX")) - return (PREFIX); - break; - case 'T': - if (!strcmp(name, ".TARGET")) - return (TARGET); - break; - default: - break; - } - } - return (name); -} - -/** - * Find the given variable in the given context and the environment. - * - * Results: - * A pointer to the structure describing the desired variable or - * NULL if the variable does not exist. - */ -static Var * -VarFindEnv(const char name[], GNode *ctxt) -{ - Var *var; - - name = VarLocal(name); - - if ((var = VarLookup(&ctxt->context, name)) != NULL) - return (var); - - if ((var = VarLookup(&VAR_ENV->context, name)) != NULL) - return (var); - - return (NULL); -} - -/** - * Look for the variable in the given context. - */ -static Var * -VarFindOnly(const char name[], GNode *ctxt) -{ - - return (VarLookup(&ctxt->context, VarLocal(name))); -} - -/** - * Look for the variable in all contexts. - */ -static Var * -VarFindAny(const char name[], GNode *ctxt) -{ - bool localCheckEnvFirst; - LstNode *ln; - Var *var; - - name = VarLocal(name); - - /* - * Note whether this is one of the specific variables we were told - * through the -E flag to use environment-variable-override for. - */ - localCheckEnvFirst = false; - LST_FOREACH(ln, &envFirstVars) { - if (strcmp(Lst_Datum(ln), name) == 0) { - localCheckEnvFirst = true; - break; - } - } - - /* - * First look for the variable in the given context. If it's not there, - * look for it in VAR_CMD, VAR_GLOBAL and the environment, - * in that order, depending on the FIND_* flags in 'flags' - */ - if ((var = VarLookup(&ctxt->context, name)) != NULL) - return (var); - - /* not there - try command line context */ - if (ctxt != VAR_CMD) { - if ((var = VarLookup(&VAR_CMD->context, name)) != NULL) - return (var); - } - - /* not there - try global context, but only if not -e/-E */ - if (ctxt != VAR_GLOBAL && (!checkEnvFirst && !localCheckEnvFirst)) { - if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL) - return (var); - } - - if ((var = VarLookup(&VAR_ENV->context, name)) != NULL) - return (var); - - /* deferred check for the environment (in case of -e/-E) */ - if ((ctxt != VAR_GLOBAL) && (checkEnvFirst || localCheckEnvFirst)) { - if ((var = VarLookup(&VAR_GLOBAL->context, name)) != NULL) - return (var); - } - - return (NULL); -} - -/** - * Add a new variable of name name and value val to the given context. - * - * Side Effects: - * The new variable is placed at the front of the given context - * The name and val arguments are duplicated so they may - * safely be freed. - */ -static void -VarAdd(const char name[], const char val[], GNode *ctxt) -{ - - Lst_AtFront(&ctxt->context, VarCreate(name, val, 0)); - DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, name, val)); -} - -/** - * Remove a variable from a context. - * - * Side Effects: - * The Var structure is removed and freed. - */ -void -Var_Delete(const char name[], GNode *ctxt) -{ - LstNode *ln; - - DEBUGF(VAR, ("%s:delete %s\n", ctxt->name, name)); - LST_FOREACH(ln, &ctxt->context) { - if (strcmp(((const Var *)Lst_Datum(ln))->name, name) == 0) { - VarDestroy(Lst_Datum(ln), true); - Lst_Remove(&ctxt->context, ln); - break; - } - } -} - -/** - * Set the variable name to the value val in the given context. - * - * Side Effects: - * If the variable doesn't yet exist, a new record is created for it. - * Else the old value is freed and the new one stuck in its place - * - * Notes: - * The variable is searched for only in its context before being - * created in that context. I.e. if the context is VAR_GLOBAL, - * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only - * VAR_CMD->context is searched. This is done to avoid the literally - * thousands of unnecessary strcmp's that used to be done to - * set, say, $(@) or $(<). - */ -void -Var_Set(const char name[], const char val[], GNode *ctxt) -{ - Var *v; - char *n; - - /* - * We only look for a variable in the given context since anything - * set here will override anything in a lower context, so there's not - * much point in searching them all just to save a bit of memory... - */ - n = VarPossiblyExpand(name, ctxt); - v = VarFindOnly(n, ctxt); - if (v == NULL) { - VarAdd(n, val, ctxt); - if (ctxt == VAR_CMD) { - /* - * Any variables given on the command line - * are automatically exported to the - * environment (as per POSIX standard) - */ - if (setenv(n, val, 1) == -1) - Punt( "setenv: %s: can't allocate memory", n); - } - } else { - Buf_Clear(v->val); - Buf_Append(v->val, val); - - if (ctxt == VAR_CMD || (v->flags & VAR_TO_ENV)) { - /* - * Any variables given on the command line - * are automatically exported to the - * environment (as per POSIX standard) - */ - char *exp_value; - - exp_value = Buf_Peel(Var_Subst(val, ctxt, false)); - if (setenv(n, exp_value, 1) == -1) - Punt( "setenv: %s: can't allocate memory", n); - free(exp_value); - } - - } - - DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, val)); - free(n); -} - -/** - * Set the a global name variable to the value. - */ -void -Var_SetGlobal(const char name[], const char value[]) -{ - - Var_Set(name, value, VAR_GLOBAL); -} - - -/** - * Set the VAR_TO_ENV flag on a variable - */ -void -Var_SetEnv(const char name[], GNode *ctxt) -{ - Var *v; - - v = VarFindOnly(name, VAR_CMD); - if (v != NULL) { - /* - * Do not allow .EXPORT: to be set on variables - * from the command line or MAKEFLAGS. - */ - Error( - "Warning: Did not set .EXPORTVAR: on %s because it " - "is from the command line or MAKEFLAGS", name); - return; - } - - v = VarFindAny(name, ctxt); - if (v == NULL) { - Lst_AtFront(&VAR_ENV->context, - VarCreate(name, NULL, VAR_TO_ENV)); - if (setenv(name, "", 1) == -1) - Punt( "setenv: %s: can't allocate memory", name); - Error("Warning: .EXPORTVAR: set on undefined variable %s", name); - } else { - if ((v->flags & VAR_TO_ENV) == 0) { - char *value; - - value = Buf_Peel(Var_Subst(Buf_Data(v->val), ctxt, false)); - v->flags |= VAR_TO_ENV; - if (setenv(v->name, value, 1) == -1) - Punt( "setenv: %s: can't allocate memory", v->name); - free(value); - } - } -} - -/** - * The variable of the given name has the given value appended to it in - * the given context. - * - * Side Effects: - * If the variable doesn't exist, it is created. Else the strings - * are concatenated (with a space in between). - * - * Notes: - * Only if the variable is being sought in the global context is the - * environment searched. - * XXX: Knows its calling circumstances in that if called with ctxt - * an actual target, it will only search that context since only - * a local variable could be being appended to. This is actually - * a big win and must be tolerated. - */ -void -Var_Append(const char name[], const char val[], GNode *ctxt) -{ - Var *v; - char *n; - - n = VarPossiblyExpand(name, ctxt); - if (ctxt == VAR_GLOBAL) { - v = VarFindEnv(n, ctxt); - } else { - v = VarFindOnly(n, ctxt); - } - if (v == NULL) { - VarAdd(n, val, ctxt); - } else { - Buf_AddByte(v->val, ' '); - Buf_Append(v->val, val); - DEBUGF(VAR, ("%s:%s = %s\n", ctxt->name, n, Buf_Data(v->val))); - } - free(n); -} - -/** - * Return the value of the named variable in the given context - * - * Results: - * The value if the variable exists, NULL if it doesn't. - */ -const char * -Var_Value(const char name[], GNode *ctxt) -{ - Var *v; - char *n; - - n = VarPossiblyExpand(name, ctxt); - v = VarFindAny(n, ctxt); - free(n); - if (v == NULL) { - return (NULL); - } else { - return (Buf_Data(v->val)); - } -} - -/** - * Modify each of the words of the passed string using the given - * function. Used to implement all modifiers. - * - * Results: - * A string of all the words modified appropriately. - */ -static char * -VarModify(const char str[], VarModifyProc *modProc, void *datum) -{ - ArgArray aa; - Buffer *buf; /* Buffer for the new string */ - int i; - bool addSpace; /* - * true if need to add a space to - * the buffer before adding the - * trimmed word - */ - - brk_string(&aa, str, false); - - addSpace = false; - buf = Buf_Init(0); - for (i = 1; i < aa.argc; i++) - addSpace = (*modProc)(aa.argv[i], addSpace, buf, datum); - - ArgArray_Done(&aa); - return (Buf_Peel(buf)); -} - -/** - * Sort the words in the string. - * - * Input: - * str String whose words should be sorted - * cmp A comparison function to control the ordering - * - * Results: - * A string containing the words sorted - */ -static char * -VarSortWords(const char str[], int (*cmp)(const void *, const void *)) -{ - ArgArray aa; - Buffer *buf; - int i; - - brk_string(&aa, str, false); - qsort(aa.argv + 1, aa.argc - 1, sizeof(char *), cmp); - - buf = Buf_Init(0); - for (i = 1; i < aa.argc; i++) { - Buf_Append(buf, aa.argv[i]); - Buf_AddByte(buf, ((i < aa.argc - 1) ? ' ' : '\0')); - } - - ArgArray_Done(&aa); - return (Buf_Peel(buf)); -} - -static int -SortIncreasing(const void *l, const void *r) -{ - - return (strcmp(*(const char* const*)l, *(const char* const*)r)); -} - -/** - * Pass through the tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution). - * Return the expanded string or NULL if the delimiter was missing - * If pattern is specified, handle escaped ampersands, and replace - * unescaped ampersands with the lhs of the pattern. - * - * Results: - * A string of all the words modified appropriately. - * If length is specified, return the string length of the buffer - * If flags is specified and the last character of the pattern is a - * $ set the VAR_MATCH_END bit of flags. - */ -static Buffer * -VarGetPattern(VarParser *vp, int delim, int *flags, VarPattern *patt) -{ - Buffer *buf; - - buf = Buf_Init(0); - - /* - * Skim through until the matching delimiter is found; pick up - * variable substitutions on the way. Also allow backslashes to quote - * the delimiter, $, and \, but don't touch other backslashes. - */ - while (*vp->ptr != '\0') { - if (*vp->ptr == delim) { - return (buf); - - } else if ((vp->ptr[0] == '\\') && - ((vp->ptr[1] == delim) || - (vp->ptr[1] == '\\') || - (vp->ptr[1] == '$') || - (vp->ptr[1] == '&' && patt != NULL))) { - vp->ptr++; /* consume backslash */ - Buf_AddByte(buf, vp->ptr[0]); - vp->ptr++; - - } else if (vp->ptr[0] == '$') { - if (vp->ptr[1] == delim) { - if (flags == NULL) { - Buf_AddByte(buf, vp->ptr[0]); - vp->ptr++; - } else { - /* - * Unescaped $ at end of patt => - * anchor patt at end. - */ - *flags |= VAR_MATCH_END; - vp->ptr++; - } - } else { - VarParser subvp = { - vp->ptr, - vp->ptr, - vp->ctxt, - vp->err, - vp->execute - }; - char *rval; - bool rfree; - - /* - * If unescaped dollar sign not - * before the delimiter, assume it's - * a variable substitution and - * recurse. - */ - rval = VarParse(&subvp, &rfree); - Buf_Append(buf, rval); - if (rfree) - free(rval); - vp->ptr = subvp.ptr; - } - } else if (vp->ptr[0] == '&' && patt != NULL) { - Buf_AppendBuf(buf, patt->lhs); - vp->ptr++; - } else { - Buf_AddByte(buf, vp->ptr[0]); - vp->ptr++; - } - } - - Buf_Destroy(buf, true); - return (NULL); -} - -/** - * Make sure this variable is fully expanded. - */ -static char * -VarExpand(Var *v, VarParser *vp) -{ - char *value; - char *result; - - if (v->flags & VAR_IN_USE) { - Fatal("Variable %s is recursive.", v->name); - /* NOTREACHED */ - } - - v->flags |= VAR_IN_USE; - - /* - * Before doing any modification, we have to make sure the - * value has been fully expanded. If it looks like recursion - * might be necessary (there's a dollar sign somewhere in the - * variable's value) we just call Var_Subst to do any other - * substitutions that are necessary. Note that the value - * returned by Var_Subst will have been - * dynamically-allocated, so it will need freeing when we - * return. - */ - value = Buf_Data(v->val); - if (strchr(value, '$') == NULL) { - result = strdup(value); - } else { - Buffer *buf; - - buf = Var_Subst(value, vp->ctxt, vp->err); - result = Buf_Peel(buf); - } - - v->flags &= ~VAR_IN_USE; - - return (result); -} - -/** - * Select only those words in value that match the modifier. - */ -static char * -modifier_M(VarParser *vp, const char value[], char endc) -{ - char *patt; - char *ptr; - char *newValue; - char modifier; - - modifier = vp->ptr[0]; - vp->ptr++; /* consume 'M' or 'N' */ - - /* - * Compress the \:'s out of the pattern, so allocate enough - * room to hold the uncompressed pattern and compress the - * pattern into that space. - */ - patt = estrdup(vp->ptr); - ptr = patt; - while (vp->ptr[0] != '\0') { - if (vp->ptr[0] == endc || vp->ptr[0] == ':') { - break; - } - if (vp->ptr[0] == '\\' && - (vp->ptr[1] == endc || vp->ptr[1] == ':')) { - vp->ptr++; /* consume backslash */ - } - *ptr = vp->ptr[0]; - ptr++; - vp->ptr++; - } - *ptr = '\0'; - - if (modifier == 'M') { - newValue = VarModify(value, VarMatch, patt); - } else { - newValue = VarModify(value, VarNoMatch, patt); - } - free(patt); - - return (newValue); -} - -/** - * Substitute the replacement string for the pattern. The substitution - * is applied to each word in value. - */ -static char * -modifier_S(VarParser *vp, const char value[], Var *v) -{ - VarPattern patt; - char delim; - char *newValue; - - patt.flags = 0; - - vp->ptr++; /* consume 'S' */ - - delim = *vp->ptr; /* used to find end of pattern */ - vp->ptr++; /* consume 1st delim */ - - /* - * If pattern begins with '^', it is anchored to the start of the - * word -- skip over it and flag pattern. - */ - if (*vp->ptr == '^') { - patt.flags |= VAR_MATCH_START; - vp->ptr++; - } - - patt.lhs = VarGetPattern(vp, delim, &patt.flags, NULL); - if (patt.lhs == NULL) { - /* - * LHS didn't end with the delim, complain and exit. - */ - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - } - - vp->ptr++; /* consume 2nd delim */ - - patt.rhs = VarGetPattern(vp, delim, NULL, &patt); - if (patt.rhs == NULL) { - /* - * RHS didn't end with the delim, complain and exit. - */ - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - } - - vp->ptr++; /* consume last delim */ - - /* - * Check for global substitution. If 'g' after the final delimiter, - * substitution is global and is marked that way. - */ - if (vp->ptr[0] == 'g') { - patt.flags |= VAR_SUB_GLOBAL; - vp->ptr++; - } - - /* - * Global substitution of the empty string causes an infinite number - * of matches, unless anchored by '^' (start of string) or '$' (end - * of string). Catch the infinite substitution here. Note that flags - * can only contain the 3 bits we're interested in so we don't have - * to mask unrelated bits. We can test for equality. - */ - if (Buf_Size(patt.lhs) == 0 && patt.flags == VAR_SUB_GLOBAL) - Fatal("Global substitution of the empty string"); - - newValue = VarModify(value, VarSubstitute, &patt); - - /* - * Free the two strings. - */ - free(patt.lhs); - free(patt.rhs); - - return (newValue); -} - -static char * -modifier_C(VarParser *vp, char value[], Var *v) -{ - VarPattern patt; - char delim; - int error; - char *newValue; - - patt.flags = 0; - - vp->ptr++; /* consume 'C' */ - - delim = *vp->ptr; /* delimiter between sections */ - - vp->ptr++; /* consume 1st delim */ - - patt.lhs = VarGetPattern(vp, delim, NULL, NULL); - if (patt.lhs == NULL) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - } - - vp->ptr++; /* consume 2st delim */ - - patt.rhs = VarGetPattern(vp, delim, NULL, NULL); - if (patt.rhs == NULL) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - } - - vp->ptr++; /* consume last delim */ - - switch (*vp->ptr) { - case 'g': - patt.flags |= VAR_SUB_GLOBAL; - vp->ptr++; /* consume 'g' */ - break; - case '1': - patt.flags |= VAR_SUB_ONE; - vp->ptr++; /* consume '1' */ - break; - default: - break; - } - - error = regcomp(&patt.re, Buf_Data(patt.lhs), REG_EXTENDED); - if (error) { - VarREError(error, &patt.re, "RE substitution error"); - free(patt.rhs); - free(patt.lhs); - return (var_Error); - } - - patt.nsub = patt.re.re_nsub + 1; - if (patt.nsub < 1) - patt.nsub = 1; - if (patt.nsub > 10) - patt.nsub = 10; - patt.matches = emalloc(patt.nsub * sizeof(regmatch_t)); - - newValue = VarModify(value, VarRESubstitute, &patt); - - regfree(&patt.re); - free(patt.matches); - free(patt.rhs); - free(patt.lhs); - - return (newValue); -} - -static char * -sysVvarsub(VarParser *vp, char startc, Var *v, const char value[]) -{ - /* - * This can either be a bogus modifier or a System-V substitution - * command. - */ - char endc; - VarPattern patt; - bool eqFound; - int cnt; - char *newStr; - const char *cp; - - endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; - - patt.flags = 0; - - /* - * First we make a pass through the string trying to verify it is a - * SYSV-make-style translation: it must be: =) - */ - eqFound = false; - cp = vp->ptr; - cnt = 1; - while (*cp != '\0' && cnt) { - if (*cp == '=') { - eqFound = true; - /* continue looking for endc */ - } else if (*cp == endc) - cnt--; - else if (*cp == startc) - cnt++; - if (cnt) - cp++; - } - - if (*cp == endc && eqFound) { - /* - * Now we break this sucker into the lhs and rhs. - */ - patt.lhs = VarGetPattern(vp, '=', &patt.flags, NULL); - if (patt.lhs == NULL) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, '='); - } - vp->ptr++; /* consume '=' */ - - patt.rhs = VarGetPattern(vp, endc, NULL, &patt); - if (patt.rhs == NULL) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, endc); - } - - /* - * SYSV modifications happen through the whole string. Note - * the pattern is anchored at the end. - */ - newStr = VarModify(value, VarSYSVMatch, &patt); - - free(patt.lhs); - free(patt.rhs); - } else { - Error("Unknown modifier '%c'\n", *vp->ptr); - vp->ptr++; - while (*vp->ptr != '\0') { - if (*vp->ptr == endc && *vp->ptr == ':') { - break; - } - vp->ptr++; - } - newStr = var_Error; - } - - return (newStr); -} - -/** - * Quote shell meta-characters in the string - * - * Results: - * The quoted string - */ -static char * -Var_Quote(const char str[]) -{ - Buffer *buf; - /* This should cover most shells :-( */ - static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; - - buf = Buf_Init(MAKE_BSIZE); - for (; *str; str++) { - if (strchr(meta, *str) != NULL) - Buf_AddByte(buf, '\\'); - Buf_AddByte(buf, *str); - } - - return (Buf_Peel(buf)); -} - - -/* - * Now we need to apply any modifiers the user wants applied. - * These are: - * :M - * words which match the given . - * is of the standard file - * wildcarding form. - * :S[g] - * Substitute for in the value - * :C[g] - * Substitute for regex in the value - * :H Substitute the head of each word - * :T Substitute the tail of each word - * :E Substitute the extension (minus '.') of - * each word - * :R Substitute the root of each word - * (pathname minus the suffix). - * :lhs=rhs - * Like :S, but the rhs goes to the end of - * the invocation. - * :U Converts variable to upper-case. - * :L Converts variable to lower-case. - * - * XXXHB update this comment or remove it and point to the man page. - */ -static char * -ParseModifier(VarParser *vp, char startc, Var *v, bool *freeResult) -{ - char *value; - char endc; - - value = VarExpand(v, vp); - *freeResult = true; - - endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; - - vp->ptr++; /* consume first colon */ - - while (*vp->ptr != '\0') { - char *newStr; /* New value to return */ - - if (*vp->ptr == endc) { - return (value); - } - - DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *vp->ptr, value)); - switch (*vp->ptr) { - case 'N': - case 'M': - newStr = modifier_M(vp, value, endc); - break; - case 'S': - newStr = modifier_S(vp, value, v); - break; - case 'C': - newStr = modifier_C(vp, value, v); - break; - default: - if (vp->ptr[1] != endc && vp->ptr[1] != ':') { -#ifdef SUNSHCMD - if ((vp->ptr[0] == 's') && - (vp->ptr[1] == 'h') && - (vp->ptr[2] == endc || vp->ptr[2] == ':')) { - const char *error; - - if (vp->execute) { - newStr = Buf_Peel( - Cmd_Exec(value, &error)); - } else { - newStr = estrdup(""); - } - - if (error) - Error(error, value); - vp->ptr += 2; - } else -#endif - { - newStr = sysVvarsub(vp, startc, v, value); - } - break; - } - - switch (vp->ptr[0]) { - case 'L': - { - const char *cp; - Buffer *buf; - buf = Buf_Init(MAKE_BSIZE); - for (cp = value; *cp; cp++) - Buf_AddByte(buf, tolower((unsigned char)*cp)); - - newStr = Buf_Peel(buf); - - vp->ptr++; - break; - } - case 'O': - newStr = VarSortWords(value, SortIncreasing); - vp->ptr++; - break; - case 'Q': - newStr = Var_Quote(value); - vp->ptr++; - break; - case 'T': - newStr = VarModify(value, VarTail, NULL); - vp->ptr++; - break; - case 'U': - { - const char *cp; - Buffer *buf; - buf = Buf_Init(MAKE_BSIZE); - for (cp = value; *cp; cp++) - Buf_AddByte(buf, toupper((unsigned char)*cp)); - - newStr = Buf_Peel(buf); - - vp->ptr++; - break; - } - case 'H': - newStr = VarModify(value, VarHead, NULL); - vp->ptr++; - break; - case 'E': - newStr = VarModify(value, VarSuffix, NULL); - vp->ptr++; - break; - case 'R': - newStr = VarModify(value, VarRoot, NULL); - vp->ptr++; - break; - default: - newStr = sysVvarsub(vp, startc, v, value); - break; - } - break; - } - - DEBUGF(VAR, ("Result is \"%s\"\n", newStr)); - if (*freeResult) { - free(value); - } - - value = newStr; - *freeResult = (value == var_Error) ? false : true; - - if (vp->ptr[0] == ':') { - vp->ptr++; /* consume colon */ - } - } - - return (value); -} - -static char * -ParseRestModifier(VarParser *vp, char startc, Buffer *buf, bool *freeResult) -{ - const char *vname; - size_t vlen; - Var *v; - char *value; - - vname = Buf_Data(buf); - vlen = Buf_Size(buf); - - v = VarFindAny(vname, vp->ctxt); - if (v != NULL) { - value = ParseModifier(vp, startc, v, freeResult); - return (value); - } - - if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { - size_t consumed; - /* - * Still need to get to the end of the variable - * specification, so kludge up a Var structure for the - * modifications - */ - v = VarCreate(vname, NULL, VAR_JUNK); - value = ParseModifier(vp, startc, v, freeResult); - if (*freeResult) { - free(value); - } - VarDestroy(v, true); - - consumed = vp->ptr - vp->input + 1; - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set when - * dynamic sources are expanded. - */ - if (vlen == 1 || - (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) { - if (strchr("!%*@", vname[0]) != NULL) { - value = emalloc(consumed + 1); - strncpy(value, vp->input, consumed); - value[consumed] = '\0'; - - *freeResult = true; - return (value); - } - } - if (vlen > 2 && - vname[0] == '.' && - isupper((unsigned char)vname[1])) { - if ((strncmp(vname, ".TARGET", vlen - 1) == 0) || - (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) || - (strncmp(vname, ".PREFIX", vlen - 1) == 0) || - (strncmp(vname, ".MEMBER", vlen - 1) == 0)) { - value = emalloc(consumed + 1); - strncpy(value, vp->input, consumed); - value[consumed] = '\0'; - - *freeResult = true; - return (value); - } - } - - *freeResult = false; - return (vp->err ? var_Error : varNoError); - } else { - /* - * Check for D and F forms of local variables since we're in - * a local context and the name is the right length. - */ - if (vlen == 2 && - (vname[1] == 'F' || vname[1] == 'D') && - (strchr("!%*<>@", vname[0]) != NULL)) { - char name[2]; - - name[0] = vname[0]; - name[1] = '\0'; - - v = VarFindOnly(name, vp->ctxt); - if (v != NULL) { - value = ParseModifier(vp, startc, v, freeResult); - return (value); - } - } - - /* - * Still need to get to the end of the variable - * specification, so kludge up a Var structure for the - * modifications - */ - v = VarCreate(vname, NULL, VAR_JUNK); - value = ParseModifier(vp, startc, v, freeResult); - if (*freeResult) { - free(value); - } - VarDestroy(v, true); - - *freeResult = false; - return (vp->err ? var_Error : varNoError); - } -} - -static char * -ParseRestEnd(VarParser *vp, Buffer *buf, bool *freeResult) -{ - const char *vname; - size_t vlen; - Var *v; - char *value; - - vname = Buf_Data(buf); - vlen = Buf_Size(buf); - - v = VarFindAny(vname, vp->ctxt); - if (v != NULL) { - value = VarExpand(v, vp); - *freeResult = true; - return (value); - } - - if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { - size_t consumed = vp->ptr - vp->input + 1; - - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set when - * dynamic sources are expanded. - */ - if (vlen == 1 || - (vlen == 2 && (vname[1] == 'F' || vname[1] == 'D'))) { - if (strchr("!%*@", vname[0]) != NULL) { - value = emalloc(consumed + 1); - strncpy(value, vp->input, consumed); - value[consumed] = '\0'; - - *freeResult = true; - return (value); - } - } - if (vlen > 2 && - vname[0] == '.' && - isupper((unsigned char)vname[1])) { - if ((strncmp(vname, ".TARGET", vlen - 1) == 0) || - (strncmp(vname, ".ARCHIVE", vlen - 1) == 0) || - (strncmp(vname, ".PREFIX", vlen - 1) == 0) || - (strncmp(vname, ".MEMBER", vlen - 1) == 0)) { - value = emalloc(consumed + 1); - strncpy(value, vp->input, consumed); - value[consumed] = '\0'; - - *freeResult = true; - return (value); - } - } - } else { - /* - * Check for D and F forms of local variables since we're in - * a local context and the name is the right length. - */ - if (vlen == 2 && - (vname[1] == 'F' || vname[1] == 'D') && - (strchr("!%*<>@", vname[0]) != NULL)) { - char name[2]; - - name[0] = vname[0]; - name[1] = '\0'; - - v = VarFindOnly(name, vp->ctxt); - if (v != NULL) { - char *val; - /* - * No need for nested expansion or anything, - * as we're the only one who sets these - * things and we sure don't put nested - * invocations in them... - */ - val = Buf_Data(v->val); - - if (vname[1] == 'D') { - val = VarModify(val, VarHead, NULL); - } else { - val = VarModify(val, VarTail, NULL); - } - - *freeResult = true; - return (val); - } - } - } - - *freeResult = false; - return (vp->err ? var_Error : varNoError); -} - -/** - * Parse a multi letter variable name, and return it's value. - */ -static char * -VarParseLong(VarParser *vp, bool *freeResult) -{ - Buffer *buf; - char startc; - char endc; - char *value; - - buf = Buf_Init(MAKE_BSIZE); - - startc = vp->ptr[0]; - vp->ptr++; /* consume opening paren or brace */ - - endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; - - /* - * Process characters until we reach an end character or a colon, - * replacing embedded variables as we go. - */ - while (*vp->ptr != '\0') { - if (*vp->ptr == endc) { - value = ParseRestEnd(vp, buf, freeResult); - vp->ptr++; /* consume closing paren or brace */ - Buf_Destroy(buf, true); - return (value); - - } else if (*vp->ptr == ':') { - value = ParseRestModifier(vp, startc, buf, freeResult); - vp->ptr++; /* consume closing paren or brace */ - Buf_Destroy(buf, true); - return (value); - - } else if (*vp->ptr == '$') { - VarParser subvp = { - vp->ptr, - vp->ptr, - vp->ctxt, - vp->err, - vp->execute - }; - char *rval; - bool rfree; - - rval = VarParse(&subvp, &rfree); - if (rval == var_Error) { - Fatal("Error expanding embedded variable."); - } - Buf_Append(buf, rval); - if (rfree) - free(rval); - vp->ptr = subvp.ptr; - } else { - Buf_AddByte(buf, *vp->ptr); - vp->ptr++; - } - } - - /* If we did not find the end character, return var_Error */ - Buf_Destroy(buf, true); - *freeResult = false; - return (var_Error); -} - -/** - * Parse a single letter variable name, and return it's value. - */ -static char * -VarParseShort(VarParser *vp, bool *freeResult) -{ - char vname[2]; - Var *v; - char *value; - - vname[0] = vp->ptr[0]; - vname[1] = '\0'; - - vp->ptr++; /* consume single letter */ - - v = VarFindAny(vname, vp->ctxt); - if (v != NULL) { - value = VarExpand(v, vp); - *freeResult = true; - return (value); - } - - /* - * If substituting a local variable in a non-local context, assume - * it's for dynamic source stuff. We have to handle this specially - * and return the longhand for the variable with the dollar sign - * escaped so it makes it back to the caller. Only four of the local - * variables are treated specially as they are the only four that - * will be set when dynamic sources are expanded. - */ - if ((vp->ctxt == VAR_CMD) || (vp->ctxt == VAR_GLOBAL)) { - - /* XXX: It looks like $% and $! are reversed here */ - switch (vname[0]) { - case '@': - *freeResult = true; - return (estrdup("$(.TARGET)")); - case '%': - *freeResult = true; - return (estrdup("$(.ARCHIVE)")); - case '*': - *freeResult = true; - return (estrdup("$(.PREFIX)")); - case '!': - *freeResult = true; - return (estrdup("$(.MEMBER)")); - default: - *freeResult = false; - return (vp->err ? var_Error : varNoError); - } - } - - /* Variable name was not found. */ - *freeResult = false; - return (vp->err ? var_Error : varNoError); -} - -static char * -VarParse(VarParser *vp, bool *freeResult) -{ - - vp->ptr++; /* consume '$' or last letter of conditional */ - - if (vp->ptr[0] == '\0') { - /* Error, there is only a dollar sign in the input string. */ - *freeResult = false; - return (vp->err ? var_Error : varNoError); - - } else if (vp->ptr[0] == OPEN_PAREN || vp->ptr[0] == OPEN_BRACE) { - /* multi letter variable name */ - return (VarParseLong(vp, freeResult)); - - } else { - /* single letter variable name */ - return (VarParseShort(vp, freeResult)); - } -} - -/** - * Given the start of a variable invocation, extract the variable - * name and find its value, then modify it according to the - * specification. - * - * Results: - * The value of the variable or var_Error if the specification - * is invalid. The number of characters in the specification - * is placed in the variable pointed to by consumed. (for - * invalid specifications, this is just 2 to skip the '$' and - * the following letter, or 1 if '$' was the last character - * in the string). A bool in *freeResult telling whether the - * returned string should be freed by the caller. - */ -char * -Var_Parse(const char input[], GNode *ctxt, bool err, - size_t *consumed, bool *freeResult) -{ - VarParser vp = { - input, - input, - ctxt, - err, - true - }; - char *value; - - value = VarParse(&vp, freeResult); - *consumed += vp.ptr - vp.input; - return (value); -} - -/* - * Given the start of a variable invocation, determine the length - * of the specification. - * - * Results: - * The number of characters in the specification. For invalid - * specifications, this is just 2 to skip the '$' and the - * following letter, or 1 if '$' was the last character in the - * string. - */ -size_t -Var_Match(const char input[], GNode *ctxt) -{ - VarParser vp = { - input, - input, - ctxt, - false, - false - }; - char *value; - bool freeResult; - - value = VarParse(&vp, &freeResult); - if (freeResult) { - free(value); - } - return (vp.ptr - vp.input); -} - -static int -match_var(const char str[], const char var[]) -{ - const char *start = str; - size_t len; - - str++; /* consume '$' */ - - if (str[0] == OPEN_PAREN || str[0] == OPEN_BRACE) { - str++; /* consume opening paren or brace */ - - while (str[0] != '\0') { - if (str[0] == '$') { - /* - * A variable inside the variable. We cannot - * expand the external variable yet. - */ - return (str - start); - } else if (str[0] == ':' || - str[0] == CLOSE_PAREN || - str[0] == CLOSE_BRACE) { - len = str - (start + 2); - - if (var[len] == '\0' && strncmp(var, start + 2, len) == 0) { - return (0); /* match */ - } else { - /* - * Not the variable we want to - * expand. - */ - return (str - start); - } - } else { - ++str; - } - } - return (str - start); - } else { - /* Single letter variable name */ - if (var[1] == '\0' && var[0] == str[0]) { - return (0); /* match */ - } else { - str++; /* consume variable name */ - return (str - start); - } - } -} - -/** - * Substitute for all variables in the given string in the given - * context If err is true, Parse_Error will be called when an - * undefined variable is encountered. - * - * Results: - * The resulting string. - * - * Side Effects: - * None. The old string must be freed by the caller - */ -Buffer * -Var_Subst(const char str[], GNode *ctxt, bool err) -{ - bool errorReported; - Buffer *buf; /* Buffer for forming things */ - - /* - * Set true if an error has already been reported to prevent a - * plethora of messages when recursing. XXXHB this comment sounds - * wrong. - */ - errorReported = false; - - buf = Buf_Init(0); - while (str[0] != '\0') { - if ((str[0] == '$') && (str[1] == '$')) { - /* - * A dollar sign may be escaped with another dollar - * sign. In such a case, we skip over the escape - * character and store the dollar sign into the - * buffer directly. - */ - str++; - Buf_AddByte(buf, str[0]); - str++; - - } else if (str[0] == '$') { - /* Variable invocation. */ - VarParser subvp = { - str, - str, - ctxt, - err, - true - }; - char *rval; - bool rfree; - - rval = VarParse(&subvp, &rfree); - - /* - * When we come down here, val should either point to - * the value of this variable, suitably modified, or - * be NULL. Length should be the total length of the - * potential variable invocation (from $ to end - * character...) - */ - if (rval == var_Error || rval == varNoError) { - /* - * If performing old-time variable - * substitution, skip over the variable and - * continue with the substitution. Otherwise, - * store the dollar sign and advance str so - * we continue with the string... - */ - if (oldVars) { - str = subvp.ptr; - } else if (err) { - /* - * If variable is undefined, complain - * and skip the variable. The - * complaint will stop us from doing - * anything when the file is parsed. - */ - if (!errorReported) { - Parse_Error(PARSE_FATAL, - "Undefined variable \"%.*s\"", (int)(subvp.ptr - subvp.input), str); - } - errorReported = true; - str = subvp.ptr; - } else { - Buf_AddByte(buf, str[0]); - str++; - } - } else { - /* - * Copy all the characters from the variable - * value straight into the new string. - */ - Buf_Append(buf, rval); - if (rfree) { - free(rval); - } - str = subvp.ptr; - } - } else { - Buf_AddByte(buf, str[0]); - str++; - } - } - - return (buf); -} - -/** - * Substitute for all variables except if it is the same as 'var', - * in the given string in the given context. If err is true, - * Parse_Error will be called when an undefined variable is - * encountered. - * - * Results: - * The resulting string. - * - * Side Effects: - * None. The old string must be freed by the caller - */ -Buffer * -Var_SubstOnly(const char var[], const char str[], bool err) -{ - GNode *ctxt = VAR_GLOBAL; - bool errorReported; - Buffer *buf; /* Buffer for forming things */ - - /* - * Set true if an error has already been reported to prevent a - * plethora of messages when recursing. XXXHB this comment sounds - * wrong. - */ - errorReported = false; - - buf = Buf_Init(0); - while (str[0] != '\0') { - if (str[0] == '$') { - int skip; - - skip = match_var(str, var); - if (skip > 0) { - Buf_AddBytes(buf, skip, str); - str += skip; - } else { - /* Variable invocation. */ - VarParser subvp = { - str, - str, - ctxt, - err, - true - }; - char *rval; - bool rfree; - - rval = VarParse(&subvp, &rfree); - - /* - * When we get down here, rval should either - * point to the value of this variable, or be - * NULL. - */ - if (rval == var_Error || rval == varNoError) { - /* - * If performing old-time variable - * substitution, skip over the - * variable and continue with the - * substitution. Otherwise, store the - * dollar sign and advance str so we - * continue with the string... - */ - if (oldVars) { - str = subvp.ptr; - } else if (err) { - /* - * If variable is undefined, - * complain and skip the - * variable. The complaint - * will stop us from doing - * anything when the file is - * parsed. - */ - if (!errorReported) { - Parse_Error(PARSE_FATAL, - "Undefined variable \"%.*s\"", (int)(subvp.ptr - subvp.input), str); - } - errorReported = true; - str = subvp.ptr; - } else { - Buf_AddByte(buf, str[0]); - str++; - } - } else { - /* - * Copy all the characters from the - * variable value straight into the - * new string. - */ - Buf_Append(buf, rval); - if (rfree) { - free(rval); - } - str = subvp.ptr; - } - } - } else { - Buf_AddByte(buf, str[0]); - str++; - } - } - - return (buf); -} - -/** - * Initialize the module - * - * Side Effects: - * The VAR_CMD and VAR_GLOBAL contexts are created - */ -void -Var_Init(char **env) -{ - char **ptr; - - VAR_CMD = Targ_NewGN("Command"); - VAR_ENV = Targ_NewGN("Environment"); - VAR_GLOBAL = Targ_NewGN("Global"); - - /* - * Copy user environment variables into ENV context. - */ - for (ptr = env; *ptr != NULL; ++ptr) { - char *tmp = estrdup(*ptr); - const char *name = tmp; - char *sep = strchr(name, '='); - - if (sep != NULL) { - const char *value = sep + 1; - - *sep = '\0'; - VarAdd(name, value, VAR_ENV); - } - free(tmp); - } -} - -/** - * Print all variables in global and command line contexts. - */ -void -Var_Dump(void) -{ - const LstNode *ln; - const Var *v; - - printf("#*** Global Variables:\n"); - LST_FOREACH(ln, &VAR_GLOBAL->context) { - v = Lst_Datum(ln); - printf("%-16s = %s\n", v->name, Buf_Data(v->val)); - } - - printf("#*** Command-line Variables:\n"); - LST_FOREACH(ln, &VAR_CMD->context) { - v = Lst_Datum(ln); - printf("%-16s = %s\n", v->name, Buf_Data(v->val)); - } -} - -/** - * Print the values of any variables requested by - * the user. - */ -void -Var_Print(Lst *vlist, bool expandVars) -{ - LstNode *n; - - LST_FOREACH(n, vlist) { - const char *name = Lst_Datum(n); - - if (expandVars) { - char *v; - char *value; - - v = emalloc(strlen(name) + 1 + 3); - sprintf(v, "${%s}", name); - - value = Buf_Peel(Var_Subst(v, VAR_GLOBAL, false)); - printf("%s\n", value); - - free(v); - free(value); - } else { - const char *value = Var_Value(name, VAR_GLOBAL); - printf("%s\n", value != NULL ? value : ""); - } - } -} - diff --git a/usr.bin/make/var.h b/usr.bin/make/var.h deleted file mode 100644 index 12af30968e..0000000000 --- a/usr.bin/make/var.h +++ /dev/null @@ -1,86 +0,0 @@ -/*- - * Copyright (c) 2002 Juli Mallett. - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: src/usr.bin/make/var.h,v 1.8 2005/02/07 16:27:19 harti Exp $ - * $DragonFly: src/usr.bin/make/var.h,v 1.43 2005/09/24 07:27:26 okumoto Exp $ - */ - -#ifndef var_h_9cccafce -#define var_h_9cccafce - -#include - -struct Buffer; -struct GNode; - -/* Variables defined in a global context, e.g in the Makefile itself */ -extern struct GNode *VAR_GLOBAL; - -/* Variables defined on the command line */ -extern struct GNode *VAR_CMD; - -/* - * Value returned by Var_Parse when an error is encountered. It actually - * points to an empty string, so naive callers needn't worry about it. - */ -extern char var_Error[]; - -/* - * true if environment should be searched for all variables before - * the global context - */ -extern bool checkEnvFirst; - -/* Do old-style variable substitution */ -extern bool oldVars; - -void Var_Append(const char [], const char [], struct GNode *); -void Var_Delete(const char [], struct GNode *); -void Var_Dump(void); -void Var_Init(char **); -size_t Var_Match(const char [], struct GNode *); -char *Var_Parse(const char [], struct GNode *, bool, size_t *, bool *); -void Var_Print(struct Lst *, bool); -void Var_Set(const char [], const char [], struct GNode *); -void Var_SetGlobal(const char [], const char []); -void Var_SetEnv(const char [], struct GNode *); -struct Buffer *Var_Subst(const char [], struct GNode *, bool); -struct Buffer *Var_SubstOnly(const char [], const char [], bool); -const char *Var_Value(const char [], struct GNode *); - -#endif /* var_h_9cccafce */