diff --git a/src/tools/jam/Build.com b/src/tools/jam/Build.com new file mode 100755 index 0000000000..d5c6a72caf --- /dev/null +++ b/src/tools/jam/Build.com @@ -0,0 +1,31 @@ +! Bootstrap build script for Jam +$ cxx /define=VMS builtins.c +$ cxx /define=VMS command.c +$ cxx /define=VMS compile.c +$ cxx /define=VMS expand.c +$ cxx /define=VMS execvms.c +$ cxx /define=VMS filevms.c +$ cxx /define=VMS glob.c +$ cxx /define=VMS hash.c +$ cxx /define=VMS headers.c +$ cxx /define=VMS jambase.c +$ cxx /define=VMS lists.c +$ cxx /define=VMS make.c +$ cxx /define=VMS make1.c +$ cxx /define=VMS newstr.c +$ cxx /define=VMS option.c +$ cxx /define=VMS parse.c +$ cxx /define=VMS pathvms.c +$ cxx /define=VMS regexp.c +$ cxx /define=VMS rules.c +$ cxx /define=VMS scan.c +$ cxx /define=VMS search.c +$ cxx /define=VMS timestamp.c +$ cxx /define=VMS variable.c +$ cxx /define=VMS jam.c +$ cxx /define=VMS jamgram.c +$ cxxlink/exe=jam.exe command.obj, compile.obj, execvms.obj, expand.obj, - + filevms.obj, glob.obj, hash.obj, headers.obj, lists.obj, make.obj, - + make1.obj, newstr.obj, option.obj, parse.obj, pathvms.obj, regexp.obj, - + rules.obj, scan.obj, search.obj, timestamp.obj, variable.obj, jam.obj, - + jamgram.obj, jambase.obj, builtins.obj diff --git a/src/tools/jam/Build.mpw b/src/tools/jam/Build.mpw new file mode 100644 index 0000000000..661bd86240 --- /dev/null +++ b/src/tools/jam/Build.mpw @@ -0,0 +1,47 @@ +# This line must be set manually to the CodeWarrior Pro 5 installation. +# Good luck! + +set CW "malyn_apps:CodeWarrior Pro 5:MetroWerks CodeWarrior" + +set -e MWCincludes "{CW}:MacOS Support:Universal:Interfaces:CIncludes,{CW}:MacOS Support:OpenTransport:Open Tpt Client Developer:Includes:CIncludes,{CW}:MacOS Support:Headers:Apple MPW,{CW}:MSL:MSL_C:MSL_Common:Include,{CW}:MSL:MSL_C++:MSL_Common:Include,{CW}:MSL:MSL_C:MSL_MacOS:Include" + +mwcppc -o :bin.mac:command.o -nomapcr -w off command.c +mwcppc -o :bin.mac:compile.o -nomapcr -w off compile.c +mwcppc -o :bin.mac:execmac.o -nomapcr -w off execmac.c +mwcppc -o :bin.mac:filemac.o -nomapcr -w off filemac.c +mwcppc -o :bin.mac:pathmac.o -nomapcr -w off pathmac.c +mwcppc -o :bin.mac:jamgram.o -nomapcr -w off jamgram.c +mwcppc -o :bin.mac:expand.o -nomapcr -w off expand.c +mwcppc -o :bin.mac:glob.o -nomapcr -w off glob.c +mwcppc -o :bin.mac:hash.o -nomapcr -w off hash.c +mwcppc -o :bin.mac:headers.o -nomapcr -w off headers.c +mwcppc -o :bin.mac:lists.o -nomapcr -w off lists.c +mwcppc -o :bin.mac:make.o -nomapcr -w off make.c +mwcppc -o :bin.mac:make1.o -nomapcr -w off make1.c +mwcppc -o :bin.mac:newstr.o -nomapcr -w off newstr.c +mwcppc -o :bin.mac:option.o -nomapcr -w off option.c +mwcppc -o :bin.mac:parse.o -nomapcr -w off parse.c +mwcppc -o :bin.mac:regexp.o -nomapcr -w off regexp.c +mwcppc -o :bin.mac:rules.o -nomapcr -w off rules.c +mwcppc -o :bin.mac:scan.o -nomapcr -w off scan.c +mwcppc -o :bin.mac:search.o -nomapcr -w off search.c +mwcppc -o :bin.mac:timestamp.o -nomapcr -w off timestamp.c +mwcppc -o :bin.mac:variable.o -nomapcr -w off variable.c + +mwlinkppc -library -o :bin.mac:libjam.lib :bin.mac:command.o :bin.mac:compile.o :bin.mac:execmac.o :bin.mac:filemac.o :bin.mac:pathmac.o :bin.mac:jamgram.o :bin.mac:expand.o :bin.mac:glob.o :bin.mac:hash.o :bin.mac:headers.o :bin.mac:lists.o :bin.mac:make.o :bin.mac:make1.o :bin.mac:newstr.o :bin.mac:option.o :bin.mac:parse.o :bin.mac:regexp.o :bin.mac:rules.o :bin.mac:scan.o :bin.mac:search.o :bin.mac:timestamp.o :bin.mac:variable.o +mwcppc -o :bin.mac:mkjambase.o -nomapcr -w off mkjambase.c + +mwlinkppc -o :bin.mac:mkjambase -mpwtool -warn :bin.mac:mkjambase.o "{CW}:MacOS Support:Universal:Libraries:StubLibraries:Interfacelib" "{CW}:MacOS Support:Universal:Libraries:StubLibraries:ThreadsLib" "{CW}:MacOS Support:Universal:Libraries:StubLibraries:Mathlib" "{CW}:MacOS Support:Libraries:Apple MPW PPC:PPCToolLibs.o" "{CW}:MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" "{CW}:MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib" +mwcppc -o :bin.mac:jam.o -nomapcr -w off jam.c + +:bin.mac:mkjambase jambase.c Jambase + +mwcppc -o :bin.mac:jambase.o -nomapcr -w off jambase.c + +mwlinkppc -o :bin.mac:jam -mpwtool -warn :bin.mac:jam.o :bin.mac:jambase.o :bin.mac:libjam.lib "{CW}:MacOS Support:Universal:Libraries:StubLibraries:Interfacelib" "{CW}:MacOS Support:Universal:Libraries:StubLibraries:ThreadsLib" "{CW}:MacOS Support:Universal:Libraries:StubLibraries:Mathlib" "{CW}:MacOS Support:Libraries:Apple MPW PPC:PPCToolLibs.o" "{CW}:MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" "{CW}:MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib" + + + + + + diff --git a/src/tools/jam/Jam.html b/src/tools/jam/Jam.html new file mode 100644 index 0000000000..91ffd3c78f --- /dev/null +++ b/src/tools/jam/Jam.html @@ -0,0 +1,1350 @@ + + + Jam/MR - Make(1) Redux + + + +
+ +

Jam/MR - Make(1) Redux

+ +

The Jam/MR Executable + +

+ +
+ +

USAGE

+ +
+jam [ -a ] [ -n ] [ -v ] [ -q ]
+    [ -d debug ] 
+    [ -f jambase ] 
+    [ -j jobs ] 
+    [ -o actionsfile ] 
+    [ -s var=value ] 
+    [ -t target ] 
+    [ target ... ]
+
+ +

DESCRIPTION

+ +

+ + Jam is a program construction tool, like make(1). + +

+ + Jam recursively builds target files from source files, + using dependency information and updating actions expressed in + the Jambase file, which is written in jam's own interpreted + language. The default Jambase is compiled into jam and + provides a boilerplate for common use, relying on a user-provide + file "Jamfile" to enumerate actual targets and sources. + +

+ + The Jambase is described in the Jambase + Reference and the document Using + Jamfiles and Jambase. + +

OPTIONS

+ +

+ + If target is provided on the command line, jam + builds target; otherwise jam builds the target + 'all'. + +

+ + Jam may be invoked with the following options: + +

+ +
-a + Build all targets anyway, even if they are up-to-date. + +
-d n + Enable cummulative debugging levels from 1 to n. + Interesting values are: + +
+
1
Show actions (the default) +
2
Show "quiet" actions and display all action text +
3
Show dependency analysis, and target/source + timestamps/paths +
4
Show shell arguments +
5
Show rule invocations and variable expansions +
6
Show directory/header file/archive scans +
7
Show variable settings +
8
Show variable fetches +
9
Show variable manipulation, scanner tokens +
+ +
-d +n + Enable debugging level n. + +
-d 0 + Turn off all debugging levels. Only errors are not suppressed. + +
-f jambase + Read jambase instead of using the built-in Jambase. + Only one -f flag is permitted, but the jambase may + explicitly include other files. + +
-j n + Run up to n shell commands concurrently (UNIX + and NT only). The default is 1. + +
-n + Don't actually execute the updating actions, but do + everything else. This changes the debug level default to -d2. + +
-o file + Write the updating actions to the specified file instead + of running them (or outputting them, as on the Mac). + +
-q + Quit quickly (as if an interrupt was received) + as soon as any target fails. + +
-s var=value + Set the variable var to value, overriding + both internal variables and variables imported from the + environment. + +
-t target + Rebuild target and everything that depends on it, + even if it is up-to-date. + +
-v + Print the version of jam and exit. + +
+ +

OPERATION

+ +

+ + Jam has four phases of operation: start-up, parsing, + binding, and updating. + +

Start-up

+ +

+ + Upon start-up, jam imports environment variable settings + into jam variables. Environment variables are split at + blanks with each word becoming an element in the variable's list + of values. Environment variables whose names end in PATH are + split at $(SPLITPATH) characters (e.g., ":" for Unix). + +

+ + To set a variable's value on the command line, overriding the + variable's environment value, use the -s option. To see variable + assignments made during jam's execution, use the -d+7 + option. + +

Parsing

+ +

+ + In the parsing phase, jam reads and parses the Jambase + file, by default the built-in one. It is written in the jam + language. See Language below. The + last action of the Jambase is to read (via the "include" rule) + a user-provided file called "Jamfile". + +

+ + Collectively, the purpose of the Jambase and the Jamfile is to + name built target and source files, construct the dependency + graph among them, and associate build actions with targets. + The Jambase defines boilerplate rules and variable assignments, + and the Jamfile uses these to specify the actual relationship + among the target and source files. See the Jambase Reference and the document Using Jamfiles and Jambase for information. + + +

Binding

+ + +

+ +

Binding
+ + After parsing, jam recursively descends the dependency + graph and binds every file target with a location in the + filesystem. If jam detects a circular dependency in the + graph, it issues a warning. + +

+ + File target names are given as absolute or relative path names + in the filesystem. If the path name is absolute, it is bound + as is. If the path name is relative, it is normally bound as + is, and thus relative to the current directory. This can be + modified by the settings of the $(SEARCH) and $(LOCATE) variables, + which enable jam to find and build targets spread across + a directory tree. See SEARCH and LOCATE + Variables below. + +

Update Determination
+ + After binding each target, jam determines whether the + target needs updating, and if so marks the target for the updating + phase. A target is normally so marked if it is missing, it is + older than any of its sources, or any of its sources are marked + for updating. This behavior can be modified by the application + of special built-in rules, ALWAYS, LEAVES, NOCARE, NOTFILE, + NOUPDATE, and TEMPORARY. See Modifying + Binding below. + +

Header File Scanning
+ +

+ + During the binding phase, jam also performs header file + scanning, where it looks inside source files for the implicit + dependencies on other files caused by C's #include syntax. This + is controlled by the special variables $(HDRSCAN) and $(HDRRULE). + The result of the scan is formed into a rule invocation, with + the scanned file as the target and the found included file names + as the sources. Note that this is the only case where rules + are invoked outside the parsing phase. See HDRSCAN and HDRRULE Variables below. + +

Updating

+ +

+ + After binding, jam again recursively descends the dependency + graph, this time executing the update actions for each target + marked for update during the binding phase. If a target's + updating actions fail, then all other targets which depend on + that target are skipped. + +

+ + The -j flag instructs jam to build more than one target + at a time. If there are multiple actions on a single target, + they are run sequentially. + + +

LANGUAGE

+ + +

Overview

+ + Jam has an interpreted, procedural language. Statements + in jam are rule (procedure) definitions, rule invocations, + flow-of-control structures, variable assignments, and sundry + language support. + +

Lexical Features

+ +

+ + Jam treats its input files as whitespace-separated tokens, + with two exceptions: double quotes (") can enclose whitespace + to embed it into a token, and everything between the matching + curly braces ({}) in the definition of a rule action is treated + as a single string. A backslash (\) can escape a double quote, + or any single whitespace character. + +

+ + Jam requires whitespace (blanks, tabs, or newlines) to + surround all tokens, including the colon (:) and semicolon + (;) tokens. + +

+ + Jam keywords (an mentioned in this document) are reserved + and generally must be quoted with double quotes (") to be used + as arbitrary tokens, such as variable or target names. + +

Targets

+ +

+ + The essential jam data entity is a target. Built targets + are files to be updated. Source targets are the files used in + updating built targets. Built targets and source targets are + collectively referred to as file targets, and frequently built + targets are source targets for other built targets. Pseudotargets + are symbols which represent dependencies on other targets, but + which are not themselves associated with any real file. + +

+ + A file target's identifier is generally the file's name, which + can be absolutely rooted, relative to the directory of jam's + invocation, or simply local (no directory). Most often it is + the last case, and the actual file path is bound using the + $(SEARCH) and $(LOCATE) special variables. See + SEARCH and LOCATE Variables below. A local filename is + optionally qualified with grist, a string value used to assure + uniqueness. A file target with an identifier of the form + file(member) is a library member (usually an ar(1) archive + on UNIX). + +

Rules

+ +

+ + The basic jam language entity is called a rule. A rule + is defined in two parts: the procedure and the actions. The + procedure is a body of jam statements to be run when the + rule is invoked; the actions are the OS shell commands to execute + when updating the built targets of the rule. + +

+ + Rules can return values, which can be expanded into a list with + "[ rule args ... ]". A rule's value is the value + of its last statement, though only the following statements + have values: 'if' (value of the leg chosen), 'switch' (value of the case + chosen), set (value of the resulting variable), and 'return' (value + of its arguments). Note that 'return' doesn't actually cause a + return, i.e., is a no-op unless it is the last statement + of the last block executed within rule body. + +

+ + The jam statements for defining and invoking rules are + as follows: + +

+ +

+ rule rulename { statements } + + +
Define a rule's procedure, replacing any previous + definition. + +

+ actions [ modifiers ] rulename { commands } + + +
Define a rule's updating actions, replacing any + previous definition. + +

+ rulename field1 : field2 : ... + : fieldN ; + + +
Invoke a rule. + +

+ on target rulename field1 : field2 : ... + : fieldN ; + + +
Invoke a rule under the influence of target's specific + variables.. + +

+ [ rulename field1 : field2 : ... + : fieldN ]
+ [ on target rulename field1 : field2 : ... + : fieldN ]
+ +
+ +
Used as an argument, expands to the return value of the rule invoked. + +
+ +

+ + A rule is invoked with values in field1 through + fieldN. They may be referenced in the procedure's + statements as $(1) through $(N) (9 max), and the + first two only may be referenced in the action's commands + as $(1) and $(2). $(<) and $(>) are synonymous with $(1) + and $(2). + +

+ + Rules fall into two categories: updating rules (with actions), + and pure procedure rules (without actions). Updating rules + treat arguments $(1) and $(2) as built targets and sources, + respectively, while pure procedure rules can take arbitrary + arguments. + +

+ + When an updating rule is invoked, its updating actions are added + to those associated with its built targets ($(1)) before the + rule's procedure is run. Later, to build the targets in the + updating phase, commands are passed to the OS command + shell, with $(1) and $(2) replaced by bound versions of the + target names. See Binding above. + +

+ + Rule invokation may be indirected through a variable: + +

+ +

+ $(var) field1 : field2 : ... + : fieldN ; + + +

+ on target $(var) field1 : field2 : ... + : fieldN ; + + +

+ [ $(var) field1 : field2 : ... + + : fieldN ]
+ [ on target $(var) field1 : field2 : ... + : fieldN ]
+ +
+ +
+ + The variable's value names the rule (or rules) to be invoked. + A rule is invoked for each element in the list of + $(var)'s values. The fields + field1 : field2 : ... are passed as + arguments for each invokation. For the [ ... ] forms, + the return value is the concatenation of the return values for + all of the invokations. + + +

Action Modifiers

+ + +

+ + The following action modifiers are understood: + +

+ +

actions bind vars +
$(vars) will be replaced with bound values. + +

actions existing +
$(>) includes only source targets currently existing. + +

actions ignore +
The return status of the commands is ignored. + +

actions piecemeal +
commands are repeatedly invoked with a subset + of $(>) small enough to fit in the command buffer on this + OS. + +

actions quietly +
The action is not echoed to the standard output. + +

actions together +
The $(>) from multiple invocations of the same action + on the same built target are glommed together. + +

actions updated +
$(>) includes only source targets themselves marked + for updating. + +
+ + +

Built-in Rules

+ +

+ Jam has eleven built-in rules, all of which are pure + procedure rules without updating actions. They are in + three groups: the first builds the dependency graph; + the second modifies it; and the third are just utility + rules. + +

+ +

Dependency Building
+ +

+ +

+ DEPENDS targets1 : targets2 ; + + +
Builds a direct dependency: makes each of targets1 + depend on each of targets2. Generally, targets1 + will be rebuilt if targets2 are themselves rebuilt are + or are newer than targets1. + +

+ INCLUDES targets1 : targets2 ; + + +
Builds a sibling dependency: makes any target that depends + on any of targets1 also depend on each of targets2. + This reflects the dependencies that arise when one source file + includes another: the object built from the source file depends + both on the original and included source file, but the two + sources files don't depend on each other. For example: + + +

DEPENDS foo.o : foo.c ; +
INCLUDES foo.c : foo.h ; +
+ +

+ + "foo.o" depends on "foo.c" and "foo.h" in this example. + +

+ + +

Modifying Binding
+ + +

+ + The six rules ALWAYS, LEAVES, NOCARE, NOTFILE, NOUPDATE, and + TEMPORARY modify the dependency graph so that jam treats + the targets differently during its target binding phase. See + Binding above. Normally, jam + updates a target if it is missing, if its filesystem modification + time is older than any of its dependencies (recursively), or if + any of its dependencies are being updated. This basic behavior + can be changed by invoking the following rules: + +

+ +

+ ALWAYS targets ; + + +
Causes targets to be rebuilt regardless of whether + they are up-to-date (they must still be in the dependency graph). + This is used for the clean and uninstall targets, as they have + no dependencies and would otherwise appear never to need building. + It is best applied to targets that are also NOTFILE targets, + but it can also be used to force a real file to be updated as + well. + +

+ LEAVES targets ; + + +
Makes each of targets depend only on its leaf sources, + and not on any intermediate targets. This makes it immune to + its dependencies being updated, as the "leaf" dependencies are + those without their own dependencies and without updating actions. + This allows a target to be updated only if original source files + change. + +

+ NOCARE targets ; + + +
Causes jam to ignore targets that neither + can be found nor have updating actions to build them. Normally + for such targets jam issues a warning and then skips + other targets that depend on these missing targets. The HdrRule + in Jambase uses NOCARE on the header file names found during + header file scanning, to let jam know that the included + files may not exist. For example, if a #include is within an + #ifdef, the included file may not actually be around. + +

+ NOTFILE targets ; + + +
Marks targets as pseudotargets and not real files. + No timestamp is checked, and so the actions on such a target + are only executed if the target's dependencies are updated, or + if the target is also marked with ALWAYS. The default jam + target "all" is a pseudotarget. In Jambase, NOTFILE is used to + define several addition convenient pseudotargets. + +

+ NOUPDATE targets ; + + +
Causes the timestamps on targets to be ignored. + This has two effects: first, once the target has been created + it will never be updated; second, manually updating target will + not cause other targets to be updated. In Jambase, for example, + this rule is applied to directories by the MkDir rule, because + MkDir only cares that the target directory exists, not when it + has last been updated. + +

+ TEMPORARY targets ; + + +
Marks targets as temporary, allowing them to be + removed after other targets that depend upon them have been + updated. If a TEMPORARY target is missing, jam uses the + timestamp of the target's parent. Jambase uses TEMPORARY to + mark object files that are archived in a library after they are + built, so that they can be deleted after they are archived. + +
+ +

Utility Rules
+ +

+ + The two rules ECHO and EXIT are utility rules, used only in + jam's parsing phase. + +

+ +

+ ECHO args ; + + +
Blurts out the message args to stdout. + +

+ EXIT args ; + + +
Blurts out the message args to stdout and then exits + with a failure status. + +

+ + "Echo", "echo", "Exit", and "exit" are accepted as aliases for ECHO + and EXIT, since it is hard to tell that these are built-in + rules and not part of the language, like "include". + +

+ +

+ + The GLOB rule does filename globbing. + +

+ +

+ GLOB directories : patterns + + +
Using the same wildcards as for the patterns in the switch statement). It is invoked by being + used as an argument to a rule invocation inside of `[ ]`. For + example: "FILES = [ GLOB dir1 dir2 : *.c *.h ]" sets + A to the list of C source and header files in dir1 or dir2. + The resulting filenames are the full pathnames, including the + directory, but the pattern is applied only to the file name + without the directory. + +
+ +

+ + The MATCH rule does pattern matching. + +

+ +

+ MATCH regexps : list + + +
Matches the egrep(1) style regular expressions + regexps against the strings in list. The result + is the concatenation of matching () subexpressions for + each string in list, and for each regular expression in + regexps. Only useful within the [ ] construct, + to change the result into a list. + +
+ +

Flow-of-Control

+ +

+ + Jam has several simple flow-of-control statements: + +

+ +

+ + for var in list { statements } + + + +

Executes statements for each element in + list, setting the variable var to the element + value. + + +

+ + + if cond { statements } +
[ else statements ] + +
+ +

Does the obvious; the else clause is optional. + cond is built of: + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
a true if any a element is a non-zero-length + string
a = b list a matches list b + string-for-string
a != b list a does not match list b
a < b a[i] string is less than b[i] + string, where i is first mismatched element + in lists a and b
a <= b every a string is less than or equal to + its b counterpart
a > b a[i] string is greater than b[i] + string, where i is first mismatched element
a >= b every a string is greater than or equal to + its b counterpart
a in b true if all elements of a can be found + in b, or if a has no elements
! cond condition not true
cond && cond conjunction
cond || cond disjunction
( cond ) precedence grouping
+ +

+ + include file ; + + + +

Causes jam to read the named file. + The file is bound like a regular target (see Binding above) but unlike a regular + target the include file cannot be built. + +

+ + The include file is inserted into the input stream during + the parsing phase. The primary input file and all the included + file(s) are treated as a single file; that is, jam + infers no scope boundaries from included files. + +

+ + local vars [ = values ] ; + + + +

Creates new vars inside to the enclosing {} + block, obscuring any previous values they might have. The + previous values for vars are restored when the current + block ends. Any rule called or file included will see the + local and not the previous value (this is sometimes called + Dynamic Scoping). The local statement may appear anywhere, + even outside of a block (in which case the previous value + is restored when the input ends). The vars are + initialized to values if present, or left uninitialized + otherwise. + +

+ + return values ; + + + +

Within a rule body, the return statement sets the return + value for an invocation of the rule. It does not cause the + rule to return; a rule's value is actually the value of the + last statement executed, so a return should be the + last statement executed before the rule "naturally" returns. + +

+ + + switch value + +
{ +
case pattern1 : statements ; +
case pattern2 : statements ; +
... +
} + +
+ +

The switch statement executes zero or one of the + enclosed statements, depending on which, if any, is + the first case whose pattern matches value. + The pattern values are not variable-expanded. The + pattern values may include the following wildcards: + + + + + + + + + + + + + + +
? match any single character
* match zero or more characters
[chars] match any single character in chars
[^chars] match any single character not in chars
\x match x (escapes the other wildcards)
+ +

+ + while cond { statements } + + + +

Repeatedly execute statements while cond + remains true upon entry. (See the description of cond + expression syntax under if, above). +
+ +

Variables

+ +

+ + Jam variables are lists of zero or more elements, with + each element being a string value. An undefined variable is + indistinguishable from a variable with an empty list, however, + a defined variable may have one more elements which are null + strings. All variables are referenced as $(variable). + +

+ + Variables are either global or target-specific. In the latter + case, the variable takes on the given value only during the + updating of the specific target. + +

+ + A variable is defined with: + +

+ +
+ variable = elements ; +
+ variable += elements ; +
+ variable on targets = elements ; +
+ variable on targets += elements ; +
+ variable default = elements ; +
+ variable ?= elements ; + +
+ +

+ + The first two forms set variable globally. The third + and forth forms set a target-specific variable. The = operator + replaces any previous elements of variable with + elements; the += operation adds elements to + variable's list of elements. The final two forms are + synonymous: they set variable globally, but only if it + was previously unset. + +

+ + Variables referenced in updating commands will be replaced with + their values; target-specific values take precedence over global + values. Variables passed as arguments ($(1) and $(2)) to actions + are replaced with their bound values; the "bind" modifier can + be used on actions to cause other variables to be replaced with + bound values. See Action Modifiers + above. + +

+ + Jam variables are not re-exported to the environment of + the shell that executes the updating actions, but the updating + actions can reference jam variables with $(variable). + +

Variable Expansion

+ +

+ + During parsing, jam performs variable expansion on each + token that is not a keyword or rule name. Such tokens with + embedded variable references are replaced with zero or more + tokens. Variable references are of the form $(v) or + $(vm), where v is the variable name, and m + are optional modifiers. + +

+ + Variable expansion in a rule's actions is similar to variable + expansion in statements, except that the action string is + tokenized at whitespace regardless of quoting. + +

+ + The result of a token after variable expansion is the + product of the components of the token, where each + component is a literal substring or a list substituting a variable + reference. For example: + +

+ +
$(X) -> a b c +
t$(X) -> ta tb tc +
$(X)z -> az bz cz +
$(X)-$(X) -> a-a a-b a-c b-a b-b b-c c-a c-b c-c + +
+ +

+ + The variable name and modifiers can themselves contain + a variable reference, and this partakes of the product + as well: + +

+ +
$(X) -> a b c +
$(Y) -> 1 2 +
$(Z) -> X Y +
$($(Z)) -> a b c 1 2 + +
+ +

+ + Because of this product expansion, if any variable reference in + a token is undefined, the result of the expansion is an empty + list. If any variable element is a null string, the result + propagates the non-null elements: + +

+ +
$(X) -> a "" +
$(Y) -> "" 1 +
$(Z) -> +
*$(X)$(Y)* -> *a* *a1* ** *1* +
*$(X)$(Z)* -> + +
+ +

+ + A variable element's string value can be parsed into grist and + filename-related components. Modifiers to a variable are used + to select elements, select components, and replace components. + The modifiers are: + +

+ +
[n] + Select element number n (starting at 1). If + the variable contains fewer than n elements, + the result is a zero-element list. + +
[n-m] + Select elements number n through m. + +
[n-] + Select elements number n through the last. + +
:B + Select filename base. + +
:S + Select (last) filename suffix. + +
:M + Select archive member name. + +
:D + Select directory path. + +
:P + Select parent directory. + +
:G + Select grist. + +
:U + Replace lowercase characters with uppercase. + +
:L + Replace uppercase characters with lowercase. + +
:chars + Select the components listed in chars. + +
:G=grist + Replace grist with grist. + +
:D=path + Replace directory with path. + +
:B=base + Replace the base part of file name with base. + +
:S=suf + Replace the suffix of file name with suf. + +
:M=mem + Replace the archive member name with mem. + +
:R=root + Prepend root to the whole file name, if not + already rooted. + +
:E=value + Assign value to the variable if it is unset. + +
:J=joinval + Concatentate list elements into single + element, separated by joinval. + +
+ +

+ + On VMS, $(var:P) is the parent directory of $(var:D); on Unix + and NT, $(var:P) and $(var:D) are the same. + +

Built-in Variables

+ +

+ + This section discusses variables that have special meaning to + jam. + + +

SEARCH and LOCATE Variables

+ + +

+ + These two variables control the binding of file target names to + locations in the file system. Generally, $(SEARCH) is used to + find existing sources while $(LOCATE) is used to fix the location + for built targets. + +

+ + Rooted (absolute path) file targets are bound as is. Unrooted + file target names are also normally bound as is, and thus relative + to the current directory, but the settings of $(LOCATE) and + $(SEARCH) alter this: + +

+ +

+ +

+ + Both $(SEARCH) and $(LOCATE) should be set target-specific and + not globally. If they were set globally, jam would use + the same paths for all file binding, which is not likely to + produce sane results. When writing your own rules, especially + ones not built upon those in Jambase, you may need to set + $(SEARCH) or $(LOCATE) directly. Almost all of the rules defined + in Jambase set $(SEARCH) and $(LOCATE) to sensible values for + sources they are looking for and targets they create, respectively. + + +

HDRSCAN and HDRRULE Variables

+ + +

+ + These two variable control header file scanning. $(HDRSCAN) is + an egrep(1) pattern, with ()'s surrounding the file name, + used to find file inclusion statements in source files. Jambase + uses $(HDRPATTERN) as the pattern for $(HDRSCAN). $(HDRRULE) + is the name of a rule to invoke with the results of the scan: + the scanned file is the target, the found files are the sources. + This is the only place where jam invokes a rule through + a variable setting. + +

+ + Both $(HDRSCAN) and $(HDRRULE) must be set for header file + scanning to take place, and they should be set target-specific + and not globally. If they were set globally, all files, including + executables and libraries, would be scanned for header file + include statements. + +

+ + The scanning for header file inclusions is not exact, but it is + at least dynamic, so there is no need to run something like + makedepend(GNU) to create a static dependency file. The + scanning mechanism errs on the side of inclusion (i.e., it is + more likely to return filenames that are not actually used by + the compiler than to miss include files) because it can't tell + if #include lines are inside #ifdefs or other conditional logic. + In Jambase, HdrRule applies the NOCARE rule to each header file + found during scanning so that if the file isn't present yet + doesn't cause the compilation to fail, jam won't care. + +

+ + Also, scanning for regular expressions only works where the + included file name is literally in the source file. It can't + handle languages that allow including files using variable names + (as the Jam language itself does). + +

Platform Identifier Variables

+ +

+ + A number of Jam built-in variables can be used to identify + runtime platform: + +

+ + + +
OSOS identifier string +
OSPLATUnderlying architecture, when applicable +
MACtrue on MAC platform +
NTtrue on NT platform +
OS2true on OS2 platform +
UNIXtrue on Unix platforms +
VMStrue on VMS platform + +
+ +

Jam Version Variables

+ +

+ + + +
JAMDATETime and date at jam start-up. +
JAMUNAMEOuput of uname(1) command (Unix only) +
JAMVERSIONjam version, currently "2.3" + +
+ +

JAMSHELL Variable

+ +

+ + When jam executes a rule's action block, it forks and + execs a shell, passing the action block as an argument to the + shell. The invocation of the shell can be controlled by + $(JAMSHELL). The default on Unix is, for example: + +

+ + JAMSHELL = /bin/sh -c % ; + +

+ + The % is replaced with the text of the action block. + +

+ + Jam does not directly support building in parallel across + multiple hosts, since that is heavily dependent on the local + environment. To build in parallel across multiple hosts, you + need to write your own shell that provides access to the multiple + hosts. You then reset $(JAMSHELL) to reference it. + +

+ + Just as jam expands a % to be the text of the rule's + action block, it expands a ! to be the multi-process slot number. + The slot number varies between 1 and the number of concurrent + jobs permitted by the -j flag given on the command line. Armed + with this, it is possible to write a multiple host shell. For + example: + +

+ +
+ +
#!/bin/sh +
+
# This sample JAMSHELL uses the SunOS on(1) command to execute a +
# command string with an identical environment on another host. +
+
# Set JAMSHELL = jamshell ! % +
# +
# where jamshell is the name of this shell file. +
# +
# This version handles up to -j6; after that they get executed +
# locally. +
+
case $1 in +
1|4) on winken sh -c "$2";; +
2|5) on blinken sh -c "$2";; +
3|6) on nod sh -c "$2";; +
*) eval "$2";; +
esac + +
+ + +

DIAGNOSTICS

+ +

+ + In addition to generic error messages, jam may emit one of + the following: + +

+ +

warning: unknown rule X
+ + A rule was invoked that has not been defined with + an "actions" or "rule" statement. + +

using N temp target(s)
+ + Targets marked as being temporary (but nonetheless + present) have been found. + +

updating N target(s)
+ + Targets are out-of-date and will be updated. + +

can't find N target(s)
+ + Source files can't be found and there are no + actions to create them. + +

can't make N target(s)
+ + Due to sources not being found, other targets cannot be made. + +

warning: X depends on itself
+ + A target depends on itself either directly or + through its sources. + +

don't know how to make X
+ + A target is not present and no actions have been + defined to create it. + +

X skipped for lack of Y
+ + A source failed to build, and thus a target cannot + be built. + +

warning: using independent target X
+ + A target that is not a dependency of any other + target is being referenced with $(<) or $(>). + +

X removed
+ + Jam removed a partially built target after being + interrupted. + +
+ +

BUGS, LIMITATIONS

+ +

+ + The -j flag can cause jam to get confused when single + actions update more than one target at a time. jam may + proceed as if the targets were built even though they are still + under construction. + +

+ + For parallel building to be successful, the dependencies among + files must be properly spelled out, as targets tend to get built + in a quickest-first ordering. Also, beware of un-parallelizable + commands that drop fixed-named files into the current directory, + like yacc(1) does. + +

+ + With the -j flag, errors from failed commands can get staggeringly + mixed up. + +

+ + A poorly set $(JAMSHELL) is likely to result in silent failure. + +

SEE ALSO

+ +

+ +

+ +

+ + Jam documentation and source are available from the Perforce + Public Depot. + +

AUTHOR

+ +

+ Jam's author is Christopher Seiwald (seiwald@perforce.com). + Documentation is provided by + Perforce Software, Inc. + +

+ +


+ +

+ + Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. +
+ Comments to info@perforce.com +
+ Last updated: April 1, 2002 +
+ $Id: //public/jam/src/Jam.html#11 $ + + + + diff --git a/src/tools/jam/Jambase b/src/tools/jam/Jambase new file mode 100644 index 0000000000..1292062841 --- /dev/null +++ b/src/tools/jam/Jambase @@ -0,0 +1,1866 @@ +# +# /+\ +# +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. +# \+/ +# +# This file is part of Jam - see jam.c for Copyright information. +# + +# +# JAMBASE - jam 2.3 ruleset providing make(1)-like functionality +# +# Supports UNIX, NT, and VMS. +# +# 12/27/93 (seiwald) - purturb library sources with SOURCE_GRIST +# 04/18/94 (seiwald) - use '?=' when setting OS specific vars +# 04/21/94 (seiwald) - do RmTemps together +# 05/05/94 (seiwald) - all supported C compilers support -o: relegate +# RELOCATE as an option; set Ranlib to "" to disable it +# 06/01/94 (seiwald) - new 'actions existing' to do existing sources +# 08/25/94 (seiwald) - new ObjectCcFlags rule to append to per-target CCFLAGS +# 08/29/94 (seiwald) - new ObjectHdrs rule to append to per-target HDRS +# 09/19/94 (seiwald) - LinkLibraries and Undefs now append +# - Rule names downshifted. +# 10/06/94 (seiwald) - Dumb yyacc stuff moved into Jamfile. +# 10/14/94 (seiwald) - (Crude) support for .s, .C, .cc, .cpp, and .f files. +# 01/08/95 (seiwald) - Shell now handled with awk, not sed +# 01/09/95 (seiwald) - Install* now take dest directory as target +# 01/10/95 (seiwald) - All entries sorted. +# 01/10/95 (seiwald) - NT support moved in, with LauraW's help. +# 01/10/95 (seiwald) - VMS support moved in. +# 02/06/95 (seiwald) - ObjectC++Flags and SubDirC++Flags added. +# 02/07/95 (seiwald) - Iron out when HDRSEARCH uses "" or SEARCH_SOURCE. +# 02/08/95 (seiwald) - SubDir works on VMS. +# 02/14/95 (seiwald) - MkDir and entourage. +# 04/30/95 (seiwald) - Use install -c flag so that it copies, not moves. +# 07/10/95 (taylor) - Support for Microsoft C++. +# 11/21/96 (peterk) - Support for BeOS +# 07/19/99 (sickel) - Support for Mac OS X Server (and maybe client) +# 02/18/00 (belmonte)- Support for Cygwin. + +# Special targets defined in this file: +# +# all - parent of first, shell, files, lib, exe +# first - first dependent of 'all', for potential initialization +# shell - parent of all Shell targets +# files - parent of all File targets +# lib - parent of all Library targets +# exe - parent of all Main targets +# dirs - parent of all MkDir targets +# clean - removes all Shell, File, Library, and Main targets +# uninstall - removes all Install targets +# + +# Rules defined by this file: +# +# as obj.o : source.s ; .s -> .o +# Bulk dir : files ; populate directory with many files +# Cc obj.o : source.c ; .c -> .o +# C++ obj.o : source.cc ; .cc -> .o +# Clean clean : sources ; remove sources with 'jam clean' +# File dest : source ; copy file +# Fortran obj.o : source.f ; .f -> .o +# GenFile source.c : program args ; make custom file +# HardLink target : source ; make link from source to target +# HdrRule source : headers ; handle #includes +# InstallInto dir : sources ; install any files +# InstallBin dir : sources ; install binaries +# InstallLib dir : sources ; install files +# InstallFile dir : sources ; install files +# InstallMan dir : sources ; install man pages +# InstallShell dir : sources ; install shell scripts +# Lex source.c : source.l ; .l -> .c +# Library lib : source ; archive library from compiled sources +# LibraryFromObjects lib : objects ; archive library from objects +# LinkLibraries images : libraries ; bag libraries onto Mains +# Main image : source ; link executable from compiled sources +# MainFromObjects image : objects ; link executable from objects +# MkDir dir ; make a directory, if not there +# Object object : source ; compile object from source +# ObjectCcFlags source : flags ; add compiler flags for object +# ObjectC++Flags source : flags ; add compiler flags for object +# ObjectHdrs source : dirs ; add include directories for object +# Objects sources ; compile sources +# RmTemps target : sources ; remove temp sources after target made +# Setuid images ; mark executables Setuid +# SoftLink target : source ; make symlink from source to target +# SubDir TOP d1 d2 ... ; start a subdirectory Jamfile +# SubDirCcFlags flags ; add compiler flags until next SubDir +# SubDirC++Flags flags ; add compiler flags until next SubDir +# SubDirHdrs d1 d2 ... ; add include dir until next SubDir +# SubInclude TOP d1 d2 ... ; include a subdirectory Jamfile +# Shell exe : source ; make a shell executable +# Undefines images : symbols ; save undef's for linking +# UserObject object : source ; handle unknown suffixes for Object +# Yacc source.c : source.y ; .y -> .c +# +# Utility rules that have no side effects (not supported): +# +# FAppendSuffix f1 f2 ... : $(SUF) ; return $(<) with suffixes +# FDirName d1 d2 ... ; return path from root to dir +# FGrist d1 d2 ... ; return d1!d2!... +# FGristFiles value ; return $(value:G=$(SOURCE_GRIST)) +# FGristSourceFiles value ; return $(value:G=$(SOURCE_GRIST)) +# FRelPath d1 : d2 ; return rel path from d1 to d2 +# FSubDir d1 d2 ... ; return path to root +# + + +# Brief review of the jam language: +# +# Statements: +# rule RULE - statements to process a rule +# actions RULE - system commands to carry out target update +# +# Modifiers on actions: +# together - multiple instances of same rule on target get executed +# once with their sources ($(>)) concatenated +# updated - refers to updated sources ($(>)) only +# ignore - ignore return status of command +# quietly - don't trace its execution unless verbose +# piecemeal - iterate command each time with a small subset of $(>) +# existing - refers to currently existing sources ($(>)) only +# bind vars - subject to binding before expanding in actions +# +# Special rules: +# Always - always build a target +# Depends - builds the dependency graph +# Echo - blurt out targets on stdout +# Exit - blurt out targets and exit +# Includes - marks sources as headers for target (a codependency) +# NoCare - don't panic if the target can't be built +# NoUpdate - create the target if needed but never update it +# NotFile - ignore the timestamp of the target (it's not a file) +# Temporary - target need not be present if sources haven't changed +# +# Special variables set by jam: +# $(<) - targets of a rule (to the left of the :) +# $(>) - sources of a rule (to the right of the :) +# $(xxx) - true on xxx (UNIX, VMS, NT, OS2, MAC) +# $(OS) - name of OS - varies wildly +# $(JAMVERSION) - version number (2.3) +# +# Special variables used by jam: +# SEARCH - where to find something (used during binding and actions) +# LOCATE - where to plop something not found with SEARCH +# HDRRULE - rule to call to handle include files +# HDRSCAN - egrep regex to extract include files +# +# Special targets: +# all - default if none given on command line +# + +# Initialize variables +# + +# +# OS specific variable settings +# + +if $(NT) +{ + MV ?= move /y ; + CP ?= copy ; + RM ?= del /f/q ; + SLASH ?= \\ ; + SUFLIB ?= .lib ; + SUFOBJ ?= .obj ; + SUFEXE ?= .exe ; + + if $(BCCROOT) + { + Echo "Compiler is Borland C++" ; + + AR ?= tlib /C /P64 ; + CC ?= bcc32 ; + CCFLAGS ?= -v -w- -q -DWIN -tWR -tWM -tWC ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) -P ; + LINK ?= $(CC) ; + LINKFLAGS ?= $(CCFLAGS) ; + STDLIBPATH ?= $(BCCROOT)\\lib ; + STDHDRS ?= $(BCCROOT)\\include ; + NOARSCAN ?= true ; + } + else if $(MSVC) + { + Echo "Compiler is Microsoft Visual C++ 16 bit" ; + + AR ?= lib /nologo ; + CC ?= cl /nologo ; + CCFLAGS ?= /D \"WIN\" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= $(CC) ; + LINKFLAGS ?= $(CCFLAGS) ; + LINKLIBS ?= + #$(MSVC)\\lib\\advapi32.lib + #$(MSVC)\\lib\\libcmt.lib + $(MSVC)\\lib\\mlibce.lib + #$(MSVC)\\lib\\slibce.lib + $(MSVC)\\lib\\oldnames.lib + #$(MSVC)\\lib\\kernel32.lib + ; + LINKLIBS ?= ; + NOARSCAN ?= true ; + OPTIM ?= ; + STDHDRS ?= $(MSVC)\\include ; + UNDEFFLAG ?= "/u _" ; + } + else if $(MSVCNT) + { + Echo "Compiler is Microsoft Visual C++" ; + + AR ?= lib ; + AS ?= masm386 ; + CC ?= cl /nologo ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= link /nologo ; + LINKFLAGS ?= "" ; + LINKLIBS ?= $(MSVCNT)\\lib\\advapi32.lib + $(MSVCNT)\\lib\\libc.lib + $(MSVCNT)\\lib\\oldnames.lib + $(MSVCNT)\\lib\\kernel32.lib ; + OPTIM ?= "" ; + STDHDRS ?= $(MSVCNT)\\include ; + UNDEFFLAG ?= "/u _" ; + } + else + { + Exit On NT, set BCCROOT, MSVCNT, or MSVC to the root of the + Borland or Microsoft directories. ; + } + +} +else if $(MINGW) +{ + Echo "MingW32" ; + + CC ?= gcc ; + C++ ?= g++ ; + CCFLAGS += -DMINGW ; + RANLIB ?= "ranlib" ; + SUFEXE ?= .exe ; +} +else if $(OS2) +{ + WATCOM ?= $(watcom) ; + + if ! $(WATCOM) + { + Exit On OS2, set WATCOM to the root of the Watcom directory. ; + } + + AR ?= wlib ; + BINDIR ?= \\os2\\apps ; + CC ?= wcc386 ; + CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\h ; # zq=quiet + C++ ?= wpp386 ; + C++FLAGS ?= $(CCFLAGS) ; + CP ?= copy ; + DOT ?= . ; + DOTDOT ?= .. ; + LINK ?= wcl386 ; + LINKFLAGS ?= /zq ; # zq=quiet + LINKLIBS ?= ; + MV ?= move ; + NOARSCAN ?= true ; + OPTIM ?= ; + RM ?= del /f ; + SLASH ?= \\ ; + STDHDRS ?= $(WATCOM)\\h ; + SUFEXE ?= .exe ; + SUFLIB ?= .lib ; + SUFOBJ ?= .obj ; + UNDEFFLAG ?= "/u _" ; + +} +else if $(VMS) +{ + C++ ?= cxx ; + C++FLAGS ?= ; + CC ?= cc ; + CCFLAGS ?= ; + CHMOD ?= set file/prot= ; + CP ?= copy/replace ; + CRELIB ?= true ; + DOT ?= [] ; + DOTDOT ?= [-] ; + EXEMODE ?= (w:e) ; + FILEMODE ?= (w:r) ; + HDRS ?= ; + LINK ?= link ; + LINKFLAGS ?= "" ; + LINKLIBS ?= ; + MKDIR ?= create/dir ; + MV ?= rename ; + OPTIM ?= "" ; + RM ?= delete ; + RUNVMS ?= mcr ; + SHELLMODE ?= (w:er) ; + SLASH ?= . ; + STDHDRS ?= decc$library_include ; + SUFEXE ?= .exe ; + SUFLIB ?= .olb ; + SUFOBJ ?= .obj ; + + switch $(OS) + { + case OPENVMS : CCFLAGS ?= /stand=vaxc ; + case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ; + } +} +else if $(MAC) +{ + local OPT ; + + CW ?= "{CW}" ; + + MACHDRS ?= + "$(UMACHDRS):Universal:Interfaces:CIncludes" + "$(CW):MSL:MSL_C:MSL_Common:Include" + "$(CW):MSL:MSL_C:MSL_MacOS:Include" ; + + MACLIBS ?= + "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib" + "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib" ; + + MPWLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib" ; + + MPWNLLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib" ; + + SIOUXHDRS ?= ; + + SIOUXLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib" ; + + C++ ?= mwcppc ; + C++FLAGS ?= -w off -nomapcr ; + CC ?= mwcppc ; + CCFLAGS ?= -w off -nomapcr ; + CP ?= duplicate -y ; + DOT ?= ":" ; + DOTDOT ?= "::" ; + HDRS ?= $(MACHDRS) $(MPWHDRS) ; + LINK ?= mwlinkppc ; + LINKFLAGS ?= -mpwtool -warn ; + LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ; + MKDIR ?= newfolder ; + MV ?= rename -y ; + NOARSCAN ?= true ; + OPTIM ?= ; + RM ?= delete -y ; + SLASH ?= ":" ; + STDHDRS ?= ; + SUFLIB ?= .lib ; + SUFOBJ ?= .o ; +} +else if $(OS) = BEOS && $(METROWERKS) +{ + AR ?= mwld -xml -o ; + BINDIR ?= /boot/apps ; + CC ?= mwcc ; + CCFLAGS ?= -nosyspath ; + C++ ?= $(CC) ; + C++FLAGS ?= -nosyspath ; + FORTRAN ?= "" ; + LIBDIR ?= /boot/develop/libraries ; + LINK ?= mwld ; + LINKFLAGS ?= "" ; + MANDIR ?= /boot/documentation/"Shell Tools"/HTML ; + NOARSCAN ?= true ; + STDHDRS ?= /boot/develop/headers/posix ; +} +else if $(OS) = BEOS +{ + BINDIR ?= /boot/apps ; + CC ?= gcc ; + C++ ?= $(CC) ; + FORTRAN ?= "" ; + LIBDIR ?= /boot/develop/libraries ; + LINK ?= gcc ; + LINKLIBS ?= -lnet ; + NOARSCAN ?= true ; + STDHDRS ?= /boot/develop/headers/posix ; +} +else if $(UNIX) +{ + switch $(OS) + { + case AIX : + LINKLIBS ?= -lbsd ; + + case AMIGA : + CC ?= gcc ; + YACC ?= bison -y ; + + case CYGWIN : + CC ?= gcc ; + CCFLAGS += -D__cygwin__ ; + LEX ?= flex ; + JAMSHELL ?= sh -c ; + RANLIB ?= "" ; + SUFEXE ?= .exe ; + YACC ?= bison -y ; + + case DGUX : + RANLIB ?= "" ; + RELOCATE ?= true ; + + case HPUX : + RANLIB ?= "" ; + + case INTERIX : + CC ?= gcc ; + JAMSHELL ?= sh -c ; + RANLIB ?= "" ; + + case IRIX : + RANLIB ?= "" ; + + case MPEIX : + CC ?= gcc ; + C++ ?= gcc ; + CCFLAGS += -D_POSIX_SOURCE ; + HDRS += /usr/include ; + RANLIB ?= "" ; + NOARSCAN ?= true ; + NOARUPDATE ?= true ; + + case MVS : + RANLIB ?= "" ; + + case NEXT : + AR ?= libtool -o ; + RANLIB ?= "" ; + + case MACOSX : + AR ?= libtool -o ; + C++ ?= c++ ; + MANDIR ?= /usr/local/share/man ; + RANLIB ?= "" ; + + case NCR : + RANLIB ?= "" ; + + case PTX : + RANLIB ?= "" ; + + case QNX : + AR ?= wlib ; + CC ?= cc ; + CCFLAGS ?= -Q ; # quiet + C++ ?= $(CC) ; + C++FLAGS ?= -Q ; # quiet + LINK ?= $(CC) ; + LINKFLAGS ?= -Q ; # quiet + NOARSCAN ?= true ; + RANLIB ?= "" ; + + case SCO : + RANLIB ?= "" ; + RELOCATE ?= true ; + + case SINIX : + RANLIB ?= "" ; + + case SOLARIS : + RANLIB ?= "" ; + AR ?= "/usr/ccs/bin/ar ru" ; + + case UNICOS : + NOARSCAN ?= true ; + OPTIM ?= -O0 ; + + case UNIXWARE : + RANLIB ?= "" ; + RELOCATE ?= true ; + } + + # UNIX defaults + + CCFLAGS ?= ; + C++FLAGS ?= $(CCFLAGS) ; + CHMOD ?= chmod ; + CHGRP ?= chgrp ; + CHOWN ?= chown ; + LEX ?= lex ; + LINKFLAGS ?= $(CCFLAGS) ; + LINKLIBS ?= ; + OPTIM ?= -O ; + RANLIB ?= ranlib ; + YACC ?= yacc ; + YACCGEN ?= .c ; + YACCFILES ?= y.tab ; + YACCFLAGS ?= -d ; +} + +# +# General defaults; a lot like UNIX +# + + AR ?= ar ru ; + AS ?= as ; + ASFLAGS ?= ; + AWK ?= awk ; + BINDIR ?= /usr/local/bin ; + C++ ?= cc ; + C++FLAGS ?= ; + CC ?= cc ; + CCFLAGS ?= ; + CP ?= cp -f ; + CRELIB ?= ; + DOT ?= . ; + DOTDOT ?= .. ; + EXEMODE ?= 711 ; + FILEMODE ?= 644 ; + FORTRAN ?= f77 ; + FORTRANFLAGS ?= ; + HDRS ?= ; + INSTALLGRIST ?= installed ; + JAMFILE ?= Jamfile ; + JAMRULES ?= Jamrules ; + LEX ?= ; + LIBDIR ?= /usr/local/lib ; + LINK ?= $(CC) ; + LINKFLAGS ?= ; + LINKLIBS ?= ; + LN ?= ln ; + MANDIR ?= /usr/local/man ; + MKDIR ?= mkdir ; + MV ?= mv -f ; + OPTIM ?= ; + RCP ?= rcp ; + RM ?= rm -f ; + RSH ?= rsh ; + SED ?= sed ; + SHELLHEADER ?= "#!/bin/sh" ; + SHELLMODE ?= 755 ; + SLASH ?= / ; + STDHDRS ?= /usr/include ; + SUFEXE ?= "" ; + SUFLIB ?= .a ; + SUFOBJ ?= .o ; + UNDEFFLAG ?= "-u _" ; + YACC ?= ; + YACCGEN ?= ; + YACCFILES ?= ; + YACCFLAGS ?= ; + + HDRPATTERN = + "^[ ]*#[ ]*include[ ]*[<\"]([^\">]*)[\">].*$" ; + + OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ; + + +# +# Base dependencies - first for "bootstrap" kinds of rules +# + +Depends all : shell files lib exe obj ; +Depends all shell files lib exe obj : first ; +NotFile all first shell files lib exe obj dirs clean uninstall ; +Always clean uninstall ; + +# +# Rules +# + +rule As +{ + Depends $(<) : $(>) ; + ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ; + ASHDRS on $(<) = [ FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ; +} + +rule Bulk +{ + local i ; + + for i in $(>) + { + File $(i:D=$(<)) : $(i) ; + } +} + +rule Cc +{ + Depends $(<) : $(>) ; + + # If the compiler's -o flag doesn't work, relocate the .o + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + # Just to clarify here: this sets the per-target CCFLAGS to + # be the current value of (global) CCFLAGS and SUBDIRCCFLAGS. + # CCHDRS and CCDEFS must be reformatted each time for some + # compiles (VMS, NT) that malign multiple -D or -I flags. + + CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) $(OPTIM) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +rule C++ +{ + Depends $(<) : $(>) ; + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) $(OPTIM) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +rule Chmod +{ + if $(CHMOD) { Chmod1 $(<) ; } +} + +rule File +{ + Depends files : $(<) ; + Depends $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MODE on $(<) = $(FILEMODE) ; + Chmod $(<) ; +} + +rule Fortran +{ + Depends $(<) : $(>) ; +} + +rule GenFile +{ + local _t = [ FGristSourceFiles $(<) ] ; + local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ; + Depends $(_t) : $(_s) $(>[2-]) ; + GenFile1 $(_t) : $(_s) $(>[2-]) ; + Clean clean : $(_t) ; +} + +rule GenFile1 +{ + MakeLocate $(<) : $(LOCATE_SOURCE) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; +} + +rule HardLink +{ + Depends files : $(<) ; + Depends $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; +} + +rule HdrRule +{ + # HdrRule source : headers ; + + # N.B. This rule is called during binding, potentially after + # the fate of many targets has been determined, and must be + # used with caution: don't add dependencies to unrelated + # targets, and don't set variables on $(<). + + # Tell Jam that anything depending on $(<) also depends on $(>), + # set SEARCH so Jam can find the headers, but then say we don't + # care if we can't actually find the headers (they may have been + # within ifdefs), + + local s = $(>:G=$(HDRGRIST:E)) ; + + Includes $(<) : $(s) ; + SEARCH on $(s) = $(HDRSEARCH) ; + NoCare $(s) ; + + # Propagate on $(<) to $(>) + + HDRSEARCH on $(s) = $(HDRSEARCH) ; + HDRSCAN on $(s) = $(HDRSCAN) ; + HDRRULE on $(s) = $(HDRRULE) ; + HDRGRIST on $(s) = $(HDRGRIST) ; +} + +rule InstallInto +{ + # InstallInto dir : sources ; + + local i t ; + + t = $(>:G=$(INSTALLGRIST)) ; + + # Arrange for jam install + # Arrange for jam uninstall + # sources are in SEARCH_SOURCE + # targets are in dir + + Depends install : $(t) ; + Clean uninstall : $(t) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MakeLocate $(t) : $(<) ; + + # For each source, make gristed target name + # and Install, Chmod, Chown, and Chgrp + + for i in $(>) + { + local tt = $(i:G=$(INSTALLGRIST)) ; + + Depends $(tt) : $(i) ; + Install $(tt) : $(i) ; + Chmod $(tt) ; + + if $(OWNER) && $(CHOWN) + { + Chown $(tt) ; + OWNER on $(tt) = $(OWNER) ; + } + + if $(GROUP) && $(CHGRP) + { + Chgrp $(tt) ; + GROUP on $(tt) = $(GROUP) ; + } + } +} + +rule InstallBin +{ + local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ; + + InstallInto $(<) : $(_t) ; + MODE on $(_t:G=$(INSTALLGRIST)) = $(EXEMODE) ; +} + +rule InstallFile +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ; +} + +rule InstallLib +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ; +} + +rule InstallMan +{ + # Really this just strips the . from the suffix + + local i s d ; + + for i in $(>) + { + switch $(i:S) + { + case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ; + case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ; + case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ; + case .n : s = n ; case .man : s = 1 ; + } + + d = man$(s) ; + + InstallInto $(d:R=$(<)) : $(i) ; + } + + MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ; +} + +rule InstallShell +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=$(INSTALLGRIST)) = $(SHELLMODE) ; +} + +rule Lex +{ + LexMv $(<) : $(>) ; + Depends $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ; + Objects $(>) ; +} + +rule LibraryFromObjects +{ + local _i _l _s ; + + # Add grist to file names + + _s = [ FGristFiles $(>) ] ; + _l = $(<:S=$(SUFLIB)) ; + + # library depends on its member objects + + if $(KEEPOBJS) + { + Depends obj : $(_s) ; + } + else + { + Depends lib : $(_l) ; + } + + # Set LOCATE for the library and its contents. The bound + # value shows up as $(NEEDLIBS) on the Link actions. + # For compatibility, we only do this if the library doesn't + # already have a path. + + if ! $(_l:D) + { + MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ; + } + + if $(NOARSCAN) + { + # If we can't scan the library to timestamp its contents, + # we have to just make the library depend directly on the + # on-disk object files. + + Depends $(_l) : $(_s) ; + } + else + { + # If we can scan the library, we make the library depend + # on its members and each member depend on the on-disk + # object file. + + Depends $(_l) : $(_l)($(_s:BS)) ; + + for _i in $(_s) + { + Depends $(_l)($(_i:BS)) : $(_i) ; + } + } + + Clean clean : $(_l) ; + + if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; } + + Archive $(_l) : $(_s) ; + + if $(RANLIB) { Ranlib $(_l) ; } + + # If we can't scan the library, we have to leave the .o's around. + + if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; } +} + +rule Link +{ + MODE on $(<) = $(EXEMODE) ; + Chmod $(<) ; +} + +rule LinkLibraries +{ + # make library dependencies of target + # set NEEDLIBS variable used by 'actions Main' + + local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ; + + Depends $(_t) : $(>:S=$(SUFLIB)) ; + NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ; + Objects $(>) ; +} + +rule MainFromObjects +{ + local _s _t ; + + # Add grist to file names + # Add suffix to exe + + _s = [ FGristFiles $(>) ] ; + _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ; + + # so 'jam foo' works when it's really foo.exe + + if $(_t) != $(<) + { + Depends $(<) : $(_t) ; + NotFile $(<) ; + } + + # make compiled sources a dependency of target + + Depends exe : $(_t) ; + Depends $(_t) : $(_s) ; + MakeLocate $(_t) : $(LOCATE_TARGET) ; + + Clean clean : $(_t) ; + + Link $(_t) : $(_s) ; +} + +rule MakeLocate +{ + if $(>) + { + LOCATE on $(<) = $(>) ; + Depends $(<) : $(>[1]) ; + MkDir $(>[1]) ; + } +} + +rule MkDir +{ + # If dir exists, don't update it + # Do this even for $(DOT). + + NoUpdate $(<) ; + + if $(<) != $(DOT) && ! $($(<)-mkdir) + { + local s ; + + # Cheesy gate to prevent multiple invocations on same dir + # MkDir1 has the actions + # Arrange for jam dirs + + $(<)-mkdir = true ; + MkDir1 $(<) ; + Depends dirs : $(<) ; + + # Recursively make parent directories. + # $(<:P) = $(<)'s parent, & we recurse until root + + s = $(<:P) ; + + if $(NT) + { + switch $(s) + { + case *: : s = ; + case *:\\ : s = ; + } + } + + if $(s) && $(s) != $(<) + { + Depends $(<) : $(s) ; + MkDir $(s) ; + } + else if $(s) + { + NotFile $(s) ; + } + + } +} + +rule Object +{ + # locate object and search for source, if wanted + + Clean clean : $(<) ; + + MakeLocate $(<) : $(LOCATE_TARGET) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + + # Save HDRS for -I$(HDRS) on compile. + # We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers + # in the .c file's directory, but generated .c files (from + # yacc, lex, etc) are located in $(LOCATE_TARGET), possibly + # different from $(SEARCH_SOURCE). + + HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ; + + # handle #includes for source: Jam scans for headers with + # the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE) + # with the scanned file as the target and the found headers + # as the sources. HDRSEARCH is the value of SEARCH used for + # the found header files. Finally, if jam must deal with + # header files of the same name in different directories, + # they can be distinguished with HDRGRIST. + + # $(SEARCH_SOURCE:E) is where cc first looks for #include + # "foo.h" files. If the source file is in a distant directory, + # look there. Else, look in "" (the current directory). + + HDRRULE on $(>) = HdrRule ; + HDRSCAN on $(>) = $(HDRPATTERN) ; + HDRSEARCH on $(>) = + $(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(STDHDRS) ; + + HDRGRIST on $(>) = $(HDRGRIST) ; + + # propagate target specific-defines + + DEFINES on $(<) += $(DEFINES) ; + + # if source is not .c, generate .c with specific rule + + switch $(>:S) + { + case .asm : As $(<) : $(>) ; + case .c : Cc $(<) : $(>) ; + case .C : C++ $(<) : $(>) ; + case .cc : C++ $(<) : $(>) ; + case .cpp : C++ $(<) : $(>) ; + case .f : Fortran $(<) : $(>) ; + case .l : Cc $(<) : $(<:S=.c) ; + Lex $(<:S=.c) : $(>) ; + case .s : As $(<) : $(>) ; + case .y : Cc $(<) : $(<:S=$(YACCGEN)) ; + Yacc $(<:S=$(YACCGEN)) : $(>) ; + case * : UserObject $(<) : $(>) ; + } +} + +rule ObjectCcFlags +{ + CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ; +} + +rule ObjectC++Flags +{ + C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ; +} + +rule ObjectDefines +{ + # must reformat CCDEFS according to current defines + + local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ; + + DEFINES on $(s) += $(>) ; + CCDEFS on $(s) = [ on $(s) FDefines $(DEFINES) ] ; +} + +rule ObjectHdrs +{ + # Add to HDRS for HdrScan's benefit. + # must reformat CCHDRS according to headers + + local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ; + + HDRS on $(s) += $(>) ; + CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] ; +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + Object $(_i:S=$(SUFOBJ)) : $(_i) ; + Depends obj : $(_i:S=$(SUFOBJ)) ; + } +} + +rule RmTemps +{ + Temporary $(>) ; +} + +rule Setuid +{ + MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ; +} + +rule Shell +{ + Depends shell : $(<) ; + Depends $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MODE on $(<) = $(SHELLMODE) ; + Clean clean : $(<) ; + Chmod $(<) ; +} + +rule SoftLink +{ + Depends files : $(<) ; + Depends $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + Clean clean : $(<) ; +} + +rule SubDir +{ + # + # SubDir TOP d1 [ ... ] + # + # This introduces a Jamfile that is part of a project tree + # rooted at $(TOP). It (only once) includes the project-specific + # rules file $(TOP)/Jamrules and then sets search & locate stuff. + # + # If the variable $(TOPRULES) is set (where TOP is the first arg + # to SubDir), that file is included instead of $(TOP)/Jamrules. + # + # d1 ... are the directory elements that lead to this directory + # from $(TOP). We construct the system dependent path from these + # directory elements in order to set search&locate stuff. + # + + local _top = $(<[1]) ; + + if ! $($(_top)) + { + if ! $(_top) + { + Exit SubDir syntax error ; + } + + $(_top) = [ FSubDir $(<[2-]) ] ; + } + + # + # If $(TOP)/Jamrules hasn't been included, do so. + # + + if ! $($(_top)-included) + { + # Gated entry. + + $(_top)-included = TRUE ; + + # File is $(TOPRULES) or $(TOP)/Jamrules. + + include $($(_top)RULES:E=$(JAMRULES:R=$($(_top)))) ; + } + + # Get path to current directory from root using SubDir. + # Save dir tokens for other potential uses. + + local _s = [ FDirName $(<[2-]) ] ; + SUBDIR = $(_s:R=$($(_top))) ; + SUBDIR_TOKENS = $(<[2-]) ; + + # Now set up SEARCH_SOURCE, LOCATE_TARGET, SOURCE_GRIST + # These can be reset if needed. For example, if the source + # directory should not hold object files, LOCATE_TARGET can + # subsequently be redefined. + + SEARCH_SOURCE = $(SUBDIR) ; + LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ; + LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ; + SOURCE_GRIST = [ FGrist $(<[2-]) ] ; + + # Reset per-directory ccflags, hdrs + + SUBDIRCCFLAGS = ; + SUBDIRC++FLAGS = ; + SUBDIRHDRS = ; +} + +rule SubDirCcFlags +{ + SUBDIRCCFLAGS += $(<) ; +} + +rule SubDirC++Flags +{ + SUBDIRC++FLAGS += $(<) ; +} + +rule SubDirHdrs +{ + SUBDIRHDRS += [ FDirName $(<) ] ; +} + +rule SubInclude +{ + local _s ; + + # That's + # SubInclude TOP d1 [ d2 [ d3 [ d4 ] ] ] + # + # to include a subdirectory's Jamfile. + + if ! $($(<[1])) + { + Exit Top level of source tree has not been set with $(<[1]) ; + } + + _s = [ FDirName $(<[2-]) ] ; + + include $(JAMFILE:D=$(_s):R=$($(<[1]))) ; +} + +rule Undefines +{ + UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ; +} + +rule UserObject +{ + Exit "Unknown suffix on" $(>) "- see UserObject rule in Jamfile(5)." ; +} + +rule Yacc +{ + local _h ; + + _h = $(<:BS=.h) ; + + # Some places don't have a yacc. + + MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ; + + if $(YACC) + { + Depends $(<) $(_h) : $(>) ; + Yacc1 $(<) $(_h) : $(>) ; + YaccMv $(<) $(_h) : $(>) ; + Clean clean : $(<) $(_h) ; + } + + # make sure someone includes $(_h) else it will be + # a deadly independent target + + Includes $(<) : $(_h) ; +} + +# +# Utility rules; no side effects on these +# + +rule FGrist +{ + return $(<:J=!) ; + +} + +rule FGristFiles +{ + return $(<:G=$(SOURCE_GRIST:E)) ; +} + +rule FGristSourceFiles +{ + # Produce source file name name with grist in it, + # if SOURCE_GRIST is set. + + # Leave header files alone, because they have a global + # visibility. + + if ! $(SOURCE_GRIST) + { + return $(<) ; + } + else + { + local _i _o ; + + for _i in $(<) + { + switch $(_i) + { + case *.h : _o += $(_i) ; + case * : _o += $(_i:G=$(SOURCE_GRIST)) ; + } + } + + return $(_o) ; + } +} + +rule FSubDir +{ + # If $(>) is the path to the current directory, compute the + # path (using ../../ etc) back to that root directory. + # Sets result in $(<) + + if ! $(<[1]) + { + return $(DOT) ; + } + else + { + local _i _d ; + + _d = $(DOTDOT) ; + + for _i in $(<[2-]) + { + _d = $(_d:R=$(DOTDOT)) ; + } + + return $(_d) ; + } +} + +rule _makeCommon +{ + # strip common initial elements + + if $($(<)[1]) && $($(<)[1]) = $($(>)[1]) + { + $(<) = $($(<)[2-]) ; + $(>) = $($(>)[2-]) ; + _makeCommon $(<) : $(>) ; + } +} + +rule FRelPath +{ + local _l _r ; + + # first strip off common parts + + _l = $(<) ; + _r = $(>) ; + + _makeCommon _l : _r ; + + # now make path to root and path down + + _l = [ FSubDir $(_l) ] ; + _r = [ FDirName $(_r) ] ; + + # Concatenate and save + + # XXX This should be better + + if $(_r) = $(DOT) { + return $(_l) ; + } else { + return $(_r:R=$(_l)) ; + } +} + +rule FAppendSuffix +{ + # E.g., "FAppendSuffix yacc lex foo.bat : $(SUFEXE) ;" + # returns (yacc,lex,foo.bat) on Unix and + # (yacc.exe,lex.exe,foo.bat) on NT. + + if $(>) + { + local _i _o ; + + for _i in $(<) + { + if $(_i:S) + { + _o += $(_i) ; + } + else + { + _o += $(_i:S=$(>)) ; + } + } + return $(_o) ; + } + else + { + return $(<) ; + } +} + +# +# Operating system specific utility rules +# First, the (generic) UNIX versions +# + +rule FQuote { return \\\"$(<)\\\" ; } +rule FDefines { return -D$(<) ; } +rule FIncludes { return -I$(<) ; } + +rule FDirName +{ + # Turn individual elements in $(<) into a usable path. + + local _i ; + local _s = $(DOT) ; + + for _i in $(<) + { + _s = $(_i:R=$(_s)) ; + } + + return $(_s) ; +} + +if $(OS2) +{ + rule FQuote { return \"$(<)\" ; } + rule FIncludes { return /I$(<) ; } +} + +else if $(NT) +{ + rule FDefines { return /D$(<) ; } + rule FIncludes { return /I$(<) ; } +} + +else if $(MAC) +{ + rule FQuote { return \"$(<)\" ; } + rule FDefines { return "-define '$(<)'" ; } + rule FIncludes { return \"$(<:J=,)\" ; } +} + +else if $(VMS) +{ + rule FQuote { return \"\"\"$(<)\"\"\" ; } + rule FDefines { return "/define=( $(<:J=,) )" ; } + rule FIncludes { return "/inc=( $(<:J=,) )" ; } + + rule FDirName + { + local _s _i ; + + # Turn individual elements in $(<) into a usable path. + + if ! $(<) + { + _s = $(DOT) ; + } + else + { + # This handles the following cases: + # a -> [.a] + # a b c -> [.a.b.c] + # x: -> x: + # x: a -> x:[a] + # x:[a] b -> x:[a.b] + + switch $(<[1]) + { + case *:* : _s = $(<[1]) ; + case \\[*\\] : _s = $(<[1]) ; + case * : _s = [.$(<[1])] ; + } + + for _i in [.$(<[2-])] + { + _s = $(_i:R=$(_s)) ; + } + } + + return $(_s) ; + } +} + +# +# Actions +# + +# +# First the defaults +# + +actions updated together piecemeal Archive +{ + $(AR) $(<) $(>) +} + +actions As +{ + $(AS) $(ASFLAGS) $(ASHDRS) -o $(<) $(>) +} + +actions C++ +{ + $(C++) -c -o $(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) +} + +actions Cc +{ + $(CC) -c -o $(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) +} + +actions Chgrp +{ + $(CHGRP) $(GROUP) $(<) +} + +actions Chmod1 +{ + $(CHMOD) $(MODE) $(<) +} + +actions Chown +{ + $(CHOWN) $(OWNER) $(<) +} + +actions piecemeal together existing Clean +{ + $(RM) $(>) +} + +actions File +{ + $(CP) $(>) $(<) +} + +actions GenFile1 +{ + $(>[1]) $(<) $(>[2-]) +} + +actions Fortran +{ + $(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>) +} + +actions HardLink +{ + $(RM) $(<) && $(LN) $(>) $(<) +} + +actions Install +{ + $(CP) $(>) $(<) +} + +actions Lex +{ + $(LEX) $(>) +} + +actions LexMv +{ + $(MV) lex.yy.c $(<) +} + +actions Link bind NEEDLIBS +{ + $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) +} + +actions MkDir1 +{ + $(MKDIR) $(<) +} + +actions together Ranlib +{ + $(RANLIB) $(<) +} + +actions quietly updated piecemeal together RmTemps +{ + $(RM) $(>) +} + +actions Shell +{ + $(AWK) ' + NR == 1 { print "$(SHELLHEADER)" } + NR == 1 && /^[#:]/ { next } + /^##/ { next } + { print } + ' < $(>) > $(<) +} + +actions SoftLink +{ + $(RM) $(<) && $(LN) -s $(>) $(<) +} + +actions Yacc1 +{ + $(YACC) $(YACCFLAGS) $(>) +} + +actions YaccMv +{ + $(MV) $(YACCFILES).c $(<[1]) + $(MV) $(YACCFILES).h $(<[2]) +} + +# +# RELOCATE - for compilers with broken -o flags +# + +if $(RELOCATE) +{ + actions C++ + { + $(C++) -c $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions Cc + { + $(CC) -c $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions ignore CcMv + { + [ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<) + } +} + +# +# NOARUPDATE - can't update an archive +# + +if $(NOARUPDATE) +{ + actions Archive + { + $(AR) $(<) $(>) + } +} + +# +# NT specific actions +# + +if $(NT) && $(MSVCNT) +{ + actions updated together piecemeal Archive + { + if exist $(<) set _$(<:B)_=$(<) + $(AR) /out:$(<) %_$(<:B)_% $(>) + } + + actions As + { + $(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul; + } + + actions Cc + { + $(CC) /c /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) $(>) + } + + actions C++ + { + $(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) /Tp$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } +} +else if $(NT) && $(MSVC) +{ + actions updated together piecemeal Archive + { + $(AR) $(<) -+$(>) + } + + actions Cc + { + $(CC) /c /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions C++ + { + $(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /Tp$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } +} +else if $(NT) && $(BCCROOT) +{ + actions updated together piecemeal Archive + { + $(AR) $(<) -+$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>) + } + + actions C++ + { + $(C++) -c -o$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions Cc + { + $(CC) -c -o$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) + } +} + +# +# OS2 specific actions +# + +else if $(OS2) && $(WATCOM) +{ + actions together piecemeal Archive + { + $(AR) $(<) +-$(>) + } + + actions Cc + { + $(CC) /Fo=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions C++ + { + $(C++) /Fo=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + + actions Shell + { + $(CP) $(>) $(<) + } +} + +# +# VMS specific actions +# + +else if $(VMS) +{ + actions updated together piecemeal Archive + { + lib/replace $(<) $(>[1]) ,$(>[2-]) + } + + actions Cc + { + $(CC)/obj=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions C++ + { + $(C++)/obj=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) + } + + actions piecemeal together existing Clean + { + $(RM) $(>[1]);* ,$(>[2-]);* + } + + actions together quietly CreLib + { + if f$search("$(<)") .eqs. "" then lib/create $(<) + } + + actions GenFile1 + { + mcr $(>[1]) $(<) $(>[2-]) + } + + actions Link bind NEEDLIBS + { + $(LINK)/exe=$(<) $(LINKFLAGS) $(>:J=,) ,$(NEEDLIBS)/lib ,$(LINKLIBS) + } + + actions quietly updated piecemeal together RmTemps + { + $(RM) $(>[1]);* ,$(>[2-]);* + } + + actions Shell + { + $(CP) $(>) $(<) + } +} + +# +# Mac specifc actions +# + +else if $(MAC) +{ + actions together Archive + { + $(LINK) -library -o $(<) $(>) + } + + actions Cc + { + set -e MWCincludes $(CCHDRS) + $(CC) -o $(<) $(CCFLAGS) $(CCDEFS) $(>) + } + + actions C++ + { + set -e MWCincludes $(CCHDRS) + $(CC) -o $(<) $(C++FLAGS) $(CCDEFS) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) "$(LINKLIBS)" + } +} + +if $(WIN98) +{ + actions existing Clean + { + del $(>) + } +} + +# +# Backwards compatibility with jam 1, where rules were uppercased. +# + +rule BULK { Bulk $(<) : $(>) ; } +rule FILE { File $(<) : $(>) ; } +rule HDRRULE { HdrRule $(<) : $(>) ; } +rule INSTALL { Install $(<) : $(>) ; } +rule LIBRARY { Library $(<) : $(>) ; } +rule LIBS { LinkLibraries $(<) : $(>) ; } +rule LINK { Link $(<) : $(>) ; } +rule MAIN { Main $(<) : $(>) ; } +rule SETUID { Setuid $(<) ; } +rule SHELL { Shell $(<) : $(>) ; } +rule UNDEFINES { Undefines $(<) : $(>) ; } + +# Old INSTALL* didn't take dest directory. + +rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; } +rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; } +rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; } + +# Compatibility with jam 2.2. + +rule addDirName { $(<) += [ FDirName $(>) ] ; } +rule makeCommon { _makeCommon $(<) : $(>) ; } +rule makeDirName { $(<) = [ FDirName $(>) ] ; } +rule makeGrist { $(<) = [ FGrist $(>) ] ; } +rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; } +rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; } +rule makeString { $(<) = $(>:J) ; } +rule makeSubDir { $(<) = [ FSubDir $(>) ] ; } +rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; } + +# +# Now include the user's Jamfile. +# + +include $(JAMFILE) ; diff --git a/src/tools/jam/Jambase.html b/src/tools/jam/Jambase.html new file mode 100644 index 0000000000..d1cb261365 --- /dev/null +++ b/src/tools/jam/Jambase.html @@ -0,0 +1,931 @@ + + +Jambase Reference + + +

+ +Jam/MR + +

+ +Jambase Reference + +

+
+

+ Jambase is a base set of Jam/MR rules which + provide roughly make(1)-like functionality for + jam, the Jam/MR executable program. + This document, which started out as the Jambase(5) man page, + is a reference guide to the + rules, + pseudotargets, + and variables + defined in Jambase for use in Jamfiles. +

+ For further information see: +

+

+Jam/MR documentation and source are available from the +Perforce Public Depot. +For detailed information about any of the rules summarized below, +see the +Jambase +file itself. +


+

+ +Jambase Rules + +

+

+ As obj.o : source.s ; +

+ Assemble the file source.s. Called by the Object + rule. +
+ Bulk directory : sources ; +
+ Copies sources into directory. +
+ Cc object : source ; +
+ Compile the file source into object, using the C + compiler $(CC), its flags $(CCFLAGS) and $(OPTIM), + and the header file directories $(HDRS). Called by + the Object rule. +
+ C++ obj.o : source.cc ; +
+ Compile the C++ source file source.cc. Called by + the Object rule. +
+ Chmod target ; +
+ (Unix and VMS only.) + Change file permissions on target to + target-specific $(MODE) value set by Link, File, + Install*, and Shell rules. +
+ + Clean clean : targets ; +
+ Removes existing targets when clean is built. + clean is not a dependency of all, and must be built + explicitly for targets to be removed. +
+ FDefines defines ;
+ Expands a list of definitions into a list of compiler + (or preprocessor) switches (such as + -Dsymbol=val on Unix) + to pass the definitions. +
+ File target : source ; +
+ Copies source into target. +
+ FIncludes dirs ;
+ Expands a list of directories into a list of compiler + (or preprocessor) switches (such as -Idir on Unix) + to add the directories to the header inclusion search path. +
+ Fortran obj.o : source.f ; +
+ Compile the Fortran source file source.f. Called + by the Object rule. +
+ FQuote files ;
+ Returns each of files suitably quoted so as to hide shell + metacharacters (such as whitespace and filename matching wildcards) + from the shell. +
+

+ GenFile target : image sources ; +

+ Runs the command "image target sources" + to create target from sources and + image. (where image is an + executable built by the Main rule.) +
+ HardLink target : source ; +
+ Makes target a hard link to source, if it isn't one + already. (Unix only.) +
+ HdrRule source : headers ; +
+ Arranges the proper dependencies when the file + source includes the files headers through the + "#include" C preprocessor directive. +

+ This rule is not intended to be called explicitly. + It is called automatically during header scanning on + sources handled by the Object rule (e.g., sources in + Main or Library rules). +

+ InstallBin dir : sources ;
+ Copy sources into dir with mode + $(EXEMODE). +
+ InstallLib dir : sources ;
+ Copy sources into dir with mode + $(FILEMODE). +
+ InstallMan dir : sources ;
+ Copy sources into the appropriate subdirectory + of dir with mode $(FILEMODE). The subdirectory + is mans, where s is the suffix of + each of sources. +
+ InstallShell dir : sources ;
+ Copy sources into dir with mode + $(SHELLMODE). +
+ Lex source.c : source.l ;
+ Process the lex(1) source file source.l and + rename the lex.yy.c to source.c. Called by + the Object rule. +
+ Library library : sources ;
+ Compiles sources and archives them into + library. The intermediate objects + are deleted. Calls Objects and LibraryFromObjects. +

+ If Library is invoked with no suffix on library, + the $(SUFLIB) suffix is used. +

+ LibraryFromObjects library : objects ; +
+ Archives objects into library. The + objects are then deleted. +

+ If library has no suffix, the $(SUFLIB) suffix is used. +

+ Link image : objects ; +
+ Links image from objects and sets + permissions on image to $(EXEMODE). + Image must be actual filename; suffix is not + supplied. + Called by Main. + +
+ LinkLibraries image : libraries ; +
+ Makes image depend on libraries and + includes them during the linking. +

+ Image may be referenced without a suffix in this + rule invocation; LinkLibraries supplies the suffix. +

+ Main image : sources ; +
+ Compiles sources and links them into image. + Calls Objects and MainFromObjects. +

+ Image may be referenced without a suffix in this + rule invocation; Main supplies the suffix. +

+ MainFromObjects image : objects ; +
+ Links objects into image. Dependency + of exe. MainFromObjects supplies the suffix on image + filename. +
+ MakeLocate target : dir ; +
+ Creates dir and causes target to be built + into dir. +
+ MkDir dir ; +
+ Creates dir and its parent directories. +
+ Object object : source ; +
+ Compiles a single source file source into + object. The Main and Library rules use + this rule to compile source files. +

+ Causes source to be scanned for "#include" + directives and calls HdrRule to make all included + files dependedencies of object. +

+ Calls one of the following rules to do the actual + compiling, depending on the suffix of source: +

+		     *.c:   Cc 
+		     *.cc:  C++ 
+		     *.cpp: C++
+		     *.C:   C++ 
+		     *.l:   Lex 
+		     *.y:   Yacc
+		     *.*:   UserObject
+
+
+ ObjectC++Flags source : flags ; +
+ ObjectCcFlags source : flags ; +
+ Add flags to the source-specific + value of $(CCFLAGS) or $(C++FLAGS) when compiling source. + Any file suffix on source is ignored. +
+ ObjectDefines object : defines ;
+ Adds preprocessor symbol definitions to the (gristed) + target-specific $(CCDEFS) for the object. +
+ ObjectHdrs source : dirs ;
+ Add dirs to the source-specific value of + $(HDRS) when scanning and compiling source. + Any file suffix on source is ignored. +
+ Objects sources ;
+ For each source file in sources, calls + Object to compile the source file into a similarly + named object file. +
+ RmTemps targets : sources ;
+ Marks sources as temporary with the TEMPORARY + rule, and deletes sources once targets + are built. Must be the last rule invoked on + targets. Used internally by LibraryFromObjects rule. +
+ Setuid images ;
+ Sets the setuid bit on each of images after + linking. (Unix only.) + +
+ SoftLink target : source ; +
+ Makes target a symbolic link to source, if it isn't one + already. (Unix only.) +
+ SubDir VAR d1 ... dn ; +
+ Sets up housekeeping for the source files located + in $(VAR)/d1/.../dn: + +

+ VAR is the name of a variable; + d1 thru dn are elements + of a directory path. +

+ SubDirC++Flags flags ; +
+ SubDirCcFlags flags ; +
+ Adds flags to the compiler flags for source files + in SubDir's directory. +
+ SubDirHdrs d1 ... dn ; +
+ Adds the path d1/.../dn/ to the header search paths for + source files in SubDir's directory. d1 through dn + are elements of a directory path. +
+ SubInclude VAR d1 ... dn ; +
+ Reads the Jamfile in $(VAR)/d1/.../dn/. +
+ Shell image : source ;
+ Copies source into the executable sh(1) + script image. Ensures that the first line of + the script is $(SHELLHEADER) (default #!/bin/sh). +
+ Undefines images : symbols ;
+ Adds flags to mark symbols as undefined + on link command for images. + Images may be referenced unsuffixed; the + Undefines rule supplies the suffix. +
+ UserObject object : source ;
+ This rule is called by Object for source + files with unknown suffixes, and should be defined + in Jamrules + with a user-provided rule to handle the source file + types not handled by the Object rule. + The Jambase UserObject rule merely issues a + complaint when it encounters source with + files suffixes it does not recognize. +
+ Yacc source.c : source.y ;
+ Process the yacc(1) file source.y and renamed + the resulting y.tab.c and y.tab.h to source.c. + Produces a y.tab.h and renames it to source.h. + Called by the Object rule. +
+

+


+ +

+Jambase Pseudotargets +

+
+

+There are two kinds of Jam targets: file targets and pseudotargets. +File targets are objects that can be found in the filesystem. +Pseudotargets are symbolic, and usually represent other targets. +Most Jambase rules that define file targets also define pseudotargets +which are dependent on types of file targets. The Jambase pseudotargets +are: +

+ +
exe + Executables linked by the Main or MainFromObjects rules + +
lib + Libraries created by the Library or LibraryFromObjects rules + +
obj + Compiled objects used to create Main or Library targets + +
dirs + Directories where target files are written + +
file + Files copied by File and Bulk rules + +
shell + Files copied by Shell rule + +
clean + Removal of built targets (except files copied by Install* rules) + +
install + Files copied by Install* rules + +
uninstall + Removal of targets copied by Install* rules + +
+
+

+In addition, Jambase makes the jam default target "all" +depend on "exe", "lib", "obj", "files", and "shell". +

+ +


+ +

+Jambase Variables +

+
+

+ Most of the following variables have default values for + each platform; refer to the Jambase file to see what those + defaults are. +

+ ALL_LOCATE_TARGET +

+ Alternative location of built targets. By default, + Jambase rules locate built targets in the source + tree. By setting $(ALL_LOCATE_TARGET) + in Jamrules, you can cause jam + to write built targets to a location outside + the source tree. +
+ + AR + +
+ The archive command used to update Library + and LibraryFromObjects targets. +
+ AS +
+ The assembler for As rule targets. +
+ + ASFLAGS + +
+ Flags handed to the assembler for As. +
+ + AWK + +
+ The name of awk interpreter, used when copying a + shell script for the Shell rule. +
+ + BCCROOT +
+ Selects Borland compile and link actions on NT. +
+ + + BINDIR + +
+ Not longer used. + (I.e., used only for backward compatibility with the + obsolete INSTALLBIN rule.) +
+ + CC + +
+ C compiler used for Cc rule targets. +
+ + CCFLAGS + +
+ Compile flags for Cc rule targets. + The Cc rule sets target-specific $(CCFLAGS) + values on its targets. +
+ + C++ + +
+ C++ compiler used for C++ rule targets. +
+ + C++FLAGS + +
+ Compile flags for C++ rule targets. + The C++ rule sets target-specific $(C++FLAGS) + values on its targets. +
+ + CHMOD + +
+ Program (usually chmod(1)) used to set file + permissions for Chmod rule. +
+ + CP + +
+ The file copy program, used by File and Install* rules. +
+ + CRELIB + +
+ If set, causes the Library rule to invoke the CreLib + rule on the target library before attempting to archive + any members, so that the library can be created if + needed. +
+ + CW + +
+ On Macintosh, the root of the Code Warrior Pro 5 directory. +
+ + DEFINES + +
+ Preprocessor symbol definitions for Cc and C++ rule targets. + The Cc and C++ rules set target-specific $(CCDEFS) + values on their targets, based on $(DEFINES). (The + "indirection" here is required to support compilers, + like VMS, with baroque command line syntax for + setting symbols). +
+ + DOT + +
+ The operating system-specific name for the current directory. +
+ + DOTDOT + +
+ The operating system-specific name for the parent directory. +
+ + EXEMODE + +
+ Permissions for executables linked with Link, Main, + and MainFromObjects, on platforms with a Chmod action. +
+ + FILEMODE + +
+ Permissions for files copied by File or Bulk, + on platforms with a Chmod action. +
+ + FORTRAN + +
+ The Fortran compiler used by Fortran rule. +
+ + FORTRANFLAGS + +
+ Fortran compiler flags for Fortran rule targets. +
+ + GROUP + +
+ (Unix only.) + The group owner for Install* rule targets. +
+ + HDRGRIST + +
+ If set, used by the HdrRule to distinguish header files + with the same name in diffrent directories. +
+ + HDRPATTERN + +
+ A regular expression pattern that matches + C preprocessor "#include" directives in source files + and returns the name of the included file. +
+ + HDRRULE + +
+ Name of the rule to invoke with the results of header file + scanning. Default is "HdrRule". +

+ This is a jam-special variable. If both HDRRULE and HDRSCAN + are set on a target, + that target will be scanned for lines + matching $(HDRSCAN), and $(HDDRULE) will be + invoked on included files found in the matching $(HDRSCAN) lines. +

+ + HDRS + +
+ Directories to be searched for header files. + This is used by the Object rule to: + + (See STDHDRS.) +
+ + HDRSCAN + +
+ Regular expression pattern to use for header file + scanning. The Object rule sets this to $(HDRPATTERN). + This is a jam-special variable; see HDRRULE. +
+ + HDRSEARCH + +
+ Used by the HdrRule to fix the list of directories where + header files can be found for a given source file. +
+ + INSTALLGRIST + +
+ Used by the Install* rules to grist paths to installed + files; defaults to "installed". +
+ + JAMFILE + +
+ Default is "Jamfile"; the name of the user-written + rules file found in each source directory. +
+ + JAMRULES + +
+ Default is "Jamrules"; the name of a rule definition + file to be read in at the first SubDir rule invocation. +
+ + KEEPOBJS + +
+ If set, tells the LibraryFromObjects rule not to delete + object files once they are archived. +
+ + LEX + +
+ The lex(1) command and flags. +
+ + LIBDIR + +
+ Not longer used. + (I.e., used only for backward compatibility with the + obsolete INSTALLLIB rule.) +
+ + LINK + +
+ The linker. Defaults to $(CC). +
+ + LINKFLAGS + +
+ Flags handed to the linker. Defaults to $(CCFLAGS). +
+ + LINKLIBS + +
+ List of external libraries to link with. The target image + does not depend on these libraries. +
+ + LN + +
+ The hard link command for HardLink rule. +
+ + LOCATE_SOURCE +
+ Used to set the location of generated source files. + The Yacc, Lex, and GenFile rules set LOCATE on + their targets to $(LOCATE_SOURCE). + $(LOCATE_SOURCE) is initialized by the SubDir rule + to the source directory itself. + (Also, see ALL_LOCATE_TARGET.) +
+ + LOCATE_TARGET +
+ Used to set the location of built binary targets. + The Object rule, and hence the Main and Library rules, + set LOCATE on their targets to $(LOCATE_TARGET). + $(LOCATE_TARGET) is initialized by the + SubDir rule to the source directory itself. + (See ALL_LOCATE_TARGET.) +
+ + + MANDIR + +
+ Not longer used. + (I.e., used only for backward compatibility with the + obsolete INSTALLMAN rule.) +
+ + MKDIR + +
+ The 'create directory' command used for the MkDir + rule. +
+ + MODE + +
+ The target-specific file mode (permissions) for targets + of the Shell, Setuid, Link, and Install* rules. + Used by the Chmod action; hence relevant to NT and VMS + only. +
+ + MSVC +
+ Selects Microsoft Visual C 16-bit compile & link + actions on NT. +
+ + MSVCNT +
+ Selects Microsoft Visual C NT compile & link + actions on NT. +
+ + + MV + +
+ The file rename command and options. +
+ + NEEDLIBS + +
+ The list of libraries used when linking an executable. + Used by the Link rule. +
+ + NOARSCAN + +
+ If set, indicates that library members' timestamps can't + be found, and prevents the individual objects from being + deleted, so that their timestamps can be used instead. +
+ + NOARUPDATE + +
+ If set, indicates that libraries can't be updated, but only + created whole. +
+ + OPTIM + +
+ The C compiler flag for optimization, used by Cc and C++ + rules. +
+ + OSFULL + +
+ The concatenation of $(OS)$(OSVER)$(OSPLAT), used when jam + builds itself to determine the target binary directory. + $(OS) and $(OSPLAT) are determined by jam at its compile + time (in jam.h). $(OSVER) can optionally be set by the user. + +
+ + OWNER + +
+ The owner of installed files. Used by Install* rules. +
+ + RANLIB + +
+ The name of the ranlib command. If set, causes + the Ranlib action to be applied after the + Archive action to targets of the Library rule. +
+ + RELOCATE + +
+ If set, tells the Cc rule to move the output object + file to its target directory because the cc command + has a broken -o option. +
+ + RM + +
+ The command and options to remove a file. +
+ + SEARCH_SOURCE + +
+ The directory to find sources listed with Main, + Library, Object, Bulk, File, Shell, InstallBin, + InstallLib, and InstallMan rules. This works by + setting the jam-special variable SEARCH to the + value of $(SEARCH_SOURCE) for each of the rules' + sources. The SubDir rule initializes SEARCH_SOURCE + for each directory. +
+ + SHELLHEADER + +
+ A string inserted to the first line of every file + created by the Shell rule. +
+ + SHELLMODE + +
+ Permissions for files installed by Shell rule. +
+ + SOURCE_GRIST + +
+ Set by the SubDir to a value derived from the + directory name, and used by Objects and related + rules as 'grist' to perturb file names. +
+ + STDHDRS + +
+ Directories where headers can be found without + resorting to using the flag to the C compiler. + The $(STDHDRS) directories are used to find + headers during scanning, but are not passed to the + compiler commands as -I paths. +
+ + SUBDIR + +
+ The path from the current directory to the directory + last named by the SubDir rule. +
+ + TOP + +
+ The path from the current directory to the directory + that has the Jamrules file. Used by the SubDir rule. +
+ + SUFEXE + +
+ The suffix for executable files, if none provided. + Used by the Main rule. +
+ + SUFLIB + +
+ The suffix for libraries. Used by the Library and + related rules. +
+ + SUFOBJ + +
+ The suffix for object files. Used by the Objects + and related rules. +
+ + UNDEFFLAG + +
+ The flag prefixed to each symbol for the Undefines + rule (i.e., the compiler flag for undefined symbols). +
+ + WATCOM +
+ Selects Watcom compile and link actions on OS2. +
+ + YACC + +
+ The yacc(1) command. +
+ + YACCFILES + +
+ The base filename generated by yacc(1). +
+ + YACCFLAGS + +
+ The yacc(1) command flags. +
+ + YACCGEN + +
+ The suffix used on generated yacc(1) output. +
+ +

+


+Back to top. +

+ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. +
+ Comments to info@perforce.com +
+ Last updated: Dec 31, 2000 +
+ $Id: //public/jam/src/Jambase.html#7 $ + + diff --git a/src/tools/jam/Jamfile.html b/src/tools/jam/Jamfile.html new file mode 100644 index 0000000000..7cd9202084 --- /dev/null +++ b/src/tools/jam/Jamfile.html @@ -0,0 +1,1450 @@ + + +Jamfiles and Jambase + + +

+ +Jam/MR + + +

+Using Jamfiles and Jambase +

+
+
+

+This document describes how to write Jamfiles using the Jam/MR Jambase +rules to build software products. +Related documents of interest are: +

+

+Jam/MR documentation and source are available from the +Perforce Public Depot. +


+

+

+Overview +

+

+ jam, the Jam executable program, + recursively builds target files from source files + using dependency and build specifications defined + in Jam rules files. + jam parses the rules files to identify targets + and sources, + examines the filesystem to determine which + targets need updating, and issues OS commands to update + targets. +

+ A base rules file called "Jambase" is provided with the + Jam distribution. + The Jambase file defines rules and variables which support + standard software build operations, like compiling, linking, + etc. +

+ When the Jambase rules are used, + jam reads Jambase, then reads a file called + "Jamfile" in the current directory. + The Jamfile describes what to do with the source files in + its directory. It may also cause + Jamfiles in other directories to be read. +

+ Under certain circumstances, the first Jamfile read + also causes a site-specific "Jamrules" file to be read. + The Jamrules file is an optional set of rule and variable + definitions used to define site-specific processing. +

+

+The Basic Jamfile +

+

+Jamfiles contain rule invocations, which usually look like: +

+	RuleName targets : targets ;
+
+The target(s) to the left of the colon usually indicate +what gets built, and the target(s) to the right of the +colon usually indicate what it is built from. +

+

+A Jamfile can be as simple as this: +

+	Main myprog : main.c util.c ;
+
+This specifies that there is a main.c and util.c file in the same +directory as the Jamfile, and that those source files should be +compiled and linked into an executable called myprog. +If you cd to the directory where this Jamfile lives, +you can see the exactly how jam would +build myprog with: +
+	jam -n
+
+Or, you can actually build myprog with the command: +
+	jam
+
+ +

+

+Whitespace +

+Jamfile elements are delimited by whitespace (blanks, tabs, or +newlines). Elements to be delimited include rule names, targets, +colons, and semicolons. A common mistake users make is to forget the +whitespace, e.g., +
+	Main myprog: main.c util.c ; #WRONG!
+
+Jam doesn't distinguish between a typo and a target called "myprog:", +so if you get strange results, the first thing +you should check for in your Jamfile is missing whitespace. +

+

+Filenames, Target Identifiers, and Buildable Targets +

+

+Consider this Jamfile: +

+	Main myprog : main.c util.c ;                   
+	LinkLibraries myprog : libtree ;     
+	Library libtree : treemake.c treetrav.c ;    
+
+

+The Main rule specifies that an executable called myprog will be built. +The compiled main.c and util.c objects will be linked to produce +myprog. +The LinkLibraries rule specifies that libtree will +be linked into myprog as well. +The Library rule specifies which source files will be compiled and +archived into the libtree library. +

+The Jamfile above refers to targets like "myprog" and "libtree". +However, depending on the platform you're building on, the actual +filenames of those targets could be "myprog.exe" and "libtree.lib". +Most Jambase rules supply the actual filenames of targets, +so that Jamfiles themselves need not make any +platform-specific filename references. +

+The jam program builds up a list of unique target identifiers. +Unless you are using the SubDir rules (described later), +the default identifier for a file target is its filename. In the above +example, the target identifiers are the filenames: myprog.exe, +libtree.lib, main.obj, etc. +

+While all Jambase rules refer to "targets", +not all targets are buildable. +There are two kinds of buildable targets: +file targets and pseudotargets. +File targets are objects that can be found in the filesystem. +Pseudotargets are symbolic, and represent other targets. +

+You can use any buildable target on the jam command line to +build a subset of defined targets. For example: +

+        jam libtree.a 
+
+on Unix builds the libtree library and all the compiled objects +that go in it. +

+

+Pseudotargets +

+

+Most Jambase rules that define file targets also define pseudotargets +which are dependent on types of file targets. +For example, Jambase defines a pseudotarget called "lib", which +is dependent on file targets created by the Library rule. So +the command: +

+        jam lib
+
+used with the above example would cause the libtree library to be built. +Also, there is one pseudotarget built into jam itself, called +"all". Jambase sets "all" dependent on (almost) all other targets. +

+In the unfortunate case where you have a buildable target whose name +is the same as one of the Jambase pseudotargets, you'll have problems +with the conflicting target name. +Your workaround choices are: +

+

    +
  1. Change the name of your buildable file or directory that conflicts. +

    +

  2. Modify your Jambase and change the name of the conflicting pseudotarget. +(Pseudotargets are defined in Jambase using the NOTFILE rule.) +

    +

  3. Use grist on the conflicting target name in your Jamfile. E.g., instead + of +
    +    File lib : libfoo.a ;
    +    
    + try +
    +    File <dir>lib : libfoo.a ;
    +    
    +
+

+ +

+Dependencies +

+

+Jambase rules set dependencies on targets, so that if you update a +source file, all the file targets that depend on that source +file, and only the ones that depend on that source file, +will be updated (rebuilt) the next time you run jam. +

+Here are some of the dependencies +that get set when jam runs on NT using the example Jamfile above: +

+ + +
Target   Depends on
myprog.exemain.obj, util.obj, libtree.lib +
libtree.libtreemake.obj, treetrav.obj +
treetrav.objtreetrav.c +
+
+

+Furthermore, the Main and Library rules set up recursive +header scanning on their source targets. +So after jam has finished parsing the Jamfile and +setting the rule-driven dependencies, it scans the source +files for "#include" lines. All #include files found during +this scan become dependencies of the compiled object. +E.g., all header files used to compile treetrav.c would +be made dependencies of treetrav.obj. +

+As a result, when you run jam, it will rebuild targets +if either the source files change or the +header files change. You can't tell by looking at a Jamfile +which header files are dependencies, but you can easily +display those dependencies with: +

+	jam -nd+3
+
+

+Rule Ordering +

+

+Rules which specify dependencies, like the Main, Library, and +LinkLibrary rules, can be invoked in any order. jam +figures out the order in which targets are built from +their dependencies. +

+Some rules, however, set variables which are used by subsequent +rule invocations, and their ordering is important. +For example, the SubDir* rules (discussed +later) must be invoked in a particular order. + +

+

+Detailed Jambase Specifications +

+

+This document describes how to use various Jambase rules +from a functional point of view. +You can see the summary of available Jambase rules in the +Jambase Reference. +The detailed specifications for any Jambase rule +can be found by reading the rule definition itself +in the Jambase file. +

+ +


+

+Handling Directory Trees +

+ The SubDir* rules are used to + define source code directory hierarchies. + With SubDir and SubInclude, you can use jam + to build software from source files and Jamfiles spread + across many directories, as is typical for large projects. + The SubDir* rules unify an entire + source code tree so that jam can read in + all the Jamfiles in one pass and + compute dependencies across the entire project. +

+ To use the SubDir* rules, you must: +

+

    +
  1. Preface the Jamfile in each directory with an invocation + of the SubDir rule. +

    +

  2. Place at the root of the tree a file named Jamrules. + This file could be empty, but in + practice it contains user-provided rules and variable + definitions that are shared throughout the + tree. Examples of such definitions are library + names, header directories, install directories, + compiler flags, etc. This file is good candidate + for automatic customizing with autoconf(GNU). +

    +

  3. Optionally, set an environment variable pointing + to the root directory of the srouce tree. The + variable's name is left up to you, but in these + examples, we use TOP. +
+

+

+ SubDir Rule +

+

+ The SubDir rule must be invoked before any rules that + refer to the contents of the directory - it is best to put + it at the top of each Jamfile. For example: +

+	# Jamfile in $(TOP)/src/util directory.
+
+	SubDir TOP src util ;
+
+	Main myprog : main.c util.c ;                   
+	LinkLibraries myprog : libtree ;     
+	Library libtree : treemake.c treetrav.c ;    
+
+ This compiles four files in $(TOP)/src/util, archives + two of the objects into libtree, and links the whole + thing into myprog. + Outputs are placed in the $(TOP)/src/util + directory. +

+ This doesn't appear to be any different from + the previous example that didn't have a SubDir rule, + but two things are happening behind the scenes: +

    +
  1. The SubDir rule causes jam to read + in the $(TOP)/Jamrules file. + (The Jamrules file can alternately be named by the + variable $(xxxRULES), where xxx is the name of the + root variable, e.g., $(TOPRULES)). +

    + The Jamrules file can contain variable definitions + and rule definitions specific to your codeline. + It allows you to completely customize your build + environment without having to rewrite Jambase. + Jamrules is only read + in once, at the first SubDir invocation. +

    +

  2. + The SubDir rule initializes a set of variables + that are used by Main and other rules to + uniquely identify the source files in this + directory and assign locations to the targets + built from files in this directory. +

    + When you have set a root variable, e.g., $(TOP), + SubDir constructs path names rooted with $(TOP), + e.g., $(TOP)/src/util. + Otherwise, SubDir constructs relative pathnames + to the root directory, computed from the number + of arguments to the first SubDir rule, e.g., + ../../src/util. In either case, the SubDir + rule constructs the path names that locate source + files. + You'll see how this is useful later. +

    + + +

    + The SubDir rule takes as its first argument the root + variable's name and takes as subsequent arguments the + directory names leading from the root to the directory of + the current Jamfile. Note that the name of the subdirectory + is given as individual elements: the SubDir rule + does not use system-specific directory name syntax. +

    +

    +

    + SubInclude Rule +

    + The SubInclude rule is used in a Jamfile to cause another + Jamfile to be read in. + Its arguments are in the same format as + SubDir's. +

    + The recommended practice is only to include one level of + subdirectories at a time, and let the Jamfile in each subdirectory + include its own subdirectories. This allows a + user to sit in any arbitrary directory of the source tree + and build that subtree. For example: +

    +       # This is $(TOP)/Jamfile, top level Jamfile for mondo project.
    +
    +       SubInclude TOP src ;
    +       SubInclude TOP man ;
    +       SubInclude TOP misc ;
    +       SubInclude TOP util ;
    +
    + If a directory has both subdirectories of its own as well + as files that need building, the SubIncludes should be + either before the SubDir rule or be at the end of the Jamfile + - not between the SubDir and other rule invocations. + For example: +
    +	# This is $(TOP)/src/Jamfile:
    +
    +	SubDir TOP src ;
    +
    +	Main mondo : mondo.c ;
    +	LinkLibraries mondo : libmisc libutil ;
    +	
    +	SubInclude TOP src misc ;
    +	SubInclude TOP src util ;
    +
    +

    + (jam processes all the Jamfiles it reads as if + it were reading one single, large Jamfile. + Build rules like Main and LinkLibraries rely on the + preceding SubDir rule to set up source file and + output file locations, and SubIncludes rules read in + Jamfiles that contain SubDir rules. So if you put + a SubIncludes rule between a SubDir and a Main + rule, jam will try to find the source files + for the Main rule in the wrong directory.) +

    +

    + Variables Used to Handle Directory Trees +

    + The following variables are set by the SubDir rule + and used by the Jambase rules that define file targets: +

    +

    + +
    + SEARCH_SOURCE + The SubDir targets (e.g., "TOP src util") + are used to construct a pathname (e.g., $(TOP)/src/util), + and that pathname is assigned to $(SEARCH_SOURCE). + Rules like Main and Library use $(SEARCH_SOURCE) + to set search paths on source files. +
    + LOCATE_SOURCE + Initialized by the SubDir rule to the same + value as $(SEARCH_SOURCE), unless ALL_LOCATE_TARGET + is set. + $(LOCATE_SOURCE) is used by rules that build + generated source files (e.g., Yacc and Lex) to + set location of output files. + Thus the default location of built source files + is the directory of the Jamfile that defines them. +
    + LOCATE_TARGET + Initalized by the SubDir rule to the same + value as $(SEARCH_SOURCE), unless ALL_LOCATE_TARGET + is set. + $(LOCATE_TARGET) is used by rules that build + binary objects (e.g., Main and Library) to + set location of output files. + Thus the default location of built binaray files + is the directory of the Jamfile that defines them. +
    + ALL_LOCATE_TARGET + + If $(ALL_LOCATE_TARGET) is set, LOCATE_SOURCE + and and LOCATE_TARGET are set to $(ALL_LOCATE_TARGET) + instead of to $(SEARCH_SOURCE). This can be used to + direct built files to be written to a location outside + of the source tree, and enables building from read-only + source trees. +
    + SOURCE_GRIST + The SubDir targets are formed into a string + like "src!util" and that string is assigned to + SOURCE_GRIST. Rules that define file targets + use $(SOURCE_GRIST) to set the "grist" attribute + on targets. This is used to assure uniqueness + of target identifiers where filenames themselves + are not unique. + For example, the target identifiers of + $(TOP)/src/client/main.c and $(TOP)/src/server/main.c + would be <src!client>main.c and <src!server>main.c. +
    +
    +

    + The $(LOCATE_TARGET) and $(SEARCH_SOURCE) variables are used + extensively by rules in Jambase: most rules that generate + targets (like Main, Object, etc.) set $(LOCATE) to + $(LOCATE_TARGET) for the targets they generate, and rules + that use sources (most all of them) set $(SEARCH) to be + $(SEARCH_SOURCE) for the sources they use. +

    + $(LOCATE) and $(SEARCH) are better explained in + The Jam Executable Program + but in brief they tell jam where to create new targets and + where to find existing ones, respectively. +

    + Note that you can reset these variables + after SubDir sets them. For example, this Jamfile builds + a program called gensrc, then runs it to create a source file + called new.c: +

    +       SubDir TOP src util ;
    +       Main gensrc : gensrc.c ;
    +       LOCATE_SOURCE = $(NEWSRC) ;
    +       GenFile new.c : gensrc ;
    +       
    + By default, new.c would be written into the + $(TOP)/src/util directory, but resetting LOCATE_SOURCE causes + it to be written to the $(NEWSRC) directory. ($(NEWSRC) is assumed + to have been set elsewhere, e.g., in Jamrules.) +

    +

    + VMS Notes +

    + On VMS, the logical name table is not imported as is the + environment on UNIX. To use the SubDir and related rules, + you must set the value of the variable that names the root + directory. For example: +
    +              TOP = USR_DISK:[JONES.SRC] ;
    +
    +              SubInclude TOP util ;
    +
    + The variable must have a value that looks like a directory + or device. If you choose, you can use a concealed logical. + For example: +
    +              TOP = TOP: ;
    +
    +              SubInclude TOP util ;
    +
    + The : at the end of TOP makes the value of $(TOP) look + like a device name, which jam respects as a directory name + and will use when trying to access files. TOP must then + be defined from DCL: +
    +              $ define/job/translation=concealed TOP DK100:[USERS.JONES.SRC.]
    +
    + Note three things: the concealed translation allows the + logical to be used as a device name; the device name in + the logical (here DK100) cannot itself be concealed logical + (VMS rules, man); and the directory component of the + definition must end in a period (more VMS rules). +

    +

    +Building Executables and Libraries +

    +

    +The rules that build executables and libraries are: Main, Library, +and LinkLibraries. +

    + Main Rule +

    + The Main rule compiles source files and links the resulting + objects into an executable. For example: +
    +              Main myprog : main.c util.c ;
    +
    + This compiles main.c and util.c and links main.o and + util.o into myprog. The object files and resulting + executable are named appropriately for the platform. +

    + Main can also be used to build shared libraries and/or + dynamic link libraries, since those are also linked + objects. E.g.: +

    +		Main driver$(SUFSHR) : driver.c ;
    +	
    + Normally, Main uses $(SUFEXE) to determine the suffix on + the filename of the built target. To override it, + you can supply a suffix explicity. + In this case, + $(SUFSHR) is assumed to be the OS-specific shared library + suffix, defined in Jamrules with something + like: +
    +		if $(UNIX)      { SUFSHR = .so ; }
    +		else if $(NT)   { SUFSHR = .dll ; }
    +	
    +

    + Main uses the Objects rule to compile source targets. + +

    + Library Rule +

    + The Library rule compiles source files, archives the + resulting object files into a library, and then deletes + the object files. For example: +
    +              Library libstring : strcmp.c strcpy.c strlen.c ;
    +              Library libtree : treemake.c treetrav.c ;
    +
    + This compiles five source files, archives three of the + object files into libstring and the other two into libtree. + Actual library filenames are formed with the $(SUFLIB) suffix. + Once the objects are safely in the libraries, the + objects are deleted. +

    + Library uses the Objects rule to compile source files. +

    +

    + LinkLibraries Rule +

    + To link executables with built libraries, use + the LinkLibraries rule. For example: +
    +              Main myprog : main.c util.c ;
    +              LinkLibraries myprog : libstring libtree ;
    +
    + The LinkLibraries rule does two things: it makes the + libraries dependencies of the executable, so that they get + built first; and it makes the libraries show up on the + command line that links the executable. The ordering of + the lines above is not important, because jam builds targets + in the order that they are needed. +

    + You can put multiple libraries on a single invocation of + the LinkLibraries rule, or you can provide them in multiple + invocations. In both cases, the libraries appear on + the link command line in the order in which they were + encountered. You can also provide multiple executables to + the LinkLibraries rule, if they need the same libraries, + e.g.: +

    +		LinkLibraries prog1 prog2 prog3 : libstring libtree ;
    +       
    +

    +

    + Variables Used in Building Executables and Libraries +

    +
    + +
    + AR + Archive command, used for Library targets. +
    + SUFEXE + *Suffix on filenames of executables referenced + by Main and LinkLibraries. +
    + LINK + Link command, used for Main targets. +
    + LINKFLAGS + Linker flags. +
    + LINKLIBS + Link libraries that aren't dependencies. (See note + below.) +
    + EXEMODE + *File permissions on Main targets. +
    + MODE + Target-specific file permissions on Main targets + (set from $(EXEMODE)) +
    + RANLIB + Name of ranlib program, if any. +
    +
    + +

    + Variables above marked with "*" are used by the Main, + Library, and LinkLibraries rules. Their values at the + time the rules are invoked are used to set target-specific + variables. +

    + All other variables listed above are globally defined, + and are used in actions that update Main and Library + targets. This means that the global values of those + variables are used, uness target-specific values have + been set. + (For instance, a target-specific MODE value is set by + the Main rule.) + The target-specific values always override + global values. +

    + Note that there are two ways to specify link libraries for + executables: +

    +

    + For example: +

    +	#In Jamrules:
    +              if $(UNIX) { X11LINKLIBS = -lXext -lX11 ; }
    +              if $(NT)   { X11LINKLIBS = libext.lib libX11.lib ; }
    +
    +	#In Jamfile:
    +              Main xprog : xprog.c ;
    +              LINKLIBS on xprog$(SUFEXE) = $(X11LINKLIBS) ;
    +              LinkLibraries xprog : libxutil ;
    +              Library libxutil : xtop.c xbottom.c xutil.c ;
    +
    + This example uses the Jam syntax "variable on target" to + set a target-specific variable. In this way, only xprog + will be linked with this special $(X11LINKLIBS), + even if other executables were going to be built + by the same Jamfile. Note that when you set a variable + on a target, you have to specify the target identifer + exactly, which in this case is the suffixed filename of + the executable. + The actual link command line on Unix, for example, would + look something like this: +
    +              cc -o xprog xprog.o libxutil.a -lXext -lX11
    +
    +

    +Compiling +

    + Compiling of source files occurs normally as a byproduct + of the Main or Library rules, which call the rules + described here. These rules may also be called explicitly + if the Main and Library behavior doesn't satisfy your + requirements. +

    +

    + Objects Rule +

    + The Main and Library rules call the Objects rule on source files. + Compiled object files built by + the Objects rule are a dependency of the obj + pseudotarget, so "jam obj" will build object files used in + Main and Library rules. +

    + Target identifiers created by the Objects rule have grist + set to $(SOURCE_GRIST). So given this Jamfile: +

    +		SubDir TOP src lock ;
    +		Main locker : lock.c ;
    +       
    + the object file created is lock.o (or lock.obj) and + its target identifier is <src!lock>lock.o + (or <src!lock>lock.obj). + +

    + You can also call Objects directly. For example: +

    +              Objects a.c b.c c.c ;
    +
    + This compiles a.c into a.o, b.c into b.o, etc. The object + file suffix is supplied by the Objects rule. +

    +

    + Object Rule +

    + Objects gets its work done by calling the Object rule on + each of the source files. + You could use the Object rule directly. + For example, on Unix, you could use: +
    +              Object foo.o : foo.c ;
    +
    + However, the Object rule does not provide suffixes, and + it does not provide the grist needed to construct target + identifiers if you are using the SubDir* rules. + A portable and robust Jamfile would need to invoke Object thus: +
    +	      Object <src!util>foo$(SUFOBJ) : <src!util>foo.c ;
    +	
    + which is inelegant and clearly shows why using Objects + is better than using Object. +

    + If there's any advantage to the Object rule, it's + that it doesn't require that the object name bear + any relationship to the source. It is thus possible to + compile the same file into different objects. For example: + +

    +              Object a.o : foo.c ;
    +              Object b.o : foo.c ;
    +              Object c.o : foo.c ;
    +
    + This compiles foo.c (three times) into a.o, b.o, and c.o. + Later examples show how this is useful. +

    + The Object rule looks at the suffix of the source file and + calls the appropriate rules to do the actual preprocessing + (if any) and compiling needed to produce the output object file. + The Object rule is + capable of the generating of an object file from any + type of source. For example: +

    +              Object grammar$(SUFOBJ) : grammar.y ;
    +              Object scanner$(SUFOBJ) : scanner.l ;
    +              Object fastf$(SUFOBJ) : fastf.f ;
    +              Object util$(SUFOBJ) : util.c ;
    +
    + An even more elegant way to get the same result is to let the + Objects rule call Object: +
    +              Objects grammar.y scanner.l fastf.f util.c ;
    +	
    +

    + In addition to calling the compile rules, Object sets up + a bunch of variables specific to the source and target + files. (See Variables Used in Compiling, below.) +

    +

    + Cc, C++, Yacc, Lex, Fortran, As, etc. Rules +

    +

    + The Object rule calls compile rules specific to the suffix of + the source file. (You can see which suffixes are supported + by looking at the Object rule definition in Jambase.) + Because the extra work done by the + Object rule, it is not always useful to call the compile + rules directly. But the adventurous user might attempt + it. For example: +

    +              Yacc grammar.c : grammar.y ;
    +              Lex scan.c : scan.l ;
    +              Cc prog.o : prog.c ;
    +
    + These examples individually run yacc(1), lex(1), and the C + compiler on their sources. +

    +

    + UserObject Rule +

    + Any files with suffixes not understood by the Object rule + are passed to the UserObject rule. The default definition + of UserObject simply emits a warning that the suffix is + not understood. This Jambase rule definition is intended to be + overridden in Jamrules with one that recognizes the project-specific + source file suffixes. For example: + +
    +	#In Jamrules:
    +
    +              rule UserObject
    +              {
    +                  switch $(>)
    +                  {
    +                  case *.rc   : ResourceCompiler $(<) : $(>) ;
    +                  case *      : ECHO "unknown suffix on" $(>) ;
    +                  }
    +              }
    +
    +              rule ResourceCompiler
    +              {
    +                  DEPENDS $(<) : $(>) ;
    +		  Clean clean : $(<) ;
    +              }
    +
    +              actions ResourceCompiler
    +              {
    +                  rc /fo $(<) $(RCFLAGS) $(>)
    +              }
    +
    +
    +	#In Jamfile:
    +
    +              Library liblock : lockmgr.c ;
    +	      if $(NT) { Library liblock : lock.rc ; }
    +
    +

    + In this example, the UserObject definition in Jamrules + allows *.rc files to be handle as regular Main and Library + sources. The lock.rc file is compiled into lock.obj + by the "rc" command, and lock.obj is archived into a library + with other compiled objects. +

    + LibraryFromObjects Rule +

    + Sometimes the Library rule's straightforward compiling of + source into object modules to be archived isn't flexible + enough. The LibraryFromObjects rule does the archiving + (and deleting) job of the Library rule, but not the compiling. + The user can make use of the Objects or Object + rule for that. For example: +
    +              LibraryFromObjects libfoo.a : max.o min.o ;
    +              Object max.o : maxmin.c ;
    +              Object min.o : maxmin.c ;
    +              ObjectCcFlags max.o : -DUSEMAX ;
    +              ObjectCcFlags min.o : -DUSEMIN ;
    +
    + This Unix-specific example compiles the same source file into + two different + objects, with different compile flags, and archives them. + (The ObjectCcFlags rule is described shortly.) + Unfortunately, the portable and robust implementation of the + above example is not as pleasant to read: +
    +	      SubDir TOP foo bar ;
    +              LibraryFromObjects libfoo$(SUFLIB) : <foo!bar>max$(SUFOBJ) 
    +			                           <foo!bar>min$(SUFOBJ) ;
    +              Object <foo!bar>min$(SUFOBJ) : <foo!bar>maxmin.c ;
    +              Object <foo!bar>max$(SUFOBJ) : <foo!bar>maxmin.c ;
    +	      ObjectCcFlags <foo!bar>min$(SUFOBJ) : -DUSEMIN ;
    +	      ObjectCcFlags <foo!bar>max$(SUFOBJ) : -DUSEMAX ;
    +       
    + Note that, among other things, you must supply the library + file suffix when using the LibraryFromObjects rule. +

    +

    + MainFromObjects Rule +

    + Similar to LibraryFromObjects, MainFromObjects does the + linking part of the Main rule, but not the compiling. + MainFromObjects can be used when there are no + objects at all, and everything is to be loaded from + libraries. For example: +
    +              MainFromObjects testprog ;
    +              LinkLibraries testprog : libprog ;
    +              Library libprog : main.c util.c ;
    +
    + On Unix, say, this generates a link command that looks like: +
    +              cc -o testprog libprog.a
    +
    + Linking purely from libraries is something that doesn't + work everywhere: it depends on the symbol "main" being + undefined when the linker encounters the library that contains + the definition of "main". +

    +

    + Variables Used in Compiling +

    + The following variables control the compiling of source + files: +

    +

    + +
    + C++ + The C++ compiler command +
    + CC + The C compiler command +
    + C++FLAGS +
    + CCFLAGS +
    Compile flags, used to + create or update compiled objects +
    + SUBDIRC++FLAGS +
    + SUBDIRCCFLAGS +
    Additonal compile flags + for source files in this directory. +
    + OPTIM + Compiler optimization flag. The Cc and C++ + actions use this as well as C++FLAGS or CCFLAGS. +
    + HDRS + Non-standard header directories; i.e., + the directories the compiler will not look in + by default and which therefore must be supplied + to the compile command. These directories are + also used by jam to scan for include files. +
    + STDHDRS + Standard header directories, i.e., the + directories the compiler searches automatically. + These are not passed to the compiler, but they + are used by jam to scan for include files. +
    + SUBDIRHDRS + Additional paths to add to HDRS for source files + in this directory. +
    + LEX + The lex(1) command +
    + YACC + The yacc(1) command +
    +
    +

    + The Cc rule sets a target-specific $(CCFLAGS) to the current + value of $(CCFLAGS) and $(SUBDIRCCFLAGS). Similarly + for the C++ rule. The Object rule sets a target-specific + $(HDRS) to the current value of $(HDRS) and $(SUBDDIRHDRS). + +

    + $(CC), $(C++), $(CCFLAGS), $(C++FLAGS), $(OPTIM), and + $(HDRS) all affect the compiling of C and C++ files. + $(OPTIM) is separate from $(CCFLAGS) and $(C++FLAGS) so + they can be set independently. +

    + $(HDRS) lists the directories to search for header files, + and it is used in two ways: first, it is passed to the C + compiler (with the flag -I prepended); second, it is used + by HdrRule to locate the header files whose names were + found when scanning source files. $(STDHDRS) lists the + header directories that the C compiler already knows + about. It does not need passing to the C compiler, but is + used by HdrRule. +

    + Note that these variables, if set as target-specific variables, + must be set on the target, not the source file. + The target file in this case is the object file to be generated. + For example: +

    +              Library libximage : xtiff.c xjpeg.c xgif.c ;
    +
    +              HDRS on xjpeg$(SUFOBJ) = /usr/local/src/jpeg ;
    +              CCFLAGS on xtiff$(SUFOBJ) = -DHAVE_TIFF ;
    +
    + This can be done more easily with the rules that follow. +

    +

    + ObjectCcFlags, ObjectC++Flags, ObjectHdrs Rules +

    + $(CCFLAGS), $(C++FLAGS) and $(HDRS) can be set on object file + targets + directly, but there are rules that allow these variables + to be set by referring to the original source file name, + rather than to the derived object file name. ObjectCcFlags + adds object-specific flags to the $(CCFLAGS) variable, + ObjectC++Flags adds object-specific flags to the + $(C++FLAGS) variable, and ObjectHdrs add object-specific + directories to the $(HDRS) variable. For example: +
    +	#In Jamrules:
    +		if $(NT) { CCFLAGS_X = /DXVERSION ;	
    +			   HDRS_X = \\\\SPARKY\\X11\\INCLUDE\\X11 ;
    +		         }
    +
    +	#In Jamfile:
    +              Main xviewer : viewer.c ;
    +              ObjectCcFlags viewer.c : $(CCFLAGS_X) ;
    +              ObjectHdrs viewer.c : $(HDRS_X) ;
    +
    + The ObjectCcFlags and ObjectHdrs rules take .c files + as targets, but actually set $(CCFLAGS) and $(HDRS) values + on the .obj (or .o) files. As a result, the action + that updates the target .obj file uses the target-specific + values of $(CCFLAGS) and $(HDRS). +

    +

    + SubDirCcFlags, SubDirC++Flags, SubDirHdrs Rules +

    + These rules set the values of $(SUBDIRCCFLAGS), $(SUBDIRC++FLAGS) + and $(SUBDIRHDRS), which are used by the Cc, + C++, and Object rules when setting the target-specific + values for $(CCFLAGS), $(C++FLAGS) and $(HDRS). The SubDir + rule clears these variables out, and thus they provide + directory-specific values of $(CCFLAGS), $(C++FLAGS) and + $(HDRS). For example: +
    +	#In Jamrules:
    +              GZHDRS = $(TOP)/src/gz/include ;
    +	      GZFLAG = -DGZ ;
    +		
    +	#In Jamfile:
    +              SubDir TOP src gz utils ;
    +
    +              SubDirHdrs $(GZHDRS) ;
    +              SubDirCcFlags $(GZFLAG) ;
    +
    +	      Library libgz : gizmo.c ;
    +	      Main gizmo : main.c ;
    +	      LinkLibraries gizmo : libgz ;
    +
    + All .c files in this directory files will be compiled with + $(GZFLAG) as well as the default $(CCFLAG), and the include + paths used on the compile command will be $(GZHDRS) as well + as the default $(HDRS). +

    +Header File Processing +

    + One of the functions of the Object rule is set up + scanning of source + files for (C style) header file inclusions. To do so, it + sets the special variables $(HDRSCAN) and $(HDRRULE) + as target-specific variables on the source file. The + presence of these variables triggers a special mechanism + in jam for scanning a file for header file inclusions and + invoking a rule with the results of the scan. The + $(HDRSCAN) variable is set to an egrep(1) pattern that + matches "#include" statements in C source files, and the + $(HDRRULE) variable is set to the name of the rule that + gets invoked as such: +
    +              $(HDRRULE) source-file : included-files ;
    +
    + This rule is supposed to set up the dependencies between + the source file and the included files. The Object rule + uses HdrRule to do the job. HdrRule itself expects + another variable, $(HDRSEARCH), to be set to the list of + directories where the included files can be found. Object + does this as well, setting $(HDRSEARCH) to $(HDRS) and + $(STDHDRS). +

    + The header file scanning occurs during the "file binding" + phase of jam, which means that the target-specific + variables (for the source file) are in effect. To accomodate + nested includes, one of the HdrRule's jobs is to pass + the target-specific values of $(HDRRULE), $(HDRSCAN), and + $(HDRSEARCH) onto the included files, so that they will be + scanned as well. +

    +

    + HdrRule Rule +

    + Normally, HdrRule is not invoked directly; the Object rule + (called by Main and Library) invokes it. +

    + If there are special dependencies that need to be set, + and which are not set by HdrRule itself, you can define + another rule and let it invoke HdrRule. For example: + +

    +	#In Jamrules:
    +              rule BuiltHeaders
    +              {
    +                      DEPENDS $(>) : mkhdr$(SUFEXE) ;
    +                      HdrRule $(<) : $(>) ;
    +              }
    +
    +	#In Jamfile:
    +              Main mkhdr : mkhdr.c ;
    +              Main ugly : ugly.c ;
    +
    +              HDRRULE on ugly.c = BuiltHeaders ;
    +
    +
    + This example just says that the files included by "ugly.c" + are generated by the program "mkhdr", which can be built + from "mkhdr.c". During the binding phase, jam will + scan ugly.c, and if it finds an include file, ughdr.h, + for example, it will automatically invoke the rule: +
    +              BuiltHeaders ugly.c : ughdr.h ;
    +       
    + By calling HdrRule at the end of BuiltHeaders, + all the gadgetry of HdrRule takes effect and it + doesn't need to be duplicated. +

    +

    + Variables Used for Header Scanning +

    +
    + +
    + HDRPATTERN + Default scan pattern for "include" lines. +
    + HDRSCAN + Scan pattern to use. + This is a special variable: during binding, if + both HDRSCAN and HDRRULE are set, scanning is activated + on the target being bound. + The HdrRule and Object rules sets this + to $(HDRPATTERN) on their source targets. +
    + HDRRULE + Name of rule to invoked on files found in header + scan. The HdrRule and Object rules set this to "HdrRule" + on their source targets. This is also a special variable; + it's the only jam variable that can hold the + name of a rule to be invoked. +
    + HDRSEARCH + Search paths for files found during header scanning. + This is set from $(HDRS) and $(STDHDRS), which are + described in the Compiling section. + jam will search $(HDRSEARCH) directories for + the files found by header scans. +
    +
    +

    + The Object rule sets HDRRULE and HDRSCAN specifically for + the source files to be scanned, rather than globally. If + they were set globally, jam would attempt to scan all + files, even library archives and executables, for header + file inclusions. That would be slow and probably not + yield desirable results. +

    +

    +Copying Files +

    +

    + File Rule +

    + The File rule copies one file to another. The target name + needn't be the same as the source name. For + example: +
    +	switch $(OS)
    +	{
    +           case NT*  : File config.h : confignt.h ;
    +	   case *    : File config.h : configunix.h ;
    +	}
    +	LOCATE on config.h = $(LOCATE_SOURCE) ;
    +
    + This creates a config.h file from either confignt.h or + configunix.h, depending on the current build platform. +

    + The File rule does not + use the LOCATE_SOURCE variable set by the + SubDir rule (although it does use SEARCH_SOURCE), which + means you have to set the copied file's output directory + yourself. That's done by setting the special + LOCATE variable on the target, as shown above, + or with the MakeLocate rule described below. +

    + Bulk Rule +

    + The Bulk rule is a shorthand for many invocations of the + File rule when all files are going to the same directory. + For example: +
    +	#In Jamrules:
    +              DISTRIB_GROB = d:\\distrib\\grob ;
    +
    +	#In Jamfile:
    +              Bulk $(DISTRIB_GROB) : grobvals.txt grobvars.txt ;
    +
    + This causes gobvals.txt and grobvars.txt to be copied + into the $(DISTRIB_GROB) directory. +

    + HardLink Rule +

    + The Unix-only HardLink rule makes a hard link (using ln(1)) from the + source to the target, if there isn't one already. For + example: +
    +              HardLink config.h : configunix.h ;
    +
    +

    + Shell Rule +

    + The Shell rule is like the File rule, except that on Unix it makes + sure the first line of the target is "#!/bin/sh" and sets + the permission to make the file executable. For example: +
    +              Shell /usr/local/bin/add : add.sh ;
    +
    +

    + You can also use $(SHELLHEADER) to dictate + what the first line of the copied file will be. + For + example: +

    +              Shell /usr/local/bin/add : add.awk ;
    +              SHELLHEADER on /usr/local/bin/add = "#!/bin/awk -f" ;
    +
    + This installs an awk(1) script. +

    +

    + Variables Used When Copying Files +

    +
    + +
    + FILEMODE + Default file permissions for copied files +
    + SHELLMODE + Default file permissions for Shell rule targets +
    + MODE + File permissions set on files copied by + File, Bulk, and Shell rules. + File and Shell sets a target-specific MODE to the current + value of $(FILEMODE) or $(SHELLMODE), respectively. +
    + SHELLHEADER + String to write in first line of Shell targets + (default is #!/bin/sh). + +
    +
    +

    + +

    +Installing Files +

    +Jambase provides a set of Install* rules to copy files +into an destination directory and set permissions on them. +On Unix, the install(1) program is used. +If the destination directory does not exist, jam +creates it first. +

    +All files copied with the Install* rules are dependencies +of the install pseudotarget, which means that the +command "jam install" will cause the installed copies to +be updated. Also, "jam uninstall" will cause the installed +copies to be removed. +

    +The Install* rules are: +

    + +
    InstallBin + Copies file and sets its permission to $(EXEMODE). + You must specify the suffixed executable name. E.g.: +
    InstallBin $(BINDIR) : thing$(SUFEXE) ;
    +		   
    + +
    InstallFile + Copies file and sets its permission to $(FILEMODE). E.g.: +
    InstallFile $(DESTDIR) : readme.txt ;
    +		   
    + +
    InstallLib + Copies file and sets its permission to $(FILEMODE). + You must specify the suffixed library name. E.g.: +
    InstallLib $(LIBDIR) : libzoo$(SUFLIB) ;
    +		   
    + +
    InstallMan + Copies file into the mann + subdirectory of the target directory + and sets its permission to $(FILEMODE). E.g., + this copies foo.5 into the $(DESTDIR)/man5 directory: +
    InstallMan $(DESTDIR) : foo.5 ;
    +		   
    + +
    InstallShell + Copies file and sets its permission to $(SHELLMODE). E.g.: +
    InstallShell $(DESTDIR) : startup ;
    +		   
    + +
    +
    +

    +

    +

    + Variables +

    + The following variables control the installation rules: +

    +

    + +
    + INSTALL + The install program (Unix only) +
    + FILEMODE + Default file permissions on readable files. +
    + EXEMODE + Default file permission executable files. +
    + SHELLMODE + Default file permission on shell script files. +
    + MODE + Target-specific file permissions +
    +
    +

    +

    + The Install rules set a target-specific MODE to the current + value of $(FILEMODE), $(EXEMODE), or $(SHELLMODE), + depending on which Install rule was invoked. +

    + The directory variables are just defined for convenience: + they must be passed as the target to the appropriate + Install rule. The $(INSTALL) and mode variables must be + set (globally) before calling the Install rules in order + to take effect. +

    +

    +Miscellaneous Rules +

    +

    +Clean Rule +

    +

    +The Clean rule defines files to be removed when you run "jam clean". +Any site-specific build rules defined in your Jamrules should invoke +Clean so that outputs can be removed. E.g., +

    +	rule ResourceCompiler
    +	{
    +	   DEPENDS $(<) : $(>) ;
    +	   Clean clean : $(<) ;
    +	}
    +
    +

    +

    +Most Jambase rules invoke the Clean rule on their built targets, +so "jam clean" will remove all compiled objects, libraries, +executables, etc. +

    +

    +MakeLocate Rule +

    + MakeLocate is a single convenient rule that creates a directory, + sets LOCATE on a target to that directory, and makes the directory + a dependency of the target. It is used by many Jambase rules, + and can be invoked directly, e.g.: +
    +		GenFile data.tbl : hxtract data.h ;
    +		MakeLocate data.tbl : $(TABLEDIR) ;
    +      
    + In this example, the File rule creates data.tbl from data.h. + The MakeLocate causes data.tbl to be written into the $(TABLEDIR) + directory; and if the directory doesn't exist, it is created first. +

    + The MakeLocate rule invokes another Jambase rule, MkDir, + to (recursively) create + directories. MkDir uses the $(MKDIR) variable to determine the + platform-specific command that creates directories. +

    +

    +RmTemps Rule +

    + Some intermediate files are meant to be temporary. + The RmTemps rule can be used to cause + jam to delete them after they are used. +

    + RmTemps must be: +

    + For + example: +
    +		SubDir TOP src big ;
    +		GenFile big.y : joinfiles part1.y part2.y part3.y ;
    +		Main bigworld : main.c big.y ;
    +		RmTemps bigworld$(SUFEXE) : <src!big>big.y ;
    +	
    + This causes big.y to be deleted after it has been used to create + the bigworld executable. + The exact target identifier of big.y is <src!big>big.y + (the GenFile and Main rules tack on the grist automatically); + the exact target identifier of the bigworld executable + is bigworld$(SUFEXE). +

    +


    +Back to top. +

    + Copyright 1997, 2000 Perforce Software, Inc. +
    + Comments to info@perforce.com +
    + Last updated: Dec 31, 2000 +
    + $Id: //public/jam/src/Jamfile.html#5 $ + + diff --git a/src/tools/jam/Makefile b/src/tools/jam/Makefile new file mode 100644 index 0000000000..ec8b3caf8a --- /dev/null +++ b/src/tools/jam/Makefile @@ -0,0 +1,71 @@ +# Makefile for jam + +CC = cc +TARGET = -o jam0 +CFLAGS = + +# Special flavors - uncomment appropriate lines + +# NCR seems to have a broken readdir() -- use gnu +#CC = gcc + +# AIX needs -lbsd, and has no identifying cpp symbol +# Use _AIX41 if you're not on 3.2 anymore. +#LINKLIBS = -lbsd +#CFLAGS = -D_AIX + +# NT (with Microsoft compiler) +# Use FATFS if building on a DOS FAT file system +#Lib = $(MSVCNT)/lib +#Include = $(MSVCNT)/include +#CC = cl /nologo +#CFLAGS = -I $(Include) -DNT +#TARGET = /Fejam0 +#LINKLIBS = $(Lib)/oldnames.lib $(Lib)/kernel32.lib $(Lib)/libc.lib + +# NT (with Microsoft compiler) +# People with DevStudio settings already in shell environment. +#CC = cl /nologo +#CFLAGS = -DNT +#TARGET = /Fejam0 + +# BeOS - Metroworks CodeWarrior +#CC = mwcc +#Include = /NewDisk/develop/headers/posix +#CFLAGS = -I $(Include) + +# BeOS - gcc +#CC = gcc +#LINKLIBS = -lnet + +# Interix - gcc +#CC = gcc + +# Cygwin - gcc & cygwin +#CC = gcc +#CFLAGS = -D__cygwin__ + +# MingW32 +#CC = gcc +#CFLAGS = -DMINGW + +# MPEIX +#CC = gcc +#CFLAGS = -I/usr/include -D_POSIX_SOURCE + +# QNX rtp (neutrino) +#CC = gcc + +SOURCES = \ + builtins.c \ + command.c compile.c execunix.c execvms.c expand.c \ + filent.c fileos2.c fileunix.c filevms.c glob.c hash.c \ + headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c \ + newstr.c option.c parse.c pathunix.c pathvms.c regexp.c \ + rules.c scan.c search.c timestamp.c variable.c + +all: jam0 + jam0 + +jam0: + $(CC) $(TARGET) $(CFLAGS) $(SOURCES) $(LINKLIBS) diff --git a/src/tools/jam/Porting b/src/tools/jam/Porting new file mode 100644 index 0000000000..7505565720 --- /dev/null +++ b/src/tools/jam/Porting @@ -0,0 +1,68 @@ +Notes on porting Jam - revised 12/31/2000 + +1) Working out system dependencies in the Jam code. + + Jam's OS footprint is fairly small. For OS independent work Jam + liberally uses standard libc functions like stdio, malloc, and + string. The OS dependent interfaces are: + + From filesys.h: + + file_parse() - split a file name into dir/base/suffix/member + file_build() - build a filename given dir/base/suffix/member + file_dirscan() - scan a directory for files + file_archscan() - scan an archive for files + file_time() - get the timestamp of a file, if not already + done by file_dirscan(). + + From execcmd.h: + + execcmd() - execute a shell script + execwait() - wait for any outstanding execcmd()'s. + + The current implementations are: + + filemac.c - mac MPW + filent.c - NT + fileos2.c - OS/2 + fileunix.c - all UNIX + filevms.c - VMS + + execmac.c - mac MPW + execunix.c - UNIX, OS/2, NT + execvms.c - VMS + +2) Defining OSMAJOR, OSMINOR in jam.h + + So that the Jambase and Jamfile know their host, Jam defines $(OS) + to be something useful for each platform. Make sure that there is + code in jam.h to generate a useful value for $(OS), and key it off + the platform specific C-preprocessor symbol. If the C-preprocessor + doesn't itself defines such a symbol, add a define to the Makefile. + + In addition to $(OS), you can also set $(OSPLAT) if the OS runs on + multiple platforms (like Linux or NT). + +3) Working out system dependencies in the Jambase + + With the value of $(OS) available, the Jambase can be extended to + support special variables or rules for new platforms. See the + current support for VMS, NT, and Mac. + +4) Yacc troubles + + The generated files jamgram.h and jamgram.c are distributed for the + poor souls without yacc. + +5) Known problematic systems: + + - Pyramid has no malloc.h, memory.h + + - Encore has no stdlib.h + + - Bull DPX has sys/file.h problems + +6) Send the results back. + + If you do porting work, the result can be integrated into future + releases if you send it back to the author's address in the README. diff --git a/src/tools/jam/README b/src/tools/jam/README new file mode 100644 index 0000000000..97777f09fd --- /dev/null +++ b/src/tools/jam/README @@ -0,0 +1,149 @@ +Jam/MR (aka "jam - make(1) redux") + + /+\ + +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + \+/ + + This is Release 2.4 of Jam/MR, a make-like program. + + License is hereby granted to use this software and distribute it + freely, as long as this copyright notice is retained and modifications + are clearly marked. + + ALL WARRANTIES ARE HEREBY DISCLAIMED. + +FEATURES + + -> Jam is a make(1) replacement that makes building simple things + simple and building complicated things manageable. + + -> Jam's language is expressive, making Jamfiles (c.f. Makefiles) + compact. Here's a sample: + + Main smail : main.c map.c resolve.c deliver.c + misc.c parser.y alias.c pw.c headers.c + scanner.l getpath.c str.c ; + + This builds "smail" from a dozen source files. Jam handles + header file dependencies automatically and on-the-fly. + + -> Jam is very portable: it runs on UNIX, VMS, Mac, and NT. + Most Jamfiles themselves are portable, like the sample above. + + -> Jam is unintrusive: it is small, it has negligible CPU + overhead, and it doesn't create any of its own funny files + (c.f. Odin, nmake, SunOS make). + + -> Jam can build large projects spread across many directories + in one pass, without recursing, tracking the relationships + among all files. Jam can do this with multiple, concurrent + processes. + + -> Jam isn't under the blinkin GNU copyright, so you can + incorporate it into commercial products. + + +INFORMATION GUIDE + + Jam.html jam and language reference. + + Jambase.html Reference for the Jambase boilerplate file. + + Jamfile.html Easy reading on creating a Jamfile and using jam. + + RELNOTES Release 2.3 release notes. + + Porting Notes on porting jam to wildcat platforms. + + README This file. Includes installation instructions. + + jam.c Contains the jam command's main() as well as an + introduction to the code, for serious hackers. + + +INSTALLING + + The Makefile (UNIX, NT), build.com (VMS), Build.mpw (Mac MPW) are + for bootstrapping. Once jam is built, it can rebuild itself. + + UNIX + + Build jam with make(1) on: + + Platform $(OS) + ------------------------- + AIX AIX * + BSD/386 1.0 BSDI + COHERENT/386 COHERENT + DGUX 5.4 DGUX + FreeBSD FREEBSD + HPUX 9.0 HPUX + IRIX 5.0 IRIX + Linux LINUX + NEXTSTEP 3.2 NEXT + OSF/1 OSF + PTX V2.1.0 PTX + Solaris 2 SOLARIS * + SunOS4.1 SUNOS + Ultrix 4.2 ULTRIX + BeOS BEOS * + + * requires editing Makefile + + Windows + + Build jam with nmake on: + + Platform $(OS) + ------------------------- + NT NT * + OS/2 OS2 * + + The NT MAXLINE (command line length) is still set in jam.h to + 996, which was apparently the NT 3.5 limit. On 4.0, the limit + is somewhere around 10K. For now, you can increase MAXLINE in + jam.h so that a jam running on 4.0 will use the full command + line length, but that jam.exe will fail miserably on the older OS. + + On NT, a variable must be set before invoking jam to tell + it where the C compiler lives. The name of this variable + depends on which compiler you are using: + + BCCROOT: The Borland C compiler + MSVCNT: The Microsoft Compiler 5.0 (for NT) + MSVC: The Microsoft Compiler 1.5 (for Windows) + + Only MSVCNT has really been tested and is known to work. + + Macintosh + + Build jam with Build.mpw on: + + Platform $(OS) + ------------------------- + Macintosh MAC + + You'll need to edit Build.mpw to set CW. + + VMS + + Build jam with @build.com on: + + Platform $(OS) + ------------------------- + VMS 5.4 VMS + OPENVMS OPENVMS + +Comments to the author! + +November, 1993 - release 1.0 +March, 1995 - release 2.0 +February, 1996 - release 2.1 +November, 1997 - release 2.2 +December, 2000 - release 2.3 +March, 2002 - release 2.4 + + +Christopher Seiwald + +seiwald@perforce.com diff --git a/src/tools/jam/RELNOTES b/src/tools/jam/RELNOTES new file mode 100644 index 0000000000..c5743caab8 --- /dev/null +++ b/src/tools/jam/RELNOTES @@ -0,0 +1,924 @@ +Release notes for Jam/MR 2.4 +(aka Jam - make(1) redux) + +1. Release info: + + Jam/MR 2.4 + March, 21, 2002 + VERSION 2.4 + +2. Compatibility + + Jam 2.4 is upward compatible with Jam 2.3 + + The Jam 2.4 language is a superset of the 2.3 language; + Jamfiles, Jambase, and other rulesets used in 2.3 can be used + with the 2.4 language support. + +3. Changes since 2.3. + +3.1. Changes to Jam Language + + The mechanism for calling rules that return values - "[ rule + args ...]", (and 'return' in the rule body), is now a + documented part of the language. + + Add "on ..." syntax, to invoke a + rule under the influence of a target's specific variables. + + Add "[ on targ rule ... ]" to call a rule returning a value, + under the influence of a target's specific variables. + + New 'Glob' builtin that returns a list of files, given a list + of directories, and a list of filename patterns. + + New 'while expr { block }' construct. + + New :E=value modifier provides default value if variable unset. + + New :J=joinval modifier concatenates list elements into single + element, separated by joinval. + + \ can now be used to escape a space (or any single whitespace + character), so that you don't have to resort to quotes. + + New 'Match regexp : string' rule matches regexp against string + and returns list of results. + + Rules can now be invoked indirectly, through variable names. + If the variable expands to an empty list, no rule is run. + If the variable expands to multiple entries, each rule is + run with the same arguments. The result of the rule invocation + is the concatenation of the results of the rules invoked. + + 'Echo' and 'Exit' now have aliases 'echo' and 'exit', since it + is really hard to tell that these are built-in rules and not + part of the language, like 'include'. Real rules continue to + start with a capital. + +3.2. Jambase Changes + + Support for YACCGEN, the suffix used on generated yacc output. + + Fix ups to have jam and p4 build with borland C 5.5, + and minor win98 jam support for jam clean + + SubDirHdrs now takes directory names in the same format as + SubInclude : one directory element per word. + + More portable support for specifying includes and #defines: + New ASHDRS, CCHDRS, CCDEFS, DEFINES, ObjectDefines, FQuote, + FIncludes, FDefines. Ordering of cc and c++ flags grossly + rearranged. + + Jambase has been compacted by applying the new E: and J: + expansion modifiers. + + New SoftLink rule, courtesy of David Lindes. It currently + assumes you can pass a -s flag to $(LN). + +3.3 'jam' Changes (See Jam.html) + + Added '-q' (quit quick) option; jam will exit promptly (as if it + received an interrupt), as soon as any target fails. + + Added experimental '-g' (build newest sources first) option: + all things being equal, normally targets are simply built in + the order they appear in the Jamfiles. With this flag, targets + with the newest sources are built first. From an idea by Arnt + Gulbrandsen. Undocumented (outside this note). + +3.4. Jam internal code changes + + jamgram.yy now defines YYMAXDEPTH to 10000, what it is on + FreeBSD, for older yaccs that left it at 150 or so. This is + needed for the right-recursion now used in the grammar. + + Optimize rule compilation, with right-recursion instead of left. + + Split jam's built-in rules out to builtins.c from compile.c, + so that compile.c only deals with the language. + + Split jam's pathsys.h from filesys.h, since they are really + two different pieces. + + evaluate_if(), which evaluated the condition tree for 'if' and + returned an int, has been replaced with compile_eval(), which does + essentially the same but returns a LIST. + +4. Fixed bugs + + Missing TEMPORARY targets with multiple parents no longer spoil one + parent's time with another. The parents' time is used for comparison + with dependents, but no longer taken on as the target's own time. + + 'actions updated', not 'actions together', now protects targets + from being deleted on failed/interrupted updates. + + Fixed broken $(v[1-]), which always returned an empty expansion. + Thanks to Ian Godin . + + Defining a rule within another rule, and invoking the enclosing + rule more than once, would result in giving the first rule a + null definition. Fixed. + + $(d:P) now works properly on the mac, climbing up directories. + Thanks to Miklos Fazekas . + + No longer (sometimes) treat \ as a directory separator on + UNIX. It isn't supposed to be, but was due to bungled ifdefs. + + Applying just :U or :D (or :E, :J) mods no longer causes the + variable value to be treated as a filename (parsed and rebuilt + using the OS specific pathsys routines). Previously, if _any_ + mods were present then the value was parsed and rebuilt as if + a filename, and that could in certain cases munge the value. + Only the file modifiers (:GDBSM) treat the value as a + filename. + + Four rules makeCommon, makeGrist, makeString, makeSubDir from + jam 2.2 missing in 2.3 have been re-added, with apologies to + dtb@cisco.com. + + Return status more likely to be correct when using -d0, now that + targets are could as being built even with no debugging output. + Thanks to Miklos Fazekas . + + yyacc now suffixes all terminals it defines with _t, so that they + don't conflict with other symbols (like RULE with the typedef + in rules.h). Thanks to Michael Allard. + + InstallInto now handles multiple sources properly, rather than + acting as if each installed target depended on all sources to + be installed. $(INSTALLGRIST) is now the default grist for + installed targets, rather than the hardcoded 'installed'. Thanks + to Stephen Goodson. + +5. Porting + + [MACINTOSH] Paths are now downshifted (internally) so as to + handle its case insensitivity. Thanks to Miklos Fazekas + . + + [NT] MS changed the macro for the IA64 Windows NT 64bit + compiler. + + [CYGWIN] Cygwin jam porting: dance around bison and yyacc. + Use bison's -y flag to use yacc's output file naming + conventions, and don't use yyacc on systems whose SUFEXE is + set. + + [VMS] The Jambase itself was not formatting the CCHDRS and + CCDEFS properly: on VMS they can't be appended to, because + multiple /define or /include directives don't work. Instead + now CCHDRS and CCDEFS is reformatted from HDRS and DEFINES + anytime those latter two change. This requires the recent + change to jam to allow access to target-specific variables + when setting other variables. + + [VMS] Remove exception call when file_dirscan() can't, for + some reason, scan a directory. Use a better set of #ifdefs to + determine if we're on a vax, rather than relying on the C + compiler being a specific version: we're able to build with + the C++ compiler now. + + [VMS] Port new jam to run with just cxx compiler. + (The C compiler being a extra-cost item). + + [NT] Add entry for DevStudio when the settings are already in the + system environment. + + [NT] default $(MV) to "move /y" in Jambase. + + [MINGW] Mingw port by Max Blagai. + +=============================================================================== +=============================================================================== + + +Release notes for Jam/MR 2.3 +(aka Jam - make(1) redux) + +0. Bugs fixed since 2.3.1 + + PATCHLEVEL 2 - 3/12/2001 + + NOCARE changed back: it once again does not applies to targets + with sources and/or actions. In 2.3 it was changed to apply to + such targets, but that broke header file builds: files that are + #included get marked with NOCARE, but if they have source or + actions, they still should get built. + +1. Release info: + + Jam/MR 2.3 + November 16, 2000 + VERSION 2.3 + PATCHLEVEL 1 + +2. Compatibility + + Jam 2.3 is upward compatible with Jam 2.2. + + The Jam 2.3 language is a superset of the 2.2 language; + Jamfiles, Jambase, and other rulesets used in 2.2 can be used + with the 2.3 language support. + +3. Changes since 2.2 + +3.1. Changes to Jam Language + + Rules now can have values, which can expanded into a list with + the new "[ rule args ... ]" syntax. A rule's value is the value + of its last statement, though only the following statements have + values: if (value of the leg chosen), switch (ditto), set (value + of the resulting variable), return (its arguments). Note that + 'return' doesn't actually return. This support is EXPERIEMENTAL + and otherwise undocumented. (2.3.1) + + Because of the new way lists are processed, if a rule has no + targets a warning message is no longer issued. + + NOCARE now applies to targets with sources and/or actions, + rather than just those without. + +3.2. Jambase Changes + + The HDRPATTERN variable now allows for leading blanks before + the #include, to keep up with ANSI. By john@nanaon-sha.co.jp + (John Belmonte) (2.2.3). + + HDRPATTERN has been adjusted to avoid mistaking cases like: + + # include /* could be */ + + MkDir now NOUPDATE's $(DOT), so that there are no dependencies + on the current directory's timestamp. By john@nanaon-sha.co.jp + (John Belmonte). + + The old mock functions like makeDirName, which assigned their + results to the variable named as their first argument, have + been replaced with real functions using the new [] synxtax. + E.g. "makeDirName foo : bar ola" is now "foo = [ fDirName bar ]" + + Install now always does a cp/chmod/etc, rather than using + the system's install(1), which invariably seems broken. + +3.3. Jam internal code changes + + $JAMUNAME is set on UNIX. (2.2.4). + + Jam ANSI-fied (2.3.0). + + jam.h now defines a bunch of symbols used by the other source + files, so as minimize compiler- and platform-specific ifdefs. + + OSVER is no longer set by jam.h (it was only set for AIX). + Jam does not depend on this variable at all, except to set + $(OSFULL), which is used to determine jam's build directory. + If the user needs to distinguish between various revs of + OSs, he must set OSVER in the environment. + +4. Fixed bugs + + Redefining a rule while it was executing could cause jam to + crash. Reference counts are now used to prevent that, thanks + to Matt Armstrong. + + Logic for computing chunk size when executing PIECEMEAL rules + has been reworked to be a little more accurate, without danger + of overflow, at the cost of being a little more compute intensive. + Instead of computing an estimate chunksize in the (now gone) + make1chunk(), make1cmds() now just goes full bore and tries to + use all args. When that fails, it backs off by 10% of the source + args until the command fits. It takes a little bit more compute + time compared to the old logic, but when you're executing actions + to build all of Shinola it's still pretty small in the scheme + of things. + + The NT handle leak in execunix.c has been fixed, thanks to + Gurusamy Sarathy. (2.2.1). + +5. Porting + + Platforms newly supported or updated: + + AmigaOS (with gcc), courtesy of Alain Penders (2.2.2). + + Beos + + CYGWIN 1.1.4, courtesy of John Belmonte . + + IBM AS400 via Visual Age on NT (primitive) + + IBM OS/390 Unix System Services + + Linux SuSE on OS390 + + Linux Mips, ARM + + Lynx + + HPUX 11, IA64 + + Mac OS X Server, courtesy of Jeff_Sickel@sickel.com (2.2.5). + + Mac Rhapsody + + MPE IX 6.0 + + NetBSD + + QNX RTP (QNX 6.0) + + Siemens Sinix + + UNICOS + + VMS 6.2, 7.1 + + Windows NT IA64 + +5.1. NT Porting Notes + + Always create tmp .bat file for actions if JAMSHELL is set. + That way, if JAMSHELL is a .bat file itself, it can handle + single-command actions with more than 9 cmd line args. + + COMSPEC is no longer examined: cmd.exe is always used + instead. Only cmd.exe can execute the Jambase rules anyhow. + + Jam can be built with Borland C++ 5.5. + + OS2 fixes: InstallBin now works. Filenames are now downshifted, + so mixed case works better there, too. file_dirscan() can now scan + the root ("c:\" or "\") directory, which it couldn't handle before. + + var_defines now ignores OS=Windows_NT, because it conflicts + with Jam's setting of OS (to NT). + +5.2. Mac OS 8/9 Notes + + The support for Mac is curious at best. It runs under MPW. + + It requires CodeWarrior Pro 5, but no longer requires GUSI. + + Use Build.mpw to bootstrap the build. + + The Mac specific definitions in the Jambase are not intended + to be of general purpose, but are sufficient to have Jam build + itself. + +=============================================================================== +=============================================================================== + + +Release Notes for Jam 2.2 + +1. Release info: + + Jam 2.2 + October 22, 1997 + VERSION 2.2 + PATCHLEVEL 1 + +2. Compatibility + + Jam 2.2 is a roll-up of 'Jam - make(1) redux' release 2.1+. + Most of the changes described below were available before this, + in the jam.2.1.plus.tar ball. + + The Jam 2.2 language is a superset of the 2.1 language; + Jamfiles, Jambase, and other rulesets used in 2.1 can be used + with the 2.2 language support. + + See 'Jambase Changes', below, to see if your Jamfiles need any + changes to work with the 2.2 Jambase. + + +3. Changes Since 2.1 + + New product name: Jam. (Executable program is still named 'jam'.) + + Documentation rewritten; HTML versions supplied. + + +3.1 Changes to Jam Language + + Rules may now have more fields than just $(<) and $(>). + + Local variables are now supported. + + The expression 'if $(A) in $(B)' is now supported. + + New variable modifiers :U and :L result in uppercased or lowercased + values. + + New variable modifier :P reliably results in parent directory + of either a file or directory. (Previously, :D was used, but on VMS + :D of a directory name is just the directory name.) + + The :S variable modifier now results in the _last_ suffix if a + filename has more than one dot (.) in it. + + New predefined $(JAMDATE) variable is initialized at runtime for + simple date stamping. + + New predefined variables $(OSVER) and $(OSPLAT) are used to + distinguish among operating system versions and hardware platforms, + when possible. + + New 'bind' qualifier on action definitions allows variables + other than $(<) and $(>) to be bound with SEARCH and LOCATE paths. + + Action buffer size is no longer limited by MAXCMD. Instead, each + line in an action is limited by MAXLINE, defined for each OS, and + the entire action size is limited by CMDBUF. + + +3.2 Jambase Changes (See Jamfile.html) + + Jambase has been reworked to incorporate new language features. + + A handful of new utility rules has been added: makeString, + makeDirName, etc. + + New HDRGRIST variable in Jambase allows for headers with the same + name to be distinguished. + + LOCATE_TARGET now has a new flavor, LOCATE_SOURCE, that is used by + rules that generate source files (e.g., Yacc and Lex). + + Header file includes now happen in the proper order. The limit of + 10 include files has been eliminated. + + The old "Install" rule is no longer available. Use InstallBin, + InstallFile, InstallLib, InstallMan, or InstallShell instead. + + +3.3 'jam' Changes (See Jam.html) + + 'jam' can now be built as a stand-alone program, with Jambase + compiled into the executable. An external or alternate Jambase can + still be referenced explicitly with -f. + + On command failure, 'jam' now emits the text of the command that + failed. This is a compromise between the normal -d1 behavior (where + commands were never seen) and -d2 (where commands are always seen). + + 'jam' now exits non-zero if it doesn't have a total success. A parse + error, sources that can't be found, and targets that can't be built + all generate non-zero exit status. + + The debugging levels (-d flags) have been slightly redefined. + + The supplied Jamfile now builds 'jam' into a platform specific + subdirectory. This lets you use the same source directory to + build 'jam' for more than one platform. + + The supplied Jamfile does not rebuild generated source files by + default. (They are supplied with the distribution.) See Jamfile + for more information. + + +4. Fixed Bugs + + The 'include' bug has finally been fixed, so that include + statements take effect exactly when they are executed, + rather than after the current statement block. This also + corrects the problem where an 'include' within an 'if' + block would wind up including the file one token after the + 'if' block's closing brace. Credit goes to Thomas Woods + for suggesting that the parse tree generation and parse + tree execution be paired in their own loop, rather than + having the parser execute the tree directly. + + The setting and extracting of grist has been regularized: + normally, if you set a component of a filename (using the + :DBSMG= modifiers), you are supposed to include the delimiters + that set off the component: that is, you say "$(x:S=.suffix)", + including the ".". But with grist it was inconsistent + between setting and getting: setting grist required no + <>'s, while getting grist included them. Getting grist + continues to return the <>'s, but now setting grist can + either include them (the new way) or not (the old way). + + 'actions together' now suppresses duplicate sources from + showing up in $(>). + + Accessing variables whose names contained ['s (as happens with + MkDir on VMS) wasn't working, because it treated the [ as an + array subscript. Now [ and ] are, like :, handled specially so + that they can appear in variable values. + + The 'if' statement now compares all elements in expressions; + previously, it only compared the first element of each list. + + If a command line in an action is longer than MAXLINE (formerly + MAXCMD), 'jam' now issues an error and exits rather than dumping + core. + + If a Jamfile ended without a trailing newline, jam dumped core. + This has been fixed. + + +5. Porting + + See jam.h for the definitive list of supported platforms. + Since 2.1, support has been added for: + + Macintosh MPW + Alpha VMS + Alpha NT + NT PowerPC + BeOS + MVS OE + UNIXWARE + QNX + SINIX (Nixdorf) + OS/2 + Interactive UNIX (ISC), courtesy of Matthew Newhook + + +5.1 NT Support Fixes + + The NT command executor now handles multiple line actions, by writing + multi-line actions to a batch file and executing that. + + Targets are universally lowercased on NT. (Matthew Newhook) + + Concurrent process support is fully enabled for NT. + (Gurusamy Sarathy ) + + Path handling: Jam now knows that the directory component of "D:\" + is "D:\", just as on unix it knows that the directory component of + "/" is "/". It also now successfully gets the timestamp for "D:\" + or just plain "\". + + +5.2 VMS Support Fixes + + VMS support is much, much better now. The path name manipulation + routines (in pathvms.c) were more or less rewritten, and they now + handle the vagaries of combining directory and file names properly. + + Targets are universally lowercased on VMS. + + Multi-line command blocks on VMS are now executed in a single system() + call rather than separate ones for each line, so that actions can + be DCL scripts. + +=============================================================================== +=============================================================================== + + +Release notes for Jam 2.1. + +1. Release info: + Jam 2.1 + February 1, 1996 + VERSION 2.1 + PATCHLEVEL 0 + +2. Porting + + Linux is now supported. + + FREEBSD is now supported. + + SCO ("M_XENIX") now supported. + + NCR now supported. + + NEXT support from karthy@dannug.dk (Karsten Thygesen) + + DECC support from zinser@axp614.gsi.de (Martin P.J. Zinser) + + I have changes for OS/2, but no way to test them. Volunteers? + I have VMS multiprocess support, but no way to test it. Volunteers? + +2.1. NT Support fixes. + + The NT support is considerably more real than it was in 2.0. + Filent.c had its syntax error corrected, it no longer skips the + first entry when scanning directories, and it handles string + tables in archives (for long object file names). + + The Jambase was changed a bit to support the various C/C++ + compilers on NT, although it has only been thorougly tested + with MSVC20. + + You still need to set MSVCNT or BCCROOT to the root of the + the compiler's directory tree, and you'll get an error if you + don't set it (rather than getting a pile of mysterious errors). + +2.2. Other porting fixes. + + SPLITPATH now set up for UNIX (:), NT (;), VMS (,) + + Jambase support for Solaris works better now: the location of + AR is hardwired to /usr/ccs/bin/ar and it knowns "install" + doesn't take -c. Solaris -- how the mighty have fallen. + + To handle Linux's wacko yacc, jamgram.h is now included after + scan.h so that YYSTYPE is define. + +3. Jambase Changes (see Jamfile.html) + + SubDir now computes the root directory for the source tree, if + the variable naming the root directory isn't set in the environment. + It counts the number of directory elements leading from the root + to the current directory (as passed to SubDir) and uses that many + "../"'s to identify the root. This means that to use SubDir you + no longer have to have anything special set in the environment. + + InstallFile is now an alias for InstallLib. + + 'first' is now dependency of all pseudo-targets (all, files, + exe, lib, shell), so that jamming any of these pseudo-targets + also builds any dependencies of 'first'. + + The File rule definition in the Jambase was missing an &. + + The File rule now calls the Clean rule, so that installed files + get cleaned. + +4. Jam changes (see Jam.html) + + Variables may now be set on the command line with -svar=value. + + Targets marked with NOUPDATE are now immune to the -a (anyhow) + flag. Previously, the MkDir rule would try to recreate directories + that already exist when jam was invoked with -a. + + A new variable, $(JAMVERSION), joins the small list of built-in + variables. It it set to the release of jam, currently "2.1". + + If an actions fails, jam now deletes the target(s). It won't + delete libraries or other targets that are composites. This is + now consistent with jam's behavior on interrupts (it deletes the + targets). + + Jam had a nasty bug when setting multiple variables to the same + value: if the first two variable names were the same, the variable + value got trashed. This also affected "on target" variables if + the first two targets were the same. For example: + + FOO on bar.c bar.c foo.c = a b c ; + + This would mangle the value of FOO for bar.c and foo.c. This has + been fixed. + + Jam would generate bogus numbers when reporting the number of + targets updated after an interrupt. It now is more careful about + counting. + + The debugging flag -d has been extended. In addition to supporting + -dx (turn on debugging for all levels up to x) there is also now + -d+x (turn on debugging at only level x). The default output + level is -d1 (-or d2 if -n is given); this can be turned off with + -d0. The debug levels are listed in jam.1 and jam.h. + + The parsing debug output now uses indenting to indicate when + one rule invokes another. + +=============================================================================== +=============================================================================== + + +Release notes for Jam 2.0. + +1. Release info: + Jam 2.0 + March 10, 1994 + VERSION 2.0 + PATCHLEVEL 5 + +2. Porting + + Windows/NT is now (crudely) supported, courtesy of Brett Taylor + and Laura Wingerd. + + COHERENT/386 is now supported, courtesy of Fred Smith. + + Solaris archive string table for long archive names is now + supported, thanks to Mike Matrigali. + +3. Compatibility + + Jam 2.0 syntax is a superset of Jam 1.0 syntax, and thus it can + interpret a Jam 1.0 Jambase. + + The Jam 2.0 Jambase is a superset of the Jam 1.0 Jambase, and + thus it can include a Jamfile written for Jam 1.0. + +4. Changes from Jam 1.0 to Jam 2.0 + +4.1. Documentation changes + + New Jamfile.5 manual page, with lots of examples and easy + reading. It replaces both the old "Examples" file as well as + the old Jambase.5 manual page. + + jam.1 edited by Stephen W. Liddle and Diane Holt. + +4.2. Jambase Changes (see Jamfile.5) + +4.2.1. New rules: + + There are new rules to make handling subdirectories easier: + SubDir, SubInclude, SubDirCcFlags, SubDirHdrs. + + There are new rules to handle file-specific CCFLAGS and HDRS: + ObjectCcFlags and ObjectHdrs. + + Misc new rules: HardLink, InstallShell, MkDir. + + New rule "clean" that deletes exactly what jam has built, and + "uninstall" that deletes exactly what was installed. + + New rules for handling suffixes .s, .f, .cc, .cpp, .C. + +4.2.2. Old rules: + + The InstallBin, Lib, Man, and the new Shell rules now take the + destination directory as the target and the files to be copied + as sources. These rules formerly took the files to be copied + as targets, and used built-in destination directories of + $(BINDIR), $(LIBDIR), $(MANDIR), and $(BINDIR). + + The InstallBin, Lib, Man, and Shell rules use the install(1) + program now, instead of doing their own copying. + + The Cc rule now uses -o when possible, rather than moving the + result. Some platforms (Pyramid?) have a broken -o. + + Jambase rules taking libraries, objects, and executables now + all ignore the suffixes provided and use the one defined in the + Jambase for the platform. + + Stupid yyacc support moved out of Jambase, as jam is its only + likely user. + + Jambase now purturbs library sources with a "grist" of + SOURCE_GRIST. + +4.2.3. Misc: + + The names of the default rules defined in Jambase have been + lowercased and un-abbreviated, to be more imake(1) like. + + The Jambase has been reorganized and sorted, with VMS and NT + support moved in from their own files. + + The Jambase has been relocated on UNIX from /usr/local/lib/jam + to /usr/local/lib. + +4.3. Jam changes (see jam.1) + +4.3.1. Flags: + + New -a (anyhow) flag: means build everything. + + New -j flag: run jobs in parallel. + + Old -t now rebuilds the touched target, rather that just the + target's parents. + + -n now implies -d2, so that you see what's happening. The + debug level can be subsequently overridden. + + New -v to dump version. + +4.3.2. Rules: + + New ALWAYS rule behaves like -t: always builds target. + + New EXIT rule makes it possible to raise a fatal error. + + New LEAVES rule which say target depends only on the update + times of the leaf sources. + + New NOUPDATE rule says built targets only if they don't exist. + + NOTIME has been renamed NOTFILE, to more accurately reflect its + meaning (it says a target is not to be bound to a file). + +4.3.3. Variables: + + New special variable JAMSHELL: argv template for command execution + shell. + + Variables, both normal and target-specific, can have their + value appended with the syntax "var += value" or "var on target + += value". + + "?=" is now synonymous with "default =". + + Imported enviroment variable values are now split at blanks + (:'s if the variable name ends in PATH), so that they become + proper list values. + +4.3.4. Misc: + + Files to be sourced with "include" are now bound first, so + $(SEARCH) and $(LOCATE) affect them. They still can't be + built, though. + + New modifier on "actions": "existing" causes $(>) to expand + only those files that currently exist. + +4.3.5. Bug fixes: + + When scanning tokens known to be argument lists (such as the + arguments to rule invocations and variable assignment), the + parser now tells the scanner to ignore alphabetic keywords, as + all such lists terminate with punctuation keywords (like : or + ;). This way, alphabetic keywords don't need to be quoted when + they appear as arguments. + + The scanner has been fixed to handle oversized tokens, + unterminated quotes, unterminated action blocks, and tokens + abutting EOF (i.e. a token with no white space before EOF). + + The progress report "...on xth target..." used to count all + targets, rather than just those with updating actions. Since + the original pronouncement of targets to be udpated included + only those with updating actions, the progress report has been + changed to match. + + 'If' conditionals now must be single arguments. Previously, + they could be zero or more arguments, which didn't make much + sense, and made things like 'foo == bar' true. The comparison + operator is '=', and '==' just looked like the second of three + arguments in the unary "non-empty argument list" conditional. + + Header files indirectly including themselves were mistakenly + reported as being dependent on themselves. Recursing through + header file dependencies is now done after determining the fate + of the target. + + The variable expansion support was expanding $(X)$(UNDEF) as if + it were $(X). It now expands to an empty list, like it + should. + + The UNIX version of file_build() didn't handle "dir/.suffix" + right. Now it does. + + The VMS command buffer was assumed to be as large as 1024 bytes, + which isn't the case everywhere as it is related to some weird + quota. It has been lowered to 256. + + $(>) and $(<) wouldn't expand in action blocks if the targets + were marked with NOTIME. Now they expand properly. + + Malloc() return values are now checked. + + The variable expansion routine var_expand() is now a little + faster, by taking a few often needed shortcuts. + + The VMS version of file_build() used the wrong length when + re-rooting file names that already had directory compoents. + This was fixed. + + Various tracing adjustments were made. + +5. Limitations/Known Bugs + + The new Windows/NT support has only been marginally tested. It + is dependent on certain variables being set depending on which + compiler you are using. You'll need to look in the file + Jambase and see what variables are expected to be set. + + The VMS support has been tested, courtesy of the DEC guest + machine, but has not been hammered fully in release 2.0. It + was used quite a bit in Jam 1.0. + + Jam clean when there is nothing to clean claims it is updating + a target. + + Because the include statement works by pushing a new file in + the input stream of the scanner rather than recursively + invoking the parser on the new file, multiple include + statements in a rule's procedure causes the files to be + included in reverse order. + + If the include statement appears inside an if block, the + parser's attempt to find the else will cause the text of the + included file to appear after the first token following the + statement block. This is rarely what is intended. + + In a rule's actions, only $(<) and $(>) refer to the bound file + names: all other variable references get the unbound names. + This is a pain for $(NEEDLIBS), because it means that library + path can't be bound using $(SEARCH) and $(LOCATE). + + With the -j flag, errors from failed commands can get + staggeringly mixed up. Also, because targets tend to get built + in a quickest-first ordering, dependency information must be + quite exact. Finally, beware of parallelizing commands that + drop fixed-named files into the current directory, like yacc(1) + does. + + A poorly set $(JAMSHELL) is likely to result in silent + failure. diff --git a/src/tools/jam/builtins.c b/src/tools/jam/builtins.c new file mode 100644 index 0000000000..b40e9ce753 --- /dev/null +++ b/src/tools/jam/builtins.c @@ -0,0 +1,293 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" + +# include "lists.h" +# include "parse.h" +# include "builtins.h" +# include "rules.h" +# include "filesys.h" +# include "newstr.h" +# include "regexp.h" +# include "pathsys.h" + +/* + * builtins.c - builtin jam rules + * + * External routines: + * + * load_builtin() - define builtin rules + * + * Internal routines: + * + * builtin_depends() - DEPENDS/INCLUDES rule + * builtin_echo() - ECHO rule + * builtin_exit() - EXIT rule + * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule + * builtin_glob() - GLOB rule + * builtin_match() - MATCH rule + * + * 01/10/01 (seiwald) - split from compile.c + */ + +/* + * compile_builtin() - define builtin rules + */ + +# define P0 (PARSE *)0 +# define C0 (char *)0 + +LIST *builtin_depends( PARSE *parse, LOL *args ); +LIST *builtin_echo( PARSE *parse, LOL *args ); +LIST *builtin_exit( PARSE *parse, LOL *args ); +LIST *builtin_flags( PARSE *parse, LOL *args ); +LIST *builtin_glob( PARSE *parse, LOL *args ); +LIST *builtin_match( PARSE *parse, LOL *args ); + +int glob( char *s, char *c ); + +void +load_builtins() +{ + bindrule( "Always" )->procedure = + bindrule( "ALWAYS" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED ); + + bindrule( "Depends" )->procedure = + bindrule( "DEPENDS" )->procedure = + parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_DEPENDS ); + + bindrule( "echo" )->procedure = + bindrule( "Echo" )->procedure = + bindrule( "ECHO" )->procedure = + parse_make( builtin_echo, P0, P0, P0, C0, C0, 0 ); + + bindrule( "exit" )->procedure = + bindrule( "Exit" )->procedure = + bindrule( "EXIT" )->procedure = + parse_make( builtin_exit, P0, P0, P0, C0, C0, 0 ); + + bindrule( "Glob" )->procedure = + bindrule( "GLOB" )->procedure = + parse_make( builtin_glob, P0, P0, P0, C0, C0, 0 ); + + bindrule( "Includes" )->procedure = + bindrule( "INCLUDES" )->procedure = + parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_INCLUDES ); + + bindrule( "Leaves" )->procedure = + bindrule( "LEAVES" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES ); + + bindrule( "Match" )->procedure = + bindrule( "MATCH" )->procedure = + parse_make( builtin_match, P0, P0, P0, C0, C0, 0 ); + + bindrule( "NoCare" )->procedure = + bindrule( "NOCARE" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE ); + + bindrule( "NOTIME" )->procedure = + bindrule( "NotFile" )->procedure = + bindrule( "NOTFILE" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE ); + + bindrule( "NoUpdate" )->procedure = + bindrule( "NOUPDATE" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE ); + + bindrule( "Temporary" )->procedure = + bindrule( "TEMPORARY" )->procedure = + parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP ); +} + +/* + * builtin_depends() - DEPENDS/INCLUDES rule + * + * The DEPENDS builtin rule appends each of the listed sources on the + * dependency list of each of the listed targets. It binds both the + * targets and sources as TARGETs. + */ + +LIST * +builtin_depends( + PARSE *parse, + LOL *args ) +{ + LIST *targets = lol_get( args, 0 ); + LIST *sources = lol_get( args, 1 ); + int which = parse->num; + LIST *l; + + for( l = targets; l; l = list_next( l ) ) + { + TARGET *t = bindtarget( l->string ); + t->deps[ which ] = targetlist( t->deps[ which ], sources ); + } + + return L0; +} + +/* + * builtin_echo() - ECHO rule + * + * The ECHO builtin rule echoes the targets to the user. No other + * actions are taken. + */ + +LIST * +builtin_echo( + PARSE *parse, + LOL *args ) +{ + list_print( lol_get( args, 0 ) ); + printf( "\n" ); + return L0; +} + +/* + * builtin_exit() - EXIT rule + * + * The EXIT builtin rule echoes the targets to the user and exits + * the program with a failure status. + */ + +LIST * +builtin_exit( + PARSE *parse, + LOL *args ) +{ + list_print( lol_get( args, 0 ) ); + printf( "\n" ); + exit( EXITBAD ); /* yeech */ + return L0; +} + +/* + * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule + * + * Builtin_flags() marks the target with the appropriate flag, for use + * by make0(). It binds each target as a TARGET. + */ + +LIST * +builtin_flags( + PARSE *parse, + LOL *args ) +{ + LIST *l = lol_get( args, 0 ); + + for( ; l; l = list_next( l ) ) + bindtarget( l->string )->flags |= parse->num; + + return L0; +} + +/* + * builtin_globbing() - GLOB rule + */ + +struct globbing { + LIST *patterns; + LIST *results; +} ; + +static void +builtin_glob_back( + void *closure, + char *file, + int status, + time_t time ) +{ + struct globbing *globbing = (struct globbing *)closure; + LIST *l; + PATHNAME f; + char buf[ MAXJPATH ]; + + /* Null out directory for matching. */ + /* We wish we had file_dirscan() pass up a PATHNAME. */ + + path_parse( file, &f ); + f.f_dir.len = 0; + path_build( &f, buf, 0 ); + + for( l = globbing->patterns; l; l = l->next ) + if( !glob( l->string, buf ) ) + { + globbing->results = list_new( globbing->results, newstr( file ) ); + break; + } +} + +LIST * +builtin_glob( + PARSE *parse, + LOL *args ) +{ + LIST *l = lol_get( args, 0 ); + LIST *r = lol_get( args, 1 ); + + struct globbing globbing; + + globbing.results = L0; + globbing.patterns = r; + + for( ; l; l = list_next( l ) ) + file_dirscan( l->string, builtin_glob_back, &globbing ); + + return globbing.results; +} + +/* + * builtin_match() - MATCH rule, regexp matching + */ + +LIST * +builtin_match( + PARSE *parse, + LOL *args ) +{ + LIST *l, *r; + LIST *result = 0; + + /* For each pattern */ + + for( l = lol_get( args, 0 ); l; l = l->next ) + { + regexp *re = regcomp( l->string ); + + /* For each string to match against */ + + for( r = lol_get( args, 1 ); r; r = r->next ) + if( regexec( re, r->string ) ) + { + int i, top; + + /* Find highest parameter */ + + for( top = NSUBEXP; top-- > 1; ) + if( re->startp[top] ) + break; + + /* And add all parameters up to highest onto list. */ + /* Must have parameters to have results! */ + + for( i = 1; i <= top; i++ ) + { + char buf[ MAXSYM ]; + int l = re->endp[i] - re->startp[i]; + memcpy( buf, re->startp[i], l ); + buf[ l ] = 0; + result = list_new( result, newstr( buf ) ); + } + } + + free( (char *)re ); + } + + return result; +} diff --git a/src/tools/jam/builtins.h b/src/tools/jam/builtins.h new file mode 100644 index 0000000000..e4320b7b18 --- /dev/null +++ b/src/tools/jam/builtins.h @@ -0,0 +1,12 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * builtins.h - compile parsed jam statements + */ + +void load_builtins(); + diff --git a/src/tools/jam/command.c b/src/tools/jam/command.c new file mode 100644 index 0000000000..60c3eb20a2 --- /dev/null +++ b/src/tools/jam/command.c @@ -0,0 +1,63 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * command.c - maintain lists of commands + */ + +# include "jam.h" + +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "rules.h" + +# include "command.h" + +/* + * cmd_new() - return a new CMD or 0 if too many args + */ + +CMD * +cmd_new( + RULE *rule, + LIST *targets, + LIST *sources, + LIST *shell ) +{ + CMD *cmd = (CMD *)malloc( sizeof( CMD ) ); + + cmd->rule = rule; + cmd->shell = shell; + cmd->next = 0; + + lol_init( &cmd->args ); + lol_add( &cmd->args, targets ); + lol_add( &cmd->args, sources ); + + /* Bail if the result won't fit in MAXLINE */ + /* We don't free targets/sources/shell if bailing. */ + + if( var_string( rule->actions, cmd->buf, MAXLINE, &cmd->args ) < 0 ) + { + cmd_free( cmd ); + return 0; + } + + return cmd; +} + +/* + * cmd_free() - free a CMD + */ + +void +cmd_free( CMD *cmd ) +{ + lol_free( &cmd->args ); + list_free( cmd->shell ); + free( (char *)cmd ); +} diff --git a/src/tools/jam/command.h b/src/tools/jam/command.h new file mode 100644 index 0000000000..9044cb4ea4 --- /dev/null +++ b/src/tools/jam/command.h @@ -0,0 +1,58 @@ +/* + * Copyright 1994 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * command.h - the CMD structure and routines to manipulate them + * + * Both ACTION and CMD contain a rule, targets, and sources. An + * ACTION describes a rule to be applied to the given targets and + * sources; a CMD is what actually gets executed by the shell. The + * differences are due to: + * + * ACTIONS must be combined if 'actions together' is given. + * ACTIONS must be split if 'actions piecemeal' is given. + * ACTIONS must have current sources omitted for 'actions updated'. + * + * The CMD datatype holds a single command that is to be executed + * against a target, and they can chain together to represent the + * full collection of commands used to update a target. + * + * Structures: + * + * CMD - an action, ready to be formatted into a buffer and executed + * + * External routines: + * + * cmd_new() - return a new CMD or 0 if too many args + * cmd_free() - delete CMD and its parts + * cmd_next() - walk the CMD chain + */ + +/* + * CMD - an action, ready to be formatted into a buffer and executed + */ + +typedef struct _cmd CMD; + +struct _cmd +{ + CMD *next; + CMD *tail; /* valid on in head */ + RULE *rule; /* rule->actions contains shell script */ + LIST *shell; /* $(SHELL) value */ + LOL args; /* LISTs for $(<), $(>) */ + char buf[ MAXLINE ]; /* actual commands */ +} ; + +CMD *cmd_new( + RULE *rule, /* rule (referenced) */ + LIST *targets, /* $(<) (freed) */ + LIST *sources, /* $(>) (freed) */ + LIST *shell ); /* $(SHELL) (freed) */ + +void cmd_free( CMD *cmd ); + +# define cmd_next( c ) ((c)->next) diff --git a/src/tools/jam/compile.c b/src/tools/jam/compile.c new file mode 100644 index 0000000000..c0096111e2 --- /dev/null +++ b/src/tools/jam/compile.c @@ -0,0 +1,808 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" + +# include "lists.h" +# include "parse.h" +# include "compile.h" +# include "variable.h" +# include "expand.h" +# include "rules.h" +# include "newstr.h" +# include "search.h" + +/* + * compile.c - compile parsed jam statements + * + * External routines: + * + * compile_append() - append list results of two statements + * compile_eval() - evaluate if to determine which leg to compile + * compile_foreach() - compile the "for x in y" statement + * compile_if() - compile 'if' rule + * compile_include() - support for 'include' - call include() on file + * compile_list() - expand and return a list + * compile_local() - declare (and set) local variables + * compile_null() - do nothing -- a stub for parsing + * compile_on() - run rule under influence of on-target variables + * compile_rule() - compile a single user defined rule + * compile_rules() - compile a chain of rules + * compile_set() - compile the "set variable" statement + * compile_setcomp() - support for `rule` - save parse tree + * compile_setexec() - support for `actions` - save execution string + * compile_settings() - compile the "on =" (set variable on exec) statement + * compile_switch() - compile 'switch' rule + * + * Internal routines: + * + * debug_compile() - printf with indent to show rule expansion. + * evaluate_rule() - execute a rule invocation + * + * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of + * the awkward sounding "settings". + * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 05/13/94 (seiwald) - include files are now bound as targets, and thus + * can make use of $(SEARCH) + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/22/95 (seiwald) - Exit rule. + * 02/02/95 (seiwald) - Always rule; LEAVES rule. + * 02/14/95 (seiwald) - NoUpdate rule. + * 09/11/00 (seiwald) - new evaluate_rule() for headers(). + * 09/11/00 (seiwald) - compile_xxx() now return LIST *. + * New compile_append() and compile_list() in + * support of building lists here, rather than + * in jamgram.yy. + * 01/10/00 (seiwald) - built-ins split out to builtin.c. + */ + +static void debug_compile( int which, char *s ); +int glob( char *s, char *c ); + + + +/* + * compile_append() - append list results of two statements + * + * parse->left more compile_append() by left-recursion + * parse->right single rule + */ + +LIST * +compile_append( + PARSE *parse, + LOL *args ) +{ + /* Append right to left. */ + + return list_append( + (*parse->left->func)( parse->left, args ), + (*parse->right->func)( parse->right, args ) ); +} + +/* + * compile_eval() - evaluate if to determine which leg to compile + * + * Returns: + * list if expression true - compile 'then' clause + * L0 if expression false - compile 'else' clause + */ + +static int +lcmp( LIST *t, LIST *s ) +{ + int status = 0; + + while( !status && ( t || s ) ) + { + char *st = t ? t->string : ""; + char *ss = s ? s->string : ""; + + status = strcmp( st, ss ); + + t = t ? list_next( t ) : t; + s = s ? list_next( s ) : s; + } + + return status; +} + +LIST * +compile_eval( + PARSE *parse, + LOL *args ) +{ + LIST *ll, *lr, *s, *t; + int status = 0; + + /* Short circuit lr eval for &&, ||, and 'in' */ + + ll = (*parse->left->func)( parse->left, args ); + lr = 0; + + switch( parse->num ) + { + case EXPR_AND: + case EXPR_IN: if( ll ) goto eval; break; + case EXPR_OR: if( !ll ) goto eval; break; + default: eval: lr = (*parse->right->func)( parse->right, args ); + } + + /* Now eval */ + + switch( parse->num ) + { + case EXPR_NOT: + if( !ll ) status = 1; + break; + + case EXPR_AND: + if( ll && lr ) status = 1; + break; + + case EXPR_OR: + if( ll || lr ) status = 1; + break; + + case EXPR_IN: + /* "a in b": make sure each of */ + /* ll is equal to something in lr. */ + + for( t = ll; t; t = list_next( t ) ) + { + for( s = lr; s; s = list_next( s ) ) + if( !strcmp( t->string, s->string ) ) + break; + if( !s ) break; + } + + /* No more ll? Success */ + + if( !t ) status = 1; + + break; + + case EXPR_EXISTS: if( lcmp( ll, L0 ) != 0 ) status = 1; break; + case EXPR_EQUALS: if( lcmp( ll, lr ) == 0 ) status = 1; break; + case EXPR_NOTEQ: if( lcmp( ll, lr ) != 0 ) status = 1; break; + case EXPR_LESS: if( lcmp( ll, lr ) < 0 ) status = 1; break; + case EXPR_LESSEQ: if( lcmp( ll, lr ) <= 0 ) status = 1; break; + case EXPR_MORE: if( lcmp( ll, lr ) > 0 ) status = 1; break; + case EXPR_MOREEQ: if( lcmp( ll, lr ) >= 0 ) status = 1; break; + + } + + if( DEBUG_IF ) + { + debug_compile( 0, "if" ); + list_print( ll ); + printf( "(%d) ", status ); + list_print( lr ); + printf( "\n" ); + } + + /* Find something to return. */ + /* In odd circumstances (like "" = "") */ + /* we'll have to return a new string. */ + + if( !status ) t = 0; + else if( ll ) t = ll, ll = 0; + else if( lr ) t = lr, lr = 0; + else t = list_new( L0, newstr( "1" ) ); + + if( ll ) list_free( ll ); + if( lr ) list_free( lr ); + return t; +} + +/* + * compile_foreach() - compile the "for x in y" statement + * + * Compile_foreach() resets the given variable name to each specified + * value, executing the commands enclosed in braces for each iteration. + * + * parse->string index variable + * parse->left variable values + * parse->right rule to compile + */ + +LIST * +compile_foreach( + PARSE *parse, + LOL *args ) +{ + LIST *nv = (*parse->left->func)( parse->left, args ); + LIST *l; + + /* Call var_set to reset $(parse->string) for each val. */ + + for( l = nv; l; l = list_next( l ) ) + { + LIST *val = list_new( L0, copystr( l->string ) ); + + var_set( parse->string, val, VAR_SET ); + + list_free( (*parse->right->func)( parse->right, args ) ); + } + + list_free( nv ); + + return L0; +} + +/* + * compile_if() - compile 'if' rule + * + * parse->left condition tree + * parse->right then tree + * parse->third else tree + */ + +LIST * +compile_if( + PARSE *p, + LOL *args ) +{ + LIST *l = (*p->left->func)( p->left, args ); + + if( l ) + { + list_free( l ); + return (*p->right->func)( p->right, args ); + } + else + { + return (*p->third->func)( p->third, args ); + } +} + +/* + * compile_include() - support for 'include' - call include() on file + * + * parse->left list of files to include (can only do 1) + */ + +LIST * +compile_include( + PARSE *parse, + LOL *args ) +{ + LIST *nt = (*parse->left->func)( parse->left, args ); + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "include" ); + list_print( nt ); + printf( "\n" ); + } + + if( nt ) + { + TARGET *t = bindtarget( nt->string ); + + /* Bind the include file under the influence of */ + /* "on-target" variables. Though they are targets, */ + /* include files are not built with make(). */ + + pushsettings( t->settings ); + t->boundname = search( t->name, &t->time ); + popsettings( t->settings ); + + parse_file( t->boundname ); + } + + list_free( nt ); + + return L0; +} + +/* + * compile_list() - expand and return a list + * + * parse->string - character string to expand + */ + +LIST * +compile_list( + PARSE *parse, + LOL *args ) +{ + /* voodoo 1 means: s is a copyable string */ + char *s = parse->string; + return var_expand( L0, s, s + strlen( s ), args, 1 ); +} + +/* + * compile_local() - declare (and set) local variables + * + * parse->left list of variables + * parse->right list of values + * parse->third rules to execute + */ + +LIST * +compile_local( + PARSE *parse, + LOL *args ) +{ + LIST *l; + SETTINGS *s = 0; + LIST *nt = (*parse->left->func)( parse->left, args ); + LIST *ns = (*parse->right->func)( parse->right, args ); + LIST *result; + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "local" ); + list_print( nt ); + printf( " = " ); + list_print( ns ); + printf( "\n" ); + } + + /* Initial value is ns */ + + for( l = nt; l; l = list_next( l ) ) + s = addsettings( s, 0, l->string, list_copy( (LIST*)0, ns ) ); + + list_free( ns ); + list_free( nt ); + + /* Note that callees of the current context get this "local" */ + /* variable, making it not so much local as layered. */ + + pushsettings( s ); + result = (*parse->third->func)( parse->third, args ); + popsettings( s ); + + freesettings( s ); + + return result; +} + +/* + * compile_null() - do nothing -- a stub for parsing + */ + +LIST * +compile_null( + PARSE *parse, + LOL *args ) +{ + return L0; +} + +/* + * compile_on() - run rule under influence of on-target variables + * + * parse->left list of files to include (can only do 1) + * parse->right rule to run + * + * EXPERIMENTAL! + */ + +LIST * +compile_on( + PARSE *parse, + LOL *args ) +{ + LIST *nt = (*parse->left->func)( parse->left, args ); + LIST *result = 0; + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "on" ); + list_print( nt ); + printf( "\n" ); + } + + if( nt ) + { + TARGET *t = bindtarget( nt->string ); + + pushsettings( t->settings ); + result = (*parse->right->func)( parse->right, args ); + t->boundname = search( t->name, &t->time ); + popsettings( t->settings ); + + } + + list_free( nt ); + + return result; +} + + +/* + * compile_rule() - compile a single user defined rule + * + * parse->left list of rules to run + * parse->right parameters (list of lists) to rule, recursing left + * + * Wrapped around evaluate_rule() so that headers() can share it. + */ + +LIST * +compile_rule( + PARSE *parse, + LOL *args ) +{ + LOL nargs[1]; + LIST *result = 0; + LIST *ll, *l; + PARSE *p; + + /* list of rules to run -- normally 1! */ + + ll = (*parse->left->func)( parse->left, args ); + + /* Build up the list of arg lists */ + + lol_init( nargs ); + + for( p = parse->right; p; p = p->left ) + lol_add( nargs, (*p->right->func)( p->right, args ) ); + + /* Run rules, appending results from each */ + + for( l = ll; l; l = list_next( l ) ) + result = evaluate_rule( l->string, nargs, result ); + + list_free( ll ); + lol_free( nargs ); + + return result; +} + +/* + * evaluate_rule() - execute a rule invocation + */ + +LIST * +evaluate_rule( + char *rulename, + LOL *args, + LIST *result ) +{ + RULE *rule = bindrule( rulename ); + + if( DEBUG_COMPILE ) + { + debug_compile( 1, rulename ); + lol_print( args ); + printf( "\n" ); + } + + /* Check traditional targets $(<) and sources $(>) */ + + if( !rule->actions && !rule->procedure ) + printf( "warning: unknown rule %s\n", rule->name ); + + /* If this rule will be executed for updating the targets */ + /* then construct the action for make(). */ + + if( rule->actions ) + { + TARGETS *t; + ACTION *action; + + /* The action is associated with this instance of this rule */ + + action = (ACTION *)malloc( sizeof( ACTION ) ); + memset( (char *)action, '\0', sizeof( *action ) ); + + action->rule = rule; + action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) ); + action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) ); + + /* Append this action to the actions of each target */ + + for( t = action->targets; t; t = t->next ) + t->target->actions = actionlist( t->target->actions, action ); + } + + /* Now recursively compile any parse tree associated with this rule */ + /* refer/free to ensure rule not freed during use */ + + if( rule->procedure ) + { + PARSE *parse = rule->procedure; + parse_refer( parse ); + result = list_append( result, (*parse->func)( parse, args ) ); + parse_free( parse ); + } + + if( DEBUG_COMPILE ) + debug_compile( -1, 0 ); + + return result; +} + +/* + * compile_rules() - compile a chain of rules + * + * parse->left single rule + * parse->right more compile_rules() by right-recursion + */ + +LIST * +compile_rules( + PARSE *parse, + LOL *args ) +{ + /* Ignore result from first statement; return the 2nd. */ + /* Optimize recursion on the right by looping. */ + + do list_free( (*parse->left->func)( parse->left, args ) ); + while( (parse = parse->right)->func == compile_rules ); + + return (*parse->func)( parse, args ); +} + +/* + * compile_set() - compile the "set variable" statement + * + * parse->left variable names + * parse->right variable values + * parse->num ASSIGN_SET/APPEND/DEFAULT + */ + +LIST * +compile_set( + PARSE *parse, + LOL *args ) +{ + LIST *nt = (*parse->left->func)( parse->left, args ); + LIST *ns = (*parse->right->func)( parse->right, args ); + LIST *l; + int setflag; + char *trace; + + switch( parse->num ) + { + case ASSIGN_SET: setflag = VAR_SET; trace = "="; break; + case ASSIGN_APPEND: setflag = VAR_APPEND; trace = "+="; break; + case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break; + default: setflag = VAR_SET; trace = ""; break; + } + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "set" ); + list_print( nt ); + printf( " %s ", trace ); + list_print( ns ); + printf( "\n" ); + } + + /* Call var_set to set variable */ + /* var_set keeps ns, so need to copy it */ + + for( l = nt; l; l = list_next( l ) ) + var_set( l->string, list_copy( L0, ns ), setflag ); + + list_free( nt ); + + return ns; +} + +/* + * compile_setcomp() - support for `rule` - save parse tree + * + * parse->string rule name + * parse->left rules for rule + */ + +LIST * +compile_setcomp( + PARSE *parse, + LOL *args ) +{ + RULE *rule = bindrule( parse->string ); + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "rule" ); + printf( " %s\n", parse->string ); + } + + /* Free old one, if present */ + + if( rule->procedure ) + parse_free( rule->procedure ); + + rule->procedure = parse->left; + + /* we now own this parse tree */ + /* don't let parse_free() release it */ + + parse_refer( parse->left ); + + return L0; +} + +/* + * compile_setexec() - support for `actions` - save execution string + * + * parse->string rule name + * parse->string1 OS command string + * parse->num flags + * parse->left `bind` variables + * + * Note that the parse flags (as defined in compile.h) are transfered + * directly to the rule flags (as defined in rules.h). + */ + +LIST * +compile_setexec( + PARSE *parse, + LOL *args ) +{ + RULE *rule = bindrule( parse->string ); + LIST *bindlist = (*parse->left->func)( parse->left, args ); + + /* Free old one, if present */ + + if( rule->actions ) + { + freestr( rule->actions ); + list_free( rule->bindlist ); + } + + rule->actions = copystr( parse->string1 ); + rule->bindlist = bindlist; + rule->flags = parse->num; + + return L0; +} + +/* + * compile_settings() - compile the "on =" (set variable on exec) statement + * + * parse->left variable names + * parse->right target name + * parse->third variable value + * parse->num ASSIGN_SET/APPEND + */ + +LIST * +compile_settings( + PARSE *parse, + LOL *args ) +{ + LIST *nt = (*parse->left->func)( parse->left, args ); + LIST *ns = (*parse->third->func)( parse->third, args ); + LIST *targets = (*parse->right->func)( parse->right, args ); + LIST *ts; + int append = parse->num == ASSIGN_APPEND; + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "set" ); + list_print( nt ); + printf( "on " ); + list_print( targets ); + printf( " %s ", append ? "+=" : "=" ); + list_print( ns ); + printf( "\n" ); + } + + /* Call addsettings to save variable setting */ + /* addsettings keeps ns, so need to copy it */ + /* Pass append flag to addsettings() */ + + for( ts = targets; ts; ts = list_next( ts ) ) + { + TARGET *t = bindtarget( ts->string ); + LIST *l; + + for( l = nt; l; l = list_next( l ) ) + t->settings = addsettings( t->settings, append, + l->string, list_copy( (LIST*)0, ns ) ); + } + + list_free( nt ); + list_free( targets ); + + return ns; +} + +/* + * compile_switch() - compile 'switch' rule + * + * parse->left switch value (only 1st used) + * parse->right cases + * + * cases->left 1st case + * cases->right next cases + * + * case->string argument to match + * case->left parse tree to execute + */ + +LIST * +compile_switch( + PARSE *parse, + LOL *args ) +{ + LIST *nt = (*parse->left->func)( parse->left, args ); + LIST *result = 0; + + if( DEBUG_COMPILE ) + { + debug_compile( 0, "switch" ); + list_print( nt ); + printf( "\n" ); + } + + /* Step through cases */ + + for( parse = parse->right; parse; parse = parse->right ) + { + if( !glob( parse->left->string, nt ? nt->string : "" ) ) + { + /* Get & exec parse tree for this case */ + parse = parse->left->left; + result = (*parse->func)( parse, args ); + break; + } + } + + list_free( nt ); + + return result; +} + +/* + * compile_while() - compile 'while' rule + * + * parse->left condition tree + * parse->right execution tree + */ + +LIST * +compile_while( + PARSE *p, + LOL *args ) +{ + LIST *r = 0; + LIST *l; + + /* Returns the value from the last execution of the block */ + + while( l = (*p->left->func)( p->left, args ) ) + { + list_free( l ); + if( r ) list_free( r ); + r = (*p->right->func)( p->right, args ); + } + + return r; +} + +/* + * debug_compile() - printf with indent to show rule expansion. + */ + +static void +debug_compile( int which, char *s ) +{ + static int level = 0; + static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; + int i = ((1+level) * 2) % 35; + + if( which >= 0 ) + printf( "%*.*s ", i, i, indent ); + + if( s ) + printf( "%s ", s ); + + level += which; +} diff --git a/src/tools/jam/compile.h b/src/tools/jam/compile.h new file mode 100644 index 0000000000..ea8301269a --- /dev/null +++ b/src/tools/jam/compile.h @@ -0,0 +1,52 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * compile.h - compile parsed jam statements + */ + +void compile_builtins(); + +LIST *compile_append( PARSE *parse, LOL *args ); +LIST *compile_foreach( PARSE *parse, LOL *args ); +LIST *compile_if( PARSE *parse, LOL *args ); +LIST *compile_eval( PARSE *parse, LOL *args ); +LIST *compile_include( PARSE *parse, LOL *args ); +LIST *compile_list( PARSE *parse, LOL *args ); +LIST *compile_local( PARSE *parse, LOL *args ); +LIST *compile_null( PARSE *parse, LOL *args ); +LIST *compile_on( PARSE *parse, LOL *args ); +LIST *compile_rule( PARSE *parse, LOL *args ); +LIST *compile_rules( PARSE *parse, LOL *args ); +LIST *compile_set( PARSE *parse, LOL *args ); +LIST *compile_setcomp( PARSE *parse, LOL *args ); +LIST *compile_setexec( PARSE *parse, LOL *args ); +LIST *compile_settings( PARSE *parse, LOL *args ); +LIST *compile_switch( PARSE *parse, LOL *args ); +LIST *compile_while( PARSE *parse, LOL *args ); + +LIST *evaluate_rule( char *rulename, LOL *args, LIST *result ); + +/* Flags for compile_set(), etc */ + +# define ASSIGN_SET 0x00 /* = assign variable */ +# define ASSIGN_APPEND 0x01 /* += append variable */ +# define ASSIGN_DEFAULT 0x02 /* set only if unset */ + +/* Conditions for compile_if() */ + +# define EXPR_NOT 0 /* ! cond */ +# define EXPR_AND 1 /* cond && cond */ +# define EXPR_OR 2 /* cond || cond */ + +# define EXPR_EXISTS 3 /* arg */ +# define EXPR_EQUALS 4 /* arg = arg */ +# define EXPR_NOTEQ 5 /* arg != arg */ +# define EXPR_LESS 6 /* arg < arg */ +# define EXPR_LESSEQ 7 /* arg <= arg */ +# define EXPR_MORE 8 /* arg > arg */ +# define EXPR_MOREEQ 9 /* arg >= arg */ +# define EXPR_IN 10 /* arg in arg */ diff --git a/src/tools/jam/execcmd.h b/src/tools/jam/execcmd.h new file mode 100644 index 0000000000..1e7a608351 --- /dev/null +++ b/src/tools/jam/execcmd.h @@ -0,0 +1,23 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * execcmd.h - execute a shell script + * + * 05/04/94 (seiwald) - async multiprocess interface + */ + +void execcmd( + char *string, + void (*func)( void *closure, int status ), + void *closure, + LIST *shell ); + +int execwait(); + +# define EXEC_CMD_OK 0 +# define EXEC_CMD_FAIL 1 +# define EXEC_CMD_INTR 2 diff --git a/src/tools/jam/execmac.c b/src/tools/jam/execmac.c new file mode 100644 index 0000000000..20217dab19 --- /dev/null +++ b/src/tools/jam/execmac.c @@ -0,0 +1,69 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "execcmd.h" +# include + +# ifdef OS_MAC + +/* + * execunix.c - execute a shell script on UNIX + * + * If $(JAMSHELL) is defined, uses that to formulate execvp(). + * The default is: + * + * /bin/sh -c % + * + * Each word must be an individual element in a jam variable value. + * + * In $(JAMSHELL), % expands to the command string and ! expands to + * the slot number (starting at 1) for multiprocess (-j) invocations. + * If $(JAMSHELL) doesn't include a %, it is tacked on as the last + * argument. + * + * Don't just set JAMSHELL to /bin/sh - it won't work! + * + * External routines: + * execcmd() - launch an async command execution + * execwait() - wait and drive at most one execution completion + * + * Internal routines: + * onintr() - bump intr to note command interruption + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 05/04/94 (seiwald) - async multiprocess interface + * 01/22/95 (seiwald) - $(JAMSHELL) support + */ + +/* + * execcmd() - launch an async command execution + */ + +void +execcmd( + char *string, + void (*func)( void *closure, int status ), + void *closure, + LIST *shell ) +{ + + printf( "%s", string ); + (*func)( closure, EXEC_CMD_OK ); +} + +/* + * execwait() - wait and drive at most one execution completion + */ + +int +execwait() +{ + return 0; +} + +# endif /* OS_MAC */ diff --git a/src/tools/jam/execunix.c b/src/tools/jam/execunix.c new file mode 100644 index 0000000000..0eb03ca3ae --- /dev/null +++ b/src/tools/jam/execunix.c @@ -0,0 +1,370 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "execcmd.h" +# include + +# ifdef USE_EXECUNIX + +# ifdef NO_VFORK +# define vfork() fork() +# endif + +# if defined( OS_NT ) || defined( OS_OS2 ) + +# define USE_EXECNT + +# include + +# if !defined( __BORLANDC__ ) && !defined( OS_OS2 ) +# define wait my_wait +static int my_wait( int *status ); +# endif + +# endif + +/* + * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS + * + * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). + * The default is: + * + * /bin/sh -c % [ on UNIX/AmigaOS ] + * cmd.exe /c % [ on OS2/WinNT ] + * + * Each word must be an individual element in a jam variable value. + * + * In $(JAMSHELL), % expands to the command string and ! expands to + * the slot number (starting at 1) for multiprocess (-j) invocations. + * If $(JAMSHELL) doesn't include a %, it is tacked on as the last + * argument. + * + * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work! + * + * External routines: + * execcmd() - launch an async command execution + * execwait() - wait and drive at most one execution completion + * + * Internal routines: + * onintr() - bump intr to note command interruption + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 05/04/94 (seiwald) - async multiprocess interface + * 01/22/95 (seiwald) - $(JAMSHELL) support + * 06/02/97 (gsar) - full async multiprocess support for Win32 + */ + +static int intr = 0; +static int cmdsrunning = 0; +static void (*istat)( int ); + +static struct +{ + int pid; /* on win32, a real process handle */ + void (*func)( void *closure, int status ); + void *closure; + +# ifdef USE_EXECNT + char *tempfile; +# endif + +} cmdtab[ MAXJOBS ] = {{0}}; + +/* + * onintr() - bump intr to note command interruption + */ + +void +onintr( int disp ) +{ + intr++; + printf( "...interrupted\n" ); +} + +/* + * execcmd() - launch an async command execution + */ + +void +execcmd( + char *string, + void (*func)( void *closure, int status ), + void *closure, + LIST *shell ) +{ + int pid; + int slot; + char *argv[ MAXARGC + 1 ]; /* +1 for NULL */ + +# ifdef USE_EXECNT + char *p; +# endif + + /* Find a slot in the running commands table for this one. */ + + for( slot = 0; slot < MAXJOBS; slot++ ) + if( !cmdtab[ slot ].pid ) + break; + + if( slot == MAXJOBS ) + { + printf( "no slots for child!\n" ); + exit( EXITBAD ); + } + +# ifdef USE_EXECNT + if( !cmdtab[ slot ].tempfile ) + { + char *tempdir; + + if( !( tempdir = getenv( "TEMP" ) ) && + !( tempdir = getenv( "TMP" ) ) ) + tempdir = "\\temp"; + + cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 ); + + sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat", + tempdir, slot ); + } + + /* Trim leading, ending white space */ + + while( isspace( *string ) ) + ++string; + + p = strchr( string, '\n' ); + + while( p && isspace( *p ) ) + ++p; + + /* If multi line, or too long, or JAMSHELL is set, write to bat file. */ + /* Otherwise, exec directly. */ + /* Frankly, if it is a single long line I don't think the */ + /* command interpreter will do any better -- it will fail. */ + + if( p && *p || strlen( string ) > MAXLINE || shell ) + { + FILE *f; + + /* Write command to bat file. */ + + f = fopen( cmdtab[ slot ].tempfile, "w" ); + fputs( string, f ); + fclose( f ); + + string = cmdtab[ slot ].tempfile; + } +# endif + + /* Forumulate argv */ + /* If shell was defined, be prepared for % and ! subs. */ + /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ + + if( shell ) + { + int i; + char jobno[4]; + int gotpercent = 0; + + sprintf( jobno, "%d", slot + 1 ); + + for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) + { + switch( shell->string[0] ) + { + case '%': argv[i] = string; gotpercent++; break; + case '!': argv[i] = jobno; break; + default: argv[i] = shell->string; + } + if( DEBUG_EXECCMD ) + printf( "argv[%d] = '%s'\n", i, argv[i] ); + } + + if( !gotpercent ) + argv[i++] = string; + + argv[i] = 0; + } + else + { +# ifdef USE_EXECNT + argv[0] = "cmd.exe"; + argv[1] = "/Q/C"; /* anything more is non-portable */ +# else + argv[0] = "/bin/sh"; + argv[1] = "-c"; +# endif + argv[2] = string; + argv[3] = 0; + } + + /* Catch interrupts whenever commands are running. */ + + if( !cmdsrunning++ ) + istat = signal( SIGINT, onintr ); + + /* Start the command */ + +# ifdef USE_EXECNT + if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) + { + perror( "spawn" ); + exit( EXITBAD ); + } +# else + if ((pid = vfork()) == 0) + { + execvp( argv[0], argv ); + _exit(127); + } + + if( pid == -1 ) + { + perror( "vfork" ); + exit( EXITBAD ); + } +# endif + /* Save the operation for execwait() to find. */ + + cmdtab[ slot ].pid = pid; + cmdtab[ slot ].func = func; + cmdtab[ slot ].closure = closure; + + /* Wait until we're under the limit of concurrent commands. */ + /* Don't trust globs.jobs alone. */ + + while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) + if( !execwait() ) + break; +} + +/* + * execwait() - wait and drive at most one execution completion + */ + +int +execwait() +{ + int i; + int status, w; + int rstat; + + /* Handle naive make1() which doesn't know if cmds are running. */ + + if( !cmdsrunning ) + return 0; + + /* Pick up process pid and status */ + + while( ( w = wait( &status ) ) == -1 && errno == EINTR ) + ; + + if( w == -1 ) + { + printf( "child process(es) lost!\n" ); + perror("wait"); + exit( EXITBAD ); + } + + /* Find the process in the cmdtab. */ + + for( i = 0; i < MAXJOBS; i++ ) + if( w == cmdtab[ i ].pid ) + break; + + if( i == MAXJOBS ) + { + printf( "waif child found!\n" ); + exit( EXITBAD ); + } + + /* Drive the completion */ + + if( !--cmdsrunning ) + signal( SIGINT, istat ); + + if( intr ) + rstat = EXEC_CMD_INTR; + else if( w == -1 || status != 0 ) + rstat = EXEC_CMD_FAIL; + else + rstat = EXEC_CMD_OK; + + cmdtab[ i ].pid = 0; + + (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat ); + + return 1; +} + +# if defined( OS_NT ) && !defined( __BORLANDC__ ) + +# define WIN32_LEAN_AND_MEAN + +# include /* do the ugly deed */ + +static int +my_wait( int *status ) +{ + int i, num_active = 0; + DWORD exitcode, waitcode; + static HANDLE *active_handles = 0; + + if (!active_handles) + active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) ); + + /* first see if any non-waited-for processes are dead, + * and return if so. + */ + for ( i = 0; i < globs.jobs; i++ ) { + if ( cmdtab[i].pid ) { + if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) { + if ( exitcode == STILL_ACTIVE ) + active_handles[num_active++] = (HANDLE)cmdtab[i].pid; + else { + CloseHandle((HANDLE)cmdtab[i].pid); + *status = (int)((exitcode & 0xff) << 8); + return cmdtab[i].pid; + } + } + else + goto FAILED; + } + } + + /* if a child exists, wait for it to die */ + if ( !num_active ) { + errno = ECHILD; + return -1; + } + waitcode = WaitForMultipleObjects( num_active, + active_handles, + FALSE, + INFINITE ); + if ( waitcode != WAIT_FAILED ) { + if ( waitcode >= WAIT_ABANDONED_0 + && waitcode < WAIT_ABANDONED_0 + num_active ) + i = waitcode - WAIT_ABANDONED_0; + else + i = waitcode - WAIT_OBJECT_0; + if ( GetExitCodeProcess(active_handles[i], &exitcode) ) { + CloseHandle(active_handles[i]); + *status = (int)((exitcode & 0xff) << 8); + return (int)active_handles[i]; + } + } + +FAILED: + errno = GetLastError(); + return -1; + +} + +# endif /* NT && !__BORLANDC__ */ + +# endif /* USE_EXECUNIX */ diff --git a/src/tools/jam/execvms.c b/src/tools/jam/execvms.c new file mode 100644 index 0000000000..0bf5486f2f --- /dev/null +++ b/src/tools/jam/execvms.c @@ -0,0 +1,167 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "execcmd.h" + +# ifdef OS_VMS + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * execvms.c - execute a shell script, ala VMS + * + * The approach is this: + * + * If the command is a single line, and shorter than WRTLEN (what we + * believe to be the maximum line length), we just system() it. + * + * If the command is multi-line, or longer than WRTLEN, we write the + * command block to a temp file, splitting long lines (using "-" at + * the end of the line to indicate contiuation), and then source that + * temp file. We use special logic to make sure we don't continue in + * the middle of a quoted string. + * + * 05/04/94 (seiwald) - async multiprocess interface; noop on VMS + * 12/20/96 (seiwald) - rewritten to handle multi-line commands well + * 01/14/96 (seiwald) - don't put -'s between "'s + */ + +#define WRTLEN 240 + +#define MIN( a, b ) ((a) < (b) ? (a) : (b)) + +/* 1 for the @ and 4 for the .com */ + +char tempnambuf[ L_tmpnam + 1 + 4 ] = {0}; + +void +execcmd( + char *string, + void (*func)( void *closure, int status ), + void *closure, + LIST *shell ) +{ + char *s, *e, *p; + int rstat = EXEC_CMD_OK; + int status; + + /* See if string is more than one line */ + /* discounting leading/trailing white space */ + + for( s = string; *s && isspace( *s ); s++ ) + ; + + e = p = strchr( s, '\n' ); + + while( p && isspace( *p ) ) + ++p; + + /* If multi line or long, write to com file. */ + /* Otherwise, exec directly. */ + + if( p && *p || e - s > WRTLEN ) + { + FILE *f; + + /* Create temp file invocation "@sys$scratch:tempfile.com" */ + + if( !*tempnambuf ) + { + tempnambuf[0] = '@'; + (void)tmpnam( tempnambuf + 1 ); + strcat( tempnambuf, ".com" ); + } + + /* Open tempfile */ + + if( !( f = fopen( tempnambuf + 1, "w" ) ) ) + { + printf( "can't open command file\n" ); + (*func)( closure, EXEC_CMD_FAIL ); + return; + } + + /* For each line of the string */ + + while( *string ) + { + char *s = strchr( string, '\n' ); + int len = s ? s + 1 - string : strlen( string ); + + fputc( '$', f ); + + /* For each chunk of a line that needs to be split */ + + while( len > 0 ) + { + char *q = string; + char *qe = string + MIN( len, WRTLEN ); + char *qq = q; + int quote = 0; + + /* Look for matching "'s */ + + for( ; q < qe; q++ ) + if( *q == '"' && ( quote = !quote ) ) + qq = q; + + /* Back up to opening quote, if in one */ + + if( quote ) + q = qq; + + fwrite( string, ( q - string ), 1, f ); + + len -= ( q - string ); + string = q; + + if( len ) + { + fputc( '-', f ); + fputc( '\n', f ); + } + } + } + + fclose( f ); + + status = system( tempnambuf ) & 0x07; + + unlink( tempnambuf + 1 ); + } + else + { + /* Execute single line command */ + /* Strip trailing newline before execing */ + if( e ) *e = 0; + status = system( s ) & 0x07; + } + + /* Fail for error or fatal error */ + /* OK on OK, warning, or info exit */ + + if( status == 2 || status == 4 ) + rstat = EXEC_CMD_FAIL; + + (*func)( closure, rstat ); +} + +int +execwait() +{ + return 0; +} + +# endif /* VMS */ diff --git a/src/tools/jam/expand.c b/src/tools/jam/expand.c new file mode 100644 index 0000000000..706c18379c --- /dev/null +++ b/src/tools/jam/expand.c @@ -0,0 +1,527 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "variable.h" +# include "expand.h" +# include "pathsys.h" +# include "newstr.h" + +/* + * expand.c - expand a buffer, given variable values + * + * External routines: + * + * var_expand() - variable-expand input string into list of strings + * + * Internal routines: + * + * var_edit_parse() - parse : modifiers into PATHNAME structure + * var_edit_file() - copy input target name to output, modifying filename + * var_edit_shift() - do upshift/downshift mods + * + * 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X) + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval + */ + +typedef struct { + PATHNAME f; /* :GDBSMR -- pieces */ + char parent; /* :P -- go to parent directory */ + char filemods; /* one of the above applied */ + char downshift; /* :L -- downshift result */ + char upshift; /* :U -- upshift result */ + PATHPART empty; /* :E -- default for empties */ + PATHPART join; /* :J -- join list with char */ +} VAR_EDITS ; + +static void var_edit_parse( char *mods, VAR_EDITS *edits ); +static void var_edit_file( char *in, char *out, VAR_EDITS *edits ); +static void var_edit_shift( char *out, VAR_EDITS *edits ); + +# define MAGIC_COLON '\001' +# define MAGIC_LEFT '\002' +# define MAGIC_RIGHT '\003' + +/* + * var_expand() - variable-expand input string into list of strings + * + * Would just copy input to output, performing variable expansion, + * except that since variables can contain multiple values the result + * of variable expansion may contain multiple values (a list). Properly + * performs "product" operations that occur in "$(var1)xxx$(var2)" or + * even "$($(var2))". + * + * Returns a newly created list. + */ + +LIST * +var_expand( + LIST *l, + char *in, + char *end, + LOL *lol, + int cancopyin ) +{ + char out_buf[ MAXSYM ]; + char *out = out_buf; + char *inp = in; + char *ov; /* for temp copy of variable in outbuf */ + int depth; + + if( DEBUG_VAREXP ) + printf( "expand '%.*s'\n", end - in, in ); + + /* This gets alot of cases: $(<) and $(>) */ + + if( in[0] == '$' && in[1] == '(' && in[3] == ')' && !in[4] ) + { + switch( in[2] ) + { + case '1': + case '<': + return list_copy( l, lol_get( lol, 0 ) ); + + case '2': + case '>': + return list_copy( l, lol_get( lol, 1 ) ); + } + } + + /* Just try simple copy of in to out. */ + + while( in < end ) + if( ( *out++ = *in++ ) == '$' && *in == '(' ) + goto expand; + + /* No variables expanded - just add copy of input string to list. */ + + /* Cancopyin is an optimization: if the input was already a list */ + /* item, we can use the copystr() to put it on the new list. */ + /* Otherwise, we use the slower newstr(). */ + + *out = '\0'; + + if( cancopyin ) + return list_new( l, copystr( inp ) ); + else + return list_new( l, newstr( out_buf ) ); + + expand: + /* + * Input so far (ignore blanks): + * + * stuff-in-outbuf $(variable) remainder + * ^ ^ + * in end + * Output so far: + * + * stuff-in-outbuf $ + * ^ ^ + * out_buf out + * + * + * We just copied the $ of $(...), so back up one on the output. + * We now find the matching close paren, copying the variable and + * modifiers between the $( and ) temporarily into out_buf, so that + * we can replace :'s with MAGIC_COLON. This is necessary to avoid + * being confused by modifier values that are variables containing + * :'s. Ugly. + */ + + depth = 1; + out--, in++; + ov = out; + + while( in < end && depth ) + { + switch( *ov++ = *in++ ) + { + case '(': depth++; break; + case ')': depth--; break; + case ':': ov[-1] = MAGIC_COLON; break; + case '[': ov[-1] = MAGIC_LEFT; break; + case ']': ov[-1] = MAGIC_RIGHT; break; + } + } + + /* Copied ) - back up. */ + + ov--; + + /* + * Input so far (ignore blanks): + * + * stuff-in-outbuf $(variable) remainder + * ^ ^ + * in end + * Output so far: + * + * stuff-in-outbuf variable + * ^ ^ ^ + * out_buf out ov + * + * Later we will overwrite 'variable' in out_buf, but we'll be + * done with it by then. 'variable' may be a multi-element list, + * so may each value for '$(variable element)', and so may 'remainder'. + * Thus we produce a product of three lists. + */ + + { + LIST *variables = 0; + LIST *remainder = 0; + LIST *vars; + + /* Recursively expand variable name & rest of input */ + + if( out < ov ) + variables = var_expand( L0, out, ov, lol, 0 ); + if( in < end ) + remainder = var_expand( L0, in, end, lol, 0 ); + + /* Now produce the result chain */ + + /* For each variable name */ + + for( vars = variables; vars; vars = list_next( vars ) ) + { + LIST *value, *evalue = 0; + char *colon; + char *bracket; + char varname[ MAXSYM ]; + int sub1 = 0, sub2 = -1; + VAR_EDITS edits; + + /* Look for a : modifier in the variable name */ + /* Must copy into varname so we can modify it */ + + strcpy( varname, vars->string ); + + if( colon = strchr( varname, MAGIC_COLON ) ) + { + *colon = '\0'; + var_edit_parse( colon + 1, &edits ); + } + + /* Look for [x-y] subscripting */ + /* sub1 is x (0 default) */ + /* sub2 is length (-1 means forever) */ + + if( bracket = strchr( varname, MAGIC_LEFT ) ) + { + char *dash; + + if( dash = strchr( bracket + 1, '-' ) ) + *dash = '\0'; + + sub1 = atoi( bracket + 1 ) - 1; + + if( !dash ) + sub2 = 1; + else if( !dash[1] || dash[1] == MAGIC_RIGHT ) + sub2 = -1; + else + sub2 = atoi( dash + 1 ) - sub1; + + *bracket = '\0'; + } + + /* Get variable value, specially handling $(<), $(>), $(n) */ + + if( varname[0] == '<' && !varname[1] ) + value = lol_get( lol, 0 ); + else if( varname[0] == '>' && !varname[1] ) + value = lol_get( lol, 1 ); + else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] ) + value = lol_get( lol, varname[0] - '1' ); + else + value = var_get( varname ); + + /* The fast path: $(x) - just copy the variable value. */ + /* This is only an optimization */ + + if( out == out_buf && !bracket && !colon && in == end ) + { + l = list_copy( l, value ); + continue; + } + + /* Handle start subscript */ + + while( sub1 > 0 && value ) + --sub1, value = list_next( value ); + + /* Empty w/ :E=default? */ + + if( !value && colon && edits.empty.ptr ) + evalue = value = list_new( L0, newstr( edits.empty.ptr ) ); + + /* For each variable value */ + + for( ; value; value = list_next( value ) ) + { + LIST *rem; + char *out1; + + /* Handle end subscript (length actually) */ + + if( sub2 >= 0 && --sub2 < 0 ) + break; + + /* Apply : mods, if present */ + + if( colon && edits.filemods ) + var_edit_file( value->string, out, &edits ); + else + strcpy( out, value->string ); + + if( colon && ( edits.upshift || edits.downshift ) ) + var_edit_shift( out, &edits ); + + /* Handle :J=joinval */ + /* If we have more values for this var, just */ + /* keep appending them (with the join value) */ + /* rather than creating separate LIST elements. */ + + if( colon && edits.join.ptr && + ( list_next( value ) || list_next( vars ) ) ) + { + out += strlen( out ); + strcpy( out, edits.join.ptr ); + out += strlen( out ); + continue; + } + + /* If no remainder, append result to output chain. */ + + if( in == end ) + { + l = list_new( l, newstr( out_buf ) ); + continue; + } + + /* For each remainder, append the complete string */ + /* to the output chain. */ + /* Remember the end of the variable expansion so */ + /* we can just tack on each instance of 'remainder' */ + + out1 = out + strlen( out ); + + for( rem = remainder; rem; rem = list_next( rem ) ) + { + strcpy( out1, rem->string ); + l = list_new( l, newstr( out_buf ) ); + } + } + + /* Toss used empty */ + + if( evalue ) + list_free( evalue ); + } + + /* variables & remainder were gifts from var_expand */ + /* and must be freed */ + + if( variables ) + list_free( variables ); + if( remainder) + list_free( remainder ); + + if( DEBUG_VAREXP ) + { + printf( "expanded to " ); + list_print( l ); + printf( "\n" ); + } + + return l; + } +} + +/* + * var_edit_parse() - parse : modifiers into PATHNAME structure + * + * The : modifiers in a $(varname:modifier) currently support replacing + * or omitting elements of a filename, and so they are parsed into a + * PATHNAME structure (which contains pointers into the original string). + * + * Modifiers of the form "X=value" replace the component X with + * the given value. Modifiers without the "=value" cause everything + * but the component X to be omitted. X is one of: + * + * G + * D directory name + * B base name + * S .suffix + * M (member) + * R root directory - prepended to whole path + * + * This routine sets: + * + * f->f_xxx.ptr = 0 + * f->f_xxx.len = 0 + * -> leave the original component xxx + * + * f->f_xxx.ptr = string + * f->f_xxx.len = strlen( string ) + * -> replace component xxx with string + * + * f->f_xxx.ptr = "" + * f->f_xxx.len = 0 + * -> omit component xxx + * + * var_edit_file() below and path_build() obligingly follow this convention. + */ + +static void +var_edit_parse( + char *mods, + VAR_EDITS *edits ) +{ + int havezeroed = 0; + memset( (char *)edits, 0, sizeof( *edits ) ); + + while( *mods ) + { + char *p; + PATHPART *fp; + + switch( *mods++ ) + { + case 'L': edits->downshift = 1; continue; + case 'U': edits->upshift = 1; continue; + case 'P': edits->parent = edits->filemods = 1; continue; + case 'E': fp = &edits->empty; goto strval; + case 'J': fp = &edits->join; goto strval; + case 'G': fp = &edits->f.f_grist; goto fileval; + case 'R': fp = &edits->f.f_root; goto fileval; + case 'D': fp = &edits->f.f_dir; goto fileval; + case 'B': fp = &edits->f.f_base; goto fileval; + case 'S': fp = &edits->f.f_suffix; goto fileval; + case 'M': fp = &edits->f.f_member; goto fileval; + + default: return; /* should complain, but so what... */ + } + + fileval: + + /* Handle :CHARS, where each char (without a following =) */ + /* selects a particular file path element. On the first such */ + /* char, we deselect all others (by setting ptr = "", len = 0) */ + /* and for each char we select that element (by setting ptr = 0) */ + + edits->filemods = 1; + + if( *mods != '=' ) + { + int i; + + if( !havezeroed++ ) + for( i = 0; i < 6; i++ ) + { + edits->f.part[ i ].len = 0; + edits->f.part[ i ].ptr = ""; + } + + fp->ptr = 0; + continue; + } + + strval: + + /* Handle :X=value, or :X */ + + if( *mods != '=' ) + { + fp->ptr = ""; + fp->len = 0; + } + else if( p = strchr( mods, MAGIC_COLON ) ) + { + *p = 0; + fp->ptr = ++mods; + fp->len = p - mods; + mods = p + 1; + } + else + { + fp->ptr = ++mods; + fp->len = strlen( mods ); + mods += fp->len; + } + } +} + +/* + * var_edit_file() - copy input target name to output, modifying filename + */ + +static void +var_edit_file( + char *in, + char *out, + VAR_EDITS *edits ) +{ + PATHNAME pathname; + + /* Parse apart original filename, putting parts into "pathname" */ + + path_parse( in, &pathname ); + + /* Replace any pathname with edits->f */ + + if( edits->f.f_grist.ptr ) + pathname.f_grist = edits->f.f_grist; + + if( edits->f.f_root.ptr ) + pathname.f_root = edits->f.f_root; + + if( edits->f.f_dir.ptr ) + pathname.f_dir = edits->f.f_dir; + + if( edits->f.f_base.ptr ) + pathname.f_base = edits->f.f_base; + + if( edits->f.f_suffix.ptr ) + pathname.f_suffix = edits->f.f_suffix; + + if( edits->f.f_member.ptr ) + pathname.f_member = edits->f.f_member; + + /* If requested, modify pathname to point to parent */ + + if( edits->parent ) + path_parent( &pathname ); + + /* Put filename back together */ + + path_build( &pathname, out, 0 ); +} + +/* + * var_edit_shift() - do upshift/downshift mods + */ + +static void +var_edit_shift( + char *out, + VAR_EDITS *edits ) +{ + /* Handle upshifting, downshifting now */ + + if( edits->upshift ) + { + for( ; *out; ++out ) + *out = toupper( *out ); + } + else if( edits->downshift ) + { + for( ; *out; ++out ) + *out = tolower( *out ); + } +} diff --git a/src/tools/jam/expand.h b/src/tools/jam/expand.h new file mode 100644 index 0000000000..c0644b150a --- /dev/null +++ b/src/tools/jam/expand.h @@ -0,0 +1,11 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * expand.h - expand a buffer, given variable values + */ + +LIST *var_expand( LIST *l, char *in, char *end, LOL *lol, int cancopyin ); diff --git a/src/tools/jam/filemac.c b/src/tools/jam/filemac.c new file mode 100644 index 0000000000..fcfa93cf9d --- /dev/null +++ b/src/tools/jam/filemac.c @@ -0,0 +1,165 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_MAC + +#include +#include + +# include <:sys:stat.h> + +/* + * filemac.c - manipulate file names and scan directories on macintosh + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/19/94 (mikem) - solaris string table insanity support + * 02/14/95 (seiwald) - parse and build /xxx properly + * 05/03/96 (seiwald) - split into pathunix.c + * 11/21/96 (peterk) - BEOS does not have Unix-style archives + */ + +void CopyC2PStr(const char * cstr, StringPtr pstr) +{ + int len; + + for (len = 0; *cstr && len<255; pstr[++len] = *cstr++) + ; + + pstr[0] = len; +} + +/* + * file_dirscan() - scan a directory for files + */ + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + PATHNAME f; + char filename[ MAXJPATH ]; + unsigned char fullPath[ 512 ]; + + FSSpec spec; + WDPBRec vol; + Str63 volName; + CInfoPBRec lastInfo; + int index = 1; + + /* First enter directory itself */ + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_dir.ptr = dir; + f.f_dir.len = strlen(dir); + + if( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + /* Special case ":" - enter it */ + + if( f.f_dir.len == 1 && f.f_dir.ptr[0] == ':' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + + /* Now enter contents of directory */ + + vol.ioNamePtr = volName; + + if( PBHGetVolSync( &vol ) ) + return; + + CopyC2PStr( dir, fullPath ); + + if( FSMakeFSSpec( vol.ioWDVRefNum, vol.ioWDDirID, fullPath, &spec ) ) + return; + + lastInfo.dirInfo.ioVRefNum = spec.vRefNum; + lastInfo.dirInfo.ioDrDirID = spec.parID; + lastInfo.dirInfo.ioNamePtr = spec.name; + lastInfo.dirInfo.ioFDirIndex = 0; + lastInfo.dirInfo.ioACUser = 0; + + if( PBGetCatInfoSync(&lastInfo) ) + return; + + if (!(lastInfo.dirInfo.ioFlAttrib & 0x10)) + return; + + // ioDrDirID must be reset each time. + + spec.parID = lastInfo.dirInfo.ioDrDirID; + + for( ;; ) + { + lastInfo.dirInfo.ioVRefNum = spec.vRefNum; + lastInfo.dirInfo.ioDrDirID = spec.parID; + lastInfo.dirInfo.ioNamePtr = fullPath; + lastInfo.dirInfo.ioFDirIndex = index++; + + if( PBGetCatInfoSync(&lastInfo) ) + return; + + f.f_base.ptr = (char *)fullPath + 1; + f.f_base.len = *fullPath; + + path_build( &f, filename, 0 ); + + (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 ); + } +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + struct stat statbuf; + + if( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + + return 0; +} + +/* + * file_archscan() - scan an archive for files + */ + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ +} + + +# endif /* macintosh */ + diff --git a/src/tools/jam/filent.c b/src/tools/jam/filent.c new file mode 100644 index 0000000000..d061d4b3a8 --- /dev/null +++ b/src/tools/jam/filent.c @@ -0,0 +1,265 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_NT + +# ifdef __BORLANDC__ +# if __BORLANDC__ < 0x550 +# include +# include +# endif +# undef PATHNAME /* cpp namespace collision */ +# define _finddata_t ffblk +# endif + +# include +# include + +/* + * filent.c - scan directories and archives on NT + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 07/10/95 (taylor) Findfirst() returns the first file on NT. + * 05/03/96 (seiwald) split apart into pathnt.c + */ + +/* + * file_dirscan() - scan a directory for files + */ + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + PATHNAME f; + char filespec[ MAXJPATH ]; + char filename[ MAXJPATH ]; + long handle; + int ret; + struct _finddata_t finfo[1]; + + /* First enter directory itself */ + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_dir.ptr = dir; + f.f_dir.len = strlen(dir); + + dir = *dir ? dir : "."; + + /* Special case \ or d:\ : enter it */ + + if( f.f_dir.len == 1 && f.f_dir.ptr[0] == '\\' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + else if( f.f_dir.len == 3 && f.f_dir.ptr[1] == ':' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + + /* Now enter contents of directory */ + + sprintf( filespec, "%s/*", dir ); + + if( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + +# if defined(__BORLANDC__) && __BORLANDC__ < 0x550 + if ( ret = findfirst( filespec, finfo, FA_NORMAL | FA_DIREC ) ) + return; + + while( !ret ) + { + time_t time_write = finfo->ff_fdate; + + time_write = (time_write << 16) | finfo->ff_ftime; + f.f_base.ptr = finfo->ff_name; + f.f_base.len = strlen( finfo->ff_name ); + + path_build( &f, filename ); + + (*func)( closure, filename, 1 /* stat()'ed */, time_write ); + + ret = findnext( finfo ); + } +# else + handle = _findfirst( filespec, finfo ); + + if( ret = ( handle < 0L ) ) + return; + + while( !ret ) + { + f.f_base.ptr = finfo->name; + f.f_base.len = strlen( finfo->name ); + + path_build( &f, filename, 0 ); + + (*func)( closure, filename, 1 /* stat()'ed */, finfo->time_write ); + + ret = _findnext( handle, finfo ); + } + + _findclose( handle ); +# endif + +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + /* On NT this is called only for C:/ */ + + struct stat statbuf; + + if( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + + return 0; +} + +/* + * file_archscan() - scan an archive for files + */ + +/* Straight from SunOS */ + +#define ARMAG "!\n" +#define SARMAG 8 + +#define ARFMAG "`\n" + +struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +}; + +# define SARFMAG 2 +# define SARHDR sizeof( struct ar_hdr ) + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ + struct ar_hdr ar_hdr; + char *string_table = 0; + char buf[ MAXJPATH ]; + long offset; + int fd; + + if( ( fd = open( archive, O_RDONLY | O_BINARY, 0 ) ) < 0 ) + return; + + if( read( fd, buf, SARMAG ) != SARMAG || + strncmp( ARMAG, buf, SARMAG ) ) + { + close( fd ); + return; + } + + offset = SARMAG; + + if( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while( read( fd, &ar_hdr, SARHDR ) == SARHDR && + !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) ) + { + long lar_date; + long lar_size; + char *name = 0; + char *endname; + char *c; + + sscanf( ar_hdr.ar_date, "%ld", &lar_date ); + sscanf( ar_hdr.ar_size, "%ld", &lar_size ); + + lar_size = ( lar_size + 1 ) & ~1; + + if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] == '/' ) + { + /* this is the "string table" entry of the symbol table, + ** which holds strings of filenames that are longer than + ** 15 characters (ie. don't fit into a ar_name + */ + + string_table = malloc(lar_size); + if (read(fd, string_table, lar_size) != lar_size) + printf("error reading string table\n"); + offset += SARHDR + lar_size; + continue; + } + else if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] != ' ') + { + /* Long filenames are recognized by "/nnnn" where nnnn is + ** the offset of the string in the string table represented + ** in ASCII decimals. + */ + + name = string_table + atoi( ar_hdr.ar_name + 1 ); + endname = name + strlen( name ); + } + else + { + /* normal name */ + name = ar_hdr.ar_name; + endname = name + sizeof( ar_hdr.ar_name ); + } + + /* strip trailing space, slashes, and backslashes */ + + while( endname-- > name ) + if( *endname != ' ' && *endname != '\\' && *endname != '/' ) + break; + *++endname = 0; + + /* strip leading directory names, an NT specialty */ + + if( c = strrchr( name, '/' ) ) + name = c + 1; + if( c = strrchr( name, '\\' ) ) + name = c + 1; + + sprintf( buf, "%s(%.*s)", archive, endname - name, name ); + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + + offset += SARHDR + lar_size; + lseek( fd, offset, 0 ); + } + + close( fd ); +} + +# endif /* NT */ diff --git a/src/tools/jam/fileos2.c b/src/tools/jam/fileos2.c new file mode 100644 index 0000000000..bcf9fe6b58 --- /dev/null +++ b/src/tools/jam/fileos2.c @@ -0,0 +1,131 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_OS2 + +# include +# include + +/* + * fileos2.c - scan directories and archives on NT + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 07/10/95 (taylor) Findfirst() returns the first file on NT. + * 05/03/96 (seiwald) split apart into pathnt.c + * 09/22/00 (seiwald) handle \ and c:\ specially: don't add extra / + */ + +/* + * file_dirscan() - scan a directory for files + */ + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + PATHNAME f; + char filespec[ MAXJPATH ]; + char filename[ MAXJPATH ]; + long handle; + int ret; + struct _find_t finfo[1]; + + /* First enter directory itself */ + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_dir.ptr = dir; + f.f_dir.len = strlen(dir); + + dir = *dir ? dir : "."; + + /* Special case \ or d:\ : enter it */ + + strcpy( filespec, dir ); + + if( f.f_dir.len == 1 && f.f_dir.ptr[0] == '\\' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + else if( f.f_dir.len == 3 && f.f_dir.ptr[1] == ':' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + else + strcat( filespec, "/" ); + + strcat( filespec, "*" ); + + /* Now enter contents of directory */ + + if( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", filespec ); + + /* Time info in dos find_t is not very useful. It consists */ + /* of a separate date and time, and putting them together is */ + /* not easy. So we leave that to a later stat() call. */ + + if( !_dos_findfirst( filespec, _A_NORMAL|_A_RDONLY|_A_SUBDIR, finfo ) ) + { + do + { + f.f_base.ptr = finfo->name; + f.f_base.len = strlen( finfo->name ); + + path_build( &f, filename, 0 ); + + (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 ); + } + while( !_dos_findnext( finfo ) ); + } + +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + /* This is called on OS2, not NT. */ + /* NT fills in the time in the dirscan. */ + + struct stat statbuf; + + if( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + + return 0; +} + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ +} + +# endif /* OS2 */ + diff --git a/src/tools/jam/filesys.h b/src/tools/jam/filesys.h new file mode 100644 index 0000000000..a99a0b5502 --- /dev/null +++ b/src/tools/jam/filesys.h @@ -0,0 +1,16 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * filesys.h - OS specific file routines + */ + +typedef void (*scanback)( void *closure, char *file, int found, time_t t ); + +void file_dirscan( char *dir, scanback func, void *closure ); +void file_archscan( char *arch, scanback func, void *closure ); + +int file_time( char *filename, time_t *time ); diff --git a/src/tools/jam/fileunix.c b/src/tools/jam/fileunix.c new file mode 100644 index 0000000000..406d31fecb --- /dev/null +++ b/src/tools/jam/fileunix.c @@ -0,0 +1,331 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef USE_FILEUNIX + +# if defined( OS_SEQUENT ) || \ + defined( OS_DGUX ) || \ + defined( OS_SCO ) || \ + defined( OS_ISC ) +# define PORTAR 1 +# endif + +# if defined( OS_RHAPSODY ) || \ + defined( OS_MACOSX ) || \ + defined( OS_NEXT ) +/* need unistd for rhapsody's proper lseek */ +# include +# include +# define STRUCT_DIRENT struct direct +# else +# include +# define STRUCT_DIRENT struct dirent +# endif + +# ifdef OS_COHERENT +# include +# define HAVE_AR +# endif + +# if defined( OS_MVS ) || \ + defined( OS_INTERIX ) || defined(OS_AIX) + +#define ARMAG "!\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +struct ar_hdr /* archive file member header - printable ascii */ +{ + char ar_name[16]; /* file member name - `/' terminated */ + char ar_date[12]; /* file member date - decimal */ + char ar_uid[6]; /* file member user id - decimal */ + char ar_gid[6]; /* file member group id - decimal */ + char ar_mode[8]; /* file member mode - octal */ + char ar_size[10]; /* file member size - decimal */ + char ar_fmag[2]; /* ARFMAG - string to end header */ +}; + +# define HAVE_AR +# endif + +# if defined( OS_QNX ) || \ + defined( OS_BEOS ) || \ + defined( OS_MPEIX ) +# define NO_AR +# define HAVE_AR +# endif + +# ifndef HAVE_AR +# include +# endif + +/* + * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/19/94 (mikem) - solaris string table insanity support + * 02/14/95 (seiwald) - parse and build /xxx properly + * 05/03/96 (seiwald) - split into pathunix.c + * 11/21/96 (peterk) - BEOS does not have Unix-style archives + */ + +/* + * file_dirscan() - scan a directory for files + */ + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + PATHNAME f; + DIR *d; + STRUCT_DIRENT *dirent; + char filename[ MAXJPATH ]; + + /* First enter directory itself */ + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_dir.ptr = dir; + f.f_dir.len = strlen(dir); + + dir = *dir ? dir : "."; + + /* Special case / : enter it */ + + if( f.f_dir.len == 1 && f.f_dir.ptr[0] == '/' ) + (*func)( closure, dir, 0 /* not stat()'ed */, (time_t)0 ); + + /* Now enter contents of directory */ + + if( !( d = opendir( dir ) ) ) + return; + + if( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + while( dirent = readdir( d ) ) + { +# ifdef old_sinix + /* Broken structure definition on sinix. */ + f.f_base.ptr = dirent->d_name - 2; +# else + f.f_base.ptr = dirent->d_name; +# endif + f.f_base.len = strlen( f.f_base.ptr ); + + path_build( &f, filename, 0 ); + + (*func)( closure, filename, 0 /* not stat()'ed */, (time_t)0 ); + } + + closedir( d ); +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + struct stat statbuf; + + if( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + return 0; +} + +/* + * file_archscan() - scan an archive for files + */ + +# ifndef AIAMAG /* God-fearing UNIX */ + +# define SARFMAG 2 +# define SARHDR sizeof( struct ar_hdr ) + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ +# ifndef NO_AR + struct ar_hdr ar_hdr; + char buf[ MAXJPATH ]; + long offset; + char *string_table = 0; + int fd; + + if( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 ) + return; + + if( read( fd, buf, SARMAG ) != SARMAG || + strncmp( ARMAG, buf, SARMAG ) ) + { + close( fd ); + return; + } + + offset = SARMAG; + + if( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while( read( fd, &ar_hdr, SARHDR ) == SARHDR && + !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) ) + { + char lar_name[256]; + long lar_date; + long lar_size; + long lar_offset; + char *c; + char *src, *dest; + + strncpy( lar_name, ar_hdr.ar_name, sizeof(ar_hdr.ar_name) ); + + sscanf( ar_hdr.ar_date, "%ld", &lar_date ); + sscanf( ar_hdr.ar_size, "%ld", &lar_size ); + + if (ar_hdr.ar_name[0] == '/') + { + if (ar_hdr.ar_name[1] == '/') + { + /* this is the "string table" entry of the symbol table, + ** which holds strings of filenames that are longer than + ** 15 characters (ie. don't fit into a ar_name + */ + + string_table = (char *)malloc(lar_size); + lseek(fd, offset + SARHDR, 0); + if (read(fd, string_table, lar_size) != lar_size) + printf("error reading string table\n"); + } + else if (string_table && ar_hdr.ar_name[1] != ' ') + { + /* Long filenames are recognized by "/nnnn" where nnnn is + ** the offset of the string in the string table represented + ** in ASCII decimals. + */ + dest = lar_name; + lar_offset = atoi(lar_name + 1); + src = &string_table[lar_offset]; + while (*src != '/') + *dest++ = *src++; + *dest = '/'; + } + } + + c = lar_name - 1; + while( *++c != ' ' && *c != '/' ) + ; + *c = '\0'; + + if ( DEBUG_BINDSCAN ) + printf( "archive name %s found\n", lar_name ); + + sprintf( buf, "%s(%s)", archive, lar_name ); + + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + + offset += SARHDR + ( ( lar_size + 1 ) & ~1 ); + lseek( fd, offset, 0 ); + } + + if (string_table) + free(string_table); + + close( fd ); + +# endif /* NO_AR */ + +} + +# else /* AIAMAG - RS6000 AIX */ + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ + struct fl_hdr fl_hdr; + + struct { + struct ar_hdr hdr; + char pad[ 256 ]; + } ar_hdr ; + + char buf[ MAXJPATH ]; + long offset; + int fd; + + if( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 ) + return; + + if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ || + strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) ) + { + close( fd ); + return; + } + + sscanf( fl_hdr.fl_fstmoff, "%ld", &offset ); + + if( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while( offset > 0 && + lseek( fd, offset, 0 ) >= 0 && + read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) ) + { + long lar_date; + int lar_namlen; + + sscanf( ar_hdr.hdr.ar_namlen, "%d", &lar_namlen ); + sscanf( ar_hdr.hdr.ar_date, "%ld", &lar_date ); + sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset ); + + if( !lar_namlen ) + continue; + + ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0'; + + sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name ); + + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + } + + close( fd ); +} + +# endif /* AIAMAG - RS6000 AIX */ + +# endif /* USE_FILEUNIX */ + diff --git a/src/tools/jam/filevms.c b/src/tools/jam/filevms.c new file mode 100644 index 0000000000..dc2fe4c114 --- /dev/null +++ b/src/tools/jam/filevms.c @@ -0,0 +1,318 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_VMS + +/* + * filevms.c - scan directories and libaries on VMS + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! + * 05/03/96 (seiwald) - split into pathvms.c + */ + +# include +# include +# include +# include +# include +# include +# include + +#include +#include +#include +#include +#include +#include + +/* Supply missing prototypes for lbr$-routines*/ + +extern "C" { + +int lbr$set_module( + void **, + unsigned long *, + struct dsc$descriptor_s *, + unsigned short *, + void * ); + +int lbr$open( void **, + struct dsc$descriptor_s *, + void *, + void *, + void *, + void *, + void * ); + +int lbr$ini_control( + void **, + unsigned long *, + unsigned long *, + void * ); + +int lbr$get_index( + void **, + unsigned long *, + int (*func)( struct dsc$descriptor_s *, unsigned long *), + void * ); + +int lbr$close( + void ** ); + +} + +static void +file_cvttime( + unsigned int *curtime, + time_t *unixtime ) +{ + static const size_t divisor = 10000000; + static unsigned int bastim[2] = { 0x4BEB4000, 0x007C9567 }; /* 1/1/1970 */ + int delta[2], remainder; + + lib$subx( curtime, bastim, delta ); + lib$ediv( &divisor, delta, unixtime, &remainder ); +} + +# define DEFAULT_FILE_SPECIFICATION "[]*.*;0" + +# define min( a,b ) ((a)<(b)?(a):(b)) + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + + struct FAB xfab; + struct NAM xnam; + struct XABDAT xab; + char esa[256]; + char filename[256]; + char filename2[256]; + char dirname[256]; + register int status; + PATHNAME f; + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_root.ptr = dir; + f.f_root.len = strlen( dir ); + + /* get the input file specification + */ + xnam = cc$rms_nam; + xnam.nam$l_esa = esa; + xnam.nam$b_ess = sizeof( esa ) - 1; + xnam.nam$l_rsa = filename; + xnam.nam$b_rss = min( sizeof( filename ) - 1, NAM$C_MAXRSS ); + + xab = cc$rms_xabdat; /* initialize extended attributes */ + xab.xab$b_cod = XAB$C_DAT; /* ask for date */ + xab.xab$l_nxt = NULL; /* terminate XAB chain */ + + xfab = cc$rms_fab; + xfab.fab$l_dna = DEFAULT_FILE_SPECIFICATION; + xfab.fab$b_dns = sizeof( DEFAULT_FILE_SPECIFICATION ) - 1; + xfab.fab$l_fop = FAB$M_NAM; + xfab.fab$l_fna = dir; /* address of file name */ + xfab.fab$b_fns = strlen( dir ); /* length of file name */ + xfab.fab$l_nam = &xnam; /* address of NAB block */ + xfab.fab$l_xab = (char *)&xab; /* address of XAB block */ + + + status = sys$parse( &xfab ); + + if( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + if ( !( status & 1 ) ) + return; + + + + /* Add bogus directory for [000000] */ + + if( !strcmp( dir, "[000000]" ) ) + { + (*func)( closure, "[000000]", 1 /* time valid */, 1 /* old but true */ ); + } + + /* Add bogus directory for [] */ + + if( !strcmp( dir, "[]" ) ) + { + (*func)( closure, "[]", 1 /* time valid */, 1 /* old but true */ ); + (*func)( closure, "[-]", 1 /* time valid */, 1 /* old but true */ ); + } + + while ( (status = sys$search( &xfab )) & 1 ) + { + char *s; + time_t time; + + /* "I think that might work" - eml */ + + sys$open( &xfab ); + sys$close( &xfab ); + + file_cvttime( (unsigned int *)&xab.xab$q_rdt, &time ); + + filename[xnam.nam$b_rsl] = '\0'; + + /* What we do with the name depends on the suffix: */ + /* .dir is a directory */ + /* .xxx is a file with a suffix */ + /* . is no suffix at all */ + + if( xnam.nam$b_type == 4 && !strncmp( xnam.nam$l_type, ".DIR", 4 ) ) + { + /* directory */ + sprintf( dirname, "[.%.*s]", xnam.nam$b_name, xnam.nam$l_name ); + f.f_dir.ptr = dirname; + f.f_dir.len = strlen( dirname ); + f.f_base.ptr = 0; + f.f_base.len = 0; + f.f_suffix.ptr = 0; + f.f_suffix.len = 0; + } + else + { + /* normal file with a suffix */ + f.f_dir.ptr = 0; + f.f_dir.len = 0; + f.f_base.ptr = xnam.nam$l_name; + f.f_base.len = xnam.nam$b_name; + f.f_suffix.ptr = xnam.nam$l_type; + f.f_suffix.len = xnam.nam$b_type; + } + + path_build( &f, filename2, 0 ); + + /* + if( DEBUG_SEARCH ) + printf("root '%s' base %.*s suf %.*s = %s\n", + dir, + xnam.nam$b_name, xnam.nam$l_name, + xnam.nam$b_type, xnam.nam$l_type, + filename2); + */ + + (*func)( closure, filename2, 1 /* time valid */, time ); + } +} + +int +file_time( + char *filename, + time_t *time ) +{ + /* This should never be called, as all files are */ + /* timestampped in file_dirscan() and file_archscan() */ + return -1; +} + +static char *VMS_archive = 0; +static scanback VMS_func; +static void *VMS_closure; +static void *context; + +static int +file_archmember( + struct dsc$descriptor_s *module, + unsigned long *rfa ) +{ + static struct dsc$descriptor_s bufdsc = + {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; + + struct mhddef *mhd; + char filename[128]; + char buf[ MAXJPATH ]; + + int status; + time_t library_date; + + register int i; + register char *p; + + bufdsc.dsc$a_pointer = filename; + bufdsc.dsc$w_length = sizeof( filename ); + status = lbr$set_module( &context, rfa, &bufdsc, + &bufdsc.dsc$w_length, NULL ); + + if ( !(status & 1) ) + return ( 1 ); + + mhd = (struct mhddef *)filename; + + file_cvttime( &mhd->mhd$l_datim, &library_date ); + + for ( i = 0, p = module->dsc$a_pointer; i < module->dsc$w_length; i++, p++ ) + filename[i] = *p; + + filename[i] = '\0'; + + sprintf( buf, "%s(%s.obj)", VMS_archive, filename ); + + (*VMS_func)( VMS_closure, buf, 1 /* time valid */, (time_t)library_date ); + + return ( 1 ); +} + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ + static struct dsc$descriptor_s library = + {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; + + unsigned long lfunc = LBR$C_READ; + unsigned long typ = LBR$C_TYP_UNK; + unsigned long index = 1; + + register int status; + + VMS_archive = archive; + VMS_func = func; + VMS_closure = closure; + + status = lbr$ini_control( &context, &lfunc, &typ, NULL ); + if ( !( status & 1 ) ) + return; + + library.dsc$a_pointer = archive; + library.dsc$w_length = strlen( archive ); + + status = lbr$open( &context, &library, NULL, NULL, NULL, NULL, NULL ); + if ( !( status & 1 ) ) + return; + + (void) lbr$get_index( &context, &index, file_archmember, NULL ); + + (void) lbr$close( &context ); +} + +# endif /* VMS */ + diff --git a/src/tools/jam/glob.c b/src/tools/jam/glob.c new file mode 100644 index 0000000000..abb56eb1be --- /dev/null +++ b/src/tools/jam/glob.c @@ -0,0 +1,157 @@ +/* + * Copyright 1994 Christopher Seiwald. All rights reserved. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * glob.c - match a string against a simple pattern + * + * Understands the following patterns: + * + * * any number of characters + * ? any single character + * [a-z] any single character in the range a-z + * [^a-z] any single character not in the range a-z + * \x match x + * + * External functions: + * + * glob() - match a string against a simple pattern + * + * Internal functions: + * + * globchars() - build a bitlist to check for character group match + */ + +# include "jam.h" + +# define CHECK_BIT( tab, bit ) ( tab[ (bit)/8 ] & (1<<( (bit)%8 )) ) +# define BITLISTSIZE 16 /* bytes used for [chars] in compiled expr */ + +static void globchars( char *s, char *e, char *b ); + +/* + * glob() - match a string against a simple pattern + */ + +int +glob( + register char *c, + register char *s ) +{ + char bitlist[ BITLISTSIZE ]; + char *here; + + for( ;; ) + switch( *c++ ) + { + case '\0': + return *s ? -1 : 0; + + case '?': + if( !*s++ ) + return 1; + break; + + case '[': + /* scan for matching ] */ + + here = c; + do if( !*c++ ) + return 1; + while( here == c || *c != ']' ); + c++; + + /* build character class bitlist */ + + globchars( here, c, bitlist ); + + if( !CHECK_BIT( bitlist, *(unsigned char *)s ) ) + return 1; + s++; + break; + + case '*': + here = s; + + while( *s ) + s++; + + /* Try to match the rest of the pattern in a recursive */ + /* call. If the match fails we'll back up chars, retrying. */ + + while( s != here ) + { + int r; + + /* A fast path for the last token in a pattern */ + + r = *c ? glob( c, s ) : *s ? -1 : 0; + + if( !r ) + return 0; + else if( r < 0 ) + return 1; + + --s; + } + break; + + case '\\': + /* Force literal match of next char. */ + + if( !*c || *s++ != *c++ ) + return 1; + break; + + default: + if( *s++ != c[-1] ) + return 1; + break; + } +} + +/* + * globchars() - build a bitlist to check for character group match + */ + +static void +globchars( + char *s, + char *e, + char *b ) +{ + int neg = 0; + + memset( b, '\0', BITLISTSIZE ); + + if( *s == '^') + neg++, s++; + + while( s < e ) + { + int c; + + if( s+2 < e && s[1] == '-' ) + { + for( c = s[0]; c <= s[2]; c++ ) + b[ c/8 ] |= (1<<(c%8)); + s += 3; + } else { + c = *s++; + b[ c/8 ] |= (1<<(c%8)); + } + } + + if( neg ) + { + int i; + for( i = 0; i < BITLISTSIZE; i++ ) + b[ i ] ^= 0377; + } + + /* Don't include \0 in either $[chars] or $[^chars] */ + + b[0] &= 0376; +} diff --git a/src/tools/jam/hash.c b/src/tools/jam/hash.c new file mode 100644 index 0000000000..1d11efa22b --- /dev/null +++ b/src/tools/jam/hash.c @@ -0,0 +1,259 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "hash.h" + +/* + * hash.c - simple in-memory hashing routines + * + * External routines: + * + * hashinit() - initialize a hash table, returning a handle + * hashitem() - find a record in the table, and optionally enter a new one + * hashdone() - free a hash table, given its handle + * + * Internal routines: + * + * hashrehash() - resize and rebuild hp->tab, the hash table + * + * 4/29/93 - ensure ITEM's are aligned + */ + +char *hashsccssid="@(#)hash.c 1.14 () 6/20/88"; + +/* Header attached to all data items entered into a hash table. */ + +struct hashhdr { + struct item *next; + int keyval; /* for quick comparisons */ +} ; + +/* This structure overlays the one handed to hashenter(). */ +/* It's actual size is given to hashinit(). */ + +struct hashdata { + char *key; + /* rest of user data */ +} ; + +typedef struct item { + struct hashhdr hdr; + struct hashdata data; +} ITEM ; + +# define MAX_LISTS 32 + +struct hash +{ + /* + * the hash table, just an array of item pointers + */ + struct { + int nel; + ITEM **base; + } tab; + + int bloat; /* tab.nel / items.nel */ + int inel; /* initial number of elements */ + + /* + * the array of records, maintained by these routines + * essentially a microallocator + */ + struct { + int more; /* how many more ITEMs fit in lists[ list ] */ + char *next; /* where to put more ITEMs in lists[ list ] */ + int datalen; /* length of records in this hash table */ + int size; /* sizeof( ITEM ) + aligned datalen */ + int nel; /* total ITEMs held by all lists[] */ + int list; /* index into lists[] */ + + struct { + int nel; /* total ITEMs held by this list */ + char *base; /* base of ITEMs array */ + } lists[ MAX_LISTS ]; + } items; + + char *name; /* just for hashstats() */ +} ; + +static void hashrehash( struct hash *hp ); +static void hashstat( struct hash *hp ); + +/* + * hashitem() - find a record in the table, and optionally enter a new one + */ + +int +hashitem( + register struct hash *hp, + HASHDATA **data, + int enter ) +{ + ITEM **base; + register ITEM *i; + char *b = (*data)->key; + int keyval; + + if( enter && !hp->items.more ) + hashrehash( hp ); + + if( !enter && !hp->items.nel ) + return 0; + + keyval = *b; + + while( *b ) + keyval = keyval * 2147059363 + *b++; + + keyval &= 0x7FFFFFFF; + + base = hp->tab.base + ( keyval % hp->tab.nel ); + + for( i = *base; i; i = i->hdr.next ) + if( keyval == i->hdr.keyval && + !strcmp( i->data.key, (*data)->key ) ) + { + *data = &i->data; + return !0; + } + + if( enter ) + { + i = (ITEM *)hp->items.next; + hp->items.next += hp->items.size; + hp->items.more--; + memcpy( (char *)&i->data, (char *)*data, hp->items.datalen ); + i->hdr.keyval = keyval; + i->hdr.next = *base; + *base = i; + *data = &i->data; + } + + return 0; +} + +/* + * hashrehash() - resize and rebuild hp->tab, the hash table + */ + +static void hashrehash( register struct hash *hp ) +{ + int i = ++hp->items.list; + + hp->items.more = i ? 2 * hp->items.nel : hp->inel; + hp->items.next = (char *)malloc( hp->items.more * hp->items.size ); + + hp->items.lists[i].nel = hp->items.more; + hp->items.lists[i].base = hp->items.next; + hp->items.nel += hp->items.more; + + if( hp->tab.base ) + free( (char *)hp->tab.base ); + + hp->tab.nel = hp->items.nel * hp->bloat; + hp->tab.base = (ITEM **)malloc( hp->tab.nel * sizeof(ITEM **) ); + + memset( (char *)hp->tab.base, '\0', hp->tab.nel * sizeof( ITEM * ) ); + + for( i = 0; i < hp->items.list; i++ ) + { + int nel = hp->items.lists[i].nel; + char *next = hp->items.lists[i].base; + + for( ; nel--; next += hp->items.size ) + { + register ITEM *i = (ITEM *)next; + ITEM **ip = hp->tab.base + i->hdr.keyval % hp->tab.nel; + + i->hdr.next = *ip; + *ip = i; + } + } +} + +/* --- */ + +# define ALIGNED(x) ( ( x + sizeof( ITEM ) - 1 ) & ~( sizeof( ITEM ) - 1 ) ) + +/* + * hashinit() - initialize a hash table, returning a handle + */ + +struct hash * +hashinit( + int datalen, + char *name ) +{ + struct hash *hp = (struct hash *)malloc( sizeof( *hp ) ); + + hp->bloat = 3; + hp->tab.nel = 0; + hp->tab.base = (ITEM **)0; + hp->items.more = 0; + hp->items.datalen = datalen; + hp->items.size = sizeof( struct hashhdr ) + ALIGNED( datalen ); + hp->items.list = -1; + hp->items.nel = 0; + hp->inel = 11; + hp->name = name; + + return hp; +} + +/* + * hashdone() - free a hash table, given its handle + */ + +void +hashdone( struct hash *hp ) +{ + int i; + + if( !hp ) + return; + + if( DEBUG_MEM ) + hashstat( hp ); + + if( hp->tab.base ) + free( (char *)hp->tab.base ); + for( i = 0; i <= hp->items.list; i++ ) + free( hp->items.lists[i].base ); + free( (char *)hp ); +} + +/* ---- */ + +static void +hashstat( struct hash *hp ) +{ + ITEM **tab = hp->tab.base; + int nel = hp->tab.nel; + int count = 0; + int sets = 0; + int run = ( tab[ nel - 1 ] != (ITEM *)0 ); + int i, here; + + for( i = nel; i > 0; i-- ) + { + if( here = ( *tab++ != (ITEM *)0 ) ) + count++; + if( here && !run ) + sets++; + run = here; + } + + printf( "%s table: %d+%d+%d (%dK+%dK) items+table+hash, %f density\n", + hp->name, + count, + hp->items.nel, + hp->tab.nel, + hp->items.nel * hp->items.size / 1024, + hp->tab.nel * sizeof( ITEM ** ) / 1024, + (float)count / (float)sets ); +} diff --git a/src/tools/jam/hash.h b/src/tools/jam/hash.h new file mode 100644 index 0000000000..65af38c323 --- /dev/null +++ b/src/tools/jam/hash.h @@ -0,0 +1,18 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * hash.h - simple in-memory hashing routines + */ + +typedef struct hashdata HASHDATA; + +struct hash * hashinit( int datalen, char *name ); +int hashitem( struct hash *hp, HASHDATA **data, int enter ); +void hashdone( struct hash *hp ); + +# define hashenter( hp, data ) !hashitem( hp, data, !0 ) +# define hashcheck( hp, data ) hashitem( hp, data, 0 ) diff --git a/src/tools/jam/headers.c b/src/tools/jam/headers.c new file mode 100644 index 0000000000..1c087f056a --- /dev/null +++ b/src/tools/jam/headers.c @@ -0,0 +1,130 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "compile.h" +# include "rules.h" +# include "variable.h" +# include "regexp.h" +# include "headers.h" +# include "newstr.h" + +/* + * headers.c - handle #includes in source files + * + * Using regular expressions provided as the variable $(HDRSCAN), + * headers() searches a file for #include files and phonies up a + * rule invocation: + * + * $(HDRRULE) : ; + * + * External routines: + * headers() - scan a target for include files and call HDRRULE + * + * Internal routines: + * headers1() - using regexp, scan a file and build include LIST + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 09/10/00 (seiwald) - replaced call to compile_rule with evaluate_rule, + * so that headers() doesn't have to mock up a parse structure + * just to invoke a rule. + */ + +static LIST *headers1( LIST *l, char *file, int rec, regexp *re[] ); + +/* + * headers() - scan a target for include files and call HDRRULE + */ + +# define MAXINC 10 + +void +headers( TARGET *t ) +{ + LIST *hdrscan; + LIST *hdrrule; + LIST *headlist = 0; + LOL lol; + regexp *re[ MAXINC ]; + int rec = 0; + + if( !( hdrscan = var_get( "HDRSCAN" ) ) || + !( hdrrule = var_get( "HDRRULE" ) ) ) + return; + + if( DEBUG_HEADER ) + printf( "header scan %s\n", t->name ); + + /* Compile all regular expressions in HDRSCAN */ + + while( rec < MAXINC && hdrscan ) + { + re[rec++] = regcomp( hdrscan->string ); + hdrscan = list_next( hdrscan ); + } + + /* Doctor up call to HDRRULE rule */ + /* Call headers1() to get LIST of included files. */ + + lol_init( &lol ); + lol_add( &lol, list_new( L0, t->name ) ); + lol_add( &lol, headers1( headlist, t->boundname, rec, re ) ); + + if( lol_get( &lol, 1 ) ) + list_free( evaluate_rule( hdrrule->string, &lol, L0 ) ); + + /* Clean up */ + + lol_free( &lol ); + + while( rec ) + free( (char *)re[--rec] ); +} + +/* + * headers1() - using regexp, scan a file and build include LIST + */ + +static LIST * +headers1( + LIST *l, + char *file, + int rec, + regexp *re[] ) +{ + FILE *f; + char buf[ 1024 ]; + int i; + + if( !( f = fopen( file, "r" ) ) ) + return l; + + while( fgets( buf, sizeof( buf ), f ) ) + { + for( i = 0; i < rec; i++ ) + if( regexec( re[i], buf ) && re[i]->startp[1] ) + { + re[i]->endp[1][0] = '\0'; + + if( DEBUG_HEADER ) + printf( "header found: %s\n", re[i]->startp[1] ); + + l = list_new( l, newstr( re[i]->startp[1] ) ); + } + } + + fclose( f ); + + return l; +} + +void +regerror( char *s ) +{ + printf( "re error %s\n", s ); +} diff --git a/src/tools/jam/headers.h b/src/tools/jam/headers.h new file mode 100644 index 0000000000..8c33735c60 --- /dev/null +++ b/src/tools/jam/headers.h @@ -0,0 +1,11 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * headers.h - handle #includes in source files + */ + +void headers( TARGET *t ); diff --git a/src/tools/jam/jam.c b/src/tools/jam/jam.c new file mode 100644 index 0000000000..42a393fbe7 --- /dev/null +++ b/src/tools/jam/jam.c @@ -0,0 +1,365 @@ +/* + * /+\ + * +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * \+/ + * + * This file is part of jam. + * + * License is hereby granted to use this software and distribute it + * freely, as long as this copyright notice is retained and modifications + * are clearly marked. + * + * ALL WARRANTIES ARE HEREBY DISCLAIMED. + */ + +/* + * jam.c - make redux + * + * See Jam.html for usage information. + * + * These comments document the code. + * + * The top half of the code is structured such: + * + * jam + * / | \ + * +---+ | \ + * / | \ + * jamgram option \ + * / | \ \ + * / | \ \ + * / | \ | + * scan | compile make + * | | / | \ / | \ + * | | / | \ / | \ + * | | / | \ / | \ + * jambase parse | rules search make1 + * | | | \ + * | | | \ + * | | | \ + * builtins timestamp command execute + * | + * | + * | + * filesys + * + * + * The support routines are called by all of the above, but themselves + * are layered thus: + * + * variable|expand + * / | | | + * / | | | + * / | | | + * lists | | pathsys + * \ | | + * \ | | + * \ | | + * newstr | + * \ | + * \ | + * \ | + * hash + * + * Roughly, the modules are: + * + * builtins.c - jam's built-in rules + * command.c - maintain lists of commands + * compile.c - compile parsed jam statements + * execunix.c - execute a shell script on UNIX + * execvms.c - execute a shell script, ala VMS + * expand.c - expand a buffer, given variable values + * file*.c - scan directories and archives on * + * hash.c - simple in-memory hashing routines + * headers.c - handle #includes in source files + * jambase.c - compilable copy of Jambase + * jamgram.y - jam grammar + * lists.c - maintain lists of strings + * make.c - bring a target up to date, once rules are in place + * make1.c - execute command to bring targets up to date + * newstr.c - string manipulation routines + * option.c - command line option processing + * parse.c - make and destroy parse trees as driven by the parser + * path*.c - manipulate file names on * + * hash.c - simple in-memory hashing routines + * regexp.c - Henry Spencer's regexp + * rules.c - access to RULEs, TARGETs, and ACTIONs + * scan.c - the jam yacc scanner + * search.c - find a target along $(SEARCH) or $(LOCATE) + * timestamp.c - get the timestamp of a file or archive member + * variable.c - handle jam multi-element variables + * + * 05/04/94 (seiwald) - async multiprocess (-j) support + * 02/08/95 (seiwald) - -n implies -d2. + * 02/22/95 (seiwald) - -v for version info. + * 09/11/00 (seiwald) - PATCHLEVEL folded into VERSION. + * 01/10/01 (seiwald) - pathsys.h split from filesys.h + */ + +# include "jam.h" +# include "option.h" +# include "patchlevel.h" + +/* These get various function declarations. */ + +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "compile.h" +# include "builtins.h" +# include "rules.h" +# include "newstr.h" +# include "scan.h" +# include "timestamp.h" +# include "make.h" + +/* Macintosh is "special" */ + +# ifdef OS_MAC +# include +# endif + +/* And UNIX for this */ + +# ifdef unix +# include +# endif + +struct globs globs = { + 0, /* noexec */ + 1, /* jobs */ + 0, /* quitquick */ + 0, /* newestfirst */ +# ifdef OS_MAC + { 0, 0 }, /* debug - suppress tracing output */ +# else + { 0, 1 }, /* debug ... */ +# endif + 0 /* output commands, not run them */ +} ; + +/* Symbols to be defined as true for use in Jambase */ + +static char *othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 } ; + +/* Known for sure: + * mac needs arg_enviro + * OS2 needs extern environ + */ + +# ifdef OS_MAC +# define use_environ arg_environ +# ifdef MPW +QDGlobals qd; +# endif +# endif + +# ifndef use_environ +# define use_environ environ +# if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT ) +extern char **environ; +# endif +# endif + +main( int argc, char **argv, char **arg_environ ) +{ + int n; + char *s; + struct option optv[N_OPTS]; + char *all = "all"; + int anyhow = 0; + int status; + +# ifdef OS_MAC + InitGraf(&qd.thePort); +# endif + + argc--, argv++; + + if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:qv", optv ) ) < 0 ) + { + printf( "\nusage: jam [ options ] targets...\n\n" ); + + printf( "-a Build all targets, even if they are current.\n" ); + printf( "-dx Set the debug level to x (0-9).\n" ); + printf( "-fx Read x instead of Jambase.\n" ); + /* printf( "-g Build from newest sources first.\n" ); */ + printf( "-jx Run up to x shell commands concurrently.\n" ); + printf( "-n Don't actually execute the updating actions.\n" ); + printf( "-ox Write the updating actions to file x.\n" ); + printf( "-q Quit quickly as soon as a target fails.\n" ); + printf( "-sx=y Set variable x=y, overriding environment.\n" ); + printf( "-tx Rebuild x, even if it is up-to-date.\n" ); + printf( "-v Print the version of jam and exit.\n\n" ); + + exit( EXITBAD ); + } + + argc -= n, argv += n; + + /* Version info. */ + + if( ( s = getoptval( optv, 'v', 0 ) ) ) + { + printf( "Jam %s. %s. ", VERSION, OSMINOR ); + printf( "Copyright 1993-2002 Christopher Seiwald.\n" ); + + return EXITOK; + } + + /* Pick up interesting options */ + + if( ( s = getoptval( optv, 'n', 0 ) ) ) + globs.noexec++, globs.debug[2] = 1; + + if( ( s = getoptval( optv, 'q', 0 ) ) ) + globs.quitquick = 1; + + if( ( s = getoptval( optv, 'a', 0 ) ) ) + anyhow++; + + if( ( s = getoptval( optv, 'j', 0 ) ) ) + globs.jobs = atoi( s ); + + if( ( s = getoptval( optv, 'g', 0 ) ) ) + globs.newestfirst = 1; + + /* Turn on/off debugging */ + + for( n = 0; s = getoptval( optv, 'd', n ); n++ ) + { + int i; + + /* First -d, turn off defaults. */ + + if( !n ) + for( i = 0; i < DEBUG_MAX; i++ ) + globs.debug[i] = 0; + + i = atoi( s ); + + if( i < 0 || i >= DEBUG_MAX ) + { + printf( "Invalid debug level '%s'.\n", s ); + continue; + } + + /* n turns on levels 1-n */ + /* +n turns on level n */ + + if( *s == '+' ) + globs.debug[i] = 1; + else while( i ) + globs.debug[i--] = 1; + } + + /* Set JAMDATE first */ + + { + char *date; + time_t clock; + time( &clock ); + date = newstr( ctime( &clock ) ); + + /* Trim newline from date */ + + if( strlen( date ) == 25 ) + date[ 24 ] = 0; + + var_set( "JAMDATE", list_new( L0, newstr( date ) ), VAR_SET ); + } + + /* And JAMUNAME */ +# ifdef unix + { + struct utsname u; + + if( uname( &u ) >= 0 ) + { + var_set( "JAMUNAME", + list_new( + list_new( + list_new( + list_new( + list_new( L0, + newstr( u.sysname ) ), + newstr( u.nodename ) ), + newstr( u.release ) ), + newstr( u.version ) ), + newstr( u.machine ) ), VAR_SET ); + } + } +# endif /* unix */ + + /* + * Jam defined variables OS, OSPLAT + */ + + var_defines( othersyms ); + + /* load up environment variables */ + + var_defines( use_environ ); + + /* Load up variables set on command line. */ + + for( n = 0; s = getoptval( optv, 's', n ); n++ ) + { + char *symv[2]; + symv[0] = s; + symv[1] = 0; + var_defines( symv ); + } + + /* Initialize built-in rules */ + + load_builtins(); + + /* Parse ruleset */ + + for( n = 0; s = getoptval( optv, 'f', n ); n++ ) + parse_file( s ); + + if( !n ) + parse_file( "+" ); + + status = yyanyerrors(); + + /* Manually touch -t targets */ + + for( n = 0; s = getoptval( optv, 't', n ); n++ ) + touchtarget( s ); + + /* If an output file is specified, set globs.cmdout to that */ + + if( s = getoptval( optv, 'o', 0 ) ) + { + if( !( globs.cmdout = fopen( s, "w" ) ) ) + { + printf( "Failed to write to '%s'\n", s ); + exit( EXITBAD ); + } + globs.noexec++; + } + + /* Now make target */ + + if( !argc ) + status |= make( 1, &all, anyhow ); + else + status |= make( argc, argv, anyhow ); + + /* Widely scattered cleanup */ + + var_done(); + donerules(); + donestamps(); + donestr(); + + /* close cmdout */ + + if( globs.cmdout ) + fclose( globs.cmdout ); + + return status ? EXITBAD : EXITOK; +} diff --git a/src/tools/jam/jam.h b/src/tools/jam/jam.h new file mode 100644 index 0000000000..86be7a5015 --- /dev/null +++ b/src/tools/jam/jam.h @@ -0,0 +1,507 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * jam.h - includes and globals for jam + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 04/21/94 (seiwald) - DGUX is __DGUX__, not just __DGUX. + * 05/04/94 (seiwald) - new globs.jobs (-j jobs) + * 11/01/94 (wingerd) - let us define path of Jambase at compile time. + * 12/30/94 (wingerd) - changed command buffer size for NT (MS-DOS shell). + * 02/22/95 (seiwald) - Jambase now in /usr/local/lib. + * 04/30/95 (seiwald) - FreeBSD added. Live Free or Die. + * 05/10/95 (seiwald) - SPLITPATH character set up here. + * 08/20/95 (seiwald) - added LINUX. + * 08/21/95 (seiwald) - added NCR. + * 10/23/95 (seiwald) - added SCO. + * 01/03/96 (seiwald) - SINIX (nixdorf) added. + * 03/13/96 (seiwald) - Jambase now compiled in; remove JAMBASE variable. + * 04/29/96 (seiwald) - AIX now has 31 and 42 OSVERs. + * 11/21/96 (peterk) - added BeOS with MW CW mwcc + * 12/21/96 (seiwald) - OSPLAT now defined for NT. + * 07/19/99 (sickel) - Mac OS X Server and Client support added + * 02/18/00 (belmonte)- Support for Cygwin. + * 09/12/00 (seiwald) - OSSYMS split to OSMAJOR/OSMINOR/OSPLAT + * 12/29/00 (seiwald) - OSVER dropped. + */ + +/* + * VMS, OPENVMS + */ + +# ifdef VMS + +# define unlink remove + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define OSMINOR "OS=VMS" +# define OSMAJOR "VMS=true" +# define OS_VMS +# define MAXLINE 1024 /* longest 'together' actions */ +# define SPLITPATH ',' +# define EXITOK 1 +# define EXITBAD 0 +# define DOWNSHIFT_PATHS + +/* Do any of these work? */ +# if defined( VAX ) || defined( __VAX ) || defined( vax ) +# define OSPLAT "OSPLAT=VAX" +# endif + +# endif + +/* + * Windows NT + */ + +# ifdef NT + +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define OSMAJOR "NT=true" +# define OSMINOR "OS=NT" +# define OS_NT +# define SPLITPATH ';' +# define MAXLINE 996 /* longest 'together' actions */ +# define USE_EXECUNIX +# define USE_PATHUNIX +# define PATH_DELIM '\\' +# define DOWNSHIFT_PATHS + +/* AS400 cross-compile from NT */ + +# ifdef AS400 +# undef OSMINOR +# undef OSMAJOR +# define OSMAJOR "AS400=true" +# define OSMINOR "OS=AS400" +# define OS_AS400 +# endif + +# endif + +/* + * Windows MingW32 + */ + +# ifdef MINGW + +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define OSMAJOR "MINGW=true" +# define OSMINOR "OS=MINGW" +# define OS_NT +# define SPLITPATH ';' +# define MAXLINE 996 /* longest 'together' actions */ +# define USE_EXECUNIX +# define USE_PATHUNIX +# define PATH_DELIM '\\' +# define DOWNSHIFT_PATHS + +# endif + +/* + * OS2 + */ + +# ifdef __OS2__ + +# include +# include +# include +# include +# include +# include +# include +# include + +# define OSMAJOR "OS2=true" +# define OSMINOR "OS=OS2" +# define OS_OS2 +# define SPLITPATH ';' +# define MAXLINE 996 /* longest 'together' actions */ +# define USE_EXECUNIX +# define USE_PATHUNIX +# define PATH_DELIM '\\' +# define DOWNSHIFT_PATHS + +# endif + +/* + * Macintosh MPW + */ + +# ifdef macintosh + +# include +# include +# include +# include + +# define OSMAJOR "MAC=true" +# define OSMINOR "OS=MAC" +# define OS_MAC +# define SPLITPATH ',' +# define DOWNSHIFT_PATHS + +# endif + +/* + * God fearing UNIX + */ + +# ifndef OSMINOR + +# define OSMAJOR "UNIX=true" +# define USE_EXECUNIX +# define USE_FILEUNIX +# define USE_PATHUNIX +# define PATH_DELIM '/' + +# ifdef _AIX +# define unix +# define OSMINOR "OS=AIX" +# define OS_AIX +# define NO_VFORK +# endif +# ifdef AMIGA +# define OSMINOR "OS=AMIGA" +# define OS_AMIGA +# endif +# ifdef __BEOS__ +# define unix +# define OSMINOR "OS=BEOS" +# define OS_BEOS +# define NO_VFORK +# endif +# ifdef __bsdi__ +# define OSMINOR "OS=BSDI" +# define OS_BSDI +# endif +# if defined (COHERENT) && defined (_I386) +# define OSMINOR "OS=COHERENT" +# define OS_COHERENT +# define NO_VFORK +# endif +# ifdef __cygwin__ +# define OSMINOR "OS=CYGWIN" +# define OS_CYGWIN +# endif +# ifdef __FreeBSD__ +# define OSMINOR "OS=FREEBSD" +# define OS_FREEBSD +# endif +# ifdef __DGUX__ +# define OSMINOR "OS=DGUX" +# define OS_DGUX +# endif +# ifdef __hpux +# define OSMINOR "OS=HPUX" +# define OS_HPUX +# endif +# ifdef __OPENNT +# define unix +# define OSMINOR "OS=INTERIX" +# define OS_INTERIX +# define NO_VFORK +# endif +# ifdef __sgi +# define OSMINOR "OS=IRIX" +# define OS_IRIX +# define NO_VFORK +# endif +# ifdef __ISC +# define OSMINOR "OS=ISC" +# define OS_ISC +# define NO_VFORK +# endif +# ifdef linux +# define OSMINOR "OS=LINUX" +# define OS_LINUX +# endif +# ifdef __Lynx__ +# define OSMINOR "OS=LYNX" +# define OS_LYNX +# define NO_VFORK +# define unix +# endif +# ifdef __MACHTEN__ +# define OSMINOR "OS=MACHTEN" +# define OS_MACHTEN +# endif +# ifdef mpeix +# define unix +# define OSMINOR "OS=MPEIX" +# define OS_MPEIX +# define NO_VFORK +# endif +# ifdef __MVS__ +# define unix +# define OSMINOR "OS=MVS" +# define OS_MVS +# endif +# ifdef _ATT4 +# define OSMINOR "OS=NCR" +# define OS_NCR +# endif +# ifdef __NetBSD__ +# define unix +# define OSMINOR "OS=NETBSD" +# define OS_NETBSD +# define NO_VFORK +# endif +# ifdef __QNX__ +# ifdef __QNXNTO__ +# define OSMINOR "OS=QNXNTO" +# define OS_QNXNTO +# else +# define unix +# define OSMINOR "OS=QNX" +# define OS_QNX +# define NO_VFORK +# define MAXLINE 996 +# endif +# endif +# ifdef NeXT +# ifdef __APPLE__ +# define OSMINOR "OS=RHAPSODY" +# define OS_RHAPSODY +# else +# define OSMINOR "OS=NEXT" +# define OS_NEXT +# endif +# endif +# ifdef __APPLE__ +# define unix +# define OSMINOR "OS=MACOSX" +# define OS_MACOSX +# endif +# ifdef __osf__ +# define OSMINOR "OS=OSF" +# define OS_OSF +# endif +# ifdef _SEQUENT_ +# define OSMINOR "OS=PTX" +# define OS_PTX +# endif +# ifdef M_XENIX +# define OSMINOR "OS=SCO" +# define OS_SCO +# define NO_VFORK +# endif +# ifdef sinix +# define unix +# define OSMINOR "OS=SINIX" +# define OS_SINIX +# endif +# ifdef sun +# if defined(__svr4__) || defined(__SVR4) +# define OSMINOR "OS=SOLARIS" +# define OS_SOLARIS +# else +# define OSMINOR "OS=SUNOS" +# define OS_SUNOS +# endif +# endif +# ifdef ultrix +# define OSMINOR "OS=ULTRIX" +# define OS_ULTRIX +# endif +# ifdef _UNICOS +# define OSMINOR "OS=UNICOS" +# define OS_UNICOS +# endif +# if defined(__USLC__) && !defined(M_XENIX) +# define OSMINOR "OS=UNIXWARE" +# define OS_UNIXWARE +# endif +# ifndef OSMINOR +# define OSMINOR "OS=UNKNOWN" +# endif + +/* All the UNIX includes */ + +# include +# include + +# ifndef OS_MPEIX +# include +# endif + +# include +# include +# include +# include +# include +# include + +# ifndef OS_QNX +# include +# endif + +# ifndef OS_ULTRIX +# include +# endif + +# if !defined(OS_BSDI) && \ + !defined(OS_FREEBSD) && \ + !defined(OS_NEXT) && \ + !defined(OS_MACHTEN) && \ + !defined(OS_MACOSX) && \ + !defined(OS_RHAPSODY) && \ + !defined(OS_MVS) +# include +# endif + +# endif + +/* + * OSPLAT definitions - suppressed when it's a one-of-a-kind + */ + +# if defined( _M_PPC ) || \ + defined( PPC ) || \ + defined( ppc ) || \ + defined( __powerpc__ ) || \ + defined( __ppc__ ) +# define OSPLAT "OSPLAT=PPC" +# endif + +# if defined( _ALPHA_ ) || \ + defined( __alpha__ ) +# define OSPLAT "OSPLAT=AXP" +# endif + +# if defined( _i386_ ) || \ + defined( __i386__ ) || \ + defined( _M_IX86 ) +# if !defined( OS_FREEBSD ) && \ + !defined( OS_OS2 ) && \ + !defined( OS_AS400 ) +# define OSPLAT "OSPLAT=X86" +# endif +# endif + +# ifdef __sparc__ +# if !defined( OS_SUNOS ) && \ + !defined( OS_SOLARIS ) +# define OSPLAT "OSPLAT=SPARC" +# endif +# endif + +# ifdef __mips__ +# if !defined( OS_SGI ) +# define OSPLAT "OSPLAT=MIPS" +# endif +# endif + +# ifdef __arm__ +# define OSPLAT "OSPLAT=ARM" +# endif + +# if defined( __ia64__ ) || \ + defined( __IA64__ ) || \ + defined( _M_IA64 ) +# define OSPLAT "OSPLAT=IA64" +# endif + +# ifdef __s390__ +# define OSPLAT "OSPLAT=390" +# endif + +# ifndef OSPLAT +# define OSPLAT "" +# endif + +/* + * Jam implementation misc. + */ + +# ifndef MAXLINE +# define MAXLINE 10240 /* longest 'together' actions' */ +# endif + +# ifndef EXITOK +# define EXITOK 0 +# define EXITBAD 1 +# endif + +# ifndef SPLITPATH +# define SPLITPATH ':' +# endif + +/* You probably don't need to muck with these. */ + +# define MAXSYM 1024 /* longest symbol in the environment */ +# define MAXJPATH 1024 /* longest filename */ + +# define MAXJOBS 64 /* silently enforce -j limit */ +# define MAXARGC 32 /* words in $(JAMSHELL) */ + +/* Jam private definitions below. */ + +# define DEBUG_MAX 10 + +struct globs { + int noexec; + int jobs; + int quitquick; + int newestfirst; /* build newest sources first */ + char debug[DEBUG_MAX]; + FILE *cmdout; /* print cmds, not run them */ +} ; + +extern struct globs globs; + +# define DEBUG_MAKE ( globs.debug[ 1 ] ) /* show actions when executed */ +# define DEBUG_MAKEQ ( globs.debug[ 2 ] ) /* show even quiet actions */ +# define DEBUG_EXEC ( globs.debug[ 2 ] ) /* show text of actons */ +# define DEBUG_MAKEPROG ( globs.debug[ 3 ] ) /* show progress of make0 */ +# define DEBUG_BIND ( globs.debug[ 3 ] ) /* show when files bound */ + +# define DEBUG_EXECCMD ( globs.debug[ 4 ] ) /* show execcmds()'s work */ + +# define DEBUG_COMPILE ( globs.debug[ 5 ] ) /* show rule invocations */ + +# define DEBUG_HEADER ( globs.debug[ 6 ] ) /* show result of header scan */ +# define DEBUG_BINDSCAN ( globs.debug[ 6 ] ) /* show result of dir scan */ +# define DEBUG_SEARCH ( globs.debug[ 6 ] ) /* show attempts at binding */ + +# define DEBUG_VARSET ( globs.debug[ 7 ] ) /* show variable settings */ +# define DEBUG_VARGET ( globs.debug[ 8 ] ) /* show variable fetches */ +# define DEBUG_VAREXP ( globs.debug[ 8 ] ) /* show variable expansions */ +# define DEBUG_IF ( globs.debug[ 8 ] ) /* show 'if' calculations */ +# define DEBUG_LISTS ( globs.debug[ 9 ] ) /* show list manipulation */ +# define DEBUG_SCAN ( globs.debug[ 9 ] ) /* show scanner tokens */ +# define DEBUG_MEM ( globs.debug[ 9 ] ) /* show memory use */ + diff --git a/src/tools/jam/jambase.c b/src/tools/jam/jambase.c new file mode 100644 index 0000000000..ffa2cbac73 --- /dev/null +++ b/src/tools/jam/jambase.c @@ -0,0 +1,1219 @@ +/* Generated by mkjambase from Jambase */ +char *jambase[] = { +/* Jambase */ +"if $(NT)\n", +"{\n", +"MV ?= move /y ;\n", +"CP ?= copy ;\n", +"RM ?= del /f/q ;\n", +"SLASH ?= \\\\ ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .obj ;\n", +"SUFEXE ?= .exe ;\n", +"if $(BCCROOT)\n", +"{\n", +"Echo \"Compiler is Borland C++\" ;\n", +"AR ?= tlib /C /P64 ;\n", +"CC ?= bcc32 ;\n", +"CCFLAGS ?= -v -w- -q -DWIN -tWR -tWM -tWC ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) -P ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"STDLIBPATH ?= $(BCCROOT)\\\\lib ;\n", +"STDHDRS ?= $(BCCROOT)\\\\include ;\n", +"NOARSCAN ?= true ;\n", +"}\n", +"else if $(MSVC)\n", +"{\n", +"Echo \"Compiler is Microsoft Visual C++ 16 bit\" ;\n", +"AR ?= lib /nologo ;\n", +"CC ?= cl /nologo ;\n", +"CCFLAGS ?= /D \\\"WIN\\\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"LINKLIBS ?= \n", +"$(MSVC)\\\\lib\\\\mlibce.lib\n", +"$(MSVC)\\\\lib\\\\oldnames.lib\n", +";\n", +"LINKLIBS ?= ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"STDHDRS ?= $(MSVC)\\\\include ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(MSVCNT)\n", +"{\n", +"Echo \"Compiler is Microsoft Visual C++\" ;\n", +"AR ?= lib ;\n", +"AS ?= masm386 ;\n", +"CC ?= cl /nologo ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= link /nologo ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= $(MSVCNT)\\\\lib\\\\advapi32.lib\n", +"$(MSVCNT)\\\\lib\\\\libc.lib\n", +"$(MSVCNT)\\\\lib\\\\oldnames.lib\n", +"$(MSVCNT)\\\\lib\\\\kernel32.lib ;\n", +"OPTIM ?= \"\" ;\n", +"STDHDRS ?= $(MSVCNT)\\\\include ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else\n", +"{\n", +"Exit On NT, set BCCROOT, MSVCNT, or MSVC to the root of the\n", +"Borland or Microsoft directories. ;\n", +"}\n", +"}\n", +"else if $(MINGW)\n", +"{\n", +"Echo \"MingW32\" ;\n", +"CC ?= gcc ;\n", +"C++ ?= g++ ;\n", +"CCFLAGS += -DMINGW ;\n", +"RANLIB ?= \"ranlib\" ;\n", +"SUFEXE ?= .exe ;\n", +"}\n", +"else if $(OS2)\n", +"{\n", +"WATCOM ?= $(watcom) ;\n", +"if ! $(WATCOM)\n", +"{\n", +"Exit On OS2, set WATCOM to the root of the Watcom directory. ;\n", +"}\n", +"AR ?= wlib ;\n", +"BINDIR ?= \\\\os2\\\\apps ;\n", +"CC ?= wcc386 ;\n", +"CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\\\h ; # zq=quiet\n", +"C++ ?= wpp386 ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"CP ?= copy ;\n", +"DOT ?= . ;\n", +"DOTDOT ?= .. ;\n", +"LINK ?= wcl386 ;\n", +"LINKFLAGS ?= /zq ; # zq=quiet\n", +"LINKLIBS ?= ;\n", +"MV ?= move ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"RM ?= del /f ;\n", +"SLASH ?= \\\\ ;\n", +"STDHDRS ?= $(WATCOM)\\\\h ;\n", +"SUFEXE ?= .exe ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .obj ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"C++ ?= cxx ;\n", +"C++FLAGS ?= ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= ;\n", +"CHMOD ?= set file/prot= ;\n", +"CP ?= copy/replace ;\n", +"CRELIB ?= true ;\n", +"DOT ?= [] ;\n", +"DOTDOT ?= [-] ;\n", +"EXEMODE ?= (w:e) ;\n", +"FILEMODE ?= (w:r) ;\n", +"HDRS ?= ;\n", +"LINK ?= link ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= ;\n", +"MKDIR ?= create/dir ;\n", +"MV ?= rename ;\n", +"OPTIM ?= \"\" ;\n", +"RM ?= delete ;\n", +"RUNVMS ?= mcr ;\n", +"SHELLMODE ?= (w:er) ;\n", +"SLASH ?= . ;\n", +"STDHDRS ?= decc$library_include ;\n", +"SUFEXE ?= .exe ;\n", +"SUFLIB ?= .olb ;\n", +"SUFOBJ ?= .obj ;\n", +"switch $(OS) \n", +"{\n", +"case OPENVMS : CCFLAGS ?= /stand=vaxc ;\n", +"case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ;\n", +"}\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"local OPT ;\n", +"CW ?= \"{CW}\" ;\n", +"MACHDRS ?=\n", +"\"$(UMACHDRS):Universal:Interfaces:CIncludes\"\n", +"\"$(CW):MSL:MSL_C:MSL_Common:Include\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Include\" ;\n", +"MACLIBS ?=\n", +"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib\"\n", +"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib\" ;\n", +"MPWLIBS ?= \n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib\" ;\n", +"MPWNLLIBS ?= \n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib\" ;\n", +"SIOUXHDRS ?= ;\n", +"SIOUXLIBS ?= \n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib\" \n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib\" ;\n", +"C++ ?= mwcppc ;\n", +"C++FLAGS ?= -w off -nomapcr ;\n", +"CC ?= mwcppc ;\n", +"CCFLAGS ?= -w off -nomapcr ;\n", +"CP ?= duplicate -y ;\n", +"DOT ?= \":\" ;\n", +"DOTDOT ?= \"::\" ;\n", +"HDRS ?= $(MACHDRS) $(MPWHDRS) ;\n", +"LINK ?= mwlinkppc ;\n", +"LINKFLAGS ?= -mpwtool -warn ; \n", +"LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ; \n", +"MKDIR ?= newfolder ;\n", +"MV ?= rename -y ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"RM ?= delete -y ;\n", +"SLASH ?= \":\" ;\n", +"STDHDRS ?= ; \n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .o ;\n", +"}\n", +"else if $(OS) = BEOS && $(METROWERKS)\n", +"{\n", +"AR ?= mwld -xml -o ;\n", +"BINDIR ?= /boot/apps ;\n", +"CC ?= mwcc ;\n", +"CCFLAGS ?= -nosyspath ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= -nosyspath ;\n", +"FORTRAN ?= \"\" ;\n", +"LIBDIR ?= /boot/develop/libraries ;\n", +"LINK ?= mwld ;\n", +"LINKFLAGS ?= \"\" ;\n", +"MANDIR ?= /boot/documentation/\"Shell Tools\"/HTML ;\n", +"NOARSCAN ?= true ;\n", +"STDHDRS ?= /boot/develop/headers/posix ;\n", +"}\n", +"else if $(OS) = BEOS \n", +"{\n", +"BINDIR ?= /boot/apps ;\n", +"CC ?= gcc ;\n", +"C++ ?= $(CC) ;\n", +"FORTRAN ?= \"\" ;\n", +"LIBDIR ?= /boot/develop/libraries ;\n", +"LINK ?= gcc ;\n", +"LINKLIBS ?= -lnet ;\n", +"NOARSCAN ?= true ;\n", +"STDHDRS ?= /boot/develop/headers/posix ;\n", +"}\n", +"else if $(UNIX)\n", +"{\n", +"switch $(OS)\n", +"{\n", +"case AIX :\n", +"LINKLIBS ?= -lbsd ;\n", +"case AMIGA :\n", +"CC ?= gcc ;\n", +"YACC ?= bison -y ;\n", +"case CYGWIN : \n", +"CC ?= gcc ;\n", +"CCFLAGS += -D__cygwin__ ;\n", +"LEX ?= flex ;\n", +"JAMSHELL ?= sh -c ;\n", +"RANLIB ?= \"\" ;\n", +"SUFEXE ?= .exe ;\n", +"YACC ?= bison -y ;\n", +"case DGUX :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"case HPUX :\n", +"RANLIB ?= \"\" ;\n", +"case INTERIX :\n", +"CC ?= gcc ;\n", +"JAMSHELL ?= sh -c ;\n", +"RANLIB ?= \"\" ;\n", +"case IRIX :\n", +"RANLIB ?= \"\" ;\n", +"case MPEIX :\n", +"CC ?= gcc ;\n", +"C++ ?= gcc ;\n", +"CCFLAGS += -D_POSIX_SOURCE ;\n", +"HDRS += /usr/include ;\n", +"RANLIB ?= \"\" ; \n", +"NOARSCAN ?= true ;\n", +"NOARUPDATE ?= true ;\n", +"case MVS :\n", +"RANLIB ?= \"\" ; \n", +"case NEXT :\n", +"AR ?= libtool -o ;\n", +"RANLIB ?= \"\" ;\n", +"case MACOSX :\n", +"AR ?= libtool -o ;\n", +"C++ ?= c++ ;\n", +"MANDIR ?= /usr/local/share/man ;\n", +"RANLIB ?= \"\" ;\n", +"case NCR :\n", +"RANLIB ?= \"\" ;\n", +"case PTX :\n", +"RANLIB ?= \"\" ;\n", +"case QNX :\n", +"AR ?= wlib ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= -Q ; # quiet\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= -Q ; # quiet\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= -Q ; # quiet\n", +"NOARSCAN ?= true ;\n", +"RANLIB ?= \"\" ;\n", +"case SCO :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"case SINIX :\n", +"RANLIB ?= \"\" ;\n", +"case SOLARIS :\n", +"RANLIB ?= \"\" ;\n", +"AR ?= \"/usr/ccs/bin/ar ru\" ;\n", +"case UNICOS :\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= -O0 ;\n", +"case UNIXWARE :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"}\n", +"CCFLAGS ?= ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"CHMOD ?= chmod ;\n", +"CHGRP ?= chgrp ;\n", +"CHOWN ?= chown ;\n", +"LEX ?= lex ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"LINKLIBS ?= ;\n", +"OPTIM ?= -O ;\n", +"RANLIB ?= ranlib ;\n", +"YACC ?= yacc ;\n", +"YACCGEN ?= .c ;\n", +"YACCFILES ?= y.tab ;\n", +"YACCFLAGS ?= -d ;\n", +"}\n", +"AR ?= ar ru ;\n", +"AS ?= as ;\n", +"ASFLAGS ?= ;\n", +"AWK ?= awk ;\n", +"BINDIR ?= /usr/local/bin ;\n", +"C++ ?= cc ;\n", +"C++FLAGS ?= ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= ;\n", +"CP ?= cp -f ;\n", +"CRELIB ?= ;\n", +"DOT ?= . ;\n", +"DOTDOT ?= .. ;\n", +"EXEMODE ?= 711 ;\n", +"FILEMODE ?= 644 ;\n", +"FORTRAN ?= f77 ;\n", +"FORTRANFLAGS ?= ;\n", +"HDRS ?= ;\n", +"INSTALLGRIST ?= installed ;\n", +"JAMFILE ?= Jamfile ;\n", +"JAMRULES ?= Jamrules ;\n", +"LEX ?= ;\n", +"LIBDIR ?= /usr/local/lib ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= ;\n", +"LINKLIBS ?= ;\n", +"LN ?= ln ;\n", +"MANDIR ?= /usr/local/man ;\n", +"MKDIR ?= mkdir ;\n", +"MV ?= mv -f ;\n", +"OPTIM ?= ;\n", +"RCP ?= rcp ;\n", +"RM ?= rm -f ;\n", +"RSH ?= rsh ;\n", +"SED ?= sed ;\n", +"SHELLHEADER ?= \"#!/bin/sh\" ;\n", +"SHELLMODE ?= 755 ;\n", +"SLASH ?= / ;\n", +"STDHDRS ?= /usr/include ;\n", +"SUFEXE ?= \"\" ;\n", +"SUFLIB ?= .a ;\n", +"SUFOBJ ?= .o ;\n", +"UNDEFFLAG ?= \"-u _\" ;\n", +"YACC ?= ;\n", +"YACCGEN ?= ;\n", +"YACCFILES ?= ;\n", +"YACCFLAGS ?= ;\n", +"HDRPATTERN = \n", +"\"^[ ]*#[ ]*include[ ]*[<\\\"]([^\\\">]*)[\\\">].*$\" ;\n", +"OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ;\n", +"Depends all : shell files lib exe obj ;\n", +"Depends all shell files lib exe obj : first ;\n", +"NotFile all first shell files lib exe obj dirs clean uninstall ;\n", +"Always clean uninstall ;\n", +"rule As\n", +"{\n", +"Depends $(<) : $(>) ;\n", +"ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;\n", +"ASHDRS on $(<) = [ FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ;\n", +"}\n", +"rule Bulk\n", +"{\n", +"local i ;\n", +"for i in $(>)\n", +"{\n", +"File $(i:D=$(<)) : $(i) ;\n", +"}\n", +"}\n", +"rule Cc\n", +"{\n", +"Depends $(<) : $(>) ;\n", +"if $(RELOCATE)\n", +"{\n", +"CcMv $(<) : $(>) ;\n", +"}\n", +"CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) $(OPTIM) ;\n", +"CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;\n", +"CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;\n", +"}\n", +"rule C++\n", +"{\n", +"Depends $(<) : $(>) ;\n", +"if $(RELOCATE)\n", +"{\n", +"CcMv $(<) : $(>) ;\n", +"}\n", +"C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) $(OPTIM) ;\n", +"CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;\n", +"CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;\n", +"}\n", +"rule Chmod\n", +"{\n", +"if $(CHMOD) { Chmod1 $(<) ; }\n", +"}\n", +"rule File\n", +"{\n", +"Depends files : $(<) ;\n", +"Depends $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MODE on $(<) = $(FILEMODE) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule Fortran\n", +"{\n", +"Depends $(<) : $(>) ;\n", +"}\n", +"rule GenFile \n", +"{\n", +"local _t = [ FGristSourceFiles $(<) ] ;\n", +"local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ;\n", +"Depends $(_t) : $(_s) $(>[2-]) ;\n", +"GenFile1 $(_t) : $(_s) $(>[2-]) ;\n", +"Clean clean : $(_t) ;\n", +"}\n", +"rule GenFile1\n", +"{\n", +"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"}\n", +"rule HardLink\n", +"{\n", +"Depends files : $(<) ;\n", +"Depends $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"}\n", +"rule HdrRule\n", +"{\n", +"local s = $(>:G=$(HDRGRIST:E)) ;\n", +"Includes $(<) : $(s) ;\n", +"SEARCH on $(s) = $(HDRSEARCH) ;\n", +"NoCare $(s) ;\n", +"HDRSEARCH on $(s) = $(HDRSEARCH) ;\n", +"HDRSCAN on $(s) = $(HDRSCAN) ;\n", +"HDRRULE on $(s) = $(HDRRULE) ;\n", +"HDRGRIST on $(s) = $(HDRGRIST) ;\n", +"}\n", +"rule InstallInto\n", +"{\n", +"local i t ;\n", +"t = $(>:G=$(INSTALLGRIST)) ;\n", +"Depends install : $(t) ;\n", +"Clean uninstall : $(t) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MakeLocate $(t) : $(<) ;\n", +"for i in $(>)\n", +"{\n", +"local tt = $(i:G=$(INSTALLGRIST)) ;\n", +"Depends $(tt) : $(i) ;\n", +"Install $(tt) : $(i) ;\n", +"Chmod $(tt) ;\n", +"if $(OWNER) && $(CHOWN) \n", +"{ \n", +"Chown $(tt) ;\n", +"OWNER on $(tt) = $(OWNER) ;\n", +"}\n", +"if $(GROUP) && $(CHGRP) \n", +"{ \n", +"Chgrp $(tt) ;\n", +"GROUP on $(tt) = $(GROUP) ;\n", +"}\n", +"}\n", +"}\n", +"rule InstallBin\n", +"{\n", +"local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;\n", +"InstallInto $(<) : $(_t) ;\n", +"MODE on $(_t:G=$(INSTALLGRIST)) = $(EXEMODE) ;\n", +"}\n", +"rule InstallFile\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;\n", +"}\n", +"rule InstallLib\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;\n", +"}\n", +"rule InstallMan\n", +"{\n", +"local i s d ;\n", +"for i in $(>)\n", +"{\n", +"switch $(i:S)\n", +"{\n", +"case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ;\n", +"case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ;\n", +"case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ;\n", +"case .n : s = n ; case .man : s = 1 ;\n", +"}\n", +"d = man$(s) ;\n", +"InstallInto $(d:R=$(<)) : $(i) ;\n", +"}\n", +"MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;\n", +"}\n", +"rule InstallShell\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=$(INSTALLGRIST)) = $(SHELLMODE) ;\n", +"}\n", +"rule Lex\n", +"{\n", +"LexMv $(<) : $(>) ;\n", +"Depends $(<) : $(>) ;\n", +"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n", +"Clean clean : $(<) ;\n", +"}\n", +"rule Library\n", +"{\n", +"LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n", +"Objects $(>) ;\n", +"}\n", +"rule LibraryFromObjects\n", +"{\n", +"local _i _l _s ;\n", +"_s = [ FGristFiles $(>) ] ;\n", +"_l = $(<:S=$(SUFLIB)) ;\n", +"if $(KEEPOBJS)\n", +"{\n", +"Depends obj : $(_s) ;\n", +"}\n", +"else\n", +"{\n", +"Depends lib : $(_l) ;\n", +"}\n", +"if ! $(_l:D)\n", +"{\n", +"MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ;\n", +"}\n", +"if $(NOARSCAN) \n", +"{ \n", +"Depends $(_l) : $(_s) ;\n", +"}\n", +"else\n", +"{\n", +"Depends $(_l) : $(_l)($(_s:BS)) ;\n", +"for _i in $(_s)\n", +"{\n", +"Depends $(_l)($(_i:BS)) : $(_i) ;\n", +"}\n", +"}\n", +"Clean clean : $(_l) ;\n", +"if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; }\n", +"Archive $(_l) : $(_s) ;\n", +"if $(RANLIB) { Ranlib $(_l) ; }\n", +"if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; }\n", +"}\n", +"rule Link\n", +"{\n", +"MODE on $(<) = $(EXEMODE) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule LinkLibraries\n", +"{\n", +"local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n", +"Depends $(_t) : $(>:S=$(SUFLIB)) ;\n", +"NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ;\n", +"}\n", +"rule Main\n", +"{\n", +"MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n", +"Objects $(>) ;\n", +"}\n", +"rule MainFromObjects\n", +"{\n", +"local _s _t ;\n", +"_s = [ FGristFiles $(>) ] ;\n", +"_t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n", +"if $(_t) != $(<)\n", +"{\n", +"Depends $(<) : $(_t) ;\n", +"NotFile $(<) ;\n", +"}\n", +"Depends exe : $(_t) ;\n", +"Depends $(_t) : $(_s) ;\n", +"MakeLocate $(_t) : $(LOCATE_TARGET) ;\n", +"Clean clean : $(_t) ;\n", +"Link $(_t) : $(_s) ;\n", +"}\n", +"rule MakeLocate\n", +"{\n", +"if $(>)\n", +"{\n", +"LOCATE on $(<) = $(>) ;\n", +"Depends $(<) : $(>[1]) ;\n", +"MkDir $(>[1]) ;\n", +"}\n", +"}\n", +"rule MkDir\n", +"{\n", +"NoUpdate $(<) ;\n", +"if $(<) != $(DOT) && ! $($(<)-mkdir) \n", +"{\n", +"local s ;\n", +"$(<)-mkdir = true ;\n", +"MkDir1 $(<) ;\n", +"Depends dirs : $(<) ;\n", +"s = $(<:P) ;\n", +"if $(NT)\n", +"{\n", +"switch $(s)\n", +"{\n", +"case *: : s = ;\n", +"case *:\\\\ : s = ;\n", +"}\n", +"}\n", +"if $(s) && $(s) != $(<)\n", +"{\n", +"Depends $(<) : $(s) ;\n", +"MkDir $(s) ;\n", +"}\n", +"else if $(s)\n", +"{\n", +"NotFile $(s) ;\n", +"}\n", +"}\n", +"}\n", +"rule Object\n", +"{\n", +"Clean clean : $(<) ;\n", +"MakeLocate $(<) : $(LOCATE_TARGET) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ;\n", +"HDRRULE on $(>) = HdrRule ;\n", +"HDRSCAN on $(>) = $(HDRPATTERN) ;\n", +"HDRSEARCH on $(>) = \n", +"$(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(STDHDRS) ;\n", +"HDRGRIST on $(>) = $(HDRGRIST) ;\n", +"DEFINES on $(<) += $(DEFINES) ;\n", +"switch $(>:S)\n", +"{\n", +"case .asm : As $(<) : $(>) ;\n", +"case .c : Cc $(<) : $(>) ;\n", +"case .C : C++ $(<) : $(>) ;\n", +"case .cc : C++ $(<) : $(>) ;\n", +"case .cpp : C++ $(<) : $(>) ;\n", +"case .f : Fortran $(<) : $(>) ;\n", +"case .l : Cc $(<) : $(<:S=.c) ;\n", +"Lex $(<:S=.c) : $(>) ;\n", +"case .s : As $(<) : $(>) ;\n", +"case .y : Cc $(<) : $(<:S=$(YACCGEN)) ;\n", +"Yacc $(<:S=$(YACCGEN)) : $(>) ;\n", +"case * : UserObject $(<) : $(>) ;\n", +"}\n", +"}\n", +"rule ObjectCcFlags\n", +"{\n", +"CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n", +"}\n", +"rule ObjectC++Flags\n", +"{\n", +"C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n", +"}\n", +"rule ObjectDefines\n", +"{\n", +"local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;\n", +"DEFINES on $(s) += $(>) ;\n", +"CCDEFS on $(s) = [ on $(s) FDefines $(DEFINES) ] ;\n", +"}\n", +"rule ObjectHdrs\n", +"{\n", +"local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;\n", +"HDRS on $(s) += $(>) ;\n", +"CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] ;\n", +"}\n", +"rule Objects\n", +"{\n", +"local _i ;\n", +"for _i in [ FGristFiles $(<) ]\n", +"{\n", +"Object $(_i:S=$(SUFOBJ)) : $(_i) ;\n", +"Depends obj : $(_i:S=$(SUFOBJ)) ;\n", +"}\n", +"}\n", +"rule RmTemps\n", +"{\n", +"Temporary $(>) ;\n", +"}\n", +"rule Setuid\n", +"{\n", +"MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ;\n", +"}\n", +"rule Shell\n", +"{\n", +"Depends shell : $(<) ;\n", +"Depends $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MODE on $(<) = $(SHELLMODE) ;\n", +"Clean clean : $(<) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule SoftLink\n", +"{\n", +"Depends files : $(<) ;\n", +"Depends $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"Clean clean : $(<) ;\n", +"}\n", +"rule SubDir\n", +"{\n", +"local _top = $(<[1]) ;\n", +"if ! $($(_top))\n", +"{\n", +"if ! $(_top)\n", +"{\n", +"Exit SubDir syntax error ;\n", +"}\n", +"$(_top) = [ FSubDir $(<[2-]) ] ;\n", +"}\n", +"if ! $($(_top)-included)\n", +"{\n", +"$(_top)-included = TRUE ;\n", +"include $($(_top)RULES:E=$(JAMRULES:R=$($(_top)))) ;\n", +"}\n", +"local _s = [ FDirName $(<[2-]) ] ;\n", +"SUBDIR = $(_s:R=$($(_top))) ;\n", +"SUBDIR_TOKENS = $(<[2-]) ;\n", +"SEARCH_SOURCE = $(SUBDIR) ;\n", +"LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n", +"LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n", +"SOURCE_GRIST = [ FGrist $(<[2-]) ] ;\n", +"SUBDIRCCFLAGS = ;\n", +"SUBDIRC++FLAGS = ;\n", +"SUBDIRHDRS = ;\n", +"}\n", +"rule SubDirCcFlags\n", +"{\n", +"SUBDIRCCFLAGS += $(<) ;\n", +"}\n", +"rule SubDirC++Flags\n", +"{\n", +"SUBDIRC++FLAGS += $(<) ;\n", +"}\n", +"rule SubDirHdrs\n", +"{\n", +"SUBDIRHDRS += [ FDirName $(<) ] ;\n", +"}\n", +"rule SubInclude\n", +"{\n", +"local _s ;\n", +"if ! $($(<[1]))\n", +"{\n", +"Exit Top level of source tree has not been set with $(<[1]) ;\n", +"}\n", +"_s = [ FDirName $(<[2-]) ] ;\n", +"include $(JAMFILE:D=$(_s):R=$($(<[1]))) ;\n", +"}\n", +"rule Undefines\n", +"{\n", +"UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ;\n", +"}\n", +"rule UserObject\n", +"{\n", +"Exit \"Unknown suffix on\" $(>) \"- see UserObject rule in Jamfile(5).\" ;\n", +"}\n", +"rule Yacc\n", +"{\n", +"local _h ;\n", +"_h = $(<:BS=.h) ;\n", +"MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;\n", +"if $(YACC)\n", +"{\n", +"Depends $(<) $(_h) : $(>) ;\n", +"Yacc1 $(<) $(_h) : $(>) ;\n", +"YaccMv $(<) $(_h) : $(>) ;\n", +"Clean clean : $(<) $(_h) ;\n", +"}\n", +"Includes $(<) : $(_h) ;\n", +"}\n", +"rule FGrist\n", +"{\n", +"return $(<:J=!) ;\n", +"}\n", +"rule FGristFiles \n", +"{\n", +"return $(<:G=$(SOURCE_GRIST:E)) ;\n", +"}\n", +"rule FGristSourceFiles\n", +"{\n", +"if ! $(SOURCE_GRIST)\n", +"{\n", +"return $(<) ;\n", +"}\n", +"else \n", +"{\n", +"local _i _o ;\n", +"for _i in $(<)\n", +"{\n", +"switch $(_i)\n", +"{\n", +"case *.h : _o += $(_i) ;\n", +"case * : _o += $(_i:G=$(SOURCE_GRIST)) ;\n", +"}\n", +"}\n", +"return $(_o) ;\n", +"}\n", +"}\n", +"rule FSubDir\n", +"{\n", +"if ! $(<[1]) \n", +"{\n", +"return $(DOT) ;\n", +"} \n", +"else\n", +"{\n", +"local _i _d ;\n", +"_d = $(DOTDOT) ;\n", +"for _i in $(<[2-])\n", +"{\n", +"_d = $(_d:R=$(DOTDOT)) ;\n", +"}\n", +"return $(_d) ;\n", +"}\n", +"}\n", +"rule _makeCommon\n", +"{\n", +"if $($(<)[1]) && $($(<)[1]) = $($(>)[1])\n", +"{\n", +"$(<) = $($(<)[2-]) ;\n", +"$(>) = $($(>)[2-]) ;\n", +"_makeCommon $(<) : $(>) ;\n", +"}\n", +"}\n", +"rule FRelPath\n", +"{\n", +"local _l _r ;\n", +"_l = $(<) ;\n", +"_r = $(>) ;\n", +"_makeCommon _l : _r ;\n", +"_l = [ FSubDir $(_l) ] ;\n", +"_r = [ FDirName $(_r) ] ;\n", +"if $(_r) = $(DOT) {\n", +"return $(_l) ;\n", +"} else {\n", +"return $(_r:R=$(_l)) ;\n", +"}\n", +"}\n", +"rule FAppendSuffix\n", +"{\n", +"if $(>)\n", +"{\n", +"local _i _o ;\n", +"for _i in $(<)\n", +"{\n", +"if $(_i:S)\n", +"{\n", +"_o += $(_i) ;\n", +"}\n", +"else\n", +"{\n", +"_o += $(_i:S=$(>)) ;\n", +"}\n", +"}\n", +"return $(_o) ;\n", +"}\n", +"else\n", +"{\n", +"return $(<) ;\n", +"}\n", +"}\n", +"rule FQuote { return \\\\\\\"$(<)\\\\\\\" ; }\n", +"rule FDefines { return -D$(<) ; }\n", +"rule FIncludes { return -I$(<) ; }\n", +"rule FDirName\n", +"{\n", +"local _i ;\n", +"local _s = $(DOT) ;\n", +"for _i in $(<)\n", +"{\n", +"_s = $(_i:R=$(_s)) ;\n", +"}\n", +"return $(_s) ;\n", +"}\n", +"if $(OS2)\n", +"{\n", +"rule FQuote { return \\\"$(<)\\\" ; }\n", +"rule FIncludes { return /I$(<) ; }\n", +"}\n", +"else if $(NT)\n", +"{\n", +"rule FDefines { return /D$(<) ; }\n", +"rule FIncludes { return /I$(<) ; }\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"rule FQuote { return \\\"$(<)\\\" ; }\n", +"rule FDefines { return \"-define '$(<)'\" ; }\n", +"rule FIncludes { return \\\"$(<:J=,)\\\" ; }\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"rule FQuote { return \\\"\\\"\\\"$(<)\\\"\\\"\\\" ; }\n", +"rule FDefines { return \"/define=( $(<:J=,) )\" ; }\n", +"rule FIncludes { return \"/inc=( $(<:J=,) )\" ; }\n", +"rule FDirName\n", +"{\n", +"local _s _i ;\n", +"if ! $(<)\n", +"{\n", +"_s = $(DOT) ;\n", +"}\n", +"else \n", +"{\n", +"switch $(<[1])\n", +"{\n", +"case *:* : _s = $(<[1]) ;\n", +"case \\\\[*\\\\] : _s = $(<[1]) ;\n", +"case * : _s = [.$(<[1])] ;\n", +"}\n", +"for _i in [.$(<[2-])]\n", +"{\n", +"_s = $(_i:R=$(_s)) ;\n", +"}\n", +"}\n", +"return $(_s) ;\n", +"}\n", +"}\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) $(>)\n", +"}\n", +"actions As\n", +"{\n", +"$(AS) $(ASFLAGS) $(ASHDRS) -o $(<) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c -o $(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c -o $(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions Chgrp\n", +"{\n", +"$(CHGRP) $(GROUP) $(<)\n", +"}\n", +"actions Chmod1\n", +"{\n", +"$(CHMOD) $(MODE) $(<)\n", +"}\n", +"actions Chown\n", +"{\n", +"$(CHOWN) $(OWNER) $(<)\n", +"}\n", +"actions piecemeal together existing Clean\n", +"{\n", +"$(RM) $(>)\n", +"}\n", +"actions File\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"actions GenFile1\n", +"{\n", +"$(>[1]) $(<) $(>[2-])\n", +"}\n", +"actions Fortran\n", +"{\n", +"$(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>)\n", +"}\n", +"actions HardLink\n", +"{\n", +"$(RM) $(<) && $(LN) $(>) $(<)\n", +"}\n", +"actions Install\n", +"{\n", +"$(CP) $(>) $(<) \n", +"}\n", +"actions Lex\n", +"{\n", +"$(LEX) $(>)\n", +"}\n", +"actions LexMv\n", +"{\n", +"$(MV) lex.yy.c $(<)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) \n", +"}\n", +"actions MkDir1\n", +"{\n", +"$(MKDIR) $(<)\n", +"}\n", +"actions together Ranlib\n", +"{\n", +"$(RANLIB) $(<)\n", +"}\n", +"actions quietly updated piecemeal together RmTemps\n", +"{\n", +"$(RM) $(>)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(AWK) '\n", +"NR == 1 { print \"$(SHELLHEADER)\" }\n", +"NR == 1 && /^[#:]/ { next }\n", +"/^##/ { next }\n", +"{ print }\n", +"' < $(>) > $(<)\n", +"}\n", +"actions SoftLink\n", +"{\n", +"$(RM) $(<) && $(LN) -s $(>) $(<)\n", +"}\n", +"actions Yacc1\n", +"{\n", +"$(YACC) $(YACCFLAGS) $(>)\n", +"}\n", +"actions YaccMv\n", +"{\n", +"$(MV) $(YACCFILES).c $(<[1])\n", +"$(MV) $(YACCFILES).h $(<[2])\n", +"}\n", +"if $(RELOCATE)\n", +"{\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions ignore CcMv\n", +"{\n", +"[ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<)\n", +"}\n", +"}\n", +"if $(NOARUPDATE)\n", +"{\n", +"actions Archive\n", +"{\n", +"$(AR) $(<) $(>)\n", +"}\n", +"}\n", +"if $(NT) && $(MSVCNT)\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"if exist $(<) set _$(<:B)_=$(<)\n", +"$(AR) /out:$(<) %_$(<:B)_% $(>)\n", +"}\n", +"actions As\n", +"{\n", +"$(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul;\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) /c /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) /Tp$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"}\n", +"else if $(NT) && $(MSVC)\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) -+$(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) /c /Fo$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) /c /Fo$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) /Tp$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"}\n", +"else if $(NT) && $(BCCROOT)\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) -+$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c -o$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c -o$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"}\n", +"else if $(OS2) && $(WATCOM)\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) $(<) +-$(>) \n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) /Fo=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) /Fo=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"actions updated together piecemeal Archive \n", +"{\n", +"lib/replace $(<) $(>[1]) ,$(>[2-])\n", +"}\n", +"actions Cc\n", +"{ \n", +"$(CC)/obj=$(<) $(CCFLAGS) $(CCDEFS) $(CCHDRS) $(>) \n", +"}\n", +"actions C++\n", +"{ \n", +"$(C++)/obj=$(<) $(C++FLAGS) $(CCDEFS) $(CCHDRS) $(>) \n", +"}\n", +"actions piecemeal together existing Clean\n", +"{\n", +"$(RM) $(>[1]);* ,$(>[2-]);*\n", +"}\n", +"actions together quietly CreLib\n", +"{\n", +"if f$search(\"$(<)\") .eqs. \"\" then lib/create $(<)\n", +"}\n", +"actions GenFile1\n", +"{\n", +"mcr $(>[1]) $(<) $(>[2-])\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK)/exe=$(<) $(LINKFLAGS) $(>:J=,) ,$(NEEDLIBS)/lib ,$(LINKLIBS)\n", +"}\n", +"actions quietly updated piecemeal together RmTemps\n", +"{\n", +"$(RM) $(>[1]);* ,$(>[2-]);*\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"actions together Archive \n", +"{\n", +"$(LINK) -library -o $(<) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"set -e MWCincludes $(CCHDRS)\n", +"$(CC) -o $(<) $(CCFLAGS) $(CCDEFS) $(>) \n", +"}\n", +"actions C++\n", +"{\n", +"set -e MWCincludes $(CCHDRS)\n", +"$(CC) -o $(<) $(C++FLAGS) $(CCDEFS) $(>) \n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) \"$(LINKLIBS)\"\n", +"}\n", +"}\n", +"if $(WIN98)\n", +"{\n", +"actions existing Clean\n", +"{\n", +"del $(>)\n", +"}\n", +"}\n", +"rule BULK { Bulk $(<) : $(>) ; }\n", +"rule FILE { File $(<) : $(>) ; }\n", +"rule HDRRULE { HdrRule $(<) : $(>) ; }\n", +"rule INSTALL { Install $(<) : $(>) ; }\n", +"rule LIBRARY { Library $(<) : $(>) ; }\n", +"rule LIBS { LinkLibraries $(<) : $(>) ; }\n", +"rule LINK { Link $(<) : $(>) ; }\n", +"rule MAIN { Main $(<) : $(>) ; }\n", +"rule SETUID { Setuid $(<) ; }\n", +"rule SHELL { Shell $(<) : $(>) ; }\n", +"rule UNDEFINES { Undefines $(<) : $(>) ; }\n", +"rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; }\n", +"rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; }\n", +"rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; }\n", +"rule addDirName { $(<) += [ FDirName $(>) ] ; }\n", +"rule makeCommon { _makeCommon $(<) : $(>) ; }\n", +"rule makeDirName { $(<) = [ FDirName $(>) ] ; }\n", +"rule makeGrist { $(<) = [ FGrist $(>) ] ; }\n", +"rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }\n", +"rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }\n", +"rule makeString { $(<) = $(>:J) ; }\n", +"rule makeSubDir { $(<) = [ FSubDir $(>) ] ; }\n", +"rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }\n", +"include $(JAMFILE) ;\n", +0 }; diff --git a/src/tools/jam/jambase.h b/src/tools/jam/jambase.h new file mode 100644 index 0000000000..75393f4cc5 --- /dev/null +++ b/src/tools/jam/jambase.h @@ -0,0 +1,15 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * jambase.h - declaration for the internal jambase + * + * The file Jambase is turned into a C array of strings in jambase.c + * so that it can be built in to the executable. This is the + * declaration for that array. + */ + +extern char *jambase[]; diff --git a/src/tools/jam/jamgram.c b/src/tools/jam/jamgram.c new file mode 100644 index 0000000000..c77e6baf59 --- /dev/null +++ b/src/tools/jam/jamgram.c @@ -0,0 +1,876 @@ +#ifndef lint +static char const +yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $"; +#endif +#include +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define YYLEX yylex() +#define YYEMPTY -1 +#define yyclearin (yychar=(YYEMPTY)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING() (yyerrflag!=0) +static int yygrowstack(); +#define YYPREFIX "yy" +#line 89 "jamgram.y" +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, LOL *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r ) parse_make( compile_foreach,l,r,P0,s,S0,0 ) +# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) +# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) +# define prule( a,p ) parse_make( compile_rule,a,p,P0,S0,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 ) +# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) + +# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) +# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) + +#line 54 "y.tab.c" +#define YYERRCODE 256 +#define _BANG_t 257 +#define _BANG_EQUALS_t 258 +#define _AMPER_t 259 +#define _AMPERAMPER_t 260 +#define _LPAREN_t 261 +#define _RPAREN_t 262 +#define _PLUS_EQUALS_t 263 +#define _COLON_t 264 +#define _SEMIC_t 265 +#define _LANGLE_t 266 +#define _LANGLE_EQUALS_t 267 +#define _EQUALS_t 268 +#define _RANGLE_t 269 +#define _RANGLE_EQUALS_t 270 +#define _QUESTION_EQUALS_t 271 +#define _LBRACKET_t 272 +#define _RBRACKET_t 273 +#define ACTIONS_t 274 +#define BIND_t 275 +#define CASE_t 276 +#define DEFAULT_t 277 +#define ELSE_t 278 +#define EXISTING_t 279 +#define FOR_t 280 +#define IF_t 281 +#define IGNORE_t 282 +#define IN_t 283 +#define INCLUDE_t 284 +#define LOCAL_t 285 +#define ON_t 286 +#define PIECEMEAL_t 287 +#define QUIETLY_t 288 +#define RETURN_t 289 +#define RULE_t 290 +#define SWITCH_t 291 +#define TOGETHER_t 292 +#define UPDATED_t 293 +#define WHILE_t 294 +#define _LBRACE_t 295 +#define _BAR_t 296 +#define _BARBAR_t 297 +#define _RBRACE_t 298 +#define ARG 299 +#define STRING 300 +const short yylhs[] = { -1, + 0, 0, 2, 2, 1, 1, 1, 1, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 12, 13, 3, 7, 7, 7, 7, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 8, 14, 6, 6, 4, 15, 15, + 5, 17, 5, 16, 16, 16, 10, 10, 18, 18, + 18, 18, 18, 18, 11, 11, +}; +const short yylen[] = { 2, + 0, 1, 0, 1, 1, 2, 4, 6, 3, 3, + 3, 4, 6, 3, 7, 5, 5, 7, 5, 3, + 3, 0, 0, 9, 1, 1, 1, 2, 1, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 3, 0, 2, 4, 1, 3, 1, 0, 2, + 1, 0, 4, 2, 4, 4, 0, 2, 1, 1, + 1, 1, 1, 1, 0, 2, +}; +const short yydefred[] = { 0, + 52, 57, 0, 0, 49, 49, 0, 49, 0, 49, + 0, 0, 51, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 6, 26, 25, 27, 0, 49, + 0, 0, 49, 0, 49, 0, 64, 61, 63, 62, + 60, 59, 0, 58, 49, 41, 0, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 50, 0, 49, 21, 14, 20, 0, 0, 9, 28, + 0, 49, 11, 0, 0, 54, 53, 49, 0, 0, + 42, 40, 0, 0, 0, 32, 33, 0, 34, 35, + 0, 0, 0, 7, 0, 0, 0, 0, 0, 49, + 47, 12, 49, 49, 66, 22, 0, 0, 0, 0, + 16, 44, 19, 0, 56, 55, 0, 0, 0, 8, + 0, 13, 23, 15, 18, 45, 0, 24, +}; +const short yydgoto[] = { 14, + 33, 34, 16, 41, 23, 42, 43, 107, 24, 19, + 89, 127, 137, 108, 26, 46, 18, 54, +}; +const short yysindex[] = { -44, + 0, 0, -278, -232, 0, 0, -191, 0, -273, 0, + -232, -44, 0, 0, 0, -44, -154, -245, -66, -252, + -232, -232, -250, -103, -228, -191, -249, 31, -206, 31, + -230, -90, 0, -219, 0, 0, 0, 0, -200, 0, + -194, -180, 0, -191, 0, -187, 0, 0, 0, 0, + 0, 0, -172, 0, 0, 0, -58, 0, -232, -232, + -232, -232, -232, -232, -232, -232, -44, -232, -232, 0, + 0, -44, 0, 0, 0, 0, -169, -44, 0, 0, + -48, 0, 0, -152, -233, 0, 0, 0, -179, -175, + 0, 0, -35, -1, -1, 0, 0, -35, 0, 0, + -176, -6, -6, 0, -141, -174, -161, -169, -150, 0, + 0, 0, 0, 0, 0, 0, -44, -148, -44, -138, + 0, 0, 0, -91, 0, 0, -125, -115, 31, 0, + -44, 0, 0, 0, 0, 0, -113, 0, +}; +const short yyrindex[] = { 186, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -102, 0, 0, 0, 3, -74, 0, 0, 0, + 0, 0, -108, 0, 0, -124, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -223, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -102, 0, 0, 0, + 0, 4, 0, 0, 0, 0, -101, -102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -224, -168, -78, 0, 0, -207, 0, 0, + 0, -213, -185, 0, 0, 0, 0, -101, 0, 0, + 0, 0, 0, 0, 0, 0, -102, 1, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -276, 0, 0, 0, 0, 0, 0, 0, +}; +const short yygindex[] = { 0, + 8, -55, -23, 5, 2, -39, 118, 95, 36, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +#define YYTABLESIZE 330 +const short yytable[] = { 3, + 17, 17, 5, 3, 74, 86, 76, 15, 28, 25, + 27, 101, 29, 17, 31, 72, 104, 17, 73, 45, + 20, 3, 109, 35, 21, 30, 1, 71, 22, 17, + 55, 17, 58, 31, 31, 31, 70, 31, 1, 1, + 44, 46, 111, 31, 81, 85, 32, 84, 38, 46, + 30, 30, 30, 13, 30, 113, 56, 57, 75, 90, + 30, 128, 92, 130, 77, 13, 13, 80, 17, 82, + 31, 31, 31, 17, 126, 136, 39, 105, 79, 17, + 1, 38, 38, 38, 83, 87, 114, 30, 30, 30, + 36, 36, 115, 36, 93, 94, 95, 96, 97, 98, + 99, 100, 88, 102, 103, 135, 106, 13, 36, 39, + 39, 39, 112, 37, 124, 116, 38, 125, 17, 117, + 17, 118, 39, 119, 120, 131, 36, 36, 36, 129, + 17, 40, 17, 48, 48, 48, 121, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 123, 48, 29, + 29, 29, 48, 29, 59, 60, 61, 29, 29, 29, + 29, 29, 62, 63, 64, 65, 66, 59, 60, 61, + 48, 48, 48, 132, 133, 62, 63, 64, 65, 66, + 37, 37, 134, 37, 138, 1, 29, 29, 29, 49, + 49, 67, 68, 69, 65, 3, 43, 49, 110, 59, + 60, 61, 122, 91, 78, 68, 69, 62, 63, 64, + 65, 66, 47, 0, 36, 48, 37, 37, 37, 37, + 49, 50, 38, 0, 49, 51, 52, 1, 39, 2, + 62, 63, 53, 65, 66, 3, 4, 68, 69, 5, + 6, 7, 0, 0, 8, 9, 10, 0, 0, 11, + 12, 59, 60, 61, 13, 0, 59, 0, 0, 62, + 63, 64, 65, 66, 62, 63, 64, 65, 66, 0, + 0, 0, 17, 0, 17, 0, 17, 0, 5, 3, + 17, 17, 0, 0, 17, 17, 17, 0, 0, 17, + 17, 17, 0, 0, 17, 17, 0, 0, 17, 17, + 5, 3, 1, 0, 2, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 5, 0, 7, 0, 0, 8, + 9, 10, 0, 0, 11, 12, 0, 0, 0, 13, +}; +const short yycheck[] = { 276, + 0, 0, 0, 0, 28, 45, 30, 0, 7, 5, + 6, 67, 8, 12, 10, 265, 72, 16, 268, 18, + 299, 298, 78, 16, 257, 299, 272, 26, 261, 28, + 283, 30, 283, 258, 259, 260, 265, 262, 272, 272, + 286, 265, 82, 268, 40, 44, 11, 43, 262, 273, + 258, 259, 260, 299, 262, 289, 21, 22, 265, 55, + 268, 117, 58, 119, 295, 299, 299, 268, 67, 264, + 295, 296, 297, 72, 114, 131, 262, 73, 298, 78, + 272, 295, 296, 297, 265, 273, 85, 295, 296, 297, + 259, 260, 88, 262, 59, 60, 61, 62, 63, 64, + 65, 66, 275, 68, 69, 129, 276, 299, 263, 295, + 296, 297, 265, 268, 110, 295, 271, 113, 117, 295, + 119, 298, 277, 265, 299, 264, 295, 296, 297, 278, + 129, 286, 131, 258, 259, 260, 298, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 298, 273, 258, + 259, 260, 277, 262, 258, 259, 260, 266, 267, 268, + 269, 270, 266, 267, 268, 269, 270, 258, 259, 260, + 295, 296, 297, 265, 300, 266, 267, 268, 269, 270, + 259, 260, 298, 262, 298, 0, 295, 296, 297, 264, + 265, 295, 296, 297, 295, 298, 298, 272, 81, 258, + 259, 260, 108, 262, 295, 296, 297, 266, 267, 268, + 269, 270, 279, -1, 263, 282, 295, 296, 297, 268, + 287, 288, 271, -1, 299, 292, 293, 272, 277, 274, + 266, 267, 299, 269, 270, 280, 281, 296, 297, 284, + 285, 286, -1, -1, 289, 290, 291, -1, -1, 294, + 295, 258, 259, 260, 299, -1, 258, -1, -1, 266, + 267, 268, 269, 270, 266, 267, 268, 269, 270, -1, + -1, -1, 272, -1, 274, -1, 276, -1, 276, 276, + 280, 281, -1, -1, 284, 285, 286, -1, -1, 289, + 290, 291, -1, -1, 294, 295, -1, -1, 298, 299, + 298, 298, 272, -1, 274, -1, -1, -1, -1, -1, + 280, 281, -1, -1, 284, -1, 286, -1, -1, 289, + 290, 291, -1, -1, 294, 295, -1, -1, -1, 299, +}; +#define YYFINAL 14 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 300 +#if YYDEBUG +const char * const yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"_BANG_t","_BANG_EQUALS_t", +"_AMPER_t","_AMPERAMPER_t","_LPAREN_t","_RPAREN_t","_PLUS_EQUALS_t","_COLON_t", +"_SEMIC_t","_LANGLE_t","_LANGLE_EQUALS_t","_EQUALS_t","_RANGLE_t", +"_RANGLE_EQUALS_t","_QUESTION_EQUALS_t","_LBRACKET_t","_RBRACKET_t","ACTIONS_t", +"BIND_t","CASE_t","DEFAULT_t","ELSE_t","EXISTING_t","FOR_t","IF_t","IGNORE_t", +"IN_t","INCLUDE_t","LOCAL_t","ON_t","PIECEMEAL_t","QUIETLY_t","RETURN_t", +"RULE_t","SWITCH_t","TOGETHER_t","UPDATED_t","WHILE_t","_LBRACE_t","_BAR_t", +"_BARBAR_t","_RBRACE_t","ARG","STRING", +}; +const char * const yyrule[] = { +"$accept : run", +"run :", +"run : rules", +"block :", +"block : rules", +"rules : rule", +"rules : rule rules", +"rules : LOCAL_t list _SEMIC_t block", +"rules : LOCAL_t list _EQUALS_t list _SEMIC_t block", +"rule : _LBRACE_t block _RBRACE_t", +"rule : INCLUDE_t list _SEMIC_t", +"rule : arg lol _SEMIC_t", +"rule : arg assign list _SEMIC_t", +"rule : arg ON_t list assign list _SEMIC_t", +"rule : RETURN_t list _SEMIC_t", +"rule : FOR_t ARG IN_t list _LBRACE_t block _RBRACE_t", +"rule : SWITCH_t list _LBRACE_t cases _RBRACE_t", +"rule : IF_t expr _LBRACE_t block _RBRACE_t", +"rule : IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule", +"rule : WHILE_t expr _LBRACE_t block _RBRACE_t", +"rule : RULE_t ARG rule", +"rule : ON_t arg rule", +"$$1 :", +"$$2 :", +"rule : ACTIONS_t eflags ARG bindlist _LBRACE_t $$1 STRING $$2 _RBRACE_t", +"assign : _EQUALS_t", +"assign : _PLUS_EQUALS_t", +"assign : _QUESTION_EQUALS_t", +"assign : DEFAULT_t _EQUALS_t", +"expr : arg", +"expr : expr _EQUALS_t expr", +"expr : expr _BANG_EQUALS_t expr", +"expr : expr _LANGLE_t expr", +"expr : expr _LANGLE_EQUALS_t expr", +"expr : expr _RANGLE_t expr", +"expr : expr _RANGLE_EQUALS_t expr", +"expr : expr _AMPER_t expr", +"expr : expr _AMPERAMPER_t expr", +"expr : expr _BAR_t expr", +"expr : expr _BARBAR_t expr", +"expr : arg IN_t list", +"expr : _BANG_t expr", +"expr : _LPAREN_t expr _RPAREN_t", +"cases :", +"cases : case cases", +"case : CASE_t ARG _COLON_t block", +"lol : list", +"lol : list _COLON_t lol", +"list : listp", +"listp :", +"listp : listp arg", +"arg : ARG", +"$$3 :", +"arg : _LBRACKET_t $$3 func _RBRACKET_t", +"func : arg lol", +"func : ON_t arg arg lol", +"func : ON_t arg RETURN_t list", +"eflags :", +"eflags : eflags eflag", +"eflag : UPDATED_t", +"eflag : TOGETHER_t", +"eflag : IGNORE_t", +"eflag : QUIETLY_t", +"eflag : PIECEMEAL_t", +"eflag : EXISTING_t", +"bindlist :", +"bindlist : BIND_t list", +}; +#endif +#ifndef YYSTYPE +typedef int YYSTYPE; +#endif +#if YYDEBUG +#include +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 10000 +#define YYMAXDEPTH 10000 +#endif +#endif +#define YYINITSTACKSIZE 200 +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short *yyss; +short *yysslim; +YYSTYPE *yyvs; +int yystacksize; +/* allocate initial stack or double stack size, up to YYMAXDEPTH */ +static int yygrowstack() +{ + int newsize, i; + short *newss; + YYSTYPE *newvs; + + if ((newsize = yystacksize) == 0) + newsize = YYINITSTACKSIZE; + else if (newsize >= YYMAXDEPTH) + return -1; + else if ((newsize *= 2) > YYMAXDEPTH) + newsize = YYMAXDEPTH; + i = yyssp - yyss; + newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : + (short *)malloc(newsize * sizeof *newss); + if (newss == NULL) + return -1; + yyss = newss; + yyssp = newss + i; + newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : + (YYSTYPE *)malloc(newsize * sizeof *newvs); + if (newvs == NULL) + return -1; + yyvs = newvs; + yyvsp = newvs + i; + yystacksize = newsize; + yysslim = yyss + newsize - 1; + return 0; +} + +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab + +#ifndef YYPARSE_PARAM +#if defined(__cplusplus) || __STDC__ +#define YYPARSE_PARAM_ARG void +#define YYPARSE_PARAM_DECL +#else /* ! ANSI-C/C++ */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* ANSI-C/C++ */ +#else /* YYPARSE_PARAM */ +#ifndef YYPARSE_PARAM_TYPE +#define YYPARSE_PARAM_TYPE void * +#endif +#if defined(__cplusplus) || __STDC__ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* ! ANSI-C/C++ */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; +#endif /* ANSI-C/C++ */ +#endif /* ! YYPARSE_PARAM */ + +int +yyparse (YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int yym, yyn, yystate; +#if YYDEBUG + register const char *yys; + + if ((yys = getenv("YYDEBUG"))) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + if (yyss == NULL && yygrowstack()) goto yyoverflow; + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate])) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yysslim && yygrowstack()) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#if defined(lint) || defined(__GNUC__) + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#if defined(lint) || defined(__GNUC__) + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yysslim && yygrowstack()) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 2: +#line 132 "jamgram.y" +{ parse_save( yyvsp[0].parse ); } +break; +case 3: +#line 143 "jamgram.y" +{ yyval.parse = pnull(); } +break; +case 4: +#line 145 "jamgram.y" +{ yyval.parse = yyvsp[0].parse; } +break; +case 5: +#line 149 "jamgram.y" +{ yyval.parse = yyvsp[0].parse; } +break; +case 6: +#line 151 "jamgram.y" +{ yyval.parse = prules( yyvsp[-1].parse, yyvsp[0].parse ); } +break; +case 7: +#line 153 "jamgram.y" +{ yyval.parse = plocal( yyvsp[-2].parse, pnull(), yyvsp[0].parse ); } +break; +case 8: +#line 155 "jamgram.y" +{ yyval.parse = plocal( yyvsp[-4].parse, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 9: +#line 159 "jamgram.y" +{ yyval.parse = yyvsp[-1].parse; } +break; +case 10: +#line 161 "jamgram.y" +{ yyval.parse = pincl( yyvsp[-1].parse ); } +break; +case 11: +#line 163 "jamgram.y" +{ yyval.parse = prule( yyvsp[-2].parse, yyvsp[-1].parse ); } +break; +case 12: +#line 165 "jamgram.y" +{ yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); } +break; +case 13: +#line 167 "jamgram.y" +{ yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); } +break; +case 14: +#line 169 "jamgram.y" +{ yyval.parse = yyvsp[-1].parse; } +break; +case 15: +#line 171 "jamgram.y" +{ yyval.parse = pfor( yyvsp[-5].string, yyvsp[-3].parse, yyvsp[-1].parse ); } +break; +case 16: +#line 173 "jamgram.y" +{ yyval.parse = pswitch( yyvsp[-3].parse, yyvsp[-1].parse ); } +break; +case 17: +#line 175 "jamgram.y" +{ yyval.parse = pif( yyvsp[-3].parse, yyvsp[-1].parse, pnull() ); } +break; +case 18: +#line 177 "jamgram.y" +{ yyval.parse = pif( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[0].parse ); } +break; +case 19: +#line 179 "jamgram.y" +{ yyval.parse = pwhile( yyvsp[-3].parse, yyvsp[-1].parse ); } +break; +case 20: +#line 181 "jamgram.y" +{ yyval.parse = psetc( yyvsp[-1].string, yyvsp[0].parse ); } +break; +case 21: +#line 183 "jamgram.y" +{ yyval.parse = pon( yyvsp[-1].parse, yyvsp[0].parse ); } +break; +case 22: +#line 185 "jamgram.y" +{ yymode( SCAN_STRING ); } +break; +case 23: +#line 187 "jamgram.y" +{ yymode( SCAN_NORMAL ); } +break; +case 24: +#line 189 "jamgram.y" +{ yyval.parse = psete( yyvsp[-6].string,yyvsp[-5].parse,yyvsp[-2].string,yyvsp[-7].number ); } +break; +case 25: +#line 197 "jamgram.y" +{ yyval.number = ASSIGN_SET; } +break; +case 26: +#line 199 "jamgram.y" +{ yyval.number = ASSIGN_APPEND; } +break; +case 27: +#line 201 "jamgram.y" +{ yyval.number = ASSIGN_DEFAULT; } +break; +case 28: +#line 203 "jamgram.y" +{ yyval.number = ASSIGN_DEFAULT; } +break; +case 29: +#line 211 "jamgram.y" +{ yyval.parse = peval( EXPR_EXISTS, yyvsp[0].parse, pnull() ); } +break; +case 30: +#line 213 "jamgram.y" +{ yyval.parse = peval( EXPR_EQUALS, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 31: +#line 215 "jamgram.y" +{ yyval.parse = peval( EXPR_NOTEQ, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 32: +#line 217 "jamgram.y" +{ yyval.parse = peval( EXPR_LESS, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 33: +#line 219 "jamgram.y" +{ yyval.parse = peval( EXPR_LESSEQ, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 34: +#line 221 "jamgram.y" +{ yyval.parse = peval( EXPR_MORE, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 35: +#line 223 "jamgram.y" +{ yyval.parse = peval( EXPR_MOREEQ, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 36: +#line 225 "jamgram.y" +{ yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 37: +#line 227 "jamgram.y" +{ yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 38: +#line 229 "jamgram.y" +{ yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 39: +#line 231 "jamgram.y" +{ yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 40: +#line 233 "jamgram.y" +{ yyval.parse = peval( EXPR_IN, yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 41: +#line 235 "jamgram.y" +{ yyval.parse = peval( EXPR_NOT, yyvsp[0].parse, pnull() ); } +break; +case 42: +#line 237 "jamgram.y" +{ yyval.parse = yyvsp[-1].parse; } +break; +case 43: +#line 247 "jamgram.y" +{ yyval.parse = P0; } +break; +case 44: +#line 249 "jamgram.y" +{ yyval.parse = pnode( yyvsp[-1].parse, yyvsp[0].parse ); } +break; +case 45: +#line 253 "jamgram.y" +{ yyval.parse = psnode( yyvsp[-2].string, yyvsp[0].parse ); } +break; +case 46: +#line 262 "jamgram.y" +{ yyval.parse = pnode( P0, yyvsp[0].parse ); } +break; +case 47: +#line 264 "jamgram.y" +{ yyval.parse = pnode( yyvsp[0].parse, yyvsp[-2].parse ); } +break; +case 48: +#line 274 "jamgram.y" +{ yyval.parse = yyvsp[0].parse; yymode( SCAN_NORMAL ); } +break; +case 49: +#line 278 "jamgram.y" +{ yyval.parse = pnull(); yymode( SCAN_PUNCT ); } +break; +case 50: +#line 280 "jamgram.y" +{ yyval.parse = pappend( yyvsp[-1].parse, yyvsp[0].parse ); } +break; +case 51: +#line 284 "jamgram.y" +{ yyval.parse = plist( yyvsp[0].string ); } +break; +case 52: +#line 285 "jamgram.y" +{ yymode( SCAN_NORMAL ); } +break; +case 53: +#line 286 "jamgram.y" +{ yyval.parse = yyvsp[-1].parse; } +break; +case 54: +#line 295 "jamgram.y" +{ yyval.parse = prule( yyvsp[-1].parse, yyvsp[0].parse ); } +break; +case 55: +#line 297 "jamgram.y" +{ yyval.parse = pon( yyvsp[-2].parse, prule( yyvsp[-1].parse, yyvsp[0].parse ) ); } +break; +case 56: +#line 299 "jamgram.y" +{ yyval.parse = pon( yyvsp[-2].parse, yyvsp[0].parse ); } +break; +case 57: +#line 308 "jamgram.y" +{ yyval.number = 0; } +break; +case 58: +#line 310 "jamgram.y" +{ yyval.number = yyvsp[-1].number | yyvsp[0].number; } +break; +case 59: +#line 314 "jamgram.y" +{ yyval.number = RULE_UPDATED; } +break; +case 60: +#line 316 "jamgram.y" +{ yyval.number = RULE_TOGETHER; } +break; +case 61: +#line 318 "jamgram.y" +{ yyval.number = RULE_IGNORE; } +break; +case 62: +#line 320 "jamgram.y" +{ yyval.number = RULE_QUIETLY; } +break; +case 63: +#line 322 "jamgram.y" +{ yyval.number = RULE_PIECEMEAL; } +break; +case 64: +#line 324 "jamgram.y" +{ yyval.number = RULE_EXISTING; } +break; +case 65: +#line 333 "jamgram.y" +{ yyval.parse = pnull(); } +break; +case 66: +#line 335 "jamgram.y" +{ yyval.parse = yyvsp[0].parse; } +break; +#line 821 "y.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yysslim && yygrowstack()) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/src/tools/jam/jamgram.h b/src/tools/jam/jamgram.h new file mode 100644 index 0000000000..b37598a6a3 --- /dev/null +++ b/src/tools/jam/jamgram.h @@ -0,0 +1,48 @@ +#ifndef YYERRCODE +#define YYERRCODE 256 +#endif + +#define _BANG_t 257 +#define _BANG_EQUALS_t 258 +#define _AMPER_t 259 +#define _AMPERAMPER_t 260 +#define _LPAREN_t 261 +#define _RPAREN_t 262 +#define _PLUS_EQUALS_t 263 +#define _COLON_t 264 +#define _SEMIC_t 265 +#define _LANGLE_t 266 +#define _LANGLE_EQUALS_t 267 +#define _EQUALS_t 268 +#define _RANGLE_t 269 +#define _RANGLE_EQUALS_t 270 +#define _QUESTION_EQUALS_t 271 +#define _LBRACKET_t 272 +#define _RBRACKET_t 273 +#define ACTIONS_t 274 +#define BIND_t 275 +#define CASE_t 276 +#define DEFAULT_t 277 +#define ELSE_t 278 +#define EXISTING_t 279 +#define FOR_t 280 +#define IF_t 281 +#define IGNORE_t 282 +#define IN_t 283 +#define INCLUDE_t 284 +#define LOCAL_t 285 +#define ON_t 286 +#define PIECEMEAL_t 287 +#define QUIETLY_t 288 +#define RETURN_t 289 +#define RULE_t 290 +#define SWITCH_t 291 +#define TOGETHER_t 292 +#define UPDATED_t 293 +#define WHILE_t 294 +#define _LBRACE_t 295 +#define _BAR_t 296 +#define _BARBAR_t 297 +#define _RBRACE_t 298 +#define ARG 299 +#define STRING 300 diff --git a/src/tools/jam/jamgram.y b/src/tools/jam/jamgram.y new file mode 100644 index 0000000000..0d899b5aed --- /dev/null +++ b/src/tools/jam/jamgram.y @@ -0,0 +1,338 @@ +%token _BANG_t +%token _BANG_EQUALS_t +%token _AMPER_t +%token _AMPERAMPER_t +%token _LPAREN_t +%token _RPAREN_t +%token _PLUS_EQUALS_t +%token _COLON_t +%token _SEMIC_t +%token _LANGLE_t +%token _LANGLE_EQUALS_t +%token _EQUALS_t +%token _RANGLE_t +%token _RANGLE_EQUALS_t +%token _QUESTION_EQUALS_t +%token _LBRACKET_t +%token _RBRACKET_t +%token ACTIONS_t +%token BIND_t +%token CASE_t +%token DEFAULT_t +%token ELSE_t +%token EXISTING_t +%token FOR_t +%token IF_t +%token IGNORE_t +%token IN_t +%token INCLUDE_t +%token LOCAL_t +%token ON_t +%token PIECEMEAL_t +%token QUIETLY_t +%token RETURN_t +%token RULE_t +%token SWITCH_t +%token TOGETHER_t +%token UPDATED_t +%token WHILE_t +%token _LBRACE_t +%token _BAR_t +%token _BARBAR_t +%token _RBRACE_t +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * jamgram.yy - jam grammar + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 08/31/94 (seiwald) - Allow ?= as alias for "default =". + * 09/15/94 (seiwald) - if conditionals take only single arguments, so + * that 'if foo == bar' gives syntax error (use =). + * 02/11/95 (seiwald) - when scanning arguments to rules, only treat + * punctuation keywords as keywords. All arg lists + * are terminated with punctuation keywords. + * + * 09/11/00 (seiwald) - Support for function calls: + * + * Rules now return lists (LIST *), rather than void. + * + * New "[ rule ]" syntax evals rule into a LIST. + * + * Lists are now generated by compile_list() and + * compile_append(), and any other rule that indirectly + * makes a list, rather than being built directly here, + * so that lists values can contain rule evaluations. + * + * New 'return' rule sets the return value, though + * other statements also may have return values. + * + * 'run' production split from 'block' production so + * that empty blocks can be handled separately. + */ + +%token ARG STRING + +%left _BARBAR_t _BAR_t +%left _AMPERAMPER_t _AMPER_t +%left _EQUALS_t _BANG_EQUALS_t IN_t +%left _LANGLE_t _LANGLE_EQUALS_t _RANGLE_t _RANGLE_EQUALS_t +%left _BANG_t + +%{ +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, LOL *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r ) parse_make( compile_foreach,l,r,P0,s,S0,0 ) +# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) +# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) +# define prule( a,p ) parse_make( compile_rule,a,p,P0,S0,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 ) +# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) + +# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) +# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) + +%} + +%% + +run : /* empty */ + /* do nothing */ + | rules + { parse_save( $1.parse ); } + ; + +/* + * block - zero or more rules + * rules - one or more rules + * rule - any one of jam's rules + * right-recursive so rules execute in order. + */ + +block : /* empty */ + { $$.parse = pnull(); } + | rules + { $$.parse = $1.parse; } + ; + +rules : rule + { $$.parse = $1.parse; } + | rule rules + { $$.parse = prules( $1.parse, $2.parse ); } + | LOCAL_t list _SEMIC_t block + { $$.parse = plocal( $2.parse, pnull(), $4.parse ); } + | LOCAL_t list _EQUALS_t list _SEMIC_t block + { $$.parse = plocal( $2.parse, $4.parse, $6.parse ); } + ; + +rule : _LBRACE_t block _RBRACE_t + { $$.parse = $2.parse; } + | INCLUDE_t list _SEMIC_t + { $$.parse = pincl( $2.parse ); } + | arg lol _SEMIC_t + { $$.parse = prule( $1.parse, $2.parse ); } + | arg assign list _SEMIC_t + { $$.parse = pset( $1.parse, $3.parse, $2.number ); } + | arg ON_t list assign list _SEMIC_t + { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); } + | RETURN_t list _SEMIC_t + { $$.parse = $2.parse; } + | FOR_t ARG IN_t list _LBRACE_t block _RBRACE_t + { $$.parse = pfor( $2.string, $4.parse, $6.parse ); } + | SWITCH_t list _LBRACE_t cases _RBRACE_t + { $$.parse = pswitch( $2.parse, $4.parse ); } + | IF_t expr _LBRACE_t block _RBRACE_t + { $$.parse = pif( $2.parse, $4.parse, pnull() ); } + | IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule + { $$.parse = pif( $2.parse, $4.parse, $7.parse ); } + | WHILE_t expr _LBRACE_t block _RBRACE_t + { $$.parse = pwhile( $2.parse, $4.parse ); } + | RULE_t ARG rule + { $$.parse = psetc( $2.string, $3.parse ); } + | ON_t arg rule + { $$.parse = pon( $2.parse, $3.parse ); } + | ACTIONS_t eflags ARG bindlist _LBRACE_t + { yymode( SCAN_STRING ); } + STRING + { yymode( SCAN_NORMAL ); } + _RBRACE_t + { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); } + ; + +/* + * assign - = or += + */ + +assign : _EQUALS_t + { $$.number = ASSIGN_SET; } + | _PLUS_EQUALS_t + { $$.number = ASSIGN_APPEND; } + | _QUESTION_EQUALS_t + { $$.number = ASSIGN_DEFAULT; } + | DEFAULT_t _EQUALS_t + { $$.number = ASSIGN_DEFAULT; } + ; + +/* + * expr - an expression for if + */ + +expr : arg + { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); } + | expr _EQUALS_t expr + { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); } + | expr _BANG_EQUALS_t expr + { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); } + | expr _LANGLE_t expr + { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); } + | expr _LANGLE_EQUALS_t expr + { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); } + | expr _RANGLE_t expr + { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); } + | expr _RANGLE_EQUALS_t expr + { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); } + | expr _AMPER_t expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr _AMPERAMPER_t expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr _BAR_t expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | expr _BARBAR_t expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | arg IN_t list + { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); } + | _BANG_t expr + { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); } + | _LPAREN_t expr _RPAREN_t + { $$.parse = $2.parse; } + ; + +/* + * cases - action elements inside a 'switch' + * case - a single action element inside a 'switch' + * right-recursive rule so cases can be examined in order. + */ + +cases : /* empty */ + { $$.parse = P0; } + | case cases + { $$.parse = pnode( $1.parse, $2.parse ); } + ; + +case : CASE_t ARG _COLON_t block + { $$.parse = psnode( $2.string, $4.parse ); } + ; + +/* + * lol - list of lists + * right-recursive rule so that lists can be added in order. + */ + +lol : list + { $$.parse = pnode( P0, $1.parse ); } + | list _COLON_t lol + { $$.parse = pnode( $3.parse, $1.parse ); } + ; + +/* + * list - zero or more args in a LIST + * listp - list (in puncutation only mode) + * arg - one ARG or function call + */ + +list : listp + { $$.parse = $1.parse; yymode( SCAN_NORMAL ); } + ; + +listp : /* empty */ + { $$.parse = pnull(); yymode( SCAN_PUNCT ); } + | listp arg + { $$.parse = pappend( $1.parse, $2.parse ); } + ; + +arg : ARG + { $$.parse = plist( $1.string ); } + | _LBRACKET_t { yymode( SCAN_NORMAL ); } func _RBRACKET_t + { $$.parse = $3.parse; } + ; + +/* + * func - a function call (inside []) + * This needs to be split cleanly out of 'rule' + */ + +func : arg lol + { $$.parse = prule( $1.parse, $2.parse ); } + | ON_t arg arg lol + { $$.parse = pon( $2.parse, prule( $3.parse, $4.parse ) ); } + | ON_t arg RETURN_t list + { $$.parse = pon( $2.parse, $4.parse ); } + ; + +/* + * eflags - zero or more modifiers to 'executes' + * eflag - a single modifier to 'executes' + */ + +eflags : /* empty */ + { $$.number = 0; } + | eflags eflag + { $$.number = $1.number | $2.number; } + ; + +eflag : UPDATED_t + { $$.number = RULE_UPDATED; } + | TOGETHER_t + { $$.number = RULE_TOGETHER; } + | IGNORE_t + { $$.number = RULE_IGNORE; } + | QUIETLY_t + { $$.number = RULE_QUIETLY; } + | PIECEMEAL_t + { $$.number = RULE_PIECEMEAL; } + | EXISTING_t + { $$.number = RULE_EXISTING; } + ; + + +/* + * bindlist - list of variable to bind for an action + */ + +bindlist : /* empty */ + { $$.parse = pnull(); } + | BIND_t list + { $$.parse = $2.parse; } + ; + + diff --git a/src/tools/jam/jamgram.yy b/src/tools/jam/jamgram.yy new file mode 100644 index 0000000000..817e49bd57 --- /dev/null +++ b/src/tools/jam/jamgram.yy @@ -0,0 +1,296 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * jamgram.yy - jam grammar + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 08/31/94 (seiwald) - Allow ?= as alias for "default =". + * 09/15/94 (seiwald) - if conditionals take only single arguments, so + * that 'if foo == bar' gives syntax error (use =). + * 02/11/95 (seiwald) - when scanning arguments to rules, only treat + * punctuation keywords as keywords. All arg lists + * are terminated with punctuation keywords. + * + * 09/11/00 (seiwald) - Support for function calls: + * + * Rules now return lists (LIST *), rather than void. + * + * New "[ rule ]" syntax evals rule into a LIST. + * + * Lists are now generated by compile_list() and + * compile_append(), and any other rule that indirectly + * makes a list, rather than being built directly here, + * so that lists values can contain rule evaluations. + * + * New 'return' rule sets the return value, though + * other statements also may have return values. + * + * 'run' production split from 'block' production so + * that empty blocks can be handled separately. + */ + +%token ARG STRING + +%left `||` `|` +%left `&&` `&` +%left `=` `!=` `in` +%left `<` `<=` `>` `>=` +%left `!` + +%{ +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, LOL *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r ) parse_make( compile_foreach,l,r,P0,s,S0,0 ) +# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) +# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) +# define prule( a,p ) parse_make( compile_rule,a,p,P0,S0,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p ) parse_make( compile_setcomp,p,P0,P0,s,S0,0 ) +# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) + +# define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) +# define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) + +%} + +%% + +run : /* empty */ + /* do nothing */ + | rules + { parse_save( $1.parse ); } + ; + +/* + * block - zero or more rules + * rules - one or more rules + * rule - any one of jam's rules + * right-recursive so rules execute in order. + */ + +block : /* empty */ + { $$.parse = pnull(); } + | rules + { $$.parse = $1.parse; } + ; + +rules : rule + { $$.parse = $1.parse; } + | rule rules + { $$.parse = prules( $1.parse, $2.parse ); } + | `local` list `;` block + { $$.parse = plocal( $2.parse, pnull(), $4.parse ); } + | `local` list `=` list `;` block + { $$.parse = plocal( $2.parse, $4.parse, $6.parse ); } + ; + +rule : `{` block `}` + { $$.parse = $2.parse; } + | `include` list `;` + { $$.parse = pincl( $2.parse ); } + | arg lol `;` + { $$.parse = prule( $1.parse, $2.parse ); } + | arg assign list `;` + { $$.parse = pset( $1.parse, $3.parse, $2.number ); } + | arg `on` list assign list `;` + { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); } + | `return` list `;` + { $$.parse = $2.parse; } + | `for` ARG `in` list `{` block `}` + { $$.parse = pfor( $2.string, $4.parse, $6.parse ); } + | `switch` list `{` cases `}` + { $$.parse = pswitch( $2.parse, $4.parse ); } + | `if` expr `{` block `}` + { $$.parse = pif( $2.parse, $4.parse, pnull() ); } + | `if` expr `{` block `}` `else` rule + { $$.parse = pif( $2.parse, $4.parse, $7.parse ); } + | `while` expr `{` block `}` + { $$.parse = pwhile( $2.parse, $4.parse ); } + | `rule` ARG rule + { $$.parse = psetc( $2.string, $3.parse ); } + | `on` arg rule + { $$.parse = pon( $2.parse, $3.parse ); } + | `actions` eflags ARG bindlist `{` + { yymode( SCAN_STRING ); } + STRING + { yymode( SCAN_NORMAL ); } + `}` + { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); } + ; + +/* + * assign - = or += + */ + +assign : `=` + { $$.number = ASSIGN_SET; } + | `+=` + { $$.number = ASSIGN_APPEND; } + | `?=` + { $$.number = ASSIGN_DEFAULT; } + | `default` `=` + { $$.number = ASSIGN_DEFAULT; } + ; + +/* + * expr - an expression for if + */ + +expr : arg + { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); } + | expr `=` expr + { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); } + | expr `!=` expr + { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); } + | expr `<` expr + { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); } + | expr `<=` expr + { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); } + | expr `>` expr + { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); } + | expr `>=` expr + { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); } + | expr `&` expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr `&&` expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr `|` expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | expr `||` expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | arg `in` list + { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); } + | `!` expr + { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); } + | `(` expr `)` + { $$.parse = $2.parse; } + ; + +/* + * cases - action elements inside a 'switch' + * case - a single action element inside a 'switch' + * right-recursive rule so cases can be examined in order. + */ + +cases : /* empty */ + { $$.parse = P0; } + | case cases + { $$.parse = pnode( $1.parse, $2.parse ); } + ; + +case : `case` ARG `:` block + { $$.parse = psnode( $2.string, $4.parse ); } + ; + +/* + * lol - list of lists + * right-recursive rule so that lists can be added in order. + */ + +lol : list + { $$.parse = pnode( P0, $1.parse ); } + | list `:` lol + { $$.parse = pnode( $3.parse, $1.parse ); } + ; + +/* + * list - zero or more args in a LIST + * listp - list (in puncutation only mode) + * arg - one ARG or function call + */ + +list : listp + { $$.parse = $1.parse; yymode( SCAN_NORMAL ); } + ; + +listp : /* empty */ + { $$.parse = pnull(); yymode( SCAN_PUNCT ); } + | listp arg + { $$.parse = pappend( $1.parse, $2.parse ); } + ; + +arg : ARG + { $$.parse = plist( $1.string ); } + | `[` { yymode( SCAN_NORMAL ); } func `]` + { $$.parse = $3.parse; } + ; + +/* + * func - a function call (inside []) + * This needs to be split cleanly out of 'rule' + */ + +func : arg lol + { $$.parse = prule( $1.parse, $2.parse ); } + | `on` arg arg lol + { $$.parse = pon( $2.parse, prule( $3.parse, $4.parse ) ); } + | `on` arg `return` list + { $$.parse = pon( $2.parse, $4.parse ); } + ; + +/* + * eflags - zero or more modifiers to 'executes' + * eflag - a single modifier to 'executes' + */ + +eflags : /* empty */ + { $$.number = 0; } + | eflags eflag + { $$.number = $1.number | $2.number; } + ; + +eflag : `updated` + { $$.number = RULE_UPDATED; } + | `together` + { $$.number = RULE_TOGETHER; } + | `ignore` + { $$.number = RULE_IGNORE; } + | `quietly` + { $$.number = RULE_QUIETLY; } + | `piecemeal` + { $$.number = RULE_PIECEMEAL; } + | `existing` + { $$.number = RULE_EXISTING; } + ; + + +/* + * bindlist - list of variable to bind for an action + */ + +bindlist : /* empty */ + { $$.parse = pnull(); } + | `bind` list + { $$.parse = $2.parse; } + ; + + diff --git a/src/tools/jam/jamgramtab.h b/src/tools/jam/jamgramtab.h new file mode 100644 index 0000000000..dcc6a47fa4 --- /dev/null +++ b/src/tools/jam/jamgramtab.h @@ -0,0 +1,42 @@ + { "!", _BANG_t }, + { "!=", _BANG_EQUALS_t }, + { "&", _AMPER_t }, + { "&&", _AMPERAMPER_t }, + { "(", _LPAREN_t }, + { ")", _RPAREN_t }, + { "+=", _PLUS_EQUALS_t }, + { ":", _COLON_t }, + { ";", _SEMIC_t }, + { "<", _LANGLE_t }, + { "<=", _LANGLE_EQUALS_t }, + { "=", _EQUALS_t }, + { ">", _RANGLE_t }, + { ">=", _RANGLE_EQUALS_t }, + { "?=", _QUESTION_EQUALS_t }, + { "[", _LBRACKET_t }, + { "]", _RBRACKET_t }, + { "actions", ACTIONS_t }, + { "bind", BIND_t }, + { "case", CASE_t }, + { "default", DEFAULT_t }, + { "else", ELSE_t }, + { "existing", EXISTING_t }, + { "for", FOR_t }, + { "if", IF_t }, + { "ignore", IGNORE_t }, + { "in", IN_t }, + { "include", INCLUDE_t }, + { "local", LOCAL_t }, + { "on", ON_t }, + { "piecemeal", PIECEMEAL_t }, + { "quietly", QUIETLY_t }, + { "return", RETURN_t }, + { "rule", RULE_t }, + { "switch", SWITCH_t }, + { "together", TOGETHER_t }, + { "updated", UPDATED_t }, + { "while", WHILE_t }, + { "{", _LBRACE_t }, + { "|", _BAR_t }, + { "||", _BARBAR_t }, + { "}", _RBRACE_t }, diff --git a/src/tools/jam/lists.c b/src/tools/jam/lists.c new file mode 100644 index 0000000000..e205e0ff39 --- /dev/null +++ b/src/tools/jam/lists.c @@ -0,0 +1,243 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "newstr.h" +# include "lists.h" + +/* + * lists.c - maintain lists of strings + * + * This implementation essentially uses a singly linked list, but + * guarantees that the head element of every list has a valid pointer + * to the tail of the list, so the new elements can efficiently and + * properly be appended to the end of a list. + * + * To avoid massive allocation, list_free() just tacks the whole freed + * chain onto freelist and list_new() looks on freelist first for an + * available list struct. list_free() does not free the strings in the + * chain: it lazily lets list_new() do so. + * + * 08/23/94 (seiwald) - new list_append() + * 09/07/00 (seiwald) - documented lol_*() functions + */ + +static LIST *freelist = 0; /* junkpile for list_free() */ + +/* + * list_append() - append a list onto another one, returning total + */ + +LIST * +list_append( + LIST *l, + LIST *nl ) +{ + if( !nl ) + { + /* Just return l */ + } + else if( !l ) + { + l = nl; + } + else + { + /* Graft two non-empty lists. */ + l->tail->next = nl; + l->tail = nl->tail; + } + + return l; +} + +/* + * list_new() - tack a string onto the end of a list of strings + */ + +LIST * +list_new( + LIST *head, + char *string ) +{ + LIST *l; + + if( DEBUG_LISTS ) + printf( "list > %s <\n", string ); + + /* Get list struct from freelist, if one available. */ + /* Otherwise allocate. */ + /* If from freelist, must free string first */ + + if( freelist ) + { + l = freelist; + freestr( l->string ); + freelist = freelist->next; + } + else + { + l = (LIST *)malloc( sizeof( *l ) ); + } + + /* If first on chain, head points here. */ + /* If adding to chain, tack us on. */ + /* Tail must point to this new, last element. */ + + if( !head ) head = l; + else head->tail->next = l; + head->tail = l; + l->next = 0; + + l->string = string; + + return head; +} + +/* + * list_copy() - copy a whole list of strings (nl) onto end of another (l) + */ + +LIST * +list_copy( + LIST *l, + LIST *nl ) +{ + for( ; nl; nl = list_next( nl ) ) + l = list_new( l, copystr( nl->string ) ); + + return l; +} + +/* + * list_sublist() - copy a subset of a list of strings + */ + +LIST * +list_sublist( + LIST *l, + int start, + int count ) +{ + LIST *nl = 0; + + for( ; l && start--; l = list_next( l ) ) + ; + + for( ; l && count--; l = list_next( l ) ) + nl = list_new( nl, copystr( l->string ) ); + + return nl; +} + +/* + * list_free() - free a list of strings + */ + +void +list_free( LIST *head ) +{ + /* Just tack onto freelist. */ + + if( head ) + { + head->tail->next = freelist; + freelist = head; + } +} + +/* + * list_print() - print a list of strings to stdout + */ + +void +list_print( LIST *l ) +{ + for( ; l; l = list_next( l ) ) + printf( "%s ", l->string ); +} + +/* + * list_length() - return the number of items in the list + */ + +int +list_length( LIST *l ) +{ + int n = 0; + + for( ; l; l = list_next( l ), ++n ) + ; + + return n; +} + +/* + * lol_init() - initialize a LOL (list of lists) + */ + +void +lol_init( LOL *lol ) +{ + lol->count = 0; +} + +/* + * lol_add() - append a LIST onto an LOL + */ + +void +lol_add( + LOL *lol, + LIST *l ) +{ + if( lol->count < LOL_MAX ) + lol->list[ lol->count++ ] = l; +} + +/* + * lol_free() - free the LOL and its LISTs + */ + +void +lol_free( LOL *lol ) +{ + int i; + + for( i = 0; i < lol->count; i++ ) + list_free( lol->list[i] ); + + lol->count = 0; +} + +/* + * lol_get() - return one of the LISTs in the LOL + */ + +LIST * +lol_get( + LOL *lol, + int i ) +{ + return i < lol->count ? lol->list[i] : 0; +} + +/* + * lol_print() - debug print LISTS separated by ":" + */ + +void +lol_print( LOL *lol ) +{ + int i; + + for( i = 0; i < lol->count; i++ ) + { + if( i ) + printf( " : " ); + list_print( lol->list[i] ); + } +} diff --git a/src/tools/jam/lists.h b/src/tools/jam/lists.h new file mode 100644 index 0000000000..33afc58860 --- /dev/null +++ b/src/tools/jam/lists.h @@ -0,0 +1,80 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * lists.h - the LIST structure and routines to manipulate them + * + * The whole of jam relies on lists of strings as a datatype. This + * module, in conjunction with newstr.c, handles these relatively + * efficiently. + * + * Structures defined: + * + * LIST - list of strings + * LOL - list of LISTs + * + * External routines: + * + * list_append() - append a list onto another one, returning total + * list_new() - tack a string onto the end of a list of strings + * list_copy() - copy a whole list of strings + * list_sublist() - copy a subset of a list of strings + * list_free() - free a list of strings + * list_print() - print a list of strings to stdout + * list_length() - return the number of items in the list + * + * lol_init() - initialize a LOL (list of lists) + * lol_add() - append a LIST onto an LOL + * lol_free() - free the LOL and its LISTs + * lol_get() - return one of the LISTs in the LOL + * lol_print() - debug print LISTS separated by ":" + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 08/23/94 (seiwald) - new list_append() + */ + +/* + * LIST - list of strings + */ + +typedef struct _list LIST; + +struct _list { + LIST *next; + LIST *tail; /* only valid in head node */ + char *string; /* private copy */ +} ; + +/* + * LOL - list of LISTs + */ + +typedef struct _lol LOL; + +# define LOL_MAX 9 + +struct _lol { + int count; + LIST *list[ LOL_MAX ]; +} ; + +LIST * list_append( LIST *l, LIST *nl ); +LIST * list_copy( LIST *l, LIST *nl ); +void list_free( LIST *head ); +LIST * list_new( LIST *head, char *string ); +void list_print( LIST *l ); +int list_length( LIST *l ); +LIST * list_sublist( LIST *l, int start, int count ); + +# define list_next( l ) ((l)->next) + +# define L0 ((LIST *)0) + +void lol_add( LOL *lol, LIST *l ); +void lol_init( LOL *lol ); +void lol_free( LOL *lol ); +LIST * lol_get( LOL *lol, int i ); +void lol_print( LOL *lol ); diff --git a/src/tools/jam/make.c b/src/tools/jam/make.c new file mode 100644 index 0000000000..7c5d8c2b7b --- /dev/null +++ b/src/tools/jam/make.c @@ -0,0 +1,479 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * make.c - bring a target up to date, once rules are in place + * + * This modules controls the execution of rules to bring a target and + * its dependencies up to date. It is invoked after the targets, rules, + * et. al. described in rules.h are created by the interpreting of the + * jam files. + * + * This file contains the main make() entry point and the first pass + * make0(). The second pass, make1(), which actually does the command + * execution, is in make1.c. + * + * External routines: + * make() - make a target, given its name + * + * Internal routines: + * make0() - bind and scan everything to make a TARGET + * make0sort() - reorder TARGETS chain by their time (newest to oldest) + * + * 12/26/93 (seiwald) - allow NOTIME targets to be expanded via $(<), $(>) + * 01/04/94 (seiwald) - print all targets, bounded, when tracing commands + * 04/08/94 (seiwald) - progress report now reflects only targets with actions + * 04/11/94 (seiwald) - Combined deps & headers into deps[2] in TARGET. + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 12/20/94 (seiwald) - make0() headers after determining fate of target, so + * that headers aren't seen as dependents on themselves. + * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets. + * 02/02/95 (seiwald) - propagate leaf source time for new LEAVES rule. + * 02/14/95 (seiwald) - NOUPDATE rule means don't update existing target. + * 08/22/95 (seiwald) - NOUPDATE targets immune to anyhow (-a) flag. + * 09/06/00 (seiwald) - NOCARE affects targets with sources/actions. + * 03/02/01 (seiwald) - reverse NOCARE change. + * 03/14/02 (seiwald) - TEMPORARY targets no longer take on parents age + * 03/16/02 (seiwald) - support for -g (reorder builds by source time) + */ + +# include "jam.h" + +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "rules.h" + +# include "search.h" +# include "newstr.h" +# include "make.h" +# include "headers.h" +# include "command.h" + +# ifndef max +# define max( a,b ) ((a)>(b)?(a):(b)) +# endif + +typedef struct { + int temp; + int updating; + int cantfind; + int cantmake; + int targets; + int made; +} COUNTS ; + +static void make0( TARGET *t, time_t ptime, int depth, + COUNTS *counts, int anyhow ); + +static TARGETS *make0sort( TARGETS *c ); + +static char *target_fate[] = +{ + "init", /* T_FATE_INIT */ + "making", /* T_FATE_MAKING */ + "stable", /* T_FATE_STABLE */ + "newer", /* T_FATE_NEWER */ + "temp", /* T_FATE_ISTMP */ + "touched", /* T_FATE_TOUCHED */ + "missing", /* T_FATE_MISSING */ + "old", /* T_FATE_OUTDATED */ + "update", /* T_FATE_UPDATE */ + "nofind", /* T_FATE_CANTFIND */ + "nomake" /* T_FATE_CANTMAKE */ +} ; + +static char *target_bind[] = +{ + "unbound", + "missing", + "parents", + "exists", +} ; + +# define spaces(x) ( " " + 16 - ( x > 16 ? 16 : x ) ) + +/* + * make() - make a target, given its name + */ + +int +make( + int n_targets, + char **targets, + int anyhow ) +{ + int i; + COUNTS counts[1]; + int status = 0; /* 1 if anything fails */ + + memset( (char *)counts, 0, sizeof( *counts ) ); + + for( i = 0; i < n_targets; i++ ) + { + TARGET *t = bindtarget( targets[i] ); + + make0( t, (time_t)0, 0, counts, anyhow ); + } + + if( DEBUG_MAKE ) + { + if( counts->targets ) + printf( "...found %d target(s)...\n", counts->targets ); + if( counts->temp ) + printf( "...using %d temp target(s)...\n", counts->temp ); + if( counts->updating ) + printf( "...updating %d target(s)...\n", counts->updating ); + if( counts->cantfind ) + printf( "...can't find %d target(s)...\n", counts->cantfind ); + if( counts->cantmake ) + printf( "...can't make %d target(s)...\n", counts->cantmake ); + } + + status = counts->cantfind || counts->cantmake; + + for( i = 0; i < n_targets; i++ ) + status |= make1( bindtarget( targets[i] ) ); + + return status; +} + +/* + * make0() - bind and scan everything to make a TARGET + * + * Make0() recursively binds a target, searches for #included headers, + * calls itself on those headers, and calls itself on any dependents. + */ + +static void +make0( + TARGET *t, + time_t ptime, /* parent target's timestamp */ + int depth, /* for display purposes */ + COUNTS *counts, /* for reporting */ + int anyhow ) /* forcibly touch all (real) targets */ +{ + TARGETS *c; + int fate, hfate; + time_t last, leaf, hlast, hleaf; + char *flag = ""; + + if( DEBUG_MAKEPROG ) + printf( "make\t--\t%s%s\n", spaces( depth ), t->name ); + + /* + * Step 1: don't remake if already trying or tried + */ + + switch( t->fate ) + { + case T_FATE_MAKING: + printf( "warning: %s depends on itself\n", t->name ); + return; + + default: + return; + + case T_FATE_INIT: + break; + } + + t->fate = T_FATE_MAKING; + + /* + * Step 2: under the influence of "on target" variables, + * bind the target and search for headers. + */ + + /* Step 2a: set "on target" variables. */ + + pushsettings( t->settings ); + + /* Step 2b: find and timestamp the target file (if it's a file). */ + + if( t->binding == T_BIND_UNBOUND && !( t->flags & T_FLAG_NOTFILE ) ) + { + t->boundname = search( t->name, &t->time ); + t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING; + } + + /* If temp file doesn't exist but parent does, use parent */ + + if( t->binding == T_BIND_MISSING && t->flags & T_FLAG_TEMP && ptime ) + t->binding = T_BIND_PARENTS; + + /* Step 2c: If its a file, search for headers. */ + + if( t->binding == T_BIND_EXISTS ) + headers( t ); + + /* Step 2d: reset "on target" variables */ + + popsettings( t->settings ); + + /* + * Pause for a little progress reporting + */ + + if( DEBUG_BIND ) + { + if( strcmp( t->name, t->boundname ) ) + { + printf( "bind\t--\t%s%s: %s\n", + spaces( depth ), t->name, t->boundname ); + } + + switch( t->binding ) + { + case T_BIND_UNBOUND: + case T_BIND_MISSING: + case T_BIND_PARENTS: + printf( "time\t--\t%s%s: %s\n", + spaces( depth ), t->name, target_bind[ t->binding ] ); + break; + + case T_BIND_EXISTS: + printf( "time\t--\t%s%s: %s", + spaces( depth ), t->name, ctime( &t->time ) ); + break; + } + } + + /* + * Step 3: recursively make0() dependents + */ + + last = 0; + leaf = 0; + fate = T_FATE_STABLE; + + for( c = t->deps[ T_DEPS_DEPENDS ]; c; c = c->next ) + { + /* Pass our time or our parent's time down. */ + + int time = t->binding == T_BIND_PARENTS ? ptime : t->time; + + make0( c->target, time, depth + 1, counts, anyhow ); + leaf = max( leaf, c->target->leaf ); + leaf = max( leaf, c->target->hleaf ); + + /* If LEAVES has been applied, we only heed the timestamps of */ + /* the leaf source nodes. */ + + if( t->flags & T_FLAG_LEAVES ) + { + last = leaf; + continue; + } + + last = max( last, c->target->time ); + last = max( last, c->target->htime ); + fate = max( fate, c->target->fate ); + fate = max( fate, c->target->hfate ); + } + + /* If a NOUPDATE file exists, make dependents eternally old. */ + + if( t->flags & T_FLAG_NOUPDATE ) + { + last = 0; + t->time = 0; + + /* + * Don't inherit our fate from our dependents. Decide fate + * based only upon other flags and our binding (done later). + */ + + fate = T_FATE_STABLE; + } + + /* Step 3b: determine fate: rebuild target or what? */ + + /* + In English: + If can't find or make child, can't make target. + If children changed, make target. + If target missing, make it. + If children newer, make target. + If temp's children newer than parent, make temp. + If deliberately touched, make it. + If up-to-date temp file present, use it. + If target newer than parent, mark target newer. + Don't propagate child's "newer" status. + */ + + if( fate >= T_FATE_BROKEN ) + { + fate = T_FATE_CANTMAKE; + } + else if( fate >= T_FATE_SPOIL ) + { + fate = T_FATE_UPDATE; + } + else if( t->binding == T_BIND_MISSING ) + { + fate = T_FATE_MISSING; + } + else if( t->binding == T_BIND_EXISTS && last > t->time ) + { + fate = T_FATE_OUTDATED; + } + else if( t->binding == T_BIND_PARENTS && last > ptime ) + { + fate = T_FATE_OUTDATED; + } + else if( t->flags & T_FLAG_TOUCHED ) + { + fate = T_FATE_TOUCHED; + } + else if( anyhow && !( t->flags & T_FLAG_NOUPDATE ) ) + { + fate = T_FATE_TOUCHED; + } + else if( t->binding == T_BIND_EXISTS && t->flags & T_FLAG_TEMP ) + { + fate = T_FATE_ISTMP; + } + else if( t->binding == T_BIND_EXISTS && t->time > ptime ) + { + fate = T_FATE_NEWER; + } + else if( fate == T_FATE_NEWER ) + { + fate = T_FATE_STABLE; + } + + /* Step 3c: handle missing files */ + /* If it's missing and there are no actions to create it, boom. */ + /* If we can't make a target we don't care about, 'sokay */ + /* We could insist that there are updating actions for all missing */ + /* files, but if they have dependents we just pretend it's NOTFILE. */ + + if( fate == T_FATE_MISSING && + !t->actions && + !t->deps[ T_DEPS_DEPENDS ] ) + { + if( t->flags & T_FLAG_NOCARE ) + { + fate = T_FATE_STABLE; + } + else + { + printf( "don't know how to make %s\n", t->name ); + + fate = T_FATE_CANTFIND; + } + } + + /* Step 3d: propagate dependents' time & fate. */ + /* Set leaf time to be our time only if this is a leaf. */ + + t->time = max( t->time, last ); + t->leaf = leaf ? leaf : t->time ; + t->fate = fate; + + /* Step 3e: sort dependents by their update time. */ + + if( globs.newestfirst ) + t->deps[ T_DEPS_DEPENDS ] = make0sort( t->deps[ T_DEPS_DEPENDS ] ); + + /* + * Step 4: Recursively make0() headers. + */ + + /* Step 4a: recursively make0() headers */ + + hlast = 0; + hleaf = 0; + hfate = T_FATE_STABLE; + + for( c = t->deps[ T_DEPS_INCLUDES ]; c; c = c->next ) + { + make0( c->target, ptime, depth + 1, counts, anyhow ); + hlast = max( hlast, c->target->time ); + hlast = max( hlast, c->target->htime ); + hleaf = max( hleaf, c->target->leaf ); + hleaf = max( hleaf, c->target->hleaf ); + hfate = max( hfate, c->target->fate ); + hfate = max( hfate, c->target->hfate ); + } + + /* Step 4b: propagate dependents' time & fate. */ + + t->htime = hlast; + t->hleaf = hleaf ? hleaf : t->htime; + t->hfate = hfate; + + /* + * Step 5: a little harmless tabulating for tracing purposes + */ + + if( !( ++counts->targets % 1000 ) && DEBUG_MAKE ) + printf( "...patience...\n" ); + + if( fate == T_FATE_ISTMP ) + counts->temp++; + else if( fate == T_FATE_CANTFIND ) + counts->cantfind++; + else if( fate == T_FATE_CANTMAKE && t->actions ) + counts->cantmake++; + else if( fate >= T_FATE_BUILD && fate < T_FATE_BROKEN && t->actions ) + counts->updating++; + + if( !( t->flags & T_FLAG_NOTFILE ) && fate >= T_FATE_SPOIL ) + flag = "+"; + else if( t->binding == T_BIND_EXISTS && ptime && t->time > ptime ) + flag = "*"; + + if( DEBUG_MAKEPROG ) + printf( "made%s\t%s\t%s%s\n", + flag, target_fate[ t->fate ], + spaces( depth ), t->name ); +} + +/* + * make0sort() - reorder TARGETS chain by their time (newest to oldest) + */ + +static TARGETS * +make0sort( TARGETS *chain ) +{ + TARGETS *result = 0; + + /* We walk chain, taking each item and inserting it on the */ + /* sorted result, with newest items at the front. This involves */ + /* updating each TARGETS' c->next and c->tail. Note that we */ + /* make c->tail a valid prev pointer for every entry. Normally, */ + /* it is only valid at the head, where prev == tail. Note also */ + /* that while tail is a loop, next ends at the end of the chain. */ + + /* Walk current target list */ + + while( chain ) + { + TARGETS *c = chain; + TARGETS *s = result; + + chain = chain->next; + + /* Find point s in result for c */ + + while( s && s->target->time > c->target->time ) + s = s->next; + + /* Insert c in front of s (might be 0). */ + /* Don't even think of deciphering this. */ + + c->next = s; /* good even if s = 0 */ + if( result == s ) result = c; /* new head of chain? */ + if( !s ) s = result; /* wrap to ensure a next */ + if( result != c ) s->tail->next = c; /* not head? be prev's next */ + c->tail = s->tail; /* take on next's prev */ + s->tail = c; /* make next's prev us */ + } + + return result; +} diff --git a/src/tools/jam/make.h b/src/tools/jam/make.h new file mode 100644 index 0000000000..87ca862093 --- /dev/null +++ b/src/tools/jam/make.h @@ -0,0 +1,12 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * make.h - bring a target up to date, once rules are in place + */ + +int make( int n_targets, char **targets, int anyhow ); +int make1( TARGET *t ); diff --git a/src/tools/jam/make1.c b/src/tools/jam/make1.c new file mode 100644 index 0000000000..f2e62459f2 --- /dev/null +++ b/src/tools/jam/make1.c @@ -0,0 +1,647 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * make1.c - execute command to bring targets up to date + * + * This module contains make1(), the entry point called by make() to + * recursively decend the dependency graph executing update actions as + * marked by make0(). + * + * External routines: + * + * make1() - execute commands to update a TARGET and all its dependents + * + * Internal routines, the recursive/asynchronous command executors: + * + * make1a() - recursively traverse target tree, calling make1b() + * make1b() - dependents of target built, now build target with make1c() + * make1c() - launch target's next command, call make1b() when done + * make1d() - handle command execution completion and call back make1c() + * + * Internal support routines: + * + * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc + * make1list() - turn a list of targets into a LIST, for $(<) and $(>) + * make1settings() - for vars that get bound values, build up replacement lists + * make1bind() - bind targets that weren't bound in dependency analysis + * + * 04/16/94 (seiwald) - Split from make.c. + * 04/21/94 (seiwald) - Handle empty "updated" actions. + * 05/04/94 (seiwald) - async multiprocess (-j) support + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets. + * 01/22/94 (seiwald) - pass per-target JAMSHELL down to execcmd(). + * 02/28/95 (seiwald) - Handle empty "existing" actions. + * 03/10/95 (seiwald) - Fancy counts. + */ + +# include "jam.h" + +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "rules.h" + +# include "search.h" +# include "newstr.h" +# include "make.h" +# include "command.h" +# include "execcmd.h" + +static void make1a( TARGET *t, TARGET *parent ); +static void make1b( TARGET *t ); +static void make1c( TARGET *t ); +static void make1d( void *closure, int status ); + +static CMD *make1cmds( ACTIONS *a0 ); +static LIST *make1list( LIST *l, TARGETS *targets, int flags ); +static SETTINGS *make1settings( LIST *vars ); +static void make1bind( TARGET *t, int warn ); + +/* Ugly static - it's too hard to carry it through the callbacks. */ + +static struct { + int failed; + int skipped; + int total; + int made; +} counts[1] ; + +/* + * make1() - execute commands to update a TARGET and all its dependents + */ + +static int intr = 0; + +int +make1( TARGET *t ) +{ + memset( (char *)counts, 0, sizeof( *counts ) ); + + /* Recursively make the target and its dependents */ + + make1a( t, (TARGET *)0 ); + + /* Wait for any outstanding commands to finish running. */ + + while( execwait() ) + ; + + /* Talk about it */ + + if( DEBUG_MAKE && counts->failed ) + printf( "...failed updating %d target(s)...\n", counts->failed ); + + if( DEBUG_MAKE && counts->skipped ) + printf( "...skipped %d target(s)...\n", counts->skipped ); + + if( DEBUG_MAKE && counts->made ) + printf( "...updated %d target(s)...\n", counts->made ); + + return counts->total != counts->made; +} + +/* + * make1a() - recursively traverse target tree, calling make1b() + */ + +static void +make1a( + TARGET *t, + TARGET *parent ) +{ + TARGETS *c; + int i; + + /* If the parent is the first to try to build this target */ + /* or this target is in the make1c() quagmire, arrange for the */ + /* parent to be notified when this target is built. */ + + if( parent ) + switch( t->progress ) + { + case T_MAKE_INIT: + case T_MAKE_ACTIVE: + case T_MAKE_RUNNING: + t->parents = targetentry( t->parents, parent ); + parent->asynccnt++; + } + + if( t->progress != T_MAKE_INIT ) + return; + + /* Asynccnt counts the dependents preventing this target from */ + /* proceeding to make1b() for actual building. We start off with */ + /* a count of 1 to prevent anything from happening until we can */ + /* call all dependents. This 1 is accounted for when we call */ + /* make1b() ourselves, below. */ + + t->asynccnt = 1; + + /* Recurse on our dependents, manipulating progress to guard */ + /* against circular dependency. */ + + t->progress = T_MAKE_ONSTACK; + + for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ ) + for( c = t->deps[i]; c && !intr; c = c->next ) + make1a( c->target, t ); + + t->progress = T_MAKE_ACTIVE; + + /* Now that all dependents have bumped asynccnt, we now allow */ + /* decrement our reference to asynccnt. */ + + make1b( t ); +} + +/* + * make1b() - dependents of target built, now build target with make1c() + */ + +static void +make1b( TARGET *t ) +{ + TARGETS *c; + int i; + char *failed = "dependents"; + + /* If any dependents are still outstanding, wait until they */ + /* call make1b() to signal their completion. */ + + if( --t->asynccnt ) + return; + + /* Now ready to build target 't'... if dependents built ok. */ + + /* Collect status from dependents */ + + for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ ) + for( c = t->deps[i]; c; c = c->next ) + if( c->target->status > t->status ) + { + failed = c->target->name; + t->status = c->target->status; + } + + /* If actions on deps have failed, bail. */ + /* Otherwise, execute all actions to make target */ + + if( t->status == EXEC_CMD_FAIL && t->actions ) + { + ++counts->skipped; + printf( "...skipped %s for lack of %s...\n", t->name, failed ); + } + + if( t->status == EXEC_CMD_OK ) + switch( t->fate ) + { + case T_FATE_INIT: + case T_FATE_MAKING: + /* shouldn't happen */ + + case T_FATE_STABLE: + case T_FATE_NEWER: + break; + + case T_FATE_CANTFIND: + case T_FATE_CANTMAKE: + t->status = EXEC_CMD_FAIL; + break; + + case T_FATE_ISTMP: + if( DEBUG_MAKE ) + printf( "...using %s...\n", t->name ); + break; + + case T_FATE_TOUCHED: + case T_FATE_MISSING: + case T_FATE_OUTDATED: + case T_FATE_UPDATE: + /* Set "on target" vars, build actions, unset vars */ + /* Set "progress" so that make1c() counts this target among */ + /* the successes/failures. */ + + if( t->actions ) + { + ++counts->total; + + if( DEBUG_MAKE && !( counts->total % 100 ) ) + printf( "...on %dth target...\n", counts->total ); + + pushsettings( t->settings ); + t->cmds = (char *)make1cmds( t->actions ); + popsettings( t->settings ); + + t->progress = T_MAKE_RUNNING; + } + + break; + } + + /* Call make1c() to begin the execution of the chain of commands */ + /* needed to build target. If we're not going to build target */ + /* (because of dependency failures or because no commands need to */ + /* be run) the chain will be empty and make1c() will directly */ + /* signal the completion of target. */ + + make1c( t ); +} + +/* + * make1c() - launch target's next command, call make1b() when done + */ + +static void +make1c( TARGET *t ) +{ + CMD *cmd = (CMD *)t->cmds; + + /* If there are (more) commands to run to build this target */ + /* (and we haven't hit an error running earlier comands) we */ + /* launch the command with execcmd(). */ + + /* If there are no more commands to run, we collect the status */ + /* from all the actions then report our completion to all the */ + /* parents. */ + + if( cmd && t->status == EXEC_CMD_OK ) + { + if( DEBUG_MAKE ) + if( DEBUG_MAKEQ || ! ( cmd->rule->flags & RULE_QUIETLY ) ) + { + printf( "%s ", cmd->rule->name ); + list_print( lol_get( &cmd->args, 0 ) ); + printf( "\n" ); + } + + if( DEBUG_EXEC ) + printf( "%s\n", cmd->buf ); + + if( globs.cmdout ) + fprintf( globs.cmdout, "%s", cmd->buf ); + + if( globs.noexec ) + { + make1d( t, EXEC_CMD_OK ); + } + else + { + fflush( stdout ); + execcmd( cmd->buf, make1d, t, cmd->shell ); + } + } + else + { + TARGETS *c; + ACTIONS *actions; + + /* Collect status from actions, and distribute it as well */ + + for( actions = t->actions; actions; actions = actions->next ) + if( actions->action->status > t->status ) + t->status = actions->action->status; + + for( actions = t->actions; actions; actions = actions->next ) + if( t->status > actions->action->status ) + actions->action->status = t->status; + + /* Tally success/failure for those we tried to update. */ + + if( t->progress == T_MAKE_RUNNING ) + switch( t->status ) + { + case EXEC_CMD_OK: + ++counts->made; + break; + case EXEC_CMD_FAIL: + ++counts->failed; + break; + } + + /* Tell parents dependent has been built */ + + t->progress = T_MAKE_DONE; + + for( c = t->parents; c; c = c->next ) + make1b( c->target ); + } +} + +/* + * make1d() - handle command execution completion and call back make1c() + */ + +static void +make1d( + void *closure, + int status ) +{ + TARGET *t = (TARGET *)closure; + CMD *cmd = (CMD *)t->cmds; + + /* Execcmd() has completed. All we need to do is fiddle with the */ + /* status and signal our completion so make1c() can run the next */ + /* command. On interrupts, we bail heavily. */ + + if( status == EXEC_CMD_FAIL && ( cmd->rule->flags & RULE_IGNORE ) ) + status = EXEC_CMD_OK; + + /* On interrupt, set intr so _everything_ fails */ + + if( status == EXEC_CMD_INTR ) + ++intr; + + if( status == EXEC_CMD_FAIL && DEBUG_MAKE ) + { + /* Print command text on failure */ + + if( !DEBUG_EXEC ) + printf( "%s\n", cmd->buf ); + + printf( "...failed %s ", cmd->rule->name ); + list_print( lol_get( &cmd->args, 0 ) ); + printf( "...\n" ); + + if( globs.quitquick ) ++intr; + } + + /* If the command was interrupted or failed and the target */ + /* is not "precious", remove the targets. */ + /* Precious == 'actions updated' -- the target maintains state. */ + + if( status != EXEC_CMD_OK && !( cmd->rule->flags & RULE_UPDATED ) ) + { + LIST *targets = lol_get( &cmd->args, 0 ); + + for( ; targets; targets = list_next( targets ) ) + if( !unlink( targets->string ) ) + printf( "...removing %s\n", targets->string ); + } + + /* Free this command and call make1c() to move onto next command. */ + + t->status = status; + t->cmds = (char *)cmd_next( cmd ); + + cmd_free( cmd ); + + make1c( t ); +} + +/* + * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc + * + * Essentially copies a chain of ACTIONs to a chain of CMDs, + * grouping RULE_TOGETHER actions, splitting RULE_PIECEMEAL actions, + * and handling RULE_UPDATED actions. The result is a chain of + * CMDs which can be expanded by var_string() and executed with + * execcmd(). + */ + +static CMD * +make1cmds( ACTIONS *a0 ) +{ + CMD *cmds = 0; + LIST *shell = var_get( "JAMSHELL" ); /* shell is per-target */ + + /* Step through actions */ + /* Actions may be shared with other targets or grouped with */ + /* RULE_TOGETHER, so actions already seen are skipped. */ + + for( ; a0; a0 = a0->next ) + { + RULE *rule = a0->action->rule; + SETTINGS *boundvars; + LIST *nt, *ns; + ACTIONS *a1; + CMD *cmd; + int start, chunk, length; + + /* Only do rules with commands to execute. */ + /* If this action has already been executed, use saved status */ + + if( !rule->actions || a0->action->running ) + continue; + + a0->action->running = 1; + + /* Make LISTS of targets and sources */ + /* If `execute together` has been specified for this rule, tack */ + /* on sources from each instance of this rule for this target. */ + + nt = make1list( L0, a0->action->targets, 0 ); + ns = make1list( L0, a0->action->sources, rule->flags ); + + if( rule->flags & RULE_TOGETHER ) + for( a1 = a0->next; a1; a1 = a1->next ) + if( a1->action->rule == rule && !a1->action->running ) + { + ns = make1list( ns, a1->action->sources, rule->flags ); + a1->action->running = 1; + } + + /* If doing only updated (or existing) sources, but none have */ + /* been updated (or exist), skip this action. */ + + if( !ns && ( rule->flags & ( RULE_UPDATED | RULE_EXISTING ) ) ) + { + list_free( nt ); + continue; + } + + /* If we had 'actions xxx bind vars' we bind the vars now */ + + boundvars = make1settings( rule->bindlist ); + pushsettings( boundvars ); + + /* + * Build command, starting with all source args. + * + * If cmd_new returns 0, it's because the resulting command + * length is > MAXLINE. In this case, we'll slowly reduce + * the number of source arguments presented until it does + * fit. This only applies to actions that allow PIECEMEAL + * commands. + * + * While reducing slowly takes a bit of compute time to get + * things just right, it's worth it to get as close to MAXLINE + * as possible, because launching the commands we're executing + * is likely to be much more compute intensive! + * + * Note we loop through at least once, for sourceless actions. + */ + + start = 0; + chunk = length = list_length( ns ); + + do + { + /* Build cmd: cmd_new consumes its lists. */ + + CMD *cmd = cmd_new( rule, + list_copy( L0, nt ), + list_sublist( ns, start, chunk ), + list_copy( L0, shell ) ); + + if( cmd ) + { + /* It fit: chain it up. */ + + if( !cmds ) cmds = cmd; + else cmds->tail->next = cmd; + cmds->tail = cmd; + start += chunk; + } + else if( ( rule->flags & RULE_PIECEMEAL ) && chunk > 1 ) + { + /* Reduce chunk size slowly. */ + + chunk = chunk * 9 / 10; + } + else + { + /* Too long and not splittable. */ + + printf( "%s actions too long (max %d)!\n", + rule->name, MAXLINE ); + exit( EXITBAD ); + } + } + while( start < length ); + + /* These were always copied when used. */ + + list_free( nt ); + list_free( ns ); + + /* Free the variables whose values were bound by */ + /* 'actions xxx bind vars' */ + + popsettings( boundvars ); + freesettings( boundvars ); + } + + return cmds; +} + +/* + * make1list() - turn a list of targets into a LIST, for $(<) and $(>) + */ + +static LIST * +make1list( + LIST *l, + TARGETS *targets, + int flags ) +{ + for( ; targets; targets = targets->next ) + { + TARGET *t = targets->target; + + /* Sources to 'actions existing' are never in the dependency */ + /* graph (if they were, they'd get built and 'existing' would */ + /* be superfluous, so throttle warning message about independent */ + /* targets. */ + + if( t->binding == T_BIND_UNBOUND ) + make1bind( t, !( flags & RULE_EXISTING ) ); + + if( ( flags & RULE_EXISTING ) && t->binding != T_BIND_EXISTS ) + continue; + + if( ( flags & RULE_UPDATED ) && t->fate <= T_FATE_STABLE ) + continue; + + /* Prohibit duplicates for RULE_TOGETHER */ + + if( flags & RULE_TOGETHER ) + { + LIST *m; + + for( m = l; m; m = m->next ) + if( !strcmp( m->string, t->boundname ) ) + break; + + if( m ) + continue; + } + + /* Build new list */ + + l = list_new( l, copystr( t->boundname ) ); + } + + return l; +} + +/* + * make1settings() - for vars that get bound values, build up replacement lists + */ + +static SETTINGS * +make1settings( LIST *vars ) +{ + SETTINGS *settings = 0; + + for( ; vars; vars = list_next( vars ) ) + { + LIST *l = var_get( vars->string ); + LIST *nl = 0; + + for( ; l; l = list_next( l ) ) + { + TARGET *t = bindtarget( l->string ); + + /* Make sure the target is bound, warning if it is not in the */ + /* dependency graph. */ + + if( t->binding == T_BIND_UNBOUND ) + make1bind( t, 1 ); + + /* Build new list */ + + nl = list_new( nl, copystr( t->boundname ) ); + } + + /* Add to settings chain */ + + settings = addsettings( settings, 0, vars->string, nl ); + } + + return settings; +} + +/* + * make1bind() - bind targets that weren't bound in dependency analysis + * + * Spot the kludge! If a target is not in the dependency tree, it didn't + * get bound by make0(), so we have to do it here. Ugly. + */ + +static void +make1bind( + TARGET *t, + int warn ) +{ + if( t->flags & T_FLAG_NOTFILE ) + return; + + /* Sources to 'actions existing' are never in the dependency */ + /* graph (if they were, they'd get built and 'existing' would */ + /* be superfluous, so throttle warning message about independent */ + /* targets. */ + + if( warn ) + printf( "warning: using independent target %s\n", t->name ); + + pushsettings( t->settings ); + t->boundname = search( t->name, &t->time ); + t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING; + popsettings( t->settings ); +} diff --git a/src/tools/jam/mkjambase.c b/src/tools/jam/mkjambase.c new file mode 100644 index 0000000000..2a67db7ea5 --- /dev/null +++ b/src/tools/jam/mkjambase.c @@ -0,0 +1,124 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * mkjambase.c - turn Jambase into a big C structure + * + * Usage: mkjambase jambase.c Jambase ... + * + * Results look like this: + * + * char *jambase[] = { + * "...\n", + * ... + * 0 }; + * + * Handles \'s and "'s specially; knows to delete blank and comment lines. + * + */ + +# include +# include + +main( int argc, char **argv, char **envp ) +{ + char buf[ 1024 ]; + FILE *fin; + FILE *fout; + char *p; + int doDotC = 0; + + if( argc < 3 ) + { + fprintf( stderr, "usage: %s jambase.c Jambase ...\n", argv[0] ); + return -1; + } + + if( !( fout = fopen( argv[1], "w" ) ) ) + { + perror( argv[1] ); + return -1; + } + + /* If the file ends in .c generate a C source file */ + + if( ( p = strrchr( argv[1], '.' ) ) && !strcmp( p, ".c" ) ) + doDotC++; + + /* Now process the files */ + + argc -= 2, argv += 2; + + if( doDotC ) + { + fprintf( fout, "/* Generated by mkjambase from Jambase */\n" ); + fprintf( fout, "char *jambase[] = {\n" ); + } + + for( ; argc--; argv++ ) + { + if( !( fin = fopen( *argv, "r" ) ) ) + { + perror( *argv ); + return -1; + } + + if( doDotC ) + { + fprintf( fout, "/* %s */\n", *argv ); + } + else + { + fprintf( fout, "### %s ###\n", *argv ); + } + + while( fgets( buf, sizeof( buf ), fin ) ) + { + if( doDotC ) + { + char *p = buf; + + /* Strip leading whitespace. */ + + while( *p == ' ' || *p == '\t' || *p == '\n' ) + p++; + + /* Drop comments and empty lines. */ + + if( *p == '#' || !*p ) + continue; + + /* Copy */ + + putc( '"', fout ); + + for( ; *p && *p != '\n'; p++ ) + switch( *p ) + { + case '\\': putc( '\\', fout ); putc( '\\', fout ); break; + case '"': putc( '\\', fout ); putc( '"', fout ); break; + default: putc( *p, fout ); break; + } + + fprintf( fout, "\\n\",\n" ); + } + else + { + fprintf( fout, "%s", buf ); + } + + } + + fclose( fin ); + } + + if( doDotC ) + fprintf( fout, "0 };\n" ); + + fclose( fout ); + + return 0; +} diff --git a/src/tools/jam/newstr.c b/src/tools/jam/newstr.c new file mode 100644 index 0000000000..08336480ee --- /dev/null +++ b/src/tools/jam/newstr.c @@ -0,0 +1,96 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "newstr.h" +# include "hash.h" + +/* + * newstr.c - string manipulation routines + * + * To minimize string copying, string creation, copying, and freeing + * is done through newstr. + * + * External functions: + * + * newstr() - return a malloc'ed copy of a string + * copystr() - return a copy of a string previously returned by newstr() + * freestr() - free a string returned by newstr() or copystr() + * donestr() - free string tables + * + * Once a string is passed to newstr(), the returned string is readonly. + * + * This implementation builds a hash table of all strings, so that multiple + * calls of newstr() on the same string allocate memory for the string once. + * Strings are never actually freed. + */ + +typedef char *STRING; + +static struct hash *strhash = 0; +static int strtotal = 0; + +/* + * newstr() - return a malloc'ed copy of a string + */ + +char * +newstr( char *string ) +{ + STRING str, *s = &str; + + if( !strhash ) + strhash = hashinit( sizeof( STRING ), "strings" ); + + *s = string; + + if( hashenter( strhash, (HASHDATA **)&s ) ) + { + int l = strlen( string ); + char *m = (char *)malloc( l + 1 ); + + if (DEBUG_MEM) + printf("newstr: allocating %d bytes\n", l + 1 ); + + strtotal += l + 1; + memcpy( m, string, l + 1 ); + *s = m; + } + + return *s; +} + +/* + * copystr() - return a copy of a string previously returned by newstr() + */ + +char * +copystr( char *s ) +{ + return s; +} + +/* + * freestr() - free a string returned by newstr() or copystr() + */ + +void +freestr( char *s ) +{ +} + +/* + * donestr() - free string tables + */ + +void +donestr() +{ + hashdone( strhash ); + + if( DEBUG_MEM ) + printf( "%dK in strings\n", strtotal / 1024 ); +} diff --git a/src/tools/jam/newstr.h b/src/tools/jam/newstr.h new file mode 100644 index 0000000000..cf2dd34b7e --- /dev/null +++ b/src/tools/jam/newstr.h @@ -0,0 +1,14 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * newstr.h - string manipulation routines + */ + +char *newstr( char *string ); +char *copystr( char *s ); +void freestr( char *s ); +void donestr(); diff --git a/src/tools/jam/option.c b/src/tools/jam/option.c new file mode 100644 index 0000000000..e4491523cb --- /dev/null +++ b/src/tools/jam/option.c @@ -0,0 +1,103 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "option.h" + +/* + * option.c - command line option processing + * + * {o >o + * \<>) "Process command line options as defined in . + * Return the number of argv[] elements used up by options, + * or -1 if an invalid option flag was given or an argument + * was supplied for an option that does not require one." + */ + +int +getoptions( + int argc, + char **argv, + char *opts, + option *optv ) +{ + int i; + int optc = N_OPTS; + + memset( (char *)optv, '\0', sizeof( *optv ) * N_OPTS ); + + for( i = 0; i < argc; i++ ) + { + char *arg; + + if( argv[i][0] != '-' || !isalpha( argv[i][1] ) ) + break; + + if( !optc-- ) + { + printf( "too many options (%d max)\n", N_OPTS ); + return -1; + } + + for( arg = &argv[i][1]; *arg; arg++ ) + { + char *f; + + for( f = opts; *f; f++ ) + if( *f == *arg ) + break; + + if( !*f ) + { + printf( "Invalid option: -%c\n", *arg ); + return -1; + } + + optv->flag = *f; + + if( f[1] != ':' ) + { + optv++->val = "true"; + } + else if( arg[1] ) + { + optv++->val = &arg[1]; + break; + } + else if( ++i < argc ) + { + optv++->val = argv[i]; + break; + } + else + { + printf( "option: -%c needs argument\n", *f ); + return -1; + } + } + } + + return i; +} + +/* + * Name: getoptval() - find an option given its character + */ + +char * +getoptval( + option *optv, + char opt, + int subopt ) +{ + int i; + + for( i = 0; i < N_OPTS; i++, optv++ ) + if( optv->flag == opt && !subopt-- ) + return optv->val; + + return 0; +} diff --git a/src/tools/jam/option.h b/src/tools/jam/option.h new file mode 100644 index 0000000000..657a763a08 --- /dev/null +++ b/src/tools/jam/option.h @@ -0,0 +1,23 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * option.h - command line option processing + * + * {o >o + * \ -) "Command line option." + */ + +typedef struct option +{ + char flag; /* filled in by getoption() */ + char *val; /* set to random address if true */ +} option; + +# define N_OPTS 256 + +int getoptions( int argc, char **argv, char *opts, option *optv ); +char * getoptval( option *optv, char opt, int subopt ); diff --git a/src/tools/jam/parse.c b/src/tools/jam/parse.c new file mode 100644 index 0000000000..1e8681227e --- /dev/null +++ b/src/tools/jam/parse.c @@ -0,0 +1,116 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "scan.h" +# include "newstr.h" + +/* + * parse.c - make and destroy parse trees as driven by the parser + * + * 09/07/00 (seiwald) - ref count on PARSE to avoid freeing when used, + * as per Matt Armstrong. + * 09/11/00 (seiwald) - structure reworked to reflect that (*func)() + * returns a LIST *. + */ + +static PARSE *yypsave; + +void +parse_file( char *f ) +{ + /* Suspend scan of current file */ + /* and push this new file in the stream */ + + yyfparse(f); + + /* Now parse each block of rules and execute it. */ + /* Execute it outside of the parser so that recursive */ + /* calls to yyrun() work (no recursive yyparse's). */ + + for(;;) + { + LOL l; + PARSE *p; + + /* $(<) and $(>) empty in outer scope. */ + + lol_init( &l ); + + /* Filled by yyparse() calling parse_save() */ + + yypsave = 0; + + /* If parse error or empty parse, outta here */ + + if( yyparse() || !( p = yypsave ) ) + break; + + /* Run the parse tree. */ + + (*(p->func))( p, &l ); + + parse_free( p ); + } +} + +void +parse_save( PARSE *p ) +{ + yypsave = p; +} + +PARSE * +parse_make( + LIST *(*func)( PARSE *p, LOL *args ), + PARSE *left, + PARSE *right, + PARSE *third, + char *string, + char *string1, + int num ) +{ + PARSE *p = (PARSE *)malloc( sizeof( PARSE ) ); + + p->func = func; + p->left = left; + p->right = right; + p->third = third; + p->string = string; + p->string1 = string1; + p->num = num; + p->refs = 1; + + return p; +} + +void +parse_refer( PARSE *p ) +{ + ++p->refs; +} + +void +parse_free( PARSE *p ) +{ + if( --p->refs ) + return; + + if( p->string ) + freestr( p->string ); + if( p->string1 ) + freestr( p->string1 ); + if( p->left ) + parse_free( p->left ); + if( p->right ) + parse_free( p->right ); + if( p->third ) + parse_free( p->third ); + + free( (char *)p ); +} diff --git a/src/tools/jam/parse.h b/src/tools/jam/parse.h new file mode 100644 index 0000000000..e1c0575a38 --- /dev/null +++ b/src/tools/jam/parse.h @@ -0,0 +1,41 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * parse.h - make and destroy parse trees as driven by the parser + */ + +/* + * parse tree node + */ + +typedef struct _PARSE PARSE; + +struct _PARSE { + LIST *(*func)( PARSE *p, LOL *args ); + PARSE *left; + PARSE *right; + PARSE *third; + char *string; + char *string1; + int num; + int refs; +} ; + +void parse_file( char *f ); +void parse_save( PARSE *p ); + +PARSE * parse_make( + LIST *(*func)( PARSE *p, LOL *args ), + PARSE *left, + PARSE *right, + PARSE *third, + char *string, + char *string1, + int num ); + +void parse_refer( PARSE *p ); +void parse_free( PARSE *p ); diff --git a/src/tools/jam/patchlevel.h b/src/tools/jam/patchlevel.h new file mode 100644 index 0000000000..ae63fd4eba --- /dev/null +++ b/src/tools/jam/patchlevel.h @@ -0,0 +1,5 @@ +/* Keep JAMVERSYM in sync with VERSION. */ +/* It can be accessed as $(JAMVERSION) in the Jamfile. */ + +#define VERSION "2.4" +#define JAMVERSYM "JAMVERSION=2.4" diff --git a/src/tools/jam/pathmac.c b/src/tools/jam/pathmac.c new file mode 100644 index 0000000000..541729bb43 --- /dev/null +++ b/src/tools/jam/pathmac.c @@ -0,0 +1,282 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "pathsys.h" + +# ifdef OS_MAC + +# define DELIM ':' + +/* + * pathunix.c - manipulate file names on UNIX, NT, OS2 + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build() + * 12/19/94 (mikem) - solaris string table insanity support + * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way. + * 02/14/95 (seiwald) - parse and build /xxx properly + * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we + * should expect hdr searches to come up with strings + * like "thing/thing.h". So we need to test for "/" as + * well as "\" when parsing pathnames. + * 03/16/95 (seiwald) - fixed accursed typo on line 69. + * 05/03/96 (seiwald) - split from filent.c, fileunix.c + * 12/20/96 (seiwald) - when looking for the rightmost . in a file name, + * don't include the archive member name. + * 01/10/01 (seiwald) - path_parse now strips the trailing : from the + * directory name, unless the directory name is all + * :'s, so that $(d:P) works. + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member + */ + +void +path_parse( + char *file, + PATHNAME *f ) +{ + char *p, *q; + char *end; + + memset( (char *)f, 0, sizeof( *f ) ); + + /* Look for */ + + if( file[0] == '<' && ( p = strchr( file, '>' ) ) ) + { + f->f_grist.ptr = file; + f->f_grist.len = p - file; + file = p + 1; + } + + /* Look for dir: */ + + if( p = strrchr( file, DELIM ) ) + { + f->f_dir.ptr = file; + f->f_dir.len = p - file; + file = p + 1; + + /* All :'s? Include last : as part of directory name */ + + while( p > f->f_dir.ptr && *--p == DELIM ) + ; + + if( p == f->f_dir.ptr ) + f->f_dir.len++; + } + + end = file + strlen( file ); + + /* Look for (member) */ + + if( ( p = strchr( file, '(' ) ) && end[-1] == ')' ) + { + f->f_member.ptr = p + 1; + f->f_member.len = end - p - 2; + end = p; + } + + /* Look for .suffix */ + /* This would be memrchr() */ + + p = 0; + q = file; + + while( q = memchr( q, '.', end - q ) ) + p = q++; + + if( p ) + { + f->f_suffix.ptr = p; + f->f_suffix.len = end - p; + end = p; + } + + /* Leaves base */ + + f->f_base.ptr = file; + f->f_base.len = end - file; +} + +/* + * path_build() - build a filename given dir/base/suffix/member + */ + +# define DIR_EMPTY 0 /* "" */ +# define DIR_DOT 1 /* : */ +# define DIR_DOTDOT 2 /* :: */ +# define DIR_ABS 3 /* dira:dirb: */ +# define DIR_REL 4 /* :dira:dirb: */ + +# define G_DIR 0 /* take dir */ +# define G_ROOT 1 /* take root */ +# define G_CAT 2 /* prepend root to dir */ +# define G_DTDR 3 /* :: of rel dir */ +# define G_DDDD 4 /* make it ::: (../..) */ +# define G_MT 5 /* leave it empty */ + +char grid[5][5] = { +/* EMPTY DOT DOTDOT ABS REL */ +/* EMPTY */ { G_MT, G_DIR, G_DIR, G_DIR, G_DIR }, +/* DOT */ { G_ROOT, G_DIR, G_DIR, G_DIR, G_DIR }, +/* DOTDOT */ { G_ROOT, G_ROOT, G_DDDD, G_DIR, G_DTDR }, +/* ABS */ { G_ROOT, G_ROOT, G_ROOT, G_DIR, G_CAT }, +/* REL */ { G_ROOT, G_ROOT, G_ROOT, G_DIR, G_CAT } +} ; + +static int +file_flags( + char *ptr, + int len ) +{ + if( !len ) + return DIR_EMPTY; + if( len == 1 && ptr[0] == DELIM ) + return DIR_DOT; + if( len == 2 && ptr[0] == DELIM && ptr[1] == DELIM ) + return DIR_DOTDOT; + if( ptr[0] == DELIM ) + return DIR_REL; + return DIR_ABS; +} + +void +path_build( + PATHNAME *f, + char *file, + int binding ) +{ + char *ofile = file; + int dflag, rflag, act; + + if( DEBUG_SEARCH ) + { + printf("build file: "); + if( f->f_root.len ) + printf( "root = '%.*s' ", f->f_root.len, f->f_root.ptr ); + if( f->f_dir.len ) + printf( "dir = '%.*s' ", f->f_dir.len, f->f_dir.ptr ); + if( f->f_base.len ) + printf( "base = '%.*s' ", f->f_base.len, f->f_base.ptr ); + } + + /* Start with the grist. If the current grist isn't */ + /* surrounded by <>'s, add them. */ + + if( f->f_grist.len ) + { + if( f->f_grist.ptr[0] != '<' ) *file++ = '<'; + memcpy( file, f->f_grist.ptr, f->f_grist.len ); + file += f->f_grist.len; + if( file[-1] != '>' ) *file++ = '>'; + } + + /* Combine root & directory, according to the grid. */ + + dflag = file_flags( f->f_dir.ptr, f->f_dir.len ); + rflag = file_flags( f->f_root.ptr, f->f_root.len ); + + switch( act = grid[ rflag ][ dflag ] ) + { + case G_DTDR: + /* :: of rel dir */ + *file++ = DELIM; + /* fall through */ + + case G_DIR: + /* take dir */ + memcpy( file, f->f_dir.ptr, f->f_dir.len ); + file += f->f_dir.len; + break; + + case G_ROOT: + /* take root */ + memcpy( file, f->f_root.ptr, f->f_root.len ); + file += f->f_root.len; + break; + + case G_CAT: + /* prepend root to dir */ + memcpy( file, f->f_root.ptr, f->f_root.len ); + file += f->f_root.len; + if( file[-1] == DELIM ) --file; + memcpy( file, f->f_dir.ptr, f->f_dir.len ); + file += f->f_dir.len; + break; + + case G_DDDD: + /* make it ::: (../..) */ + strcpy( file, ":::" ); + file += 3; + break; + } + + /* Put : between dir and file (if none already) */ + + if( act != G_MT && + file[-1] != DELIM && + ( f->f_base.len || f->f_suffix.len ) ) + { + *file++ = DELIM; + } + + if( f->f_base.len ) + { + memcpy( file, f->f_base.ptr, f->f_base.len ); + file += f->f_base.len; + } + + if( f->f_suffix.len ) + { + memcpy( file, f->f_suffix.ptr, f->f_suffix.len ); + file += f->f_suffix.len; + } + + if( f->f_member.len ) + { + *file++ = '('; + memcpy( file, f->f_member.ptr, f->f_member.len ); + file += f->f_member.len; + *file++ = ')'; + } + *file = 0; + + if( DEBUG_SEARCH ) + printf(" -> '%s'\n", ofile); +} + +/* + * path_parent() - make a PATHNAME point to its parent dir + */ + +void +path_parent( PATHNAME *f ) +{ + /* just set everything else to nothing */ + + f->f_base.ptr = + f->f_suffix.ptr = + f->f_member.ptr = ""; + + f->f_base.len = + f->f_suffix.len = + f->f_member.len = 0; +} + +# endif /* OS_MAC */ diff --git a/src/tools/jam/pathsys.h b/src/tools/jam/pathsys.h new file mode 100644 index 0000000000..c16442bbc4 --- /dev/null +++ b/src/tools/jam/pathsys.h @@ -0,0 +1,49 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * pathsys.h - PATHNAME struct + */ + +/* + * PATHNAME - a name of a file, broken into dir/base/suffix(member) + * + * is salt to distinguish between targets that otherwise would + * have the same name: it never appears in the bound name of a target. + * (member) is an archive member name: the syntax is arbitrary, but must + * agree in path_parse(), path_build() and the Jambase. + * + * On VMS, we keep track of whether the original path was a directory + * (without a file), so that $(VAR:D) can climb to the parent. + */ + +typedef struct _pathname PATHNAME; +typedef struct _pathpart PATHPART; + +struct _pathpart { + char *ptr; + int len; +}; + +struct _pathname { + PATHPART part[6]; +# ifdef OS_VMS + int parent; +# endif + +# define f_grist part[0] +# define f_root part[1] +# define f_dir part[2] +# define f_base part[3] +# define f_suffix part[4] +# define f_member part[5] + +} ; + +void path_build( PATHNAME *f, char *file, int binding ); +void path_parse( char *file, PATHNAME *f ); +void path_parent( PATHNAME *f ); + diff --git a/src/tools/jam/pathunix.c b/src/tools/jam/pathunix.c new file mode 100644 index 0000000000..ae0bd01a44 --- /dev/null +++ b/src/tools/jam/pathunix.c @@ -0,0 +1,234 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "pathsys.h" + +# ifdef USE_PATHUNIX + +/* + * pathunix.c - manipulate file names on UNIX, NT, OS2, AmigaOS + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build() + * 12/19/94 (mikem) - solaris string table insanity support + * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way. + * 02/14/95 (seiwald) - parse and build /xxx properly + * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we + * should expect hdr searches to come up with strings + * like "thing/thing.h". So we need to test for "/" as + * well as "\" when parsing pathnames. + * 03/16/95 (seiwald) - fixed accursed typo on line 69. + * 05/03/96 (seiwald) - split from filent.c, fileunix.c + * 12/20/96 (seiwald) - when looking for the rightmost . in a file name, + * don't include the archive member name. + * 01/13/01 (seiwald) - turn on \ handling on UNIX, on by accident + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member + */ + +void +path_parse( + char *file, + PATHNAME *f ) +{ + char *p, *q; + char *end; + + memset( (char *)f, 0, sizeof( *f ) ); + + /* Look for */ + + if( file[0] == '<' && ( p = strchr( file, '>' ) ) ) + { + f->f_grist.ptr = file; + f->f_grist.len = p - file; + file = p + 1; + } + + /* Look for dir/ */ + + p = strrchr( file, '/' ); + +# if PATH_DELIM == '\\' + /* On NT, look for dir\ as well */ + { + char *p1 = strrchr( file, '\\' ); + p = p1 > p ? p1 : p; + } +# endif + + if( p ) + { + f->f_dir.ptr = file; + f->f_dir.len = p - file; + + /* Special case for / - dirname is /, not "" */ + + if( !f->f_dir.len ) + f->f_dir.len = 1; + +# if PATH_DELIM == '\\' + /* Special case for D:/ - dirname is D:/, not "D:" */ + + if( f->f_dir.len == 2 && file[1] == ':' ) + f->f_dir.len = 3; +# endif + + file = p + 1; + } + + end = file + strlen( file ); + + /* Look for (member) */ + + if( ( p = strchr( file, '(' ) ) && end[-1] == ')' ) + { + f->f_member.ptr = p + 1; + f->f_member.len = end - p - 2; + end = p; + } + + /* Look for .suffix */ + /* This would be memrchr() */ + + p = 0; + q = file; + + while( q = (char *)memchr( q, '.', end - q ) ) + p = q++; + + if( p ) + { + f->f_suffix.ptr = p; + f->f_suffix.len = end - p; + end = p; + } + + /* Leaves base */ + + f->f_base.ptr = file; + f->f_base.len = end - file; +} + +/* + * path_build() - build a filename given dir/base/suffix/member + */ + +void +path_build( + PATHNAME *f, + char *file, + int binding ) +{ + /* Start with the grist. If the current grist isn't */ + /* surrounded by <>'s, add them. */ + + if( f->f_grist.len ) + { + if( f->f_grist.ptr[0] != '<' ) *file++ = '<'; + memcpy( file, f->f_grist.ptr, f->f_grist.len ); + file += f->f_grist.len; + if( file[-1] != '>' ) *file++ = '>'; + } + + /* Don't prepend root if it's . or directory is rooted */ + +# if PATH_DELIM == '/' + + if( f->f_root.len + && !( f->f_root.len == 1 && f->f_root.ptr[0] == '.' ) + && !( f->f_dir.len && f->f_dir.ptr[0] == '/' ) ) + +# else /* unix */ + + if( f->f_root.len + && !( f->f_root.len == 1 && f->f_root.ptr[0] == '.' ) + && !( f->f_dir.len && f->f_dir.ptr[0] == '/' ) + && !( f->f_dir.len && f->f_dir.ptr[0] == '\\' ) + && !( f->f_dir.len && f->f_dir.ptr[1] == ':' ) ) + +# endif /* unix */ + + { + memcpy( file, f->f_root.ptr, f->f_root.len ); + file += f->f_root.len; + *file++ = PATH_DELIM; + } + + if( f->f_dir.len ) + { + memcpy( file, f->f_dir.ptr, f->f_dir.len ); + file += f->f_dir.len; + } + + /* UNIX: Put / between dir and file */ + /* NT: Put \ between dir and file */ + + if( f->f_dir.len && ( f->f_base.len || f->f_suffix.len ) ) + { + /* UNIX: Special case for dir \ : don't add another \ */ + /* NT: Special case for dir / : don't add another / */ + +# if PATH_DELIM == '\\' + if( !( f->f_dir.len == 3 && f->f_dir.ptr[1] == ':' ) ) +# endif + if( !( f->f_dir.len == 1 && f->f_dir.ptr[0] == PATH_DELIM ) ) + *file++ = PATH_DELIM; + } + + if( f->f_base.len ) + { + memcpy( file, f->f_base.ptr, f->f_base.len ); + file += f->f_base.len; + } + + if( f->f_suffix.len ) + { + memcpy( file, f->f_suffix.ptr, f->f_suffix.len ); + file += f->f_suffix.len; + } + + if( f->f_member.len ) + { + *file++ = '('; + memcpy( file, f->f_member.ptr, f->f_member.len ); + file += f->f_member.len; + *file++ = ')'; + } + *file = 0; +} + +/* + * path_parent() - make a PATHNAME point to its parent dir + */ + +void +path_parent( PATHNAME *f ) +{ + /* just set everything else to nothing */ + + f->f_base.ptr = + f->f_suffix.ptr = + f->f_member.ptr = ""; + + f->f_base.len = + f->f_suffix.len = + f->f_member.len = 0; +} + +# endif /* unix, NT, OS/2, AmigaOS */ diff --git a/src/tools/jam/pathvms.c b/src/tools/jam/pathvms.c new file mode 100644 index 0000000000..c0140cca79 --- /dev/null +++ b/src/tools/jam/pathvms.c @@ -0,0 +1,430 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "pathsys.h" + +# ifdef OS_VMS + +# define DEBUG + +/* + * pathvms.c - manipulate file names on VMS + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * WARNING! This file contains voodoo logic, as black magic is + * necessary for wrangling with VMS file name. Woe be to people + * who mess with this code. + * + * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! + * 05/03/96 (seiwald) - split from filevms.c + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member + */ + +void +path_parse( + char *file, + PATHNAME *f ) +{ + char *p, *q; + char *end; + + memset( (char *)f, 0, sizeof( *f ) ); + + /* Look for */ + + if( file[0] == '<' && ( p = strchr( file, '>' ) ) ) + { + f->f_grist.ptr = file; + f->f_grist.len = p - file; + file = p + 1; + } + + /* Look for dev:[dir] or dev: */ + + if( ( p = strchr( file, ']' ) ) || ( p = strchr( file, ':' ) ) ) + { + f->f_dir.ptr = file; + f->f_dir.len = p + 1 - file; + file = p + 1; + } + + end = file + strlen( file ); + + /* Look for (member) */ + + if( ( p = strchr( file, '(' ) ) && end[-1] == ')' ) + { + f->f_member.ptr = p + 1; + f->f_member.len = end - p - 2; + end = p; + } + + /* Look for .suffix */ + /* This would be memrchr() */ + + p = 0; + q = file; + + while( q = (char *)memchr( q, '.', end - q ) ) + p = q++; + + if( p ) + { + f->f_suffix.ptr = p; + f->f_suffix.len = end - p; + end = p; + } + + /* Leaves base */ + + f->f_base.ptr = file; + f->f_base.len = end - file; + + /* Is this a directory without a file spec? */ + + f->parent = 0; +} + +/* + * dir mods result + * --- --- ------ + * Rerooting: + * + * (none) :R=dev: dev: + * devd: :R=dev: devd: + * devd:[dir] :R=dev: devd:[dir] + * [.dir] :R=dev: dev:[dir] questionable + * [dir] :R=dev: dev:[dir] + * + * (none) :R=[rdir] [rdir] questionable + * devd: :R=[rdir] devd: + * devd:[dir] :R=[rdir] devd:[dir] + * [.dir] :R=[rdir] [rdir.dir] questionable + * [dir] :R=[rdir] [rdir] + * + * (none) :R=dev:[root] dev:[root] + * devd: :R=dev:[root] devd: + * devd:[dir] :R=dev:[root] devd:[dir] + * [.dir] :R=dev:[root] dev:[root.dir] + * [dir] :R=dev:[root] [dir] + * + * Climbing to parent: + * + */ + +# define DIR_EMPTY 0 /* empty string */ +# define DIR_DEV 1 /* dev: */ +# define DIR_DEVDIR 2 /* dev:[dir] */ +# define DIR_DOTDIR 3 /* [.dir] */ +# define DIR_DASHDIR 4 /* [-] or [-.dir] */ +# define DIR_ABSDIR 5 /* [dir] */ +# define DIR_ROOT 6 /* [000000] or dev:[000000] */ + +# define G_DIR 0 /* take just dir */ +# define G_ROOT 1 /* take just root */ +# define G_VAD 2 /* root's dev: + [abs] */ +# define G_DRD 3 /* root's dev:[dir] + [.rel] */ +# define G_VRD 4 /* root's dev: + [.rel] made [abs] */ +# define G_DDD 5 /* root's dev:[dir] + . + [dir] */ + +static int grid[7][7] = { + +/* root/dir EMPTY DEV DEVDIR DOTDIR DASH, ABSDIR ROOT */ +/* EMPTY */ G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, G_DIR, +/* DEV */ G_ROOT, G_DIR, G_DIR, G_VRD, G_VAD, G_VAD, G_VAD, +/* DEVDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_VAD, G_VAD, G_VAD, +/* DOTDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, +/* DASHDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DDD, G_DIR, G_DIR, +/* ABSDIR */ G_ROOT, G_DIR, G_DIR, G_DRD, G_DIR, G_DIR, G_DIR, +/* ROOT */ G_ROOT, G_DIR, G_DIR, G_VRD, G_DIR, G_DIR, G_DIR, + +} ; + +struct dirinf { + int flags; + + struct { + char *ptr; + int len; + } dev, dir; +} ; + +static char * +strnchr( + char *buf, + int c, + int len ) +{ + while( len-- ) + if( *buf && *buf++ == c ) + return buf - 1; + + return 0; +} + +static void +dir_flags( + char *buf, + int len, + struct dirinf *i ) +{ + char *p; + + if( !buf || !len ) + { + i->flags = DIR_EMPTY; + i->dev.ptr = + i->dir.ptr = 0; + i->dev.len = + i->dir.len = 0; + } + else if( p = strnchr( buf, ':', len ) ) + { + i->dev.ptr = buf; + i->dev.len = p + 1 - buf; + i->dir.ptr = buf + i->dev.len; + i->dir.len = len - i->dev.len; + i->flags = i->dir.len && *i->dir.ptr == '[' ? DIR_DEVDIR : DIR_DEV; + } + else + { + i->dev.ptr = buf; + i->dev.len = 0; + i->dir.ptr = buf; + i->dir.len = len; + + if( *buf == '[' && buf[1] == ']' ) + i->flags = DIR_EMPTY; + else if( *buf == '[' && buf[1] == '.' ) + i->flags = DIR_DOTDIR; + else if( *buf == '[' && buf[1] == '-' ) + i->flags = DIR_DASHDIR; + else + i->flags = DIR_ABSDIR; + } + + /* But if its rooted in any way */ + + if( i->dir.len == 8 && !strncmp( i->dir.ptr, "[000000]", 8 ) ) + i->flags = DIR_ROOT; +} + +/* + * path_build() - build a filename given dir/base/suffix/member + */ + +void +path_build( + PATHNAME *f, + char *file, + int binding ) +{ + char *ofile = file; + struct dirinf root, dir; + int g; + + /* Start with the grist. If the current grist isn't */ + /* surrounded by <>'s, add them. */ + + if( f->f_grist.len ) + { + if( f->f_grist.ptr[0] != '<' ) *file++ = '<'; + memcpy( file, f->f_grist.ptr, f->f_grist.len ); + file += f->f_grist.len; + if( file[-1] != '>' ) *file++ = '>'; + } + + /* Get info on root and dir for combining. */ + + dir_flags( f->f_root.ptr, f->f_root.len, &root ); + dir_flags( f->f_dir.ptr, f->f_dir.len, &dir ); + + /* Combine */ + + switch( g = grid[ root.flags ][ dir.flags ] ) + { + case G_DIR: + /* take dir */ + memcpy( file, f->f_dir.ptr, f->f_dir.len ); + file += f->f_dir.len; + break; + + case G_ROOT: + /* take root */ + memcpy( file, f->f_root.ptr, f->f_root.len ); + file += f->f_root.len; + break; + + case G_VAD: + /* root's dev + abs directory */ + memcpy( file, root.dev.ptr, root.dev.len ); + file += root.dev.len; + memcpy( file, dir.dir.ptr, dir.dir.len ); + file += dir.dir.len; + break; + + case G_DRD: + case G_DDD: + /* root's dev:[dir] + rel directory */ + memcpy( file, f->f_root.ptr, f->f_root.len ); + file += f->f_root.len; + + /* sanity checks: root ends with ] */ + + if( file[-1] == ']' ) + --file; + + /* Add . if separating two -'s */ + + if( g == G_DDD ) + *file++ = '.'; + + /* skip [ of dir */ + memcpy( file, dir.dir.ptr + 1, dir.dir.len - 1 ); + file += dir.dir.len - 1; + break; + + case G_VRD: + /* root's dev + rel directory made abs */ + memcpy( file, root.dev.ptr, root.dev.len ); + file += root.dev.len; + *file++ = '['; + /* skip [. of rel dir */ + memcpy( file, dir.dir.ptr + 2, dir.dir.len - 2 ); + file += dir.dir.len - 2; + break; + } + +# ifdef DEBUG + if( DEBUG_SEARCH && ( root.flags || dir.flags ) ) + { + *file = 0; + printf( "%d x %d = %d (%s)\n", root.flags, dir.flags, + grid[ root.flags ][ dir.flags ], ofile ); + } +# endif + + /* + * Now do the special :P modifier when no file was present. + * (none) (none) + * [dir1.dir2] [dir1] + * [dir] [000000] + * [.dir] [] + * [] [] + */ + + if( file[-1] == ']' && f->parent ) + { + while( file-- > ofile ) + { + if( *file == '.' ) + { + *file++ = ']'; + break; + } + else if( *file == '-' ) + { + /* handle .- or - */ + if( file > ofile && file[-1] == '.' ) + --file; + *file++ = ']'; + break; + } + else if( *file == '[' ) + { + if( file[1] == ']' ) + { + file += 2; + } + else + { + strcpy( file, "[000000]" ); + file += 8; + } + break; + } + } + } + + /* Now copy the file pieces. */ + + if( f->f_base.len ) + { + memcpy( file, f->f_base.ptr, f->f_base.len ); + file += f->f_base.len; + } + + /* If there is no suffix, we append a "." onto all generated */ + /* names. This keeps VMS from appending its own (wrong) idea */ + /* of what the suffix should be. */ + + if( f->f_suffix.len ) + { + memcpy( file, f->f_suffix.ptr, f->f_suffix.len ); + file += f->f_suffix.len; + } + else if( binding && f->f_base.len ) + { + *file++ = '.'; + } + + if( f->f_member.len ) + { + *file++ = '('; + memcpy( file, f->f_member.ptr, f->f_member.len ); + file += f->f_member.len; + *file++ = ')'; + } + *file = 0; + +# ifdef DEBUG + if( DEBUG_SEARCH ) + printf("built %.*s + %.*s / %.*s suf %.*s mem %.*s -> %s\n", + f->f_root.len, f->f_root.ptr, + f->f_dir.len, f->f_dir.ptr, + f->f_base.len, f->f_base.ptr, + f->f_suffix.len, f->f_suffix.ptr, + f->f_member.len, f->f_member.ptr, + ofile ); +# endif +} + +/* + * path_parent() - make a PATHNAME point to its parent dir + */ + +void +path_parent( PATHNAME *f ) +{ + if( f->f_base.len ) + { + f->f_base.ptr = + f->f_suffix.ptr = + f->f_member.ptr = ""; + + f->f_base.len = + f->f_suffix.len = + f->f_member.len = 0; + } + else + { + f->parent = 1; + } +} + +# endif /* VMS */ diff --git a/src/tools/jam/regexp.c b/src/tools/jam/regexp.c new file mode 100644 index 0000000000..92ee8efd8a --- /dev/null +++ b/src/tools/jam/regexp.c @@ -0,0 +1,1317 @@ +/* + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to | + *** to assist in implementing egrep. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching + *** as in BSD grep and ex. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \. + *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods, + *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy. + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@vix.com, on 28 August 1993, for use in jam. Regmagic.h + *** was moved into regexp.h, and the include of regexp.h now uses "'s + *** to avoid conflicting with the system regexp.h. Const, bless its + *** soul, was removed so it can compile everywhere. The declaration + *** of strchr() was in conflict on AIX, so it was removed (as it is + *** happily defined in string.h). + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@perforce.com, on 20 January 2000, to use function prototypes. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ +#include "regexp.h" +#include +#include +#ifndef ultrix +#include +#endif +#include + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ +#define WORDA 12 /* no Match "" at wordchar, where prev is nonword */ +#define WORDZ 13 /* no Match "" at nonwordchar, where prev is word */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * See regmagic.h for one further detail of program structure. + */ + + +/* + * Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +/* + * Global work variables for regcomp(). + */ +static char *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +#ifndef STATIC +#define STATIC static +#endif +STATIC char *reg( int paren, int *flagp ); +STATIC char *regbranch( int *flagp ); +STATIC char *regpiece( int *flagp ); +STATIC char *regatom( int *flagp ); +STATIC char *regnode( int op ); +STATIC char *regnext( register char *p ); +STATIC void regc( int b ); +STATIC void reginsert( char op, char *opnd ); +STATIC void regtail( char *p, char *val ); +STATIC void regoptail( char *p, char *val ); +#ifdef STRCSPN +STATIC int strcspn(); +#endif + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp * +regcomp( char *exp ) +{ + register regexp *r; + register char *scan; + register char *longest; + register unsigned len; + int flags; + + if (exp == NULL) + FAIL("NULL argument"); + + /* First pass: determine size, legality. */ +#ifdef notdef + if (exp[0] == '.' && exp[1] == '*') exp += 2; /* aid grep */ +#endif + regparse = (char *)exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); + if (r == NULL) + FAIL("out of space"); + + /* Second pass: emit code. */ + regparse = (char *)exp; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program+1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char * +reg( + int paren, /* Parenthesized? */ + int *flagp ) +{ + register char *ret; + register char *br; + register char *ender; + register int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN+parno); + } else + ret = NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == NULL) + return(NULL); + if (ret != NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*regparse == '|' || *regparse == '\n') { + regparse++; + br = regbranch(&flags); + if (br == NULL) + return(NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE+parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char * +regbranch( int *flagp ) +{ + register char *ret; + register char *chain; + register char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = NULL; + while (*regparse != '\0' && *regparse != ')' && + *regparse != '\n' && *regparse != '|') { + latest = regpiece(&flags); + if (latest == NULL) + return(NULL); + *flagp |= flags&HASWIDTH; + if (chain == NULL) /* First piece. */ + *flagp |= flags&SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == NULL) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char * +regpiece( int *flagp ) +{ + register char *ret; + register char op; + register char *next; + int flags; + + ret = regatom(&flags); + if (ret == NULL) + return(NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); + + if (op == '*' && (flags&SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags&SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING); /* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static char * +regatom( int *flagp ) +{ + register char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + /* FIXME: these chars only have meaning at beg/end of pat? */ + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + register int classr; + register int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + classr = UCHARAT(regparse-2)+1; + classend = UCHARAT(regparse); + if (classr > classend+1) + FAIL("invalid [] range"); + for (; classr <= classend; classr++) + regc(classr); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == NULL) + return(NULL); + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case '\n': + case ')': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + break; + case '\\': + switch (*regparse++) { + case '\0': + FAIL("trailing \\"); + break; + case '<': + ret = regnode(WORDA); + break; + case '>': + ret = regnode(WORDZ); + break; + /* FIXME: Someday handle \1, \2, ... */ + default: + /* Handle general quoted chars in exact-match routine */ + goto de_fault; + } + break; + de_fault: + default: + /* + * Encode a string of characters to be matched exactly. + * + * This is a bit tricky due to quoted chars and due to + * '*', '+', and '?' taking the SINGLE char previous + * as their operand. + * + * On entry, the char at regparse[-1] is going to go + * into the string, no matter what it is. (It could be + * following a \ if we are entered from the '\' case.) + * + * Basic idea is to pick up a good char in ch and + * examine the next char. If it's *+? then we twiddle. + * If it's \ then we frozzle. If it's other magic char + * we push ch and terminate the string. If none of the + * above, we push ch on the string and go around again. + * + * regprev is used to remember where "the current char" + * starts in the string, if due to a *+? we need to back + * up and put the current char in a separate, 1-char, string. + * When regprev is NULL, ch is the only char in the + * string; this is used in *+? handling, and in setting + * flags |= SIMPLE at the end. + */ + { + char *regprev; + register char ch; + + regparse--; /* Look at cur char */ + ret = regnode(EXACTLY); + for ( regprev = 0 ; ; ) { + ch = *regparse++; /* Get current char */ + switch (*regparse) { /* look at next one */ + + default: + regc(ch); /* Add cur to string */ + break; + + case '.': case '[': case '(': + case ')': case '|': case '\n': + case '$': case '^': + case '\0': + /* FIXME, $ and ^ should not always be magic */ + magic: + regc(ch); /* dump cur char */ + goto done; /* and we are done */ + + case '?': case '+': case '*': + if (!regprev) /* If just ch in str, */ + goto magic; /* use it */ + /* End mult-char string one early */ + regparse = regprev; /* Back up parse */ + goto done; + + case '\\': + regc(ch); /* Cur char OK */ + switch (regparse[1]){ /* Look after \ */ + case '\0': + case '<': + case '>': + /* FIXME: Someday handle \1, \2, ... */ + goto done; /* Not quoted */ + default: + /* Backup point is \, scan * point is after it. */ + regprev = regparse; + regparse++; + continue; /* NOT break; */ + } + } + regprev = regparse; /* Set backup point */ + } + done: + regc('\0'); + *flagp |= HASWIDTH; + if (!regprev) /* One char? */ + *flagp |= SIMPLE; + } + break; + } + + return(ret); +} + +/* + - regnode - emit a node + */ +static char * /* Location. */ +regnode( int op ) +{ + register char *ret; + register char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void +regc( int b ) +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void +reginsert( + char op, + char *opnd ) +{ + register char *src; + register char *dst; + register char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void +regtail( + char *p, + char *val ) +{ + register char *scan; + register char *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan+1) = (offset>>8)&0377; + *(scan+2) = offset&0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ + +static void +regoptail( + char *p, + char *val ) +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* + * Forwards. + */ +STATIC int regtry( regexp *prog, char *string ); +STATIC int regmatch( char *prog ); +STATIC int regrepeat( char *p ); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int +regexec( + register regexp *prog, + register char *string ) +{ + register char *s; + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { + regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != NULL) { + s = (char *)string; + while ((s = strchr(s, prog->regmust[0])) != NULL) { + if (strncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == NULL) /* Not present. */ + return(0); + } + + /* Mark beginning of line for ^ . */ + regbol = (char *)string; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return(regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = (char *)string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = strchr(s, prog->regstart)) != NULL) { + if (regtry(prog, s)) + return(1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return(1); + } while (*s++ != '\0'); + + /* Failure. */ + return(0); +} + +/* + - regtry - try match at specific point + */ +static int /* 0 failure, 1 success */ +regtry( + regexp *prog, + char *string ) +{ + register int i; + register char **sp; + register char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = NULL; + *ep++ = NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return(1); + } else + return(0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +static int /* 0 failure, 1 success */ +regmatch( char *prog ) +{ + register char *scan; /* Current node. */ + char *next; /* Next node. */ + + scan = prog; +#ifdef DEBUG + if (scan != NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return(0); + break; + case EOL: + if (*reginput != '\0') + return(0); + break; + case WORDA: + /* Must be looking at a letter, digit, or _ */ + if ((!isalnum(*reginput)) && *reginput != '_') + return(0); + /* Prev must be BOL or nonword */ + if (reginput > regbol && + (isalnum(reginput[-1]) || reginput[-1] == '_')) + return(0); + break; + case WORDZ: + /* Must be looking at non letter, digit, or _ */ + if (isalnum(*reginput) || *reginput == '_') + return(0); + /* We don't care what the previous char was */ + break; + case ANY: + if (*reginput == '\0') + return(0); + reginput++; + break; + case EXACTLY: { + register int len; + register char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return(0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: { + register int no; + register char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (regstartp[no] == NULL) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: { + register int no; + register char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later + * invocation of the same parentheses + * already has. + */ + if (regendp[no] == NULL) + regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH: { + register char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != NULL && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS: { + register char nextch; + register int no; + register char *save; + register int min; + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + break; + default: + regerror("memory corruption"); + return(0); + break; + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int +regrepeat( char *p ) +{ + register int count = 0; + register char *scan; + register char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static char * +regnext( register char *p ) +{ + register int offset; + + if (p == ®dummy) + return(NULL); + + offset = NEXT(p); + if (offset == 0) + return(NULL); + + if (OP(p) == BACK) + return(p-offset); + else + return(p+offset); +} + +#ifdef DEBUG + +STATIC char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void +regdump( regexp *r ) +{ + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *next; + + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (s-r->program)+(next-s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') + printf("start `%c' ", r->regstart); + if (r->reganch) + printf("anchored "); + if (r->regmust != NULL) + printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +static char * +regprop( char *op ) +{ + register char *p; + static char buf[50]; + + (void) strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: + sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); + p = NULL; + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: + sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + case WORDA: + p = "WORDA"; + break; + case WORDZ: + p = "WORDZ"; + break; + default: + regerror("corrupted opcode"); + break; + } + if (p != NULL) + (void) strcat(buf, p); + return(buf); +} +#endif + +/* + * The following is provided for those people who do not have strcspn() in + * their C libraries. They should get off their butts and do something + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +#ifdef STRCSPN +/* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + +static int +strcspn( + char *s1, + char *s2 ) +{ + register char *scan1; + register char *scan2; + register int count; + + count = 0; + for (scan1 = s1; *scan1 != '\0'; scan1++) { + for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ + if (*scan1 == *scan2++) + return(count); + count++; + } + return(count); +} +#endif diff --git a/src/tools/jam/regexp.h b/src/tools/jam/regexp.h new file mode 100644 index 0000000000..40b7538a07 --- /dev/null +++ b/src/tools/jam/regexp.h @@ -0,0 +1,26 @@ +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define NSUBEXP 10 +typedef struct regexp { + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +regexp *regcomp( char *exp ); +int regexec( regexp *prog, char *string ); +void regerror( char *s ); + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 diff --git a/src/tools/jam/rules.c b/src/tools/jam/rules.c new file mode 100644 index 0000000000..dc3f8dbc56 --- /dev/null +++ b/src/tools/jam/rules.c @@ -0,0 +1,267 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "rules.h" +# include "newstr.h" +# include "hash.h" + +/* + * rules.c - access to RULEs, TARGETs, and ACTIONs + * + * External routines: + * + * bindrule() - return pointer to RULE, creating it if necessary + * bindtarget() - return pointer to TARGET, creating it if necessary + * touchtarget() - mark a target to simulate being new + * targetlist() - turn list of target names into a TARGET chain + * targetentry() - add a TARGET to a chain of TARGETS + * actionlist() - append to an ACTION chain + * addsettings() - add a deferred "set" command to a target + * usesettings() - set all target specific variables + * pushsettings() - set all target specific variables + * popsettings() - reset target specific variables to their pre-push values + * freesettings() - delete a settings list + * donerules() - free RULE and TARGET tables + * + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + */ + +static struct hash *rulehash = 0; +static struct hash *targethash = 0; + + +/* + * bindrule() - return pointer to RULE, creating it if necessary + */ + +RULE * +bindrule( char *rulename ) +{ + RULE rule, *r = &rule; + + if( !rulehash ) + rulehash = hashinit( sizeof( RULE ), "rules" ); + + r->name = rulename; + + if( hashenter( rulehash, (HASHDATA **)&r ) ) + { + r->name = newstr( rulename ); /* never freed */ + r->procedure = (PARSE *)0; + r->actions = (char *)0; + r->bindlist = L0; + r->flags = 0; + } + + return r; +} + +/* + * bindtarget() - return pointer to TARGET, creating it if necessary + */ + +TARGET * +bindtarget( char *targetname ) +{ + TARGET target, *t = ⌖ + + if( !targethash ) + targethash = hashinit( sizeof( TARGET ), "targets" ); + + t->name = targetname; + + if( hashenter( targethash, (HASHDATA **)&t ) ) + { + memset( (char *)t, '\0', sizeof( *t ) ); + t->name = newstr( targetname ); /* never freed */ + t->boundname = t->name; /* default for T_FLAG_NOTFILE */ + } + + return t; +} + +/* + * touchtarget() - mark a target to simulate being new + */ + +void +touchtarget( char *t ) +{ + bindtarget( t )->flags |= T_FLAG_TOUCHED; +} + +/* + * targetlist() - turn list of target names into a TARGET chain + * + * Inputs: + * chain existing TARGETS to append to + * targets list of target names + */ + +TARGETS * +targetlist( + TARGETS *chain, + LIST *targets ) +{ + for( ; targets; targets = list_next( targets ) ) + chain = targetentry( chain, bindtarget( targets->string ) ); + + return chain; +} + +/* + * targetentry() - add a TARGET to a chain of TARGETS + * + * Inputs: + * chain exisitng TARGETS to append to + * target new target to append + */ + +TARGETS * +targetentry( + TARGETS *chain, + TARGET *target ) +{ + TARGETS *c; + + c = (TARGETS *)malloc( sizeof( TARGETS ) ); + c->target = target; + + if( !chain ) chain = c; + else chain->tail->next = c; + chain->tail = c; + c->next = 0; + + return chain; +} + +/* + * actionlist() - append to an ACTION chain + */ + +ACTIONS * +actionlist( + ACTIONS *chain, + ACTION *action ) +{ + ACTIONS *actions = (ACTIONS *)malloc( sizeof( ACTIONS ) ); + + actions->action = action; + + if( !chain ) chain = actions; + else chain->tail->next = actions; + chain->tail = actions; + actions->next = 0; + + return chain; +} + +/* + * addsettings() - add a deferred "set" command to a target + * + * Adds a variable setting (varname=list) onto a chain of settings + * for a particular target. Replaces the previous previous value, + * if any, unless 'append' says to append the new list onto the old. + * Returns the head of the chain of settings. + */ + +SETTINGS * +addsettings( + SETTINGS *head, + int append, + char *symbol, + LIST *value ) +{ + SETTINGS *v; + + /* Look for previous setting */ + + for( v = head; v; v = v->next ) + if( !strcmp( v->symbol, symbol ) ) + break; + + /* If not previously set, alloc a new. */ + /* If appending, do so. */ + /* Else free old and set new. */ + + if( !v ) + { + v = (SETTINGS *)malloc( sizeof( *v ) ); + v->symbol = newstr( symbol ); + v->value = value; + v->next = head; + head = v; + } + else if( append ) + { + v->value = list_append( v->value, value ); + } + else + { + list_free( v->value ); + v->value = value; + } + + /* Return (new) head of list. */ + + return head; +} + +/* + * pushsettings() - set all target specific variables + */ + +void +pushsettings( SETTINGS *v ) +{ + for( ; v; v = v->next ) + v->value = var_swap( v->symbol, v->value ); +} + +/* + * popsettings() - reset target specific variables to their pre-push values + */ + +void +popsettings( SETTINGS *v ) +{ + pushsettings( v ); /* just swap again */ +} + +/* + * freesettings() - delete a settings list + */ + +void +freesettings( SETTINGS *v ) +{ + while( v ) + { + SETTINGS *n = v->next; + + freestr( v->symbol ); + list_free( v->value ); + free( (char *)v ); + + v = n; + } +} + +/* + * donerules() - free RULE and TARGET tables + */ + +void +donerules() +{ + hashdone( rulehash ); + hashdone( targethash ); +} diff --git a/src/tools/jam/rules.h b/src/tools/jam/rules.h new file mode 100644 index 0000000000..40503d97a2 --- /dev/null +++ b/src/tools/jam/rules.h @@ -0,0 +1,172 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * rules.h - targets, rules, and related information + * + * This file describes the structures holding the targets, rules, and + * related information accumulated by interpreting the statements + * of the jam files. + * + * The following are defined: + * + * RULE - a generic jam rule, the product of RULE and ACTIONS + * ACTIONS - a chain of ACTIONs + * ACTION - a RULE instance with targets and sources + * SETTINGS - variables to set when executing a TARGET's ACTIONS + * TARGETS - a chain of TARGETs + * TARGET - a file or "thing" that can be built + * + * 04/11/94 (seiwald) - Combined deps & headers into deps[2] in TARGET. + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/19/95 (seiwald) - split DONTKNOW into CANTFIND/CANTMAKE. + * 02/02/95 (seiwald) - new LEAVES modifier on targets. + * 02/14/95 (seiwald) - new NOUPDATE modifier on targets. + */ + +typedef struct _rule RULE; +typedef struct _target TARGET; +typedef struct _targets TARGETS; +typedef struct _action ACTION; +typedef struct _actions ACTIONS; +typedef struct _settings SETTINGS ; + +/* RULE - a generic jam rule, the product of RULE and ACTIONS */ + +struct _rule { + char *name; + PARSE *procedure; /* parse tree from RULE */ + char *actions; /* command string from ACTIONS */ + LIST *bindlist; /* variable to bind for actions */ + int flags; /* modifiers on ACTIONS */ + +# define RULE_UPDATED 0x01 /* $(>) is updated sources only */ +# define RULE_TOGETHER 0x02 /* combine actions on single target */ +# define RULE_IGNORE 0x04 /* ignore return status of executes */ +# define RULE_QUIETLY 0x08 /* don't mention it unless verbose */ +# define RULE_PIECEMEAL 0x10 /* split exec so each $(>) is small */ +# define RULE_EXISTING 0x20 /* $(>) is pre-exisitng sources only */ + +} ; + +/* ACTIONS - a chain of ACTIONs */ + +struct _actions { + ACTIONS *next; + ACTIONS *tail; /* valid only for head */ + ACTION *action; +} ; + +/* ACTION - a RULE instance with targets and sources */ + +struct _action { + RULE *rule; + TARGETS *targets; + TARGETS *sources; /* aka $(>) */ + char running; /* has been started */ + char status; /* see TARGET status */ +} ; + +/* SETTINGS - variables to set when executing a TARGET's ACTIONS */ + +struct _settings { + SETTINGS *next; + char *symbol; /* symbol name for var_set() */ + LIST *value; /* symbol value for var_set() */ +} ; + +/* TARGETS - a chain of TARGETs */ + +struct _targets { + TARGETS *next; + TARGETS *tail; /* valid only for head */ + TARGET *target; +} ; + +/* TARGET - a file or "thing" that can be built */ + +struct _target { + char *name; + char *boundname; /* if search() relocates target */ + ACTIONS *actions; /* rules to execute, if any */ + SETTINGS *settings; /* variables to define */ + + char flags; /* status info */ + +# define T_FLAG_TEMP 0x01 /* TEMPORARY applied */ +# define T_FLAG_NOCARE 0x02 /* NOCARE applied */ +# define T_FLAG_NOTFILE 0x04 /* NOTFILE applied */ +# define T_FLAG_TOUCHED 0x08 /* ALWAYS applied or -t target */ +# define T_FLAG_LEAVES 0x10 /* LEAVES applied */ +# define T_FLAG_NOUPDATE 0x20 /* NOUPDATE applied */ + + char binding; /* how target relates to real file */ + +# define T_BIND_UNBOUND 0 /* a disembodied name */ +# define T_BIND_MISSING 1 /* couldn't find real file */ +# define T_BIND_PARENTS 2 /* using parent's timestamp */ +# define T_BIND_EXISTS 3 /* real file, timestamp valid */ + + TARGETS *deps[2]; /* dependencies */ + +# define T_DEPS_DEPENDS 0 /* due to DEPENDS */ +# define T_DEPS_INCLUDES 1 /* due to INCLUDES */ + + time_t time; /* update time */ + time_t leaf; /* update time of leaf sources */ + time_t htime; /* header's time */ + time_t hleaf; /* update time of leaf headers */ + + char fate; /* make0()'s diagnosis */ + char hfate; /* collected fate for headers */ + +# define T_FATE_INIT 0 /* nothing done to target */ +# define T_FATE_MAKING 1 /* make0(target) on stack */ + +# define T_FATE_STABLE 2 /* target didn't need updating */ +# define T_FATE_NEWER 3 /* target newer than parent */ + +# define T_FATE_SPOIL 4 /* >= SPOIL rebuilds parents */ +# define T_FATE_ISTMP 4 /* unneeded temp target oddly present */ + +# define T_FATE_BUILD 5 /* >= BUILD rebuilds target */ +# define T_FATE_TOUCHED 5 /* manually touched with -t */ +# define T_FATE_MISSING 6 /* is missing, needs updating */ +# define T_FATE_OUTDATED 7 /* is out of date, needs updating */ +# define T_FATE_UPDATE 8 /* deps updated, needs updating */ + +# define T_FATE_BROKEN 9 /* >= BROKEN ruins parents */ +# define T_FATE_CANTFIND 9 /* no rules to make missing target */ +# define T_FATE_CANTMAKE 10 /* can't find dependents */ + + char progress; /* tracks make1() progress */ + +# define T_MAKE_INIT 0 /* make1(target) not yet called */ +# define T_MAKE_ONSTACK 1 /* make1(target) on stack */ +# define T_MAKE_ACTIVE 2 /* make1(target) in make1b() */ +# define T_MAKE_RUNNING 3 /* make1(target) running commands */ +# define T_MAKE_DONE 4 /* make1(target) done */ + + char status; /* execcmd() result */ + + int asynccnt; /* child deps outstanding */ + TARGETS *parents; /* used by make1() for completion */ + char *cmds; /* type-punned command list */ +} ; + +RULE *bindrule( char *rulename ); +TARGET *bindtarget( char *targetname ); +void touchtarget( char *t ); +TARGETS *targetlist( TARGETS *chain, LIST *targets ); +TARGETS *targetentry( TARGETS *chain, TARGET *target ); +ACTIONS *actionlist( ACTIONS *chain, ACTION *action ); +SETTINGS *addsettings( SETTINGS *head, int append, char *symbol, LIST *value ); +void pushsettings( SETTINGS *v ); +void popsettings( SETTINGS *v ); +void freesettings( SETTINGS *v ); +void donerules(); diff --git a/src/tools/jam/scan.c b/src/tools/jam/scan.c new file mode 100644 index 0000000000..38c1d3ec13 --- /dev/null +++ b/src/tools/jam/scan.c @@ -0,0 +1,393 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "scan.h" +# include "jamgram.h" +# include "jambase.h" +# include "newstr.h" + +/* + * scan.c - the jam yacc scanner + * + * 12/26/93 (seiwald) - bump buf in yylex to 10240 - yuk. + * 09/16/94 (seiwald) - check for overflows, unmatched {}'s, etc. + * Also handle tokens abutting EOF by remembering + * to return EOF now matter how many times yylex() + * reinvokes yyline(). + * 02/11/95 (seiwald) - honor only punctuation keywords if SCAN_PUNCT. + * 07/27/95 (seiwald) - Include jamgram.h after scan.h, so that YYSTYPE is + * defined before Linux's yacc tries to redefine it. + */ + +struct keyword { + char *word; + int type; +} keywords[] = { +# include "jamgramtab.h" + { 0, 0 } +} ; + +struct include { + struct include *next; /* next serial include file */ + char *string; /* pointer into current line */ + char **strings; /* for yyfparse() -- text to parse */ + FILE *file; /* for yyfparse() -- file being read */ + char *fname; /* for yyfparse() -- file name */ + int line; /* line counter for error messages */ + char buf[ 512 ]; /* for yyfparse() -- line buffer */ +} ; + +static struct include *incp = 0; /* current file; head of chain */ + +static int scanmode = SCAN_NORMAL; +static int anyerrors = 0; +static char *symdump( YYSTYPE *s ); + +# define BIGGEST_TOKEN 10240 /* no single token can be larger */ + +/* + * Set parser mode: normal, string, or keyword + */ + +void +yymode( int n ) +{ + scanmode = n; +} + +void +yyerror( char *s ) +{ + if( incp ) + printf( "%s: line %d: ", incp->fname, incp->line ); + + printf( "%s at %s\n", s, symdump( &yylval ) ); + + ++anyerrors; +} + +int +yyanyerrors() +{ + return anyerrors != 0; +} + +void +yyfparse( char *s ) +{ + struct include *i = (struct include *)malloc( sizeof( *i ) ); + + /* Push this onto the incp chain. */ + + i->string = ""; + i->strings = 0; + i->file = 0; + i->fname = copystr( s ); + i->line = 0; + i->next = incp; + incp = i; + + /* If the filename is "+", it means use the internal jambase. */ + + if( !strcmp( s, "+" ) ) + i->strings = jambase; +} + +/* + * yyline() - read new line and return first character + * + * Fabricates a continuous stream of characters across include files, + * returning EOF at the bitter end. + */ + +int +yyline() +{ + struct include *i = incp; + + if( !incp ) + return EOF; + + /* Once we start reading from the input stream, we reset the */ + /* include insertion point so that the next include file becomes */ + /* the head of the list. */ + + /* If there is more data in this line, return it. */ + + if( *i->string ) + return *i->string++; + + /* If we're reading from an internal string list, go to the */ + /* next string. */ + + if( i->strings ) + { + if( !*i->strings ) + goto next; + + i->line++; + i->string = *(i->strings++); + return *i->string++; + } + + /* If necessary, open the file */ + + if( !i->file ) + { + FILE *f = stdin; + + if( strcmp( i->fname, "-" ) && !( f = fopen( i->fname, "r" ) ) ) + perror( i->fname ); + + i->file = f; + } + + /* If there's another line in this file, start it. */ + + if( i->file && fgets( i->buf, sizeof( i->buf ), i->file ) ) + { + i->line++; + i->string = i->buf; + return *i->string++; + } + + next: + /* This include is done. */ + /* Free it up and return EOF so yyparse() returns to parse_file(). */ + + incp = i->next; + + /* Close file, free name */ + + if( i->file && i->file != stdin ) + fclose( i->file ); + freestr( i->fname ); + free( (char *)i ); + + return EOF; +} + +/* + * yylex() - set yylval to current token; return its type + * + * Macros to move things along: + * + * yychar() - return and advance character; invalid after EOF + * yyprev() - back up one character; invalid before yychar() + * + * yychar() returns a continuous stream of characters, until it hits + * the EOF of the current include file. + */ + +# define yychar() ( *incp->string ? *incp->string++ : yyline() ) +# define yyprev() ( incp->string-- ) + +int +yylex() +{ + int c; + char buf[BIGGEST_TOKEN]; + char *b = buf; + + if( !incp ) + goto eof; + + /* Get first character (whitespace or of token) */ + + c = yychar(); + + if( scanmode == SCAN_STRING ) + { + /* If scanning for a string (action's {}'s), look for the */ + /* closing brace. We handle matching braces, if they match! */ + + int nest = 1; + + while( c != EOF && b < buf + sizeof( buf ) ) + { + if( c == '{' ) + nest++; + + if( c == '}' && !--nest ) + break; + + *b++ = c; + + c = yychar(); + } + + /* We ate the ending brace -- regurgitate it. */ + + if( c != EOF ) + yyprev(); + + /* Check obvious errors. */ + + if( b == buf + sizeof( buf ) ) + { + yyerror( "action block too big" ); + goto eof; + } + + if( nest ) + { + yyerror( "unmatched {} in action block" ); + goto eof; + } + + *b = 0; + yylval.type = STRING; + yylval.string = newstr( buf ); + + } + else + { + char *b = buf; + struct keyword *k; + int inquote = 0; + int notkeyword; + + /* Eat white space */ + + for( ;; ) + { + /* Skip past white space */ + + while( c != EOF && isspace( c ) ) + c = yychar(); + + /* Not a comment? Swallow up comment line. */ + + if( c != '#' ) + break; + while( ( c = yychar() ) != EOF && c != '\n' ) + ; + } + + /* c now points to the first character of a token. */ + + if( c == EOF ) + goto eof; + + /* While scanning the word, disqualify it for (expensive) */ + /* keyword lookup when we can: $anything, "anything", \anything */ + + notkeyword = c == '$'; + + /* look for white space to delimit word */ + /* "'s get stripped but preserve white space */ + /* \ protects next character */ + + while( + c != EOF && + b < buf + sizeof( buf ) && + ( inquote || !isspace( c ) ) ) + { + if( c == '"' ) + { + /* begin or end " */ + inquote = !inquote; + notkeyword = 1; + } + else if( c != '\\' ) + { + /* normal char */ + *b++ = c; + } + else if( ( c = yychar()) != EOF ) + { + /* \c */ + *b++ = c; + notkeyword = 1; + } + else + { + /* \EOF */ + break; + } + + c = yychar(); + } + + /* Check obvious errors. */ + + if( b == buf + sizeof( buf ) ) + { + yyerror( "string too big" ); + goto eof; + } + + if( inquote ) + { + yyerror( "unmatched \" in string" ); + goto eof; + } + + /* We looked ahead a character - back up. */ + + if( c != EOF ) + yyprev(); + + /* scan token table */ + /* don't scan if it's obviously not a keyword or if its */ + /* an alphabetic when were looking for punctuation */ + + *b = 0; + yylval.type = ARG; + + if( !notkeyword && !( isalpha( *buf ) && scanmode == SCAN_PUNCT ) ) + { + for( k = keywords; k->word; k++ ) + if( *buf == *k->word && !strcmp( k->word, buf ) ) + { + yylval.type = k->type; + yylval.string = k->word; /* used by symdump */ + break; + } + } + + if( yylval.type == ARG ) + yylval.string = newstr( buf ); + } + + if( DEBUG_SCAN ) + printf( "scan %s\n", symdump( &yylval ) ); + + return yylval.type; + +eof: + yylval.type = EOF; + return yylval.type; +} + +static char * +symdump( YYSTYPE *s ) +{ + static char buf[ BIGGEST_TOKEN + 20 ]; + + switch( s->type ) + { + case EOF: + sprintf( buf, "EOF" ); + break; + case 0: + sprintf( buf, "unknown symbol %s", s->string ); + break; + case ARG: + sprintf( buf, "argument %s", s->string ); + break; + case STRING: + sprintf( buf, "string \"%s\"", s->string ); + break; + default: + sprintf( buf, "keyword %s", s->string ); + break; + } + return buf; +} diff --git a/src/tools/jam/scan.h b/src/tools/jam/scan.h new file mode 100644 index 0000000000..37b026f1f8 --- /dev/null +++ b/src/tools/jam/scan.h @@ -0,0 +1,52 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * scan.h - the jam yacc scanner + * + * External functions: + * + * yyerror( char *s ) - print a parsing error message + * yyfparse( char *s ) - scan include file s + * yylex() - parse the next token, returning its type + * yymode() - adjust lexicon of scanner + * yyparse() - declaration for yacc parser + * yyanyerrors() - indicate if any parsing errors occured + * + * The yymode() function is for the parser to adjust the lexicon of the + * scanner. Aside from normal keyword scanning, there is a mode to + * handle action strings (look only for the closing }) and a mode to + * ignore most keywords when looking for a punctuation keyword. This + * allows non-punctuation keywords to be used in lists without quoting. + */ + +/* + * YYSTYPE - value of a lexical token + */ + +# define YYSTYPE YYSYMBOL + +typedef struct _YYSTYPE { + int type; + char *string; + PARSE *parse; + LIST *list; + int number; +} YYSTYPE; + +extern YYSTYPE yylval; + +void yymode( int n ); +void yyerror( char *s ); +int yyanyerrors(); +void yyfparse( char *s ); +int yyline(); +int yylex(); +int yyparse(); + +# define SCAN_NORMAL 0 /* normal parsing */ +# define SCAN_STRING 1 /* look only for matching } */ +# define SCAN_PUNCT 2 /* only punctuation keywords */ diff --git a/src/tools/jam/search.c b/src/tools/jam/search.c new file mode 100644 index 0000000000..c260c2a01f --- /dev/null +++ b/src/tools/jam/search.c @@ -0,0 +1,85 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "search.h" +# include "timestamp.h" +# include "pathsys.h" +# include "variable.h" +# include "newstr.h" + +/* + * search.c - find a target along $(SEARCH) or $(LOCATE) + */ + +char * +search( + char *target, + time_t *time ) +{ + PATHNAME f[1]; + LIST *varlist; + char buf[ MAXJPATH ]; + + /* Parse the filename */ + + path_parse( target, f ); + + f->f_grist.ptr = 0; + f->f_grist.len = 0; + + if( varlist = var_get( "LOCATE" ) ) + { + f->f_root.ptr = varlist->string; + f->f_root.len = strlen( varlist->string ); + + path_build( f, buf, 1 ); + + if( DEBUG_SEARCH ) + printf( "locate %s: %s\n", target, buf ); + + timestamp( buf, time ); + + return newstr( buf ); + } + else if( varlist = var_get( "SEARCH" ) ) + { + while( varlist ) + { + f->f_root.ptr = varlist->string; + f->f_root.len = strlen( varlist->string ); + + path_build( f, buf, 1 ); + + if( DEBUG_SEARCH ) + printf( "search %s: %s\n", target, buf ); + + timestamp( buf, time ); + + if( *time ) + return newstr( buf ); + + varlist = list_next( varlist ); + } + } + + /* Look for the obvious */ + /* This is a questionable move. Should we look in the */ + /* obvious place if SEARCH is set? */ + + f->f_root.ptr = 0; + f->f_root.len = 0; + + path_build( f, buf, 1 ); + + if( DEBUG_SEARCH ) + printf( "search %s: %s\n", target, buf ); + + timestamp( buf, time ); + + return newstr( buf ); +} diff --git a/src/tools/jam/search.h b/src/tools/jam/search.h new file mode 100644 index 0000000000..bb8035723c --- /dev/null +++ b/src/tools/jam/search.h @@ -0,0 +1,11 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * search.h - find a target along $(SEARCH) or $(LOCATE) + */ + +char *search( char *target, time_t *time ); diff --git a/src/tools/jam/timestamp.c b/src/tools/jam/timestamp.c new file mode 100644 index 0000000000..a5bbcc8c40 --- /dev/null +++ b/src/tools/jam/timestamp.c @@ -0,0 +1,203 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "hash.h" +# include "filesys.h" +# include "pathsys.h" +# include "timestamp.h" +# include "newstr.h" + +/* + * timestamp.c - get the timestamp of a file or archive member + * + * 09/22/00 (seiwald) - downshift names on OS2, too + */ + +/* + * BINDING - all known files + */ + +typedef struct _binding BINDING; + +struct _binding { + char *name; + short flags; + +# define BIND_SCANNED 0x01 /* if directory or arch, has been scanned */ + + short progress; + +# define BIND_INIT 0 /* never seen */ +# define BIND_NOENTRY 1 /* timestamp requested but file never found */ +# define BIND_SPOTTED 2 /* file found but not timed yet */ +# define BIND_MISSING 3 /* file found but can't get timestamp */ +# define BIND_FOUND 4 /* file found and time stamped */ + + time_t time; /* update time - 0 if not exist */ +} ; + +static struct hash *bindhash = 0; +static void time_enter( void *, char *, int , time_t ); + +static char *time_progress[] = +{ + "INIT", + "NOENTRY", + "SPOTTED", + "MISSING", + "FOUND" +} ; + + +/* + * timestamp() - return timestamp on a file, if present + */ + +void +timestamp( + char *target, + time_t *time ) +{ + PATHNAME f1, f2; + BINDING binding, *b = &binding; + char buf[ MAXJPATH ]; + +# ifdef DOWNSHIFT_PATHS + char path[ MAXJPATH ]; + char *p = path; + + do *p++ = tolower( *target ); + while( *target++ ); + + target = path; +# endif + + if( !bindhash ) + bindhash = hashinit( sizeof( BINDING ), "bindings" ); + + /* Quick path - is it there? */ + + b->name = target; + b->time = b->flags = 0; + b->progress = BIND_INIT; + + if( hashenter( bindhash, (HASHDATA **)&b ) ) + b->name = newstr( target ); /* never freed */ + + if( b->progress != BIND_INIT ) + goto afterscanning; + + b->progress = BIND_NOENTRY; + + /* Not found - have to scan for it */ + + path_parse( target, &f1 ); + + /* Scan directory if not already done so */ + + { + BINDING binding, *b = &binding; + + f2 = f1; + f2.f_grist.len = 0; + path_parent( &f2 ); + path_build( &f2, buf, 0 ); + + b->name = buf; + b->time = b->flags = 0; + b->progress = BIND_INIT; + + if( hashenter( bindhash, (HASHDATA **)&b ) ) + b->name = newstr( buf ); /* never freed */ + + if( !( b->flags & BIND_SCANNED ) ) + { + file_dirscan( buf, time_enter, bindhash ); + b->flags |= BIND_SCANNED; + } + } + + /* Scan archive if not already done so */ + + if( f1.f_member.len ) + { + BINDING binding, *b = &binding; + + f2 = f1; + f2.f_grist.len = 0; + f2.f_member.len = 0; + path_build( &f2, buf, 0 ); + + b->name = buf; + b->time = b->flags = 0; + b->progress = BIND_INIT; + + if( hashenter( bindhash, (HASHDATA **)&b ) ) + b->name = newstr( buf ); /* never freed */ + + if( !( b->flags & BIND_SCANNED ) ) + { + file_archscan( buf, time_enter, bindhash ); + b->flags |= BIND_SCANNED; + } + } + + afterscanning: + + if( b->progress == BIND_SPOTTED ) + { + if( file_time( b->name, &b->time ) < 0 ) + b->progress = BIND_MISSING; + else + b->progress = BIND_FOUND; + } + + *time = b->progress == BIND_FOUND ? b->time : 0; +} + +static void +time_enter( + void *closure, + char *target, + int found, + time_t time ) +{ + BINDING binding, *b = &binding; + struct hash *bindhash = (struct hash *)closure; + +# ifdef DOWNSHIFT_PATHS + char path[ MAXJPATH ]; + char *p = path; + + do *p++ = tolower( *target ); + while( *target++ ); + + target = path; +# endif + + b->name = target; + b->flags = 0; + + if( hashenter( bindhash, (HASHDATA **)&b ) ) + b->name = newstr( target ); /* never freed */ + + b->time = time; + b->progress = found ? BIND_FOUND : BIND_SPOTTED; + + if( DEBUG_BINDSCAN ) + printf( "time ( %s ) : %s\n", target, time_progress[b->progress] ); +} + +/* + * donestamps() - free timestamp tables + */ + +void +donestamps() +{ + hashdone( bindhash ); +} diff --git a/src/tools/jam/timestamp.h b/src/tools/jam/timestamp.h new file mode 100644 index 0000000000..f290f82cb2 --- /dev/null +++ b/src/tools/jam/timestamp.h @@ -0,0 +1,12 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * timestamp.h - get the timestamp of a file or archive member + */ + +void timestamp( char *target, time_t *time ); +void donestamps(); diff --git a/src/tools/jam/variable.c b/src/tools/jam/variable.c new file mode 100644 index 0000000000..d62e4c19cf --- /dev/null +++ b/src/tools/jam/variable.c @@ -0,0 +1,341 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "expand.h" +# include "hash.h" +# include "newstr.h" + +/* + * variable.c - handle jam multi-element variables + * + * External routines: + * + * var_defines() - load a bunch of variable=value settings + * var_string() - expand a string with variables in it + * var_get() - get value of a user defined symbol + * var_set() - set a variable in jam's user defined symbol table + * var_swap() - swap a variable's value with the given one + * var_done() - free variable tables + * + * Internal routines: + * + * var_enter() - make new var symbol table entry, returning var ptr + * var_dump() - dump a variable to stdout + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 01/22/95 (seiwald) - split environment variables at blanks or :'s + * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :) + * 09/11/00 (seiwald) - defunct var_list() removed + */ + +static struct hash *varhash = 0; + +/* + * VARIABLE - a user defined multi-value variable + */ + +typedef struct _variable VARIABLE ; + +struct _variable { + char *symbol; + LIST *value; +} ; + +static VARIABLE *var_enter( char *symbol ); +static void var_dump( char *symbol, LIST *value, char *what ); + + + +/* + * var_defines() - load a bunch of variable=value settings + * + * If variable name ends in PATH, split value at :'s. + * Otherwise, split at blanks. + */ + +void +var_defines( char **e ) +{ + for( ; *e; e++ ) + { + char *val; + + /* Just say "no": windows defines this in the env, */ + /* but we don't want it to override our notion of OS. */ + + if( !strcmp( *e, "OS=Windows_NT" ) ) + continue; + +# ifdef OS_MAC + /* On the mac (MPW), the var=val is actually var\0val */ + /* Think different. */ + + if( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) ) +# else + if( val = strchr( *e, '=' ) ) +# endif + { + LIST *l = L0; + char *pp, *p; +# ifdef OS_MAC + char split = ','; +# else + char split = ' '; +# endif + char buf[ MAXSYM ]; + + /* Split *PATH at :'s, not spaces */ + + if( val - 4 >= *e ) + { + if( !strncmp( val - 4, "PATH", 4 ) || + !strncmp( val - 4, "Path", 4 ) || + !strncmp( val - 4, "path", 4 ) ) + split = SPLITPATH; + } + + /* Do the split */ + + for( pp = val + 1; p = strchr( pp, split ); pp = p + 1 ) + { + strncpy( buf, pp, p - pp ); + buf[ p - pp ] = '\0'; + l = list_new( l, newstr( buf ) ); + } + + l = list_new( l, newstr( pp ) ); + + /* Get name */ + + strncpy( buf, *e, val - *e ); + buf[ val - *e ] = '\0'; + + var_set( buf, l, VAR_SET ); + } + } +} + +/* + * var_string() - expand a string with variables in it + * + * Copies in to out; doesn't modify targets & sources. + */ + +int +var_string( + char *in, + char *out, + int outsize, + LOL *lol ) +{ + char *out0 = out; + char *oute = out + outsize - 1; + + while( *in ) + { + char *lastword; + int dollar = 0; + + /* Copy white space */ + + while( isspace( *in ) ) + { + if( out >= oute ) + return -1; + + *out++ = *in++; + } + + lastword = out; + + /* Copy non-white space, watching for variables */ + + while( *in && !isspace( *in ) ) + { + if( out >= oute ) + return -1; + + if( in[0] == '$' && in[1] == '(' ) + dollar++; + + *out++ = *in++; + } + + /* If a variable encountered, expand it and and embed the */ + /* space-separated members of the list in the output. */ + + if( dollar ) + { + LIST *l; + + l = var_expand( L0, lastword, out, lol, 0 ); + + out = lastword; + + for( ; l; l = list_next( l ) ) + { + int so = strlen( l->string ); + + if( out + so >= oute ) + return -1; + + strcpy( out, l->string ); + out += so; + *out++ = ' '; + } + + list_free( l ); + } + } + + if( out >= oute ) + return -1; + + *out++ = '\0'; + + return out - out0; +} + +/* + * var_get() - get value of a user defined symbol + * + * Returns NULL if symbol unset. + */ + +LIST * +var_get( char *symbol ) +{ + VARIABLE var, *v = &var; + + v->symbol = symbol; + + if( varhash && hashcheck( varhash, (HASHDATA **)&v ) ) + { + if( DEBUG_VARGET ) + var_dump( v->symbol, v->value, "get" ); + return v->value; + } + + return 0; +} + +/* + * var_set() - set a variable in jam's user defined symbol table + * + * 'flag' controls the relationship between new and old values of + * the variable: SET replaces the old with the new; APPEND appends + * the new to the old; DEFAULT only uses the new if the variable + * was previously unset. + * + * Copies symbol. Takes ownership of value. + */ + +void +var_set( + char *symbol, + LIST *value, + int flag ) +{ + VARIABLE *v = var_enter( symbol ); + + if( DEBUG_VARSET ) + var_dump( symbol, value, "set" ); + + switch( flag ) + { + case VAR_SET: + /* Replace value */ + list_free( v->value ); + v->value = value; + break; + + case VAR_APPEND: + /* Append value */ + v->value = list_append( v->value, value ); + break; + + case VAR_DEFAULT: + /* Set only if unset */ + if( !v->value ) + v->value = value; + else + list_free( value ); + break; + } +} + +/* + * var_swap() - swap a variable's value with the given one + */ + +LIST * +var_swap( + char *symbol, + LIST *value ) +{ + VARIABLE *v = var_enter( symbol ); + LIST *oldvalue = v->value; + + if( DEBUG_VARSET ) + var_dump( symbol, value, "set" ); + + v->value = value; + + return oldvalue; +} + + + +/* + * var_enter() - make new var symbol table entry, returning var ptr + */ + +static VARIABLE * +var_enter( char *symbol ) +{ + VARIABLE var, *v = &var; + + if( !varhash ) + varhash = hashinit( sizeof( VARIABLE ), "variables" ); + + v->symbol = symbol; + v->value = 0; + + if( hashenter( varhash, (HASHDATA **)&v ) ) + v->symbol = newstr( symbol ); /* never freed */ + + return v; +} + +/* + * var_dump() - dump a variable to stdout + */ + +static void +var_dump( + char *symbol, + LIST *value, + char *what ) +{ + printf( "%s %s = ", what, symbol ); + list_print( value ); + printf( "\n" ); +} + +/* + * var_done() - free variable tables + */ + +void +var_done() +{ + hashdone( varhash ); +} diff --git a/src/tools/jam/variable.h b/src/tools/jam/variable.h new file mode 100644 index 0000000000..77b89f1eaa --- /dev/null +++ b/src/tools/jam/variable.h @@ -0,0 +1,25 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * variable.h - handle jam multi-element variables + */ + +void var_defines( char **e ); +int var_string( char *in, char *out, int outsize, LOL *lol ); +LIST * var_get( char *symbol ); +void var_set( char *symbol, LIST *value, int flag ); +LIST * var_swap( char *symbol, LIST *value ); +void var_done(); + +/* + * Defines for var_set(). + */ + +# define VAR_SET 0 /* override previous value */ +# define VAR_APPEND 1 /* append to previous value */ +# define VAR_DEFAULT 2 /* set only if no previous value */ + diff --git a/src/tools/jam/yyacc b/src/tools/jam/yyacc new file mode 100755 index 0000000000..f566868dda --- /dev/null +++ b/src/tools/jam/yyacc @@ -0,0 +1,89 @@ +#!/bin/sh + +# yyacc - yacc wrapper +# +# Allows tokens to be written as `literal` and then automatically +# substituted with #defined tokens. +# +# Usage: +# yyacc file.y filetab.h file.yy +# +# inputs: +# file.yy yacc grammar with ` literals +# +# outputs: +# file.y yacc grammar +# filetab.h array of string <-> token mappings +# +# 3-13-93 +# Documented and p moved in sed command (for some reason, +# s/x/y/p doesn't work). +# 10-12-93 +# Take basename as second argument. +# 12-31-96 +# reversed order of args to be compatible with GenFile rule + +outy=${1?} +outh=${2?} +in=${3?} +out=`basename $in .yy` + +T=/tmp/yy$$ +trap 'rm -f $T.*' 0 + +sed ' + : 1 + /`/{ + h + s/[^`]*`\([^`]*\)`.*/\1/ + p + g + s/[^`]*`[^`]*`// + b 1 + } + d +' $in | sort -u | sed ' + h + y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/ + s/:/_COLON/ + s/!/_BANG/ + s/&&/_AMPERAMPER/ + s/&/_AMPER/ + s/+/_PLUS/ + s/||/_BARBAR/ + s/|/_BAR/ + s/;/_SEMIC/ + s/-/_MINUS/ + s//_RANGLE/ + s/\./_PERIOD/ + s/?/_QUESTION/ + s/=/_EQUALS/ + s/,/_COMMA/ + s/\[/_LBRACKET/ + s/]/_RBRACKET/ + s/{/_LBRACE/ + s/}/_RBRACE/ + s/(/_LPAREN/ + s/)/_RPAREN/ + s/.*/&_t/ + G + s/\n/ / +' > $T.1 + +sed ' + s:^\(.*\) \(.*\)$:s/`\2`/\1/g: + s:\.:\\.:g + s:\[:\\[:g +' $T.1 > $T.s + +rm -f $outy $outh + +( + sed 's:^\(.*\) \(.*\)$:%token \1:' $T.1 + sed -f $T.s $in +) > $outy + +( + sed 's:^\(.*\) \(.*\)$: { "\2", \1 },:' $T.1 +) > $outh