Compare commits

...

26 Commits

Author SHA1 Message Date
Oliver Tappe
9283ff9dee * updated binutils to 2.17, most problems should be solved now...
except one: the binaries produced by this version of binutils crashes the loader of
  BeOS versions BONE, Dano (and probably Zeta, too). That's a pity, but I currently
  do not know how to fix this (as the fix available for older versions of binutils
  does not work anymore).



git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@20191 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-02-21 21:11:47 +00:00
François Revol
1227028b2b No reason to keep those 2 in, they are automatically generated.
This fixes one fo the problems of building gcc4 under Zeta.
See http://gcc.gnu.org/ml/gcc/2003-10/msg01501.html
If you want to try you'll have to also remove the -lm in gcc/gcc/Makefile.in or symlink it to libroot.so.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@20115 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-02-08 19:04:45 +00:00
Ingo Weinhold
e938767ea3 Patch by Samuel Rodriguez Perez to fix building this gcc version for FreeBSD.
The removed header fixes don't apply to the Haiku headers anyway, so
this shouldn't be much of a problem.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@20046 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-02-02 13:53:11 +00:00
Oliver Tappe
e4766e1c34 * joined 'install' & 'INSTALL' into one folder (INSTALL) in order to fix
ticket #844 (checkout under Windows)


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18886 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-09-19 08:11:25 +00:00
Oliver Tappe
9cca43537c * added missing changes to version info I forgot to commit ages ago...tsk!
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18860 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-09-16 15:16:37 +00:00
Oliver Tappe
d86ae8ffd8 * fixed error in documentation (need to use 'legacy'-path).
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18859 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-09-16 15:14:54 +00:00
Oliver Tappe
6c33bb6291 * renamed cross-compiler documentation that targets BeOS to make that fact
more obvious.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18631 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-26 12:19:47 +00:00
Oliver Tappe
7381630985 * finally fixed the documentation bug that failed to mention that the stuff that
should be built lives in the 'legacy' folder. This confused several people (and
  rightly so >;o).


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18630 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-26 12:17:52 +00:00
Oliver Tappe
3b858bf0a8 * created explicit micro-documentation-file that describes how to build a cross
compiler for haiku on LINUX.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18629 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-26 12:15:54 +00:00
Oliver Tappe
61e8e0f75c * added haiku-<date> pattern to version such that it is (from now on)
possible to differentiate all the jam-versions out there...


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18505 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-13 14:30:12 +00:00
Oliver Tappe
742ec4efee * should now be able to build jam on Zeta, too.
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18504 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-13 14:28:32 +00:00
Oliver Tappe
c4d9c2df99 * fixed bug when waiting for sub-process related to handling of multiple
children (as created by piped commands). This caused the compilation
  of AboutHaiku to fail on Zeta.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18503 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-08-13 14:27:48 +00:00
Oliver Tappe
c8cb9d20d2 - cleaned up contents to only list user relevant changes.
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18094 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-07-10 22:32:11 +00:00
Oliver Tappe
81d6094934 - moved gcc_distribution into the legacy folder, as it relates to the
legacy version of binutils & gcc


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18091 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-07-10 21:19:26 +00:00
Oliver Tappe
3322ac8471 - added file listing changes to last gcc-release (in preparation for the next one).
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@18090 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-07-10 20:54:39 +00:00
Jérôme Duval
b619e2771e if a node entry is moved and the origin entry isn't found, an entry is created
In fact, BeIDE seems to delete the entry and move a temp entry to the right place: 
StatCacheServer was then thinking that the entry was removed


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@17926 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-06-26 15:22:41 +00:00
Ingo Weinhold
d018825503 Moving jam to the buildtools module caused the rc finding algorithm to break.
We no longer even try to do that. The user can define the environment variable
RC, if they want StatCacheServer resources, otherwise they will just not be
added (a respective note is printed).
Closes bug #176.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16533 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-02-27 15:02:06 +00:00
Ingo Weinhold
a1a02bed3c New feature: Now jam defines a variable JAM_TARGETS, which contains
the targets given on the command line (respectively "all", if none
has been given). The contents of the variable can be changed from
within the Jamrules/Jamfiles. The targets that are in JAM_TARGETS
after Jamfile parsing is done, are those that will be built (and their
dependencies, of course). This allows for interesting build system
features.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16345 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-02-11 01:57:34 +00:00
Andrew Bachmann
23c3fee924 scripts
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16213 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-02-03 02:32:59 +00:00
Ingo Weinhold
aa62780422 Patches for building a cross compiler under FreeBSD. Proposed by
Alexander Deynichenko, changed a bit though.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16178 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-02-01 00:28:59 +00:00
Oliver Tappe
5effaca78b Added cc1plus as bootstrap-dependency such that fix_bdirectwin_typeinfo.cpp can
be compiled (it requires a c++-compiler, of course). 
This fixes the build on R5.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16140 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-01-29 18:06:48 +00:00
Axel Dörfler
c1ffb3028e Don't suppress OSPLAT to be set correctly on FreeBSD (what the hell??).
Bugfix provided by Alexander Deynichenko.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16138 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-01-29 17:40:47 +00:00
Oliver Tappe
cdeb2e6d93 moved USE_EGCS_MANGLED_NAMES from host to target config file in order to
generate "correct" (aka BeOS-specific) symbols when using the cross
compiler, too.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@16119 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-01-28 14:35:27 +00:00
Ingo Weinhold
ecc89c9a6a Moved jam into the buildtools modules, where it belongs.
git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@15729 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-12-29 18:40:48 +00:00
Ingo Weinhold
17dd620975 Another piece of the puzzle: BFD has a value for a target's max page size.
The value defined for ld must be the same, since otherwise one gets
effects like we had for generated PPC objects: The BFD page size was
0x10000, the ld page size 0x1000. Thus the data segment alignment forced
in the linker script didn't allow BFD to really start a new segment
(unless by accident a 0x10000 boundary was crossed as well). Hence our
libraries and executables only had one loadable segment (containing both
text and data), which neither of the three Haiku ELF loaders (in boot
loader, kernel, and userland) likes at all.

As a fix we force 0x1000 page size for target Haiku in a similar way it
is done for QNX.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@15705 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-12-29 00:08:48 +00:00
Ingo Weinhold
dab660cbe3 * The .plt section for PPC is of type SHT_NOBITS (not SHT_PROGBITS as
for x86), so it should be placed near the .bss sections. This fixes
  linkage problems -- the linker had to split the text segment after the
  .plt section, which caused its pre-computed program header count to be
  wrong.
* I also added the stuff concerning the .fixup and .got{1,2} sections I
  found in the generic ELF PPC script. It shouldn't harm at least.


git-svn-id: file:///srv/svn/repos/haiku/buildtools/trunk@15474 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-12-10 20:07:57 +00:00
147 changed files with 33492 additions and 4004 deletions

View File

@@ -2,6 +2,13 @@ How to build gcc-2.95.3 for BeOS under Linux (a cross-compiler), which
will allow you to compile BeOS projects under Linux, i.e. the apps
that this compilers produces can only be executed on BeOS (not on Linux).
If all you are interested is compiling haiku on Linux, please refer to
the file 'INSTALL-as-haiku-cross-compiler' instead (that's easier).
These instructions are required only if you want to compile something
(applications, libraries) on LINUX which should be executed on BeOS R5,
BONE or ZETA.
*** the major work of creating the cross-compiler and describing the
*** process how to build it has been done by Eric Petit <titer@m0k.org>,
*** so if you think this cross-compiler is great, please tell him.
@@ -11,11 +18,11 @@ On your Linux-box, open a shell...
0 - Preparations
----------------
...and fetch the 'buildtools' module from the haiku CVS. You should then
...and fetch the 'buildtools' module from the haiku SVN. You should then
have a 'buildtools' folder that contains folders named 'binutils' and
'gcc' (and this file, too!).
cd buildtools
cd buildtools/legacy
Now decide where you want to install the cross-compiler. The install
folder will be referred to as $PREFIX. I suggest to install to

View File

@@ -0,0 +1,20 @@
How to create a cross-compiler on LINUX (or any other supported build platform)
for haiku (information is copied from haiku's README):
Please chdir into haiku's base directory (the one where Jamrules and README live).
If you want to build the default (legacy, 2.95.3) gcc, do this:
$ ./configure --build-cross-tools ../buildtools
One of the last output lines should tell you that the tools have been built
successfully.
If you're not interested in binary compatibility (or want to build for the PowerPC architecture), you can build gcc4 instead by doing this:
$ ./configure --build-cross-tools-gcc4 <arch> ../buildtools
Replace "<arch>" with either "x86" or "ppc", depending on which of the two
architectures you want to build for.

View File

@@ -1,6 +1,6 @@
How to build gcc-2.95.3 for BeOS:
cd into the buildtools folder (where this file lives)
cd into the buildtools/legacy folder (where this file lives)
compile binutils:

View File

@@ -1026,6 +1026,7 @@ case "${targ}" in
;;
powerpc-*-haiku*)
targ_defvec=bfd_elf32_powerpc_vec
targ_cflags=-D__HAIKU_TARGET__
;;
powerpc-*-macos*)
targ_defvec=pmac_xcoff_vec

View File

@@ -6194,7 +6194,7 @@ static struct bfd_elf_special_section const ppc_elf_special_sections[]=
#define TARGET_BIG_NAME "elf32-powerpc"
#define ELF_ARCH bfd_arch_powerpc
#define ELF_MACHINE_CODE EM_PPC
#ifdef __QNXTARGET__
#if defined(__QNXTARGET__) || defined(__HAIKU_TARGET__)
#define ELF_MAXPAGESIZE 0x1000
#else
#define ELF_MAXPAGESIZE 0x10000

View File

@@ -8,3 +8,13 @@ MACHINE=
NOP=0x60000000
TEMPLATE_NAME=elf32
GENERATE_SHLIB_SCRIPT=yes
BSS_PLT=
OTHER_RELRO_SECTIONS="
.fixup ${RELOCATING-0} : { *(.fixup) }
.got1 ${RELOCATING-0} : { *(.got1) }
.got2 ${RELOCATING-0} : { *(.got2) }
"
OTHER_GOT_RELOC_SECTIONS="
.rela.got1 ${RELOCATING-0} : { *(.rela.got1) }
.rela.got2 ${RELOCATING-0} : { *(.rela.got2) }
"

View File

@@ -85,7 +85,9 @@ call_ ## FUNC (void) \
#if defined(OBJECT_FORMAT_ELF) && defined(HAVE_LD_EH_FRAME_HDR) \
&& !defined(inhibit_libc) && !defined(CRTSTUFFT_O) \
&& defined(__GLIBC__) && __GLIBC__ >= 2
#if !(defined(__BEOS__) || defined(__HAIKU__))
#include <link.h>
#endif
# if (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
|| (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG)))
# define USE_PT_GNU_EH_FRAME

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +0,0 @@
/* A Bison parser, made by GNU Bison 2.0. */
/* Skeleton parser for Yacc-like parsing with Bison,
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* As a special exception, when this file is copied by Bison into a
Bison output file, you may use that output file without restriction.
This special exception was added by the Free Software Foundation
in version 1.24 of Bison. */
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
ENT_TYPEDEF_STRUCT = 258,
ENT_STRUCT = 259,
ENT_EXTERNSTATIC = 260,
ENT_YACCUNION = 261,
GTY_TOKEN = 262,
UNION = 263,
STRUCT = 264,
ENUM = 265,
ALIAS = 266,
NESTED_PTR = 267,
PARAM_IS = 268,
NUM = 269,
PERCENTPERCENT = 270,
SCALAR = 271,
ID = 272,
STRING = 273,
ARRAY = 274,
PERCENT_ID = 275,
CHAR = 276
};
#endif
#define ENT_TYPEDEF_STRUCT 258
#define ENT_STRUCT 259
#define ENT_EXTERNSTATIC 260
#define ENT_YACCUNION 261
#define GTY_TOKEN 262
#define UNION 263
#define STRUCT 264
#define ENUM 265
#define ALIAS 266
#define NESTED_PTR 267
#define PARAM_IS 268
#define NUM 269
#define PERCENTPERCENT 270
#define SCALAR 271
#define ID 272
#define STRING 273
#define ARRAY 274
#define PERCENT_ID 275
#define CHAR 276
#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
#line 31 "/scratch/mitchell/gcc-releases/gcc-4.0.2/gcc-4.0.2/gcc/gengtype-yacc.y"
typedef union YYSTYPE {
type_p t;
pair_p p;
options_p o;
const char *s;
} YYSTYPE;
/* Line 1318 of yacc.c. */
#line 86 "gengtype-yacc.h"
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE yylval;

View File

@@ -79,6 +79,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
BeOS threads with -D_BEOS_THREADS
*/
#include <limits.h>
#include <OS.h>
#include <TLS.h>

View File

@@ -79,6 +79,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
Haiku threads with -D_HAIKU_THREADS
*/
#include <limits.h>
#include <OS.h>
#include <TLS.h>

View File

@@ -49,6 +49,10 @@
#include <stdint.h> // For int64_t
#endif
#ifdef __BEOS__
#include <inttypes.h> // BeOS has <inttypes.h>, but hasn't <stdint.h>
#endif
namespace std
{
// The types streamoff, streampos and wstreampos and the class

31
jam/Build.com Executable file
View File

@@ -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

47
jam/Build.mpw Normal file
View File

@@ -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"

1329
jam/Jam.html Normal file

File diff suppressed because it is too large Load Diff

2195
jam/Jambase Normal file

File diff suppressed because it is too large Load Diff

939
jam/Jambase.html Normal file
View File

@@ -0,0 +1,939 @@
<HTML>
<TITLE>
Jambase Reference
</TITLE>
<BODY>
<CENTER>
<a href=http://www.perforce.com/jam/jam.html>
Jam
</a>
<H1>
<A NAME="TOP">
Jambase Reference
</A>
</H1>
</CENTER>
<P>
Jambase is a base set of Jam rules which
provide roughly make(1)-like functionality for
<a href="Jam.html"><b>jam</b></A>, the Jam executable program.
This document, which started out as the Jambase(5) man page,
is a reference guide to the
<A href="#RULES">rules</A>,
<A href="#PSEUDOTARGETS">pseudotargets</A>,
and <A href="#VARS">variables</A>
defined in Jambase for use in Jamfiles.
<P>
For further information see:
<UL>
<LI>
<a href="Jamfile.html">Using Jamfiles and Jambase</A>
<LI>
<a href="Jam.html">The Jam Executable Program</A>
</UL>
<P>
Jam documentation and source are available from the
<A HREF=http://public.perforce.com/public/index.html>Perforce Public Depot</a>.
For detailed information about any of the rules summarized below,
see the
<A HREF=http://public.perforce.com/public/jam/src/Jambase>Jambase</a>
file itself.
<HR>
<H2>
<A NAME="RULES">
Jambase Rules
</A>
</H2>
<P>
<B>As</B> <I>obj.o</I> : <I>source.s</I> ;
<BLOCKQUOTE>
Assemble the file <I>source.s.</I> Called by the Object
rule.
</BLOCKQUOTE>
<B>Bulk</B> <I>directory</I> : <I>sources</I> ;
<BLOCKQUOTE>
Copies <I>sources</I> into <I>directory.</I>
</BLOCKQUOTE>
<B>Cc</B> <I>object</I> : <I>source</I> ;
<BLOCKQUOTE>
Compile the file <I>source</I> into <I>object,</I> using the C
compiler $(CC), its flags $(CCFLAGS) and $(OPTIM),
and the header file directories $(HDRS). Called by
the Object rule.
</BLOCKQUOTE>
<B>C++</B> <I>obj.o</I> : <I>source.cc</I> ;
<BLOCKQUOTE>
Compile the C++ source file <I>source.cc.</I> Called by
the Object rule.
</BLOCKQUOTE>
<B>Chmod</B> <I>target</I> ;
<BLOCKQUOTE>
<I>(Unix and VMS only.)</I>
Change file permissions on <I>target</I> to
target-specific $(MODE) value set by Link, File,
Install*, and Shell rules.
</BLOCKQUOTE>
<B>Clean</B> <I>clean</I> : <I>targets</I> ;
<BLOCKQUOTE>
Removes existing <I>targets</I> when <I>clean</I> is built.
clean is not a dependency of all, and must be built
explicitly for targets to be removed.
</BLOCKQUOTE>
<B>FDefines</B> <I>defines</I> ; <BLOCKQUOTE>
Expands a list of definitions into a list of compiler
(or preprocessor) switches (such as
-D<I>symbol</I>=<I>val</I> on Unix)
to pass the definitions.
</BLOCKQUOTE>
<B>File</B> <I>target</I> : <I>source</I> ;
<BLOCKQUOTE>
Copies <I>source</I> into <I>target.</I>
</BLOCKQUOTE>
<B>FIncludes</B> <I>dirs</I> ; <BLOCKQUOTE>
Expands a list of directories into a list of compiler
(or preprocessor) switches (such as -I<I>dir</I> on Unix)
to add the directories to the header inclusion search path.
</BLOCKQUOTE>
<B>Fortran</B> <I>obj.o</I> : <I>source.f</I> ;
<BLOCKQUOTE>
Compile the Fortran source file <I>source.f.</I> Called
by the Object rule.
</BLOCKQUOTE>
<B>FQuote</B> <I>files</I> ; <BLOCKQUOTE>
Returns each of <I>files</I> suitably quoted so as to hide shell
metacharacters (such as whitespace and filename matching wildcards)
from the shell.
</BLOCKQUOTE>
<P>
<B>GenFile</B> <I>target</I> : <I>image</I> <I>sources</I> ;
<BLOCKQUOTE>
Runs the command "<I>image</I> <I>target</I> <I>sources</I>"
to create <I>target</I> from <I>sources</I> and
<I>image</I>. (where <I>image</I> is an
executable built by the Main rule.)
</BLOCKQUOTE>
<B>HardLink</B> <I>target</I> : <I>source</I> ;
<BLOCKQUOTE>
Makes <I>target</I> a hard link to <I>source,</I> if it isn't one
already. (Unix only.)
</BLOCKQUOTE>
<B>HdrRule</B> <I>source</I> : <I>headers</I> ;
<BLOCKQUOTE>
Arranges the proper dependencies when the file
<I>source</I> includes the files <I>headers</I> through the
"#include" C preprocessor directive.
<P>
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).
</BLOCKQUOTE>
<B>InstallBin</B> <I>dir</I> : <I>sources</I> ; <BLOCKQUOTE>
Copy <I>sources</I> into <I>dir</I> with mode
$(EXEMODE).
</BLOCKQUOTE>
<B>InstallLib</B> <I>dir</I> : <I>sources</I> ; <BLOCKQUOTE>
Copy <I>sources</I> into <I>dir</I> with mode
$(FILEMODE).
</BLOCKQUOTE>
<B>InstallMan</B> <I>dir</I> : <I>sources</I> ; <BLOCKQUOTE>
Copy <I>sources</I> into the appropriate subdirectory
of <I>dir</I> with mode $(FILEMODE). The subdirectory
is man<I>s,</I> where <I>s</I> is the suffix of
each of sources.
</BLOCKQUOTE>
<B>InstallShell</B> <I>dir</I> : <I>sources</I> ; <BLOCKQUOTE>
Copy <I>sources</I> into <I>dir</I> with mode
$(SHELLMODE).
</BLOCKQUOTE>
<B>Lex</B> <I>source.c</I> : <I>source.l</I> ; <BLOCKQUOTE>
Process the lex(1) source file <I>source.l</I> and
rename the lex.yy.c to <I>source.c.</I> Called by
the Object rule.
</BLOCKQUOTE>
<B>Library</B> <I>library</I> : <I>sources</I> ; <BLOCKQUOTE>
Compiles <I>sources</I> and archives them into
<I>library.</I> The intermediate <I>objects</I>
are deleted. Calls Objects and LibraryFromObjects.
<P>
If Library is invoked with no suffix on <I>library</I>,
the $(SUFLIB) suffix is used.
</BLOCKQUOTE>
<B>LibraryFromObjects</B> <I>library</I> : <I>objects</I> ;
<BLOCKQUOTE>
Archives <I>objects</I> into <I>library.</I> The
<I>objects</I> are then deleted.
<P>
If <I>library</I> has no suffix, the $(SUFLIB) suffix is used.
</BLOCKQUOTE>
<B>Link</B> <I>image</I> : <I>objects</I> ;
<BLOCKQUOTE>
Links <I>image</I> from <I>objects</I> and sets
permissions on <I>image</I> to $(EXEMODE).
<I>Image</I> must be actual filename; suffix is not
supplied.
Called by Main.
</BLOCKQUOTE>
<B>LinkLibraries</B> <I>image</I> : <I>libraries</I> ;
<BLOCKQUOTE>
Makes <I>image</I> depend on <I>libraries</I> and
includes them during the linking.
<P>
<I>Image</I> may be referenced without a suffix in this
rule invocation; LinkLibraries supplies the suffix.
</BLOCKQUOTE>
<B>Main</B> <I>image</I> : <I>sources</I> ;
<BLOCKQUOTE>
Compiles <I>sources</I> and links them into <I>image.</I>
Calls Objects and MainFromObjects.
<P>
<I>Image</I> may be referenced without a suffix in this
rule invocation; Main supplies the suffix.
</BLOCKQUOTE>
<B>MainFromObjects</B> <I>image</I> : <I>objects</I> ;
<BLOCKQUOTE>
Links <I>objects</I> into <I>image.</I> Dependency
of exe. MainFromObjects supplies the suffix on <I>image</I>
filename.
</BLOCKQUOTE>
<B>MakeLocate</B> <I>target</I> : <I>dir</I> ;
<BLOCKQUOTE>
Creates <I>dir</I> and causes <I>target</I> to be built
into <I>dir</I>.
</BLOCKQUOTE>
<B>MkDir</B> <I>dir</I> ;
<BLOCKQUOTE>
Creates <I>dir</I> and its parent directories.
</BLOCKQUOTE>
<B>Object</B> <I>object</I> : <I>source</I> ;
<BLOCKQUOTE>
Compiles a <I>single</I> source file source into
<I>object.</I> The Main and Library rules use
this rule to compile source files.
<P>
Causes <I>source</I> to be scanned for "#include"
directives and calls HdrRule to make all included
files dependedencies of <I>object</I>.
<P>
Calls one of the following rules to do the actual
compiling, depending on the suffix of source:
<PRE>
*.c: Cc
*.cc: C++
*.cpp: C++
*.C: C++
*.l: Lex
*.y: Yacc
*.*: UserObject
</PRE>
</BLOCKQUOTE>
<B>ObjectC++Flags</B> <I>source</I> : <I>flags</I> ;
<BR>
<B>ObjectCcFlags</B> <I>source</I> : <I>flags</I> ;
<BLOCKQUOTE>
Add <I>flags</I> to the source-specific
value of $(CCFLAGS) or $(C++FLAGS) when compiling <I>source.</I>
Any file suffix on <I>source</I> is ignored.
</BLOCKQUOTE>
<B>ObjectDefines</B> <I>object</I> : <I>defines</I> ; <BLOCKQUOTE>
Adds preprocessor symbol definitions to the (gristed)
target-specific $(CCDEFS) for the <I>object</i>.
</BLOCKQUOTE>
<B>ObjectHdrs</B> <I>source</I> : <I>dirs</I> ; <BLOCKQUOTE>
Add <I>dirs</I> to the source-specific value of
$(HDRS) when scanning and compiling <I>source.</I>
Any file suffix on <I>source</I> is ignored.
</BLOCKQUOTE>
<B>Objects</B> <I>sources</I> ; <BLOCKQUOTE>
For each source file in <I>sources,</I> calls
Object to compile the source file into a similarly
named object file.
</BLOCKQUOTE>
<B>RmTemps</B> <I>targets</I> : <I>sources</I> ; <BLOCKQUOTE>
Marks <I>sources</I> as temporary with the TEMPORARY
rule, and deletes <I>sources</I> once <I>targets</I>
are built. Must be the last rule invoked on
<I>targets.</I> Used internally by LibraryFromObjects rule.
</BLOCKQUOTE>
<B>Setuid</B> <I>images</I> ; <BLOCKQUOTE>
Sets the setuid bit on each of <I>images</I> after
linking. (Unix only.)
</BLOCKQUOTE>
<B>SoftLink</B> <I>target</I> : <I>source</I> ;
<BLOCKQUOTE>
Makes <I>target</I> a symbolic link to <I>source,</I> if it isn't one
already. (Unix only.)
</BLOCKQUOTE>
<B>SubDir</B> <I>TOP d1 ... dn</I> ;
<BLOCKQUOTE>
Sets up housekeeping for the source files located
in <I><CODE>$(TOP)/d1/.../dn</CODE></I>:
<UL>
<LI>Reads in rules file associated with <I>TOP</I>,
if it hasn't already been read.
<LI>Initializes variables for search paths,
output directories, compiler
flags, and grist, using <I>d1 ... dn</I> tokens.
</UL>
<P>
<I>TOP</I> is the name of a variable;
<I>d1</I> thru <I>dn</I> are elements
of a directory path.
</BLOCKQUOTE>
<B>SubDirC++Flags</B> <I>flags</I> ;
<BR>
<B>SubDirCcFlags</B> <I>flags</I> ;
<BLOCKQUOTE>
Adds <I>flags</I> to the compiler flags for source files
in SubDir's directory.
</BLOCKQUOTE>
<B>SubDirHdrs</B> <I>d1 ... dn</I> ;
<BLOCKQUOTE>
Adds the path <I>d1/.../dn/</I> to the header search paths for
source files in SubDir's directory. <I>d1</I> through <I>dn</I>
are elements of a directory path.
</BLOCKQUOTE>
<B>SubInclude</B> <I>VAR d1 ... dn</I> ;
<BLOCKQUOTE>
Reads the Jamfile in <I><CODE>$(VAR)/d1/.../dn/</CODE></I>.
</BLOCKQUOTE>
<B>Shell</B> <I>image</I> : <I>source</I> ; <BLOCKQUOTE>
Copies <I>source</I> into the executable sh(1)
script <I>image.</I> Ensures that the first line of
the script is $(SHELLHEADER) (default #!/bin/sh).
</BLOCKQUOTE>
<B>Undefines</B> <I>images</I> : <I>symbols</I> ; <BLOCKQUOTE>
Adds flags to mark <I>symbols</I> as undefined
on link command for <I>images</I>.
<I>Images</I> may be referenced unsuffixed; the
Undefines rule supplies the suffix.
</BLOCKQUOTE>
<B>UserObject</B> <I>object</I> : <I>source</I> ; <BLOCKQUOTE>
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 <I>source</I> with
files suffixes it does not recognize.
</BLOCKQUOTE>
<B>Yacc</B> <I>source.c</I> : <I>source.y</I> ; <BLOCKQUOTE>
Process the yacc(1) file <I>source.y</I> and renamed
the resulting y.tab.c and y.tab.h to <I>source.c.</I>
Produces a y.tab.h and renames it to <I>source.h.</I>
Called by the <B>Object</B> rule.
</BLOCKQUOTE>
<P>
<HR>
<A NAME="PSEUDOTARGETS">
<H3>
Jambase Pseudotargets
</H3>
</A>
<P>
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:
<CENTER>
<TABLE CELLPADDING=5%>
<TR><TD>exe
<TD>Executables linked by the Main or MainFromObjects rules
<TR><TD>lib
<TD>Libraries created by the Library or LibraryFromObjects rules
<TR><TD>obj
<TD>Compiled objects used to create Main or Library targets
<TR><TD>dirs
<TD>Directories where target files are written
<TR><TD>file
<TD>Files copied by File and Bulk rules
<TR><TD>shell
<TD>Files copied by Shell rule
<TR><TD>clean
<TD>Removal of built targets (except files copied by Install* rules)
<TR><TD>install
<TD>Files copied by Install* rules
<TR><TD>uninstall
<TD>Removal of targets copied by Install* rules
</TABLE>
</CENTER>
<P>
In addition, Jambase makes the <b>jam</b> default target "all"
depend on "exe", "lib", "obj", "files", and "shell".
<P>
<HR>
<A NAME="VARS">
<H3>
Jambase Variables
</H3>
</A>
<P>
Most of the following variables have default values for
each platform; refer to the Jambase file to see what those
defaults are.
<P>
ALL_LOCATE_TARGET
<BLOCKQUOTE>
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 <b>jam</b>
to write built targets to a location outside
the source tree.
</BLOCKQUOTE>
AR
<BLOCKQUOTE>
The archive command used to update Library
and LibraryFromObjects targets.
</BLOCKQUOTE>
AS
<BLOCKQUOTE>
The assembler for As rule targets.
</BLOCKQUOTE>
ASFLAGS
<BLOCKQUOTE>
Flags handed to the assembler for As.
</BLOCKQUOTE>
AWK
<BLOCKQUOTE>
The name of awk interpreter, used when copying a
shell script for the Shell rule.
</BLOCKQUOTE>
BCCROOT
<BLOCKQUOTE>
Selects Borland compile and link actions on NT.
</BLOCKQUOTE>
BINDIR
<BLOCKQUOTE>
Not longer used.
(I.e., used only for backward compatibility with the
obsolete INSTALLBIN rule.)
</BLOCKQUOTE>
CC
<BLOCKQUOTE>
C compiler used for Cc rule targets.
</BLOCKQUOTE>
CCFLAGS
<BLOCKQUOTE>
Compile flags for Cc rule targets.
The Cc rule sets target-specific $(CCFLAGS)
values on its targets.
</BLOCKQUOTE>
C++
<BLOCKQUOTE>
C++ compiler used for C++ rule targets.
</BLOCKQUOTE>
C++FLAGS
<BLOCKQUOTE>
Compile flags for C++ rule targets.
The C++ rule sets target-specific $(C++FLAGS)
values on its targets.
</BLOCKQUOTE>
CHMOD
<BLOCKQUOTE>
Program (usually chmod(1)) used to set file
permissions for Chmod rule.
</BLOCKQUOTE>
CP
<BLOCKQUOTE>
The file copy program, used by File and Install* rules.
</BLOCKQUOTE>
CRELIB
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
CW
<BLOCKQUOTE>
On Macintosh, the root of the Code Warrior Pro 5 directory.
</BLOCKQUOTE>
DEFINES
<BLOCKQUOTE>
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).
</BLOCKQUOTE>
DOT
<BLOCKQUOTE>
The operating system-specific name for the current directory.
</BLOCKQUOTE>
DOTDOT
<BLOCKQUOTE>
The operating system-specific name for the parent directory.
</BLOCKQUOTE>
EXEMODE
<BLOCKQUOTE>
Permissions for executables linked with Link, Main,
and MainFromObjects, on platforms with a Chmod action.
</BLOCKQUOTE>
FILEMODE
<BLOCKQUOTE>
Permissions for files copied by File or Bulk,
on platforms with a Chmod action.
</BLOCKQUOTE>
FORTRAN
<BLOCKQUOTE>
The Fortran compiler used by Fortran rule.
</BLOCKQUOTE>
FORTRANFLAGS
<BLOCKQUOTE>
Fortran compiler flags for Fortran rule targets.
</BLOCKQUOTE>
GROUP
<BLOCKQUOTE>
<I>(Unix only.)</I>
The group owner for Install* rule targets.
</BLOCKQUOTE>
HDRGRIST
<BLOCKQUOTE>
If set, used by the HdrRule to distinguish header files
with the same name in diffrent directories.
</BLOCKQUOTE>
HDRPATTERN
<BLOCKQUOTE>
A regular expression pattern that matches
C preprocessor "#include" directives in source files
and returns the name of the included file.
</BLOCKQUOTE>
HDRRULE
<BLOCKQUOTE>
Name of the rule to invoke with the results of header file
scanning. Default is "HdrRule".
<P>
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.
</BLOCKQUOTE>
HDRS
<BLOCKQUOTE>
Directories to be searched for header files.
This is used by the Object rule to:
<UL>
<LI>set up search paths for finding files returned
by header scans
<LI>add -I flags on compile commands
</UL>
(See STDHDRS.)
</BLOCKQUOTE>
HDRSCAN
<BLOCKQUOTE>
Regular expression pattern to use for header file
scanning. The Object rule sets this to $(HDRPATTERN).
This is a jam-special variable; see HDRRULE.
</BLOCKQUOTE>
HDRSEARCH
<BLOCKQUOTE>
Used by the HdrRule to fix the list of directories where
header files can be found for a given source file.
</BLOCKQUOTE>
INSTALLGRIST
<BLOCKQUOTE>
Used by the Install* rules to grist paths to installed
files; defaults to "installed".
</BLOCKQUOTE>
JAMFILE
<BLOCKQUOTE>
Default is "Jamfile"; the name of the user-written
rules file found in each source directory.
</BLOCKQUOTE>
JAMRULES
<BLOCKQUOTE>
Default is "Jamrules"; the name of a rule definition
file to be read in at the first SubDir rule invocation.
</BLOCKQUOTE>
KEEPOBJS
<BLOCKQUOTE>
If set, tells the LibraryFromObjects rule not to delete
object files once they are archived.
</BLOCKQUOTE>
LEX
<BLOCKQUOTE>
The lex(1) command and flags.
</BLOCKQUOTE>
LIBDIR
<BLOCKQUOTE>
Not longer used.
(I.e., used only for backward compatibility with the
obsolete INSTALLLIB rule.)
</BLOCKQUOTE>
LINK
<BLOCKQUOTE>
The linker. Defaults to $(CC).
</BLOCKQUOTE>
LINKFLAGS
<BLOCKQUOTE>
Flags handed to the linker. Defaults to $(CCFLAGS).
</BLOCKQUOTE>
LINKLIBS
<BLOCKQUOTE>
List of external libraries to link with. The target image
does not depend on these libraries.
</BLOCKQUOTE>
LN
<BLOCKQUOTE>
The hard link command for HardLink rule.
</BLOCKQUOTE>
LOCATE_SOURCE
<BLOCKQUOTE>
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.)
</BLOCKQUOTE>
LOCATE_TARGET
<BLOCKQUOTE>
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.)
</BLOCKQUOTE>
MANDIR
<BLOCKQUOTE>
Not longer used.
(I.e., used only for backward compatibility with the
obsolete INSTALLMAN rule.)
</BLOCKQUOTE>
MKDIR
<BLOCKQUOTE>
The 'create directory' command used for the MkDir
rule.
</BLOCKQUOTE>
MODE
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
MSVC
<BLOCKQUOTE>
Selects Microsoft Visual C 16-bit compile & link
actions on NT.
</BLOCKQUOTE>
MSVCNT
<BLOCKQUOTE>
Selects Microsoft Visual C NT 5.0 and earlier compile
& link actions on NT.
</BLOCKQUOTE>
MSVCDIR
<BLOCKQUOTE>
Selects Microsoft Visual C NT 6.0 and later compile
& link actions on NT. These are identical to versions
5.0 and earlier -- it just seems Microsoft changed the
name of the variable.
</BLOCKQUOTE>
MV
<BLOCKQUOTE>
The file rename command and options.
</BLOCKQUOTE>
NEEDLIBS
<BLOCKQUOTE>
The list of libraries used when linking an executable.
Used by the Link rule.
</BLOCKQUOTE>
NOARSCAN
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
NOARUPDATE
<BLOCKQUOTE>
If set, indicates that libraries can't be updated, but only
created whole.
</BLOCKQUOTE>
OPTIM
<BLOCKQUOTE>
The C compiler flag for optimization, used by Cc and C++
rules.
</BLOCKQUOTE>
OSFULL
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
OWNER
<BLOCKQUOTE>
The owner of installed files. Used by Install* rules.
</BLOCKQUOTE>
RANLIB
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
RELOCATE
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
RM
<BLOCKQUOTE>
The command and options to remove a file.
</BLOCKQUOTE>
SEARCH_SOURCE
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
SHELLHEADER
<BLOCKQUOTE>
A string inserted to the first line of every file
created by the Shell rule.
</BLOCKQUOTE>
SHELLMODE
<BLOCKQUOTE>
Permissions for files installed by Shell rule.
</BLOCKQUOTE>
SOURCE_GRIST
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
STDHDRS
<BLOCKQUOTE>
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.
</BLOCKQUOTE>
SUBDIR
<BLOCKQUOTE>
The path from the current directory to the directory
last named by the SubDir rule.
</BLOCKQUOTE>
TOP
<BLOCKQUOTE>
The path from the current directory to the directory
that has the Jamrules file. Used by the SubDir rule.
</BLOCKQUOTE>
SUFEXE
<BLOCKQUOTE>
The suffix for executable files, if none provided.
Used by the Main rule.
</BLOCKQUOTE>
SUFLIB
<BLOCKQUOTE>
The suffix for libraries. Used by the Library and
related rules.
</BLOCKQUOTE>
SUFOBJ
<BLOCKQUOTE>
The suffix for object files. Used by the Objects
and related rules.
</BLOCKQUOTE>
UNDEFFLAG
<BLOCKQUOTE>
The flag prefixed to each symbol for the Undefines
rule (i.e., the compiler flag for undefined symbols).
</BLOCKQUOTE>
WATCOM
<BLOCKQUOTE>
Selects Watcom compile and link actions on OS2.
</BLOCKQUOTE>
YACC
<BLOCKQUOTE>
The yacc(1) command.
</BLOCKQUOTE>
YACCFILES
<BLOCKQUOTE>
The base filename generated by yacc(1).
</BLOCKQUOTE>
YACCFLAGS
<BLOCKQUOTE>
The yacc(1) command flags.
</BLOCKQUOTE>
YACCGEN
<BLOCKQUOTE>
The suffix used on generated yacc(1) output.
</BLOCKQUOTE>
<P>
<HR>
<A HREF="#TOP">Back to top.</A>
<P>
Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
<BR>
Comments to <A HREF="mailto:info@perforce.com">info@perforce.com</A>
<BR>
Last updated: Dec 31, 2000
<BR>
$Id: //public/jam/src/Jambase.html#10 $
</BODY>
</HTML>

279
jam/Jamfile Normal file
View File

@@ -0,0 +1,279 @@
#
# Jamfile to build Jam (a make(1)-like program)
#
# There are no user-serviceable parts in this file.
#
# Put executables in platform-specific subdirectory.
if $(VMS) { LOCATE_TARGET ?= [.binvms] ; }
else if $(MAC) { LOCATE_TARGET ?= :bin.mac ; }
else { LOCATE_TARGET ?= bin.$(OSFULL[1]:L) ; }
# Leave generated source in current directory; it would be nice to use
# these lines below to build the source into the platform-specific
# directory, but getting scan.c to include the right jambase.h is
# hard: with ""'s, it always gets the bootstrap version; with <>'s,
# it won't find the bootstrap version.
# SEARCH_SOURCE ?= $(LOCATE_TARGET) $(DOT) ;
# LOCATE_SOURCE ?= $(LOCATE_TARGET) ;
#
# We have some different files for UNIX, VMS, and NT.
#
if $(NT) { code = execunix.c filent.c pathunix.c ; }
else if $(MINGW) { code = execunix.c filent.c pathunix.c ; }
else if $(OS2) { code = execunix.c fileos2.c pathunix.c ; }
else if $(VMS) { code = execvms.c filevms.c pathvms.c ; }
else if $(MAC) { code = execmac.c filemac.c pathmac.c ; }
else { code = execunix.c fileunix.c pathunix.c ; }
# For jam profiling/debugging.
if $(PROFILE)
{
CCFLAGS += -pg ;
LINKFLAGS += -pg ;
LOCATE_TARGET = $(LOCATE_TARGET)/pg ;
}
if $(DEBUG)
{
CCFLAGS += -g ;
C++FLAGS += -g ;
LINKFLAGS += -g ;
LOCATE_TARGET = $(LOCATE_TARGET)/g ;
}
# We have to signal jam.h for these
if $(OS) = NT { CCFLAGS += /DNT ; }
### LOCAL CHANGE
#
# Include:
# * header caching
# * jamfile caching
# * definition of JAM_TARGETS variable
#
DEFINES += OPT_HEADER_CACHE_EXT ;
DEFINES += OPT_JAMFILE_CACHE_EXT ;
DEFINES += OPT_JAM_TARGETS_VARIABLE_EXT ;
#
### LOCAL CHANGE
### LOCAL CHANGE
#
# Include rule profiling support, if specified in the environment.
#
if $(OPT_RULE_PROFILING_EXT) {
DEFINES += OPT_RULE_PROFILING_EXT ;
}
#
### LOCAL CHANGE
### LOCAL CHANGE
#
# Include stat cache server under BeOS.
#
if $(OS) = BEOS {
DEFINES += OPT_STAT_CACHE_SERVER_EXT ;
}
#
### LOCAL CHANGE
# Do we know yacc?
if $(YACC) { code += jamgram.y ; }
else { code += jamgram.c ; }
#
# Build the jamgram.y from the jamgram.yy
# yyacc is a slippery script that makes grammars a little
# easier to read/maintain.
#
if $(YACC) && $(SUFEXE) = ""
{
GenFile jamgram.y jamgramtab.h : yyacc jamgram.yy ;
}
### LOCAL CHANGE
#
# These files contain locally developed improvements.
#
code += jcache.c ;
# code primarily not written locally, but grabbed from the net
code += hcache.c ;
#
### LOCAL CHANGE
### LOCAL CHANGE
#
# Include stat cache server under BeOS.
#
if $(OS) = BEOS {
code += beos_stat_cache.c ;
}
#
### LOCAL CHANGE
#
# How to build the compiled in jambase.
#
Main mkjambase : mkjambase.c ;
if $(VMS)
{
CC = cxx ;
LINK = cxxlink ;
CCFLAGS += /define=VMS ;
}
if $(OS) = BEOS {
}
#
# The guts of the Jamfile: how to build Jam
#
Main jam : jam.c jambase.c ;
LinkLibraries jam : libjam.a ;
GenFile jambase.c : mkjambase Jambase ;
if $(OS) = BEOS {
local haveBone = [ GLOB /boot/develop/headers/be/bone/arpa : inet.h ] ;
if ! $(haveBone) {
LINKLIBS on jam += -lnet ;
}
local haveZeta = [ GLOB /boot/beos/system/lib : libzeta.so ] ;
if $(haveZeta) {
DEFINES += _ZETA_USING_DEPRECATED_API_=1 ;
}
}
Library libjam.a :
builtins.c command.c compile.c $(code) expand.c
glob.c hash.c headers.c lists.c make.c make1.c
newstr.c option.c parse.c regexp.c rules.c scan.c
search.c timestamp.c variable.c ;
if $(BINDIR) { InstallBin $(BINDIR) : jam ; }
### LOCAL CHANGE
#
# Build stat cache server under BeOS.
#
if $(OS) = BEOS {
# RC must be defined, if we are to compile the resources.
if ! $(RC) && ! $(NO_STAT_CACHE_SERVER_RESOURCES) {
Echo "NOTE: The variable RC is not defined, and therefore the" ;
Echo "NOTE: built StatCacheServer won't have resources (it will thus" ;
Echo "NOTE: appear in the Deskbar). If you want to have resources," ;
Echo "NOTE: define RC as an absolute or relative path referring to" ;
Echo "NOTE: the rc program, or just the program name, if it lives in" ;
Echo "NOTE: the PATH." ;
NO_STAT_CACHE_SERVER_RESOURCES = 1 ;
}
rule CompileResources
{
SEARCH on $(2) += $(SEARCH_SOURCE) ;
MakeLocate $(1) : $(LOCATE_TARGET) ;
Depends $(1) : $(2) ;
LocalClean clean : $(1) ;
}
actions CompileResources
{
$(RC) -o "$(1)" "$(2)"
}
rule AddResources
{
SEARCH on $(2) += $(SEARCH_SOURCE) ;
Depends $(1) : $(2) ;
}
actions AddResources
{
xres -o "$(1)" $(2)
}
LINK on StatCacheServer = g++ ;
LINKLIBS on StatCacheServer
= [ on StatCacheServer return $(LINKLIBS) ] -lbe ;
Main StatCacheServer : StatCacheServer.cpp ;
if ! $(NO_STAT_CACHE_SERVER_RESOURCES) {
CompileResources StatCacheServer.rsrc : StatCacheServer.rdef ;
AddResources StatCacheServer : StatCacheServer.rsrc ;
}
if $(BINDIR) { InstallBin $(BINDIR) : StatCacheServer ; }
}
#
### LOCAL CHANGE
#
# Distribution making from here on out.
#
ALLSOURCE =
Build.com Build.mpw Jam.html Jambase Jambase.html Jamfile
Jamfile.html Makefile Porting README RELNOTES builtins.c builtins.h
command.c command.h compile.c compile.h execcmd.h execmac.c
execunix.c execvms.c expand.c expand.h filemac.c filent.c
fileos2.c filesys.h fileunix.c filevms.c glob.c hash.c hash.h
headers.c headers.h jam.c jam.h jambase.c jambase.h jamgram.c
jamgram.h jamgram.y jamgram.yy jamgramtab.h lists.c lists.h
make.c make.h make1.c mkjambase.c newstr.c newstr.h option.c
option.h parse.c parse.h patchlevel.h pathmac.c pathsys.h
pathunix.c pathvms.c regexp.c regexp.h rules.c rules.h scan.c
scan.h search.c search.h timestamp.c timestamp.h variable.c
variable.h yyacc ;
### LOCAL CHANGE
#
ALLSOURCE += LOCAL_DIFFERENCES.txt ;
#
### LOCAL CHANGE
rule Ball
{
NotFile balls ;
Depends balls : $(<) ;
DEPENDS $(<) : $(>) ;
switch $(<)
{
case *.tar : Tar $(<) : $(>) ;
case *.shar : Shar $(<) : $(>) ;
case *.zip : Zip $(<) : $(>) ;
}
}
VERSION = jam-2.5rc1 ;
actions Tar
{
ln -s . $(VERSION)
tar cvhf $(<) $(VERSION)/$(>)
rm $(VERSION)
}
actions Shar
{
shar $(>) > $(<)
}
actions Zip
{
zip $(<) $(>)
}
Ball $(VERSION).shar : $(ALLSOURCE) ;
Ball $(VERSION).tar : $(ALLSOURCE) ;
Ball $(VERSION).zip : $(ALLSOURCE) ;

1450
jam/Jamfile.html Normal file

File diff suppressed because it is too large Load Diff

73
jam/Makefile Normal file
View File

@@ -0,0 +1,73 @@
# Makefile for jam
CC = cc
CFLAGS =
EXENAME = ./jam0
TARGET = -o $(EXENAME)
HOST_SYSTEM=$(shell uname)
# 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
#EXENAME = .\jam0.exe
# NT (with Microsoft compiler)
# People with DevStudio settings already in shell environment.
#CC = cl /nologo
#CFLAGS = -DNT
#TARGET = /Fejam0
#EXENAME = .\jam0.exe
# 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
# BeOS (R5 only, actually, but who cares)
ifeq ($(HOST_SYSTEM), BeOS)
ifeq ($(shell sh -c "strings /system/lib/libroot.so | grep uname"), )
LINKLIBS = -lnet
endif
endif
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: $(EXENAME)
LINKLIBS=$(LINKLIBS) $(EXENAME)
$(EXENAME):
$(CC) $(TARGET) $(CFLAGS) $(SOURCES) $(LINKLIBS)

68
jam/Porting Normal file
View File

@@ -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.

152
jam/README Normal file
View File

@@ -0,0 +1,152 @@
Jam - make(1) redux
/+\
+\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
\+/
This is Release 2.5 of Jam, 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
MSVCDIR: The Microsoft Compiler 6.0 (for NT)
MSVCNT: The Microsoft Compiler 5.0 (for NT)
MSVC: The Microsoft Compiler 1.5 (for Windows)
Only MSVCNT and MSVCDIR have really been tested and are 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
December, 2002 - release 2.5
Christopher Seiwald
seiwald@perforce.com

156
jam/README.CHANGES Normal file
View File

@@ -0,0 +1,156 @@
Contents: Changes to Jam 2.5rc3.
Author: Ingo Weinhold (bonefish@users.sf.net)
This version of Jam is NOT the original one distributed by Perforce
(www.perforce.com). This file lists its differences to the original version
2.5rc3. The patches have originally been applied to version 2.4 and had to be
adjusted more or less to work with 2.5rc3.
* Jamfile tree processing
Changes to Jambase, compile.{c,h}, scan.c, jamgram.yy. Jam does now always
read the whole project Jamfile tree, not only the subtree starting in the
subdirectory it has been invoked from. If not supplied with a target on the
command line, however, only the targets in that subtree are built and those
the former ones depend on.
* Header Caching
Taken from `//guest/matt_armstrong/jam/patched_version/...' from the
public Perforce depot (`public.perforce.com:1666'). Originally implemented
by Craig McPheeters, and improved by Matt Armstrong. The following text
stems from the file LOCAL_DIFFERENCES.txt in Matt's version.
<quote>
This code is taken from //guest/craig_mcpheeters/jam/src/ on the
Perforce public depot. Many thanks to Craig McPheeters for making his
code available. It is delimited by the OPT_HEADER_CACHE_EXT #define
within the code.
Jam has a facility to scan source files for other files they might
include. This code implements a cache of these scans, so the entire
source tree need not be scanned each time jam is run. This brings the
following benefits:
- If a file would otherwise be scanned multiple times in a
single jam run (because the same file is represented by
multiple targets, perhaps each with a different grist), it
will now be scanned only once. In this way, things are
faster even if the cache file is not present when Jam is
run.
- If a cache entry is present in the cache file when Jam
starts, and the file has not changed since the last time it
was scanned, Jam will not bother to re-scan it. This
markedly increaces Jam startup times for large projects.
This code has improvements over Craig McPheeters' original
version. I've described all of these changes to Craig and he
intends to incorporate them back into his version. The changes
are:
- The actual name of the cache file is controlled by the
HCACHEFILE Jam variable. If HCACHEFILE is left unset (the
default), reading and writing of a cache file is not
performed. The cache is always used internally regardless
of HCACHEFILE, which helps when HDRGRIST causes the same
file to be scanned multiple times.
Setting LOCATE and SEARCH on the the HCACHEFILE works as
well, so you can place anywhere on disk you like or even
search for it in several directories. You may also set it
in your environment to share it amongst all your projects.
- The .jamdeps file is in a new format that allows binary data
to be in any of the fields, in particular the file names.
The original code would break if a file name contained the
'@' or '\n' characters. The format is also versioned,
allowing upgrades to automatically ignore old .jamdeps
files. The format remains human readable. In addition,
care has been taken to not add the entry into the header
cache until the entire record has been successfully read from
the file.
- The cache stores the value of HDRPATTERN with each cache
entry, and it is compared along with the file's date to
determine if there is a cache hit. If the HDRPATTERN does
not match, it is treated as a cache miss. This allows
HDRPATTERN to change without worrying about stale cache
entries. It also allows the same file to be scanned
multiple times with different HDRPATTERN values.
- Each cache entry is given an "age" which is the maximum
number of times a given header cache entry can go unused
before it is purged from the cache. This helps clean up old
entries in the .jamdeps file when files move around or are
removed from your project.
You control the maximum age with the HCACHEMAXAGE variable.
If set to 0, no cache aging is performed. Otherwise it is
the number of times a jam must be run before an unused cache
entry is purged. The default for HCACHEMAXAGE if left unset
is 100.
- Jambase itself is changed.
SubDir now always sets HDRGRIST to $(SOURCE_GRIST) so header
scanning can deal with multiple header files of the same
name in different directories. With the header cache, this
does no longer incurs a performance penalty -- a given file
will still only be scanned once.
The FGristSourceFiles rule is now just an alias for
FGristFiles. Header files do not necessarily have global
visibility, and the header cache eliminates any performance
penalty this might otherwise incur.
Because of all these improvements, the following claims can be
made about this header cache implementation that can not be made
about Craig McPheeters' original version.
- The semantics of a Jam run will never be different because of
the header cache (the HDRPATTERN check ensures this).
- It will never be necessary to delete .jamdeps to fix obscure
jam problems or purge old entries.
</quote>
* Jamfile Caching
As large build systems may consist of a huge number of Jamfiles, the
mere reading of these files may take considerable time. This version
implements a cache for them. Since the time stamps of the files still
need to retrieved to check whether the cached entries are still up to
date, the benefits to be expected are not that big though.
The name of the cache file is controlled by the JCACHEFILE Jam variable.
If JCACHEFILE is left unset (the default), reading and writing of a cache
file is not performed. Setting the SEARCH and LOCATE variables does work
as expected.
* Stat Data and Directory Caching Server (BeOS only)
Also an optimization for large build systems. Since the BeOS FS cache
is terrible, stat()ing targets to get their timestamp or see if they exist
at all, and reading directories usually happens on disk, since the data
from the previous run are already out of the cache, if the build system
is large enough.
This change externalizes all stat()ing and directory reading into a
dedicated server process which caches the data, so that they can be
served from memory the next time they are requested. The server uses
the BeOS node monitoring to keep the data up to date.
The feature particularly leverages the header and jamfile caching, since
after the first run the timestamps of the jamfiles and headers are
cached too, so that reading the jamfiles and performing the header
scanning doesn't require any disk accesses at all (besides reading the
cache files, of course).
Drawbacks are that the first run of jam will be slower, mainly due to
the communication overhead with the server, and that the server consumes
memory to store the cached data. The server's memory footprint is quite
reasonable, though.

1174
jam/RELNOTES Normal file

File diff suppressed because it is too large Load Diff

1427
jam/StatCacheServer.cpp Normal file

File diff suppressed because it is too large Load Diff

35
jam/StatCacheServer.h Normal file
View File

@@ -0,0 +1,35 @@
// StatCacheServer.h
#ifndef STAT_CACHE_SERVER_H
#define STAT_CACHE_SERVER_H
#include <StorageDefs.h>
// common definitions used by server and client
#define STAT_CACHE_SERVER_PORT_NAME "stat_cache_server_port"
enum {
STAT_CACHE_COMMAND_STAT = 0,
STAT_CACHE_COMMAND_READDIR = 1,
};
typedef struct stat_cache_request {
port_id replyPort;
int32 command;
char path[B_PATH_NAME_LENGTH];
} stat_cache_request;
typedef struct stat_cache_stat_reply {
status_t error;
struct stat st;
} stat_cache_stat_reply;
typedef struct stat_cache_readdir_reply {
status_t error;
int32 entryCount;
void *clientData; // used by the client only
uint8 buffer[1];
} stat_cache_readdir_reply;
#endif // STAT_CACHE_SERVER_H

19
jam/StatCacheServer.rdef Normal file
View File

@@ -0,0 +1,19 @@
resource app_signature "application/x-vnd.haiku.jam-cacheserver";
resource file_types message
{
"types" = "application/octet-stream"
};
resource app_version
{
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_FINAL,
internal = 1,
short_info = "StatCacheServer",
long_info = "StatCacheServer 1.0.0 for Jam ©2004 Haiku"
};
resource app_flags B_BACKGROUND_APP;

264
jam/StatCacheServerImpl.h Normal file
View File

@@ -0,0 +1,264 @@
// StatCacheServerImpl.h
#ifndef STAT_CACHE_SERVER_IMPL_H
#define STAT_CACHE_SERVER_IMPL_H
#include <hash_map>
#include <string>
#include <Entry.h>
#include <MessageQueue.h>
#include <Node.h>
#include <OS.h>
class Directory;
class Entry;
class Node;
class SymLink;
// NodeRefHash
struct NodeRefHash
{
size_t operator()(const node_ref &nodeRef) const;
};
// EntryRefHash
struct EntryRefHash
{
size_t operator()(const entry_ref &entryRef) const;
};
// Referencable
class Referencable {
public:
Referencable()
: fReferenceCount(1),
fReferenceBaseCount(0)
{
}
virtual ~Referencable()
{
}
void AddReference()
{
fReferenceCount++;
}
bool RemoveReference()
{
if (--fReferenceCount <= fReferenceBaseCount) {
Unreferenced();
return true;
}
return false;
}
int32 CountReferences() const
{
return fReferenceCount;
}
protected:
virtual void Unreferenced() {};
protected:
int32 fReferenceCount;
int32 fReferenceBaseCount;
bool fDeleteWhenUnreferenced;
};
// Entry
class Entry : public Referencable {
public:
Entry();
~Entry();
status_t SetTo(Directory *parent, const char *name);
Directory *GetParent() const;
const char *GetName() const;
void SetNode(Node *node);
Node *GetNode() const;
void SetPrevious(Entry *entry);
Entry *GetPrevious() const;
void SetNext(Entry *entry);
Entry *GetNext() const;
entry_ref GetEntryRef() const;
status_t GetPath(string& path);
protected:
virtual void Unreferenced();
private:
Directory *fParent;
string fName;
Node *fNode;
Entry *fPrevious;
Entry *fNext;
};
// Node
class Node : public Referencable {
public:
Node(const struct stat &st);
virtual ~Node();
virtual status_t SetTo(Entry *entry);
status_t GetPath(string& path);
const struct stat &GetStat() const;
status_t GetStat(struct stat *st);
status_t UpdateStat();
void MarkStatInvalid();
void SetEntry(Entry *entry);
Entry *GetEntry() const;
node_ref GetNodeRef() const;
protected:
virtual void Unreferenced();
protected:
Entry *fEntry;
struct stat fStat;
bool fStatValid;
};
// Directory
class Directory : public Node {
public:
Directory(const struct stat &st);
~Directory();
virtual status_t SetTo(Entry *entry);
status_t FindEntry(const char *name, Entry **entry);
Entry *GetFirstEntry() const;
Entry *GetNextEntry(Entry *entry) const;
status_t ReadAllEntries();
bool IsComplete() const;
void AddEntry(Entry *entry);
void RemoveEntry(Entry *entry);
private:
Entry *fFirstEntry;
Entry *fLastEntry;
bool fIsComplete;
};
// SymLink
class SymLink : public Node {
public:
SymLink(const struct stat &st);
~SymLink();
virtual status_t SetTo(Entry *entry);
const char *GetTarget() const;
private:
string fTarget;
};
// NodeMonitor
class NodeMonitor : public BLooper {
public:
NodeMonitor();
virtual ~NodeMonitor();
status_t Init();
virtual void MessageReceived(BMessage *message);
status_t StartWatching(Node *node);
status_t StopWatching(Node *node);
status_t GetNextMonitoringMessage(BMessage **message);
private:
int32 fCurrentNodeMonitorLimit;
BMessageQueue fMessageQueue;
sem_id fMessageCountSem;
};
// PathResolver
class PathResolver {
public:
PathResolver();
status_t FindEntry(const char *path, bool traverse, Entry **_entry);
status_t FindEntry(Entry *entry, const char *path, bool traverse,
Entry **_entry);
status_t FindNode(const char *path, bool traverse, Node **node);
status_t ResolveSymlink(Node *node, Node **_node);
status_t ResolveSymlink(Node *node, Entry **entry);
status_t ResolveSymlink(Entry *entry, Entry **_entry);
private:
int32 fSymLinkCounter;
};
// NodeManager
class NodeManager : public BLocker {
public:
NodeManager();
~NodeManager();
static NodeManager *GetDefault();
status_t Init();
Directory *GetRootDirectory() const;
Node *GetNode(const node_ref &nodeRef);
Entry *GetEntry(const entry_ref &entryRef);
status_t CreateEntry(const entry_ref &entryRef, Entry **_entry);
status_t CreateDirectory(const node_ref &nodeRef, Directory **_dir);
void RemoveEntry(Entry *entry);
void MoveEntry(Entry *entry, const entry_ref &newRef);
void EntryUnreferenced(Entry *entry);
void NodeUnreferenced(Node *node);
status_t StartWatching(Node *node);
status_t StopWatching(Node *node);
private:
static int32 _NodeMonitoringProcessorEntry(void *data);
int32 _NodeMonitoringProcessor();
status_t _CreateNode(Entry *entry, Node **_node);
void _EntryCreated(BMessage *message);
void _EntryRemoved(BMessage *message);
void _EntryMoved(BMessage *message);
void _StatChanged(BMessage *message);
private:
typedef hash_map<entry_ref, Entry*, EntryRefHash> EntryMap;
typedef hash_map<node_ref, Node*, NodeRefHash> NodeMap;
EntryMap fEntries;
NodeMap fNodes;
Directory *fRootDirectory;
NodeMonitor *fNodeMonitor;
thread_id fNodeMonitoringProcessor;
static NodeManager sManager;
};
#endif // STAT_CACHE_SERVER_IMPL_H

221
jam/beos_stat_cache.c Normal file
View File

@@ -0,0 +1,221 @@
// beos_stat_cache.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <OS.h>
#include "beos_stat_cache.h"
#include "pathsys.h"
#include "StatCacheServer.h"
#define SET_ERRNO_AND_RETURN(error) { \
if ((error) == B_OK) \
return 0; \
errno = (error); \
return -1; \
}
// get_server_port
static
port_id
get_server_port()
{
static port_id id = -1;
static bool initialized = false;
if (!initialized) {
id = find_port(STAT_CACHE_SERVER_PORT_NAME);
initialized = true;
}
return id;
}
// get_reply_port
static
port_id
get_reply_port()
{
static port_id id = -1;
if (id < 0)
id = create_port(1, "stat cache reply port");
return id;
}
// send_request
static
status_t
send_request(int32 command, const char *path)
{
port_id requestPort = get_server_port();
port_id replyPort = get_reply_port();
stat_cache_request request;
int requestSize;
// get request port
if (requestPort < 0)
return requestPort;
// get reply port
if (replyPort < 0)
return replyPort;
// normalize the path
if (!path || !normalize_path(path, request.path, sizeof(request.path)))
return B_BAD_VALUE;
requestSize = (request.path + strlen(request.path) + 1) - (char*)&request;
// send request
request.replyPort = replyPort;
request.command = command;
return write_port(requestPort, 0, &request, requestSize);
}
// receive_reply
static
status_t
receive_reply(void **_reply, int32 *_replySize, void *buffer, int32 replySize)
{
port_id replyPort = get_reply_port();
ssize_t bytesRead;
void *reply;
int32 code;
// get reply port
if (replyPort < 0)
return replyPort;
// get the reply size
if (!buffer) {
replySize = port_buffer_size(replyPort);
if (replySize < 0)
return replySize;
}
// allocate reply
if (buffer) {
reply = buffer;
} else {
reply = malloc(replySize);
if (!reply)
return B_NO_MEMORY;
}
// read the reply
bytesRead = read_port(replyPort, &code, reply, replySize);
if (bytesRead < 0) {
if (!buffer)
free(reply);
return bytesRead;
}
if (bytesRead != replySize) {
if (!buffer)
free(reply);
return B_ERROR;
}
if (_reply)
*_reply = reply;
if (_replySize)
*_replySize = replySize;
return B_OK;
}
// beos_stat_cache_stat
int
beos_stat_cache_stat(const char *filename, struct stat *st)
{
stat_cache_stat_reply reply;
status_t error;
// fall back to standard, if there is no server
if (get_server_port() < 0)
return stat(filename, st);
// send the request
error = send_request(STAT_CACHE_COMMAND_STAT, filename);
if (error != B_OK)
SET_ERRNO_AND_RETURN(error);
// get the reply
error = receive_reply(NULL, NULL, &reply, sizeof(reply));
if (error != B_OK)
error = reply.error;
if (error != B_OK)
SET_ERRNO_AND_RETURN(error);
*st = reply.st;
return 0;
}
// beos_stat_cache_opendir
DIR *
beos_stat_cache_opendir(const char *dirName)
{
stat_cache_readdir_reply *reply;
int32 replySize;
status_t error;
// fall back to standard, if there is no server
if (get_server_port() < 0)
return opendir(dirName);
// send the request
error = send_request(STAT_CACHE_COMMAND_READDIR, dirName);
if (error != B_OK) {
errno = error;
return NULL;
}
// get the reply
error = receive_reply((void**)&reply, &replySize, NULL, 0);
if (error != B_OK)
error = reply->error;
if (error != B_OK) {
free(reply);
errno = error;
return NULL;
}
reply->clientData = reply->buffer;
// a bit ugly, but anyway...
return (DIR*)reply;
}
// beos_stat_cache_readdir
struct dirent *
beos_stat_cache_readdir(DIR *dir)
{
stat_cache_readdir_reply *reply;
struct dirent *entry;
// fall back to standard, if there is no server
if (get_server_port() < 0)
return readdir(dir);
reply = (stat_cache_readdir_reply*)dir;
if (reply->entryCount == 0)
return NULL;
entry = (struct dirent*)reply->clientData;
// get the next entry
if (--reply->entryCount > 0)
reply->clientData = (uint8*)entry + entry->d_reclen;
return entry;
}
// beos_stat_cache_closedir
int
beos_stat_cache_closedir(DIR *dir)
{
stat_cache_readdir_reply *reply;
// fall back to standard, if there is no server
if (get_server_port() < 0)
return closedir(dir);
reply = (stat_cache_readdir_reply*)dir;
free(reply);
return 0;
}

15
jam/beos_stat_cache.h Normal file
View File

@@ -0,0 +1,15 @@
// beos_stat_cache.h
#ifndef BEOS_STAT_CACHE_H
#define BEOS_STAT_CACHE_H
#include <dirent.h>
#include <sys/stat.h>
int beos_stat_cache_stat(const char *filename, struct stat *st);
DIR* beos_stat_cache_opendir(const char *dirName);
struct dirent *beos_stat_cache_readdir(DIR *dir);
int beos_stat_cache_closedir(DIR *dir);
#endif BEOS_STAT_CACHE_H

318
jam/builtins.c Normal file
View File

@@ -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.
*/
/*
* 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
* 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin
* 03/02/02 (seiwald) - new 'Match' (regexp match) builtin
* 04/03/02 (seiwald) - Glob matches only filename, not directory
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
* 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
*/
# 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"
/*
* compile_builtin() - define builtin rules
*/
# define P0 (PARSE *)0
# define C0 (char *)0
LIST *builtin_depends( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_echo( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_exit( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_flags( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_glob( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_match( PARSE *parse, LOL *args, int *jmp );
int glob( const char *s, const 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, 0 );
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, 1 );
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,
int *jmp )
{
LIST *targets = lol_get( args, 0 );
LIST *sources = lol_get( args, 1 );
LIST *l;
for( l = targets; l; l = list_next( l ) )
{
TARGET *t = bindtarget( l->string );
/* If doing INCLUDES, switch to the TARGET's include */
/* TARGET, creating it if needed. The internal include */
/* TARGET shares the name of its parent. */
if( parse->num )
{
if( !t->includes )
t->includes = copytarget( t );
t = t->includes;
}
t->depends = targetlist( t->depends, 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,
int *jmp )
{
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,
int *jmp )
{
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,
int *jmp )
{
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,
const 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, file, 0 );
break;
}
}
LIST *
builtin_glob(
PARSE *parse,
LOL *args,
int *jmp )
{
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,
int *jmp )
{
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, buf, 0 );
}
}
free( (char *)re );
}
return result;
}

14
jam/builtins.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* 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
*
* 01/10/01 (seiwald) - split from compile.h
*/
void load_builtins();

67
jam/command.c Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* command.c - maintain lists of commands
*
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 09/08/00 (seiwald) - bulletproof PIECEMEAL size computation
*/
# 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,
int maxline )
{
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 );
}

59
jam/command.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* 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) */
int maxline ); /* max line length */
void cmd_free( CMD *cmd );
# define cmd_next( c ) ((c)->next)

961
jam/compile.c Normal file
View File

@@ -0,0 +1,961 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* compile.c - compile parsed jam statements
*
* External routines:
*
* compile_append() - append list results of two statements
* compile_break() - compile 'break/continue/return' rule
* 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.
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 09/07/00 (seiwald) - stop crashing when a rule redefines itself
* 09/11/00 (seiwald) - new evaluate_rule() for headers().
* 09/11/00 (seiwald) - rules now return values, accessed via [ rule arg ... ]
* 09/12/00 (seiwald) - don't complain about rules invoked without targets
* 01/13/01 (seiwald) - fix case where rule is defined within another
* 01/10/01 (seiwald) - built-ins split out to builtin.c.
* 01/11/01 (seiwald) - optimize compile_rules() for tail recursion
* 01/21/01 (seiwald) - replace evaluate_if() with compile_eval()
* 01/24/01 (seiwald) - 'while' statement
* 03/23/01 (seiwald) - "[ on target rule ]" support
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 03/02/02 (seiwald) - rules can be invoked via variable names
* 03/12/02 (seiwald) - &&,&,||,|,in now short-circuit again
* 03/25/02 (seiwald) - if ( "" a b ) one again returns true
* 06/21/02 (seiwald) - support for named parameters
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
* 11/18/02 (seiwald) - remove bogus search() in 'on' statement.
* 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
*/
# ifdef OPT_RULE_PROFILING_EXT
# include <sys/time.h>
# endif
# 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"
static const char *set_names[] = { "=", "+=", "?=" };
static void debug_compile( int which, const char *s );
int glob( const char *s, const 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,
int *jmp )
{
/* Append right to left. */
return list_append(
(*parse->left->func)( parse->left, args, jmp ),
(*parse->right->func)( parse->right, args, jmp ) );
}
/*
* compile_break() - compile 'break/continue/return' rule
*
* parse->left results
* parse->num JMP_BREAK/CONTINUE/RETURN
*/
LIST *
compile_break(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *lv = (*parse->left->func)( parse->left, args, jmp );
*jmp = parse->num;
return lv;
}
/*
* 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 ) )
{
const char *st = t ? t->string : "";
const 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,
int *jmp )
{
LIST *ll, *lr, *s, *t;
int status = 0;
/* Short circuit lr eval for &&, ||, and 'in' */
ll = (*parse->left->func)( parse->left, args, jmp );
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, jmp );
}
/* 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, "1", 0 );
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 *p,
LOL *args,
int *jmp )
{
LIST *nv = (*p->left->func)( p->left, args, jmp );
LIST *result = 0;
LIST *l;
/* for each value for var */
for( l = nv; l && *jmp == JMP_NONE; l = list_next( l ) )
{
/* Reset $(p->string) for each val. */
var_set( p->string, list_new( L0, l->string, 1 ), VAR_SET );
/* Keep only last result. */
list_free( result );
result = (*p->right->func)( p->right, args, jmp );
/* continue loop? */
if( *jmp == JMP_CONTINUE )
*jmp = JMP_NONE;
}
/* Here by break/continue? */
if( *jmp == JMP_BREAK || *jmp == JMP_CONTINUE )
*jmp = JMP_NONE;
list_free( nv );
/* Returns result of last loop */
return result;
}
/*
* compile_if() - compile 'if' rule
*
* parse->left condition tree
* parse->right then tree
* parse->third else tree
*/
LIST *
compile_if(
PARSE *p,
LOL *args,
int *jmp )
{
LIST *l = (*p->left->func)( p->left, args, jmp );
p = l ? p->right : p->third;
list_free( l );
return (*p->func)( p, args, jmp );
}
/*
* 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,
int *jmp )
{
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
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(). */
/* Needn't copysettings(), as search sets no vars. */
pushsettings( t->settings );
t->boundname = search( t->name, &t->time );
popsettings( t->settings );
/* Don't parse missing file if NOCARE set */
if( t->time || !( t->flags & T_FLAG_NOCARE ) )
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,
int *jmp )
{
/* voodoo 1 means: s is a copyable string */
const 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,
int *jmp )
{
LIST *l;
SETTINGS *s = 0;
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
LIST *ns = (*parse->right->func)( parse->right, args, jmp );
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, jmp );
popsettings( s );
freesettings( s );
return result;
}
/*
* compile_null() - do nothing -- a stub for parsing
*/
LIST *
compile_null(
PARSE *parse,
LOL *args,
int *jmp )
{
return L0;
}
/*
* compile_on() - run rule under influence of on-target variables
*
* parse->left target list; only first used
* parse->right rule to run
*/
LIST *
compile_on(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
LIST *result = 0;
if( DEBUG_COMPILE )
{
debug_compile( 0, "on" );
list_print( nt );
printf( "\n" );
}
/*
* Copy settings, so that 'on target var on target = val'
* doesn't set var globally.
*/
if( nt )
{
TARGET *t = bindtarget( nt->string );
SETTINGS *s = copysettings( t->settings );
pushsettings( s );
result = (*parse->right->func)( parse->right, args, jmp );
popsettings( s );
freesettings( s );
}
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,
int *jmp )
{
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, jmp );
/* 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, jmp ) );
/* Run rules, appending results from each */
for( l = ll; l; l = list_next( l ) )
{
int localJmp = JMP_NONE;
result = evaluate_rule( l->string, nargs, result, &localJmp );
if (localJmp == JMP_EOF)
{
*jmp = JMP_EOF;
break;
}
}
list_free( ll );
lol_free( nargs );
return result;
}
/*
* evaluate_rule() - execute a rule invocation
*/
LIST *
evaluate_rule(
const char *rulename,
LOL *args,
LIST *result,
int *jmp )
{
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 */
if( rule->procedure )
{
PARSE *parse = rule->procedure;
SETTINGS *s = 0;
LIST *l;
int i;
# ifdef OPT_RULE_PROFILING_EXT
struct timeval startTime, endTime;
if ( DEBUG_PROFILE_RULES )
gettimeofday(&startTime, 0);
# endif
/* build parameters as local vars */
for( l = rule->params, i = 0; l; l = l->next, i++ )
s = addsettings( s, 0, l->string,
list_copy( L0, lol_get( args, i ) ) );
/* Run rule. */
/* Bring in local params. */
/* refer/free to ensure rule not freed during use. */
parse_refer( parse );
pushsettings( s );
result = list_append( result, (*parse->func)( parse, args, jmp ) );
popsettings( s );
freesettings( s );
parse_free( parse );
# ifdef OPT_RULE_PROFILING_EXT
if ( DEBUG_PROFILE_RULES )
{
gettimeofday(&endTime, 0);
rule->invocations++;
rule->invocation_time
+= (endTime.tv_sec - startTime.tv_sec) * (int64_t)1000000
+ (endTime.tv_usec - startTime.tv_usec);
}
# endif
}
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,
int *jmp )
{
/* Ignore result from first statement; return the 2nd. */
/* Optimize recursion on the right by looping. */
LIST *result = 0;
while( *jmp == JMP_NONE && parse->func == compile_rules )
{
list_free( result );
result = (*parse->left->func)( parse->left, args, jmp );
parse = parse->right;
}
if( *jmp == JMP_NONE )
{
list_free( result );
result = (*parse->func)( parse, args, jmp );
}
return result;
}
/*
* compile_set() - compile the "set variable" statement
*
* parse->left variable names
* parse->right variable values
* parse->num VAR_SET/APPEND/DEFAULT
*/
LIST *
compile_set(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
LIST *ns = (*parse->right->func)( parse->right, args, jmp );
LIST *l;
if( DEBUG_COMPILE )
{
debug_compile( 0, "set" );
list_print( nt );
printf( " %s ", set_names[ parse->num ] );
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 ), parse->num );
list_free( nt );
return ns;
}
/*
* compile_setcomp() - support for `rule` - save parse tree
*
* parse->string rule name
* parse->left list of argument names
* parse->right rules for rule
*/
LIST *
compile_setcomp(
PARSE *parse,
LOL *args,
int *jmp )
{
RULE *rule = bindrule( parse->string );
LIST *params = 0;
PARSE *p;
/* Build param list */
for( p = parse->left; p; p = p->left )
params = list_new( params, p->string, 1 );
if( DEBUG_COMPILE )
{
debug_compile( 0, "rule" );
printf( "%s ", parse->string );
list_print( params );
printf( "\n" );
}
/* Free old one, if present */
if( rule->procedure )
parse_free( rule->procedure );
if( rule->params )
list_free( rule->params );
rule->procedure = parse->right;
rule->params = params;
/* we now own this parse tree */
/* don't let parse_free() release it */
parse_refer( parse->right );
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,
int *jmp )
{
RULE *rule = bindrule( parse->string );
LIST *bindlist = (*parse->left->func)( parse->left, args, jmp );
/* 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 VAR_SET/APPEND/DEFAULT
*/
LIST *
compile_settings(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
LIST *ns = (*parse->third->func)( parse->third, args, jmp );
LIST *targets = (*parse->right->func)( parse->right, args, jmp );
LIST *ts;
if( DEBUG_COMPILE )
{
debug_compile( 0, "set" );
list_print( nt );
printf( "on " );
list_print( targets );
printf( " %s ", set_names[ parse->num ] );
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, parse->num,
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,
int *jmp )
{
LIST *nt = (*parse->left->func)( parse->left, args, jmp );
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, jmp );
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,
int *jmp )
{
LIST *result = 0;
LIST *l;
/* Returns the value from the last execution of the block */
while( ( *jmp == JMP_NONE ) &&
( l = (*p->left->func)( p->left, args, jmp ) ) )
{
/* Always toss while's expression */
list_free( l );
/* Keep only last result. */
list_free( result );
result = (*p->right->func)( p->right, args, jmp );
/* continue loop? */
if( *jmp == JMP_CONTINUE )
*jmp = JMP_NONE;
}
/* Here by break/continue? */
if( *jmp == JMP_BREAK || *jmp == JMP_CONTINUE )
*jmp = JMP_NONE;
/* Returns result of last loop */
return result;
}
/*
* debug_compile() - printf with indent to show rule expansion.
*/
static void
debug_compile( int which, const 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;
}

62
jam/compile.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* 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
*
* 01/22/01 (seiwald) - replace evaluate_if() with compile_eval()
* 01/24/01 (seiwald) - 'while' statement
* 03/02/02 (seiwald) - rules can be invoked via variable names
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
*/
void compile_builtins();
LIST *compile_append( PARSE *parse, LOL *args, int *jmp );
LIST *compile_break( PARSE *parse, LOL *args, int *jmp );
LIST *compile_foreach( PARSE *parse, LOL *args, int *jmp );
LIST *compile_if( PARSE *parse, LOL *args, int *jmp );
LIST *compile_eval( PARSE *parse, LOL *args, int *jmp );
LIST *compile_include( PARSE *parse, LOL *args, int *jmp );
LIST *compile_list( PARSE *parse, LOL *args, int *jmp );
LIST *compile_local( PARSE *parse, LOL *args, int *jmp );
LIST *compile_null( PARSE *parse, LOL *args, int *jmp );
LIST *compile_on( PARSE *parse, LOL *args, int *jmp );
LIST *compile_rule( PARSE *parse, LOL *args, int *jmp );
LIST *compile_rules( PARSE *parse, LOL *args, int *jmp );
LIST *compile_set( PARSE *parse, LOL *args, int *jmp );
LIST *compile_setcomp( PARSE *parse, LOL *args, int *jmp );
LIST *compile_setexec( PARSE *parse, LOL *args, int *jmp );
LIST *compile_settings( PARSE *parse, LOL *args, int *jmp );
LIST *compile_switch( PARSE *parse, LOL *args, int *jmp );
LIST *compile_while( PARSE *parse, LOL *args, int *jmp );
LIST *evaluate_rule( const char *rulename, LOL *args, LIST *result, int *jmp );
/* 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 */
/* Flags for compile_return */
# define JMP_NONE 0 /* flow continues */
# define JMP_BREAK 1 /* break out of loop */
# define JMP_CONTINUE 2 /* step to end of loop */
# define JMP_RETURN 3 /* return from rule */
# define JMP_EOF 4 /* stop executing the current file */

23
jam/execcmd.h Normal file
View File

@@ -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

70
jam/execmac.c Normal file
View File

@@ -0,0 +1,70 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
*/
# include "jam.h"
# include "lists.h"
# include "execcmd.h"
# include <errno.h>
# ifdef OS_MAC
/*
* 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 */

385
jam/execunix.c Normal file
View File

@@ -0,0 +1,385 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 11/04/02 (seiwald) - const-ing for string literals
* 12/27/02 (seiwald) - grist .bat file with pid for system uniqueness
*/
# include "jam.h"
# include "lists.h"
# include "execcmd.h"
# include <errno.h>
# ifdef USE_EXECUNIX
# ifdef OS_OS2
# define USE_EXECNT
# include <process.h>
# endif
# ifdef OS_NT
# define USE_EXECNT
# include <process.h>
# define WIN32_LEAN_AND_MEAN
# include <windows.h> /* do the ugly deed */
# define USE_MYWAIT
# if !defined( __BORLANDC__ )
# define wait my_wait
static int my_wait( int *status );
# endif
# endif
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;
const 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";
/* +32 is room for \jamXXXXXtSS.bat (at least) */
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 32 );
sprintf( cmdtab[ slot ].tempfile, "%s\\jam%dt%d.bat",
tempdir, GetCurrentProcessId(), 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
# ifdef NO_VFORK
if ((pid = fork()) == 0)
{
execvp( argv[0], argv );
_exit(127);
}
# else
if ((pid = vfork()) == 0)
{
execvp( argv[0], argv );
_exit(127);
}
# endif
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;
do
{
/* 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( "jam: waif child process %ld found, ignoring it!\n", w );
} while( i == MAXJOBS );
# ifdef USE_EXECNT
/* Clear the temp file */
unlink( cmdtab[ i ].tempfile );
# endif
/* 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;
}
# ifdef USE_MYWAIT
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 /* USE_MYWAIT */
# endif /* USE_EXECUNIX */

168
jam/execvms.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
*/
# include "jam.h"
# include "lists.h"
# include "execcmd.h"
# ifdef OS_VMS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iodef.h>
#include <ssdef.h>
#include <descrip.h>
#include <dvidef.h>
#include <clidef.h>
#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 */

534
jam/expand.c Normal file
View File

@@ -0,0 +1,534 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval
* 01/13/01 (seiwald) - :UDJE work on non-filename strings
* 02/19/01 (seiwald) - make $($(var):J=x) join multiple values of var
* 01/25/02 (seiwald) - fixed broken $(v[1-]), by ian godin
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
* 12/30/02 (armstrong) - fix out-of-bounds access in var_expand()
*/
# include "jam.h"
# include "lists.h"
# include "variable.h"
# include "expand.h"
# include "pathsys.h"
# include "newstr.h"
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( const char *mods, VAR_EDITS *edits );
static void var_edit_file( const 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,
const char *in,
const char *end,
LOL *lol,
int cancopyin )
{
char out_buf[ MAXSYM ];
char *out = out_buf;
const 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( end - in == 4 && in[0] == '$' && in[1] == '(' && in[3] == ')' )
{
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, inp, 1 );
else
return list_new( l, out_buf, 0 );
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, edits.empty.ptr, 0 );
/* 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, out_buf, 0 );
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, out_buf, 0 );
}
}
/* 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 <grist>
* 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(
const 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(
const 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 );
}
}

18
jam/expand.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* 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
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
LIST *var_expand(
LIST *l,
const char *in,
const char *end,
LOL *lol,
int cancopyin );

168
jam/filemac.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/21/00 (malyn) - divorced from GUSI
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "filesys.h"
# include "pathsys.h"
# ifdef OS_MAC
#include <Files.h>
#include <Folders.h>
# include <:sys:stat.h>
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(
const 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(
const 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(
const char *archive,
scanback func,
void *closure )
{
}
# endif /* macintosh */

276
jam/filent.c Normal file
View File

@@ -0,0 +1,276 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 10/03/00 (anton) - Porting for Borland C++ 5.5
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 11/04/02 (seiwald) - const-ing for string literals
* 01/23/03 (seiwald) - long long handles for NT IA64
*/
# include "jam.h"
# include "filesys.h"
# include "pathsys.h"
# ifdef OS_NT
# ifdef __BORLANDC__
# if __BORLANDC__ < 0x550
# include <dir.h>
# include <dos.h>
# endif
# undef PATHNAME /* cpp namespace collision */
# define _finddata_t ffblk
# endif
# include <io.h>
# include <sys/stat.h>
/*
* file_dirscan() - scan a directory for files
*/
# ifdef _M_IA64
# define FINDTYPE long long
# else
# define FINDTYPE long
# endif
void
file_dirscan(
const char *dir,
scanback func,
void *closure )
{
PATHNAME f;
char filespec[ MAXJPATH ];
char filename[ MAXJPATH ];
FINDTYPE 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 == (FINDTYPE)(-1) ) )
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(
const 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 "!<arch>\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(
const 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 */

134
jam/fileos2.c Normal file
View File

@@ -0,0 +1,134 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 09/22/00 (seiwald) handle \ and c:\ specially: don't add extra /
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "filesys.h"
# include "pathsys.h"
# ifdef OS_OS2
# include <io.h>
# include <dos.h>
/*
* file_dirscan() - scan a directory for files
*/
void
file_dirscan(
const 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(
const 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(
const char *archive,
scanback func,
void *closure )
{
}
# endif /* OS2 */

18
jam/filesys.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* 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
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
typedef void (*scanback)( void *closure, const char *file, int found, time_t t );
void file_dirscan( const char *dir, scanback func, void *closure );
void file_archscan( const char *arch, scanback func, void *closure );
int file_time( const char *filename, time_t *time );

404
jam/fileunix.c Normal file
View File

@@ -0,0 +1,404 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 04/03/01 (seiwald) - AIX uses SARMAG
* 07/16/02 (seiwald) - Support BSD style long filename in archives.
* 11/04/02 (seiwald) - const-ing for string literals
* 12/27/02 (seiwald) - support for AIX big archives
* 12/30/02 (seiwald) - terminate ar_hdr for solaris sscanf()
* 12/30/02 (seiwald) - skip solaris' empty archive member names (/, //xxx)
*/
# 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 <sys/dir.h>
# include <unistd.h>
# define STRUCT_DIRENT struct direct
# else
# include <dirent.h>
# define STRUCT_DIRENT struct dirent
# endif
# ifdef OS_COHERENT
# include <arcoff.h>
# define HAVE_AR
# endif
# if defined( OS_MVS ) || \
defined( OS_INTERIX )
#define ARMAG "!<arch>\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
# ifdef _AIX43
/* AIX 43 ar SUPPORTs only __AR_BIG__ */
# define __AR_BIG__
# endif
# include <ar.h>
# endif
# ifdef OPT_STAT_CACHE_SERVER_EXT
# include "beos_stat_cache.h"
# define opendir beos_stat_cache_opendir
# define readdir beos_stat_cache_readdir
# define closedir beos_stat_cache_closedir
# endif
/*
* file_dirscan() - scan a directory for files
*/
void
file_dirscan(
const 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(
const char *filename,
time_t *time )
{
struct stat statbuf;
# ifdef OPT_STAT_CACHE_SERVER_EXT
if( beos_stat_cache_stat( filename, &statbuf ) < 0 )
return -1;
# else
if( stat( filename, &statbuf ) < 0 )
return -1;
# endif
*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(
const 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 ) )
{
long lar_date;
long lar_size;
char lar_name[256];
char *dst = lar_name;
/* solaris sscanf() does strlen first, so terminate somewhere */
ar_hdr.ar_fmag[0] = 0;
/* Get date & size */
sscanf( ar_hdr.ar_date, "%ld", &lar_date );
sscanf( ar_hdr.ar_size, "%ld", &lar_size );
/* Handle solaris string table.
** The entry under the name // is the table,
** and entries with the name /nnnn refer to the table.
*/
if( ar_hdr.ar_name[0] != '/' )
{
/* traditional archive entry names:
** ends at the first space, /, or null.
*/
char *src = ar_hdr.ar_name;
const char *e = src + sizeof( ar_hdr.ar_name );
while( src < e && *src && *src != ' ' && *src != '/' )
*dst++ = *src++;
}
else 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.
*/
char *src = string_table + atoi( ar_hdr.ar_name + 1 );
while( *src != '/' )
*dst++ = *src++;
}
/* Terminate lar_name */
*dst = 0;
/* Modern (BSD4.4) long names: if the name is "#1/nnnn",
** then the actual name is the nnnn bytes after the header.
*/
if( !strcmp( lar_name, "#1" ) )
{
int len = atoi( ar_hdr.ar_name + 3 );
if( read( fd, lar_name, len ) != len )
printf("error reading archive entry\n");
lar_name[len] = 0;
}
/* Build name and pass it on. */
if( lar_name[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 );
}
/* Position at next member */
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(
const 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;
# ifdef __AR_BIG__
if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ||
strncmp( AIAMAGBIG, fl_hdr.fl_magic, SAIAMAG ) )
{
if( strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) )
printf( "Can't read new archive %s before AIX 4.3.\n" );
close( fd );
return;
}
# else
if( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ||
strncmp( AIAMAG, fl_hdr.fl_magic, SAIAMAG ) )
{
close( fd );
return;
}
# endif
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 */

321
jam/filevms.c Normal file
View File

@@ -0,0 +1,321 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 03/23/01 (seiwald) - VMS C++ changes.
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "filesys.h"
# include "pathsys.h"
# ifdef OS_VMS
# include <rms.h>
# include <iodef.h>
# include <ssdef.h>
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <descrip.h>
#include <lbrdef.h>
#include <credef.h>
#include <mhddef.h>
#include <lhidef.h>
#include <lib$routines.h>
#include <starlet.h>
/* 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(
const 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 = (char *)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(
const 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(
const 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 = (char *)archive;
VMS_func = func;
VMS_closure = closure;
status = lbr$ini_control( &context, &lfunc, &typ, NULL );
if ( !( status & 1 ) )
return;
library.dsc$a_pointer = (char *)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 */

159
jam/glob.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* 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
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
# 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( const char *s, const char *e, char *b );
/*
* glob() - match a string against a simple pattern
*/
int
glob(
const char *c,
const char *s )
{
char bitlist[ BITLISTSIZE ];
const 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(
const char *s,
const 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;
}

257
jam/hash.c Normal file
View File

@@ -0,0 +1,257 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 11/04/02 (seiwald) - const-ing for string literals
* 01/31/02 (seiwald) - keyval now unsigned (cray-ziness)
*/
# include "jam.h"
# include "hash.h"
/* Header attached to all data items entered into a hash table. */
struct hashhdr {
struct item *next;
unsigned 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;
const 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;
unsigned char *b = (unsigned char *)(*data)->key;
unsigned int keyval;
if( enter && !hp->items.more )
hashrehash( hp );
if( !enter && !hp->items.nel )
return 0;
keyval = *b;
while( *b )
keyval = keyval * 2147059363 + *b++;
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,
const 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 );
}

20
jam/hash.h Normal file
View File

@@ -0,0 +1,20 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* hash.h - simple in-memory hashing routines
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
typedef struct hashdata HASHDATA;
struct hash * hashinit( int datalen, const 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 )

428
jam/hcache.c Normal file
View File

@@ -0,0 +1,428 @@
/*
* This file has been donated to Jam.
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "rules.h"
# include "regexp.h"
# include "headers.h"
# include "newstr.h"
# include "hash.h"
# include "hcache.h"
# include "variable.h"
# include "search.h"
# include "pathsys.h"
#ifdef OPT_HEADER_CACHE_EXT
/*
* Craig W. McPheeters, Alias|Wavefront.
*
* hcache.c hcache.h - handle cacheing of #includes in source files
*
* Create a cache of files scanned for headers. When starting jam,
* look for the cache file and load it if present. When finished the
* binding phase, create a new header cache. The cache contains
* files, their timestamps and the header files found in their scan.
* During the binding phase of jam, look in the header cache first for
* the headers contained in a file. If the cache is present and
* valid, use its contents. This results in dramatic speedups with
* large projects (eg. 3min -> 1min startup for one project.)
*
* External routines:
* hcache_init() - read and parse the local .jamdeps file.
* hcache_done() - write a new .jamdeps file
* hcache() - return list of headers on target. Use cache or do a scan.
*
* The dependency file format is an ascii file with 1 line per target.
* Each line has the following fields:
* @boundname@ timestamp @file@ @file@ @file@ ... \n
* */
struct hcachedata {
char *boundname;
time_t time;
LIST *includes;
LIST *hdrscan; /* the HDRSCAN value for this target */
int age; /* if too old, we'll remove it from cache */
struct hcachedata *next;
} ;
typedef struct hcachedata HCACHEDATA ;
static struct hash *hcachehash = 0;
static HCACHEDATA *hcachelist = 0;
static int queries = 0;
static int hits = 0;
#define CACHE_FILE_VERSION "version 4"
#define CACHE_RECORD_HEADER "header"
#define CACHE_RECORD_END "end"
/*
* Return the name of the header cache file. May return NULL.
*
* The user sets this by setting the HCACHEFILE variable in a Jamfile.
* We cache the result so the user can't change the cache file during
* header scanning.
*/
static char*
cache_name(void)
{
static char* name = 0;
if (!name) {
LIST *hcachevar = var_get("HCACHEFILE");
if (hcachevar) {
TARGET *t = bindtarget( hcachevar->string );
pushsettings( t->settings );
t->boundname = search( t->name, &t->time );
popsettings( t->settings );
if (hcachevar) {
name = copystr(t->boundname);
}
}
}
return name;
}
/*
* Return the maximum age a cache entry can have before it is purged
* from the cache.
*/
static int
cache_maxage(void)
{
int age = 100;
LIST *var = var_get("HCACHEMAXAGE");
if (var) {
age = atoi(var->string);
if (age < 0)
age = 0;
}
return age;
}
/*
* Read a netstring. The caveat is that the string can't contain
* ASCII 0. The returned value is as returned by newstr(), so it need
* not be freed.
*/
char*
read_netstring(FILE* f)
{
unsigned long len;
static char* buf = NULL;
static unsigned long buf_len = 0;
if (fscanf(f, " %9lu", &len) != 1)
return NULL;
if (fgetc(f) != (int)'\t')
return NULL;
if (len > 1024 * 64)
return NULL; /* sanity check */
if (len > buf_len)
{
unsigned long new_len = buf_len * 2;
if (new_len < len)
new_len = len;
buf = realloc(buf, new_len + 1);
if (buf)
buf_len = new_len;
}
if (!buf)
return NULL;
if (fread(buf, 1, len, f) != len)
return NULL;
if (fgetc(f) != (int)'\n')
return NULL;
buf[len] = 0;
return newstr(buf);
}
/*
* Write a netstring.
*/
void
write_netstring(FILE* f, const char* s)
{
if (!s)
s = "";
fprintf(f, "%lu\t%s\n", strlen(s), s);
}
void
hcache_init()
{
HCACHEDATA cachedata, *c;
FILE *f;
char *version;
int header_count = 0;
char* hcachename;
hcachehash = hashinit (sizeof (HCACHEDATA), "hcache");
if (! (hcachename = cache_name()))
return;
if (! (f = fopen (hcachename, "rb" )))
return;
version = read_netstring(f);
if (!version || strcmp(version, CACHE_FILE_VERSION)) {
fclose(f);
return;
}
while (1)
{
char* record_type;
char *time_str;
char *age_str;
char *includes_count_str;
char *hdrscan_count_str;
int i, count;
LIST *l;
record_type = read_netstring(f);
if (!record_type) {
fprintf(stderr, "invalid %s\n", hcachename);
goto bail;
}
if (!strcmp(record_type, CACHE_RECORD_END)) {
break;
}
if (strcmp(record_type, CACHE_RECORD_HEADER)) {
fprintf(stderr, "invalid %s with record separator <%s>\n",
hcachename, record_type ? record_type : "<null>");
goto bail;
}
c = &cachedata;
c->boundname = read_netstring(f);
time_str = read_netstring(f);
age_str = read_netstring(f);
includes_count_str = read_netstring(f);
if (!c->boundname || !time_str || !age_str
|| !includes_count_str)
{
fprintf(stderr, "invalid %s\n", hcachename);
goto bail;
}
c->time = atoi(time_str);
c->age = atoi(age_str) + 1;
count = atoi(includes_count_str);
for (l = 0, i = 0; i < count; i++) {
char* s = read_netstring(f);
if (!s) {
fprintf(stderr, "invalid %s\n", hcachename);
goto bail;
}
l = list_new(l, s, 1);
}
c->includes = l;
hdrscan_count_str = read_netstring(f);
if (!includes_count_str) {
list_free(c->includes);
fprintf(stderr, "invalid %s\n", hcachename);
goto bail;
}
count = atoi(hdrscan_count_str);
for (l = 0, i = 0; i < count; i++) {
char* s = read_netstring(f);
if (!s) {
fprintf(stderr, "invalid %s\n", hcachename);
goto bail;
}
l = list_new(l, s, 1);
}
c->hdrscan = l;
if (!hashenter(hcachehash, (HASHDATA **)&c)) {
fprintf(stderr, "can't insert header cache item, bailing on %s\n",
hcachename);
goto bail;
}
c->next = hcachelist;
hcachelist = c;
header_count++;
}
if (DEBUG_HEADER) {
printf("hcache read from file %s\n", hcachename);
}
bail:
fclose(f);
}
void
hcache_done()
{
FILE *f;
HCACHEDATA *c;
int header_count = 0;
char* hcachename;
int maxage;
if (!hcachehash)
return;
if (! (hcachename = cache_name()))
return;
if (! (f = fopen (hcachename, "wb" )))
return;
maxage = cache_maxage();
/* print out the version */
write_netstring(f, CACHE_FILE_VERSION);
c = hcachelist;
for (c = hcachelist; c; c = c->next) {
LIST *l;
char time_str[30];
char age_str[30];
char includes_count_str[30];
char hdrscan_count_str[30];
if (maxage == 0)
c->age = 0;
else if (c->age > maxage)
continue;
sprintf(includes_count_str, "%lu", list_length(c->includes));
sprintf(hdrscan_count_str, "%lu", list_length(c->hdrscan));
sprintf(time_str, "%lu", c->time);
sprintf(age_str, "%lu", c->age);
write_netstring(f, CACHE_RECORD_HEADER);
write_netstring(f, c->boundname);
write_netstring(f, time_str);
write_netstring(f, age_str);
write_netstring(f, includes_count_str);
for (l = c->includes; l; l = list_next(l)) {
write_netstring(f, l->string);
}
write_netstring(f, hdrscan_count_str);
for (l = c->hdrscan; l; l = list_next(l)) {
write_netstring(f, l->string);
}
fputs("\n", f);
header_count++;
}
write_netstring(f, CACHE_RECORD_END);
if (DEBUG_HEADER) {
printf("hcache written to %s. %d dependencies, %.0f%% hit rate\n",
hcachename, header_count,
queries ? 100.0 * hits / queries : 0);
}
fclose (f);
}
LIST *
hcache (TARGET *t, LIST *hdrscan)
{
HCACHEDATA cachedata, *c = &cachedata;
LIST *l = 0;
char _normalizedPath[PATH_MAX];
char *normalizedPath = normalize_path(t->boundname, _normalizedPath,
sizeof(_normalizedPath));
++queries;
if (normalizedPath)
c->boundname = normalizedPath;
else
c->boundname = t->boundname;
if (hashcheck (hcachehash, (HASHDATA **) &c))
{
if (c->time == t->time)
{
LIST *l1 = hdrscan, *l2 = c->hdrscan;
while (l1 && l2) {
if (l1->string != l2->string) {
l1 = NULL;
} else {
l1 = list_next(l1);
l2 = list_next(l2);
}
}
if (l1 || l2) {
if (DEBUG_HEADER)
printf("HDRSCAN out of date in cache for %s\n",
t->boundname);
printf("HDRSCAN out of date for %s\n", t->boundname);
printf(" real : ");
list_print(hdrscan);
printf("\n cached: ");
list_print(c->hdrscan);
printf("\n");
list_free(c->includes);
list_free(c->hdrscan);
c->includes = 0;
c->hdrscan = 0;
} else {
if (DEBUG_HEADER)
printf ("using header cache for %s\n", t->boundname);
c->age = 0;
++hits;
l = list_copy (0, c->includes);
return l;
}
} else {
if (DEBUG_HEADER)
printf ("header cache out of date for %s\n", t->boundname);
list_free (c->includes);
list_free(c->hdrscan);
c->includes = 0;
c->hdrscan = 0;
}
} else {
if (hashenter (hcachehash, (HASHDATA **)&c)) {
c->boundname = newstr (c->boundname);
c->next = hcachelist;
hcachelist = c;
}
}
/* 'c' points at the cache entry. Its out of date. */
l = headers1 (t->boundname, hdrscan);
c->time = t->time;
c->age = 0;
c->includes = list_copy (0, l);
c->hdrscan = list_copy(0, hdrscan);
return l;
}
#endif

11
jam/hcache.h Normal file
View File

@@ -0,0 +1,11 @@
/*
* This file is not part of Jam
*/
/*
* hcache.h - handle #includes in source files
*/
void hcache_init(void);
void hcache_done(void);
LIST *hcache(TARGET *t, LIST *hdrscan);

147
jam/headers.c Normal file
View File

@@ -0,0 +1,147 @@
/*
* Copyright 1993, 2000 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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) <target> : <include files> ;
*
* 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.
* 03/02/02 (seiwald) - rules can be invoked via variable names
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
* 12/09/02 (seiwald) - push regexp creation down to headers1().
*/
# 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"
#ifdef OPT_HEADER_CACHE_EXT
# include "hcache.h"
#endif
#ifndef OPT_HEADER_CACHE_EXT
static LIST *headers1( const char *file, LIST *hdrscan );
#endif
/*
* headers() - scan a target for include files and call HDRRULE
*/
# define MAXINC 10
void
headers( TARGET *t )
{
LIST *hdrscan;
LIST *hdrrule;
LIST *hdrcache;
LOL lol;
if( !( hdrscan = var_get( "HDRSCAN" ) ) ||
!( hdrrule = var_get( "HDRRULE" ) ) )
return;
/* Doctor up call to HDRRULE rule */
/* Call headers1() to get LIST of included files. */
if( DEBUG_HEADER )
printf( "header scan %s\n", t->name );
lol_init( &lol );
lol_add( &lol, list_new( L0, t->name, 1 ) );
#ifdef OPT_HEADER_CACHE_EXT
lol_add( &lol, hcache( t, hdrscan ) );
#else
lol_add( &lol, headers1( t->boundname, hdrscan ) );
#endif
if( lol_get( &lol, 1 ) )
{
int jmp = JMP_NONE;
list_free( evaluate_rule( hdrrule->string, &lol, L0, &jmp ) );
}
/* Clean up */
lol_free( &lol );
}
/*
* headers1() - using regexp, scan a file and build include LIST
*/
#ifdef OPT_HEADER_CACHE_EXT
LIST *
#else
static LIST *
#endif
headers1(
const char *file,
LIST *hdrscan )
{
FILE *f;
int i;
int rec = 0;
LIST *result = 0;
regexp *re[ MAXINC ];
char buf[ 1024 ];
if( !( f = fopen( file, "r" ) ) )
return result;
while( rec < MAXINC && hdrscan )
{
re[rec++] = regcomp( hdrscan->string );
hdrscan = list_next( hdrscan );
}
while( fgets( buf, sizeof( buf ), f ) )
{
for( i = 0; i < rec; i++ )
if( regexec( re[i], buf ) && re[i]->startp[1] )
{
/* Copy and terminate extracted string. */
char buf2[ MAXSYM ];
int l = re[i]->endp[1] - re[i]->startp[1];
memcpy( buf2, re[i]->startp[1], l );
buf2[ l ] = 0;
result = list_new( result, buf2, 0 );
if( DEBUG_HEADER )
printf( "header found: %s\n", buf2 );
}
}
while( rec )
free( (char *)re[--rec] );
fclose( f );
return result;
}

15
jam/headers.h Normal file
View File

@@ -0,0 +1,15 @@
/*
* 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 );
#ifdef OPT_HEADER_CACHE_EXT
LIST *headers1( const char *file, LIST *hdrscan );
#endif

440
jam/jam.c Normal file
View File

@@ -0,0 +1,440 @@
/*
* /+\
* +\ 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
* 01/21/02 (seiwald) - new -q to quit quickly on build failure
* 03/16/02 (seiwald) - support for -g (reorder builds by source time)
* 09/19/02 (seiwald) - new -d displays
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
*/
# 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 "jcache.h"
# include "rules.h"
# include "newstr.h"
# include "scan.h"
# include "timestamp.h"
# include "make.h"
/* Macintosh is "special" */
# ifdef OS_MAC
# include <QuickDraw.h>
# endif
/* And UNIX for this */
# ifdef unix
# include <sys/utsname.h>
# endif
struct globs globs = {
0, /* noexec */
1, /* jobs */
0, /* quitquick */
0, /* newestfirst */
# ifdef OS_MAC
{ 0 }, /* display - suppress actions output */
# else
{ 0, 1 }, /* display actions */
# endif
0 /* output commands, not run them */
} ;
/* Symbols to be defined as true for use in Jambase */
static const 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;
const char *s;
struct option optv[N_OPTS];
const 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 Display (a)actions (c)causes (d)dependencies\n" );
printf( " (m)make tree (x)commands (0-9) debug levels.\n" );
# ifdef OPT_RULE_PROFILING_EXT
printf( " (p)profile rules.\n" );
# endif
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++, DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 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 = atoi( s );
/* First -d, turn off defaults. */
if( !n )
DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 0;
/* n turns on levels 1-n */
/* +n turns on level n */
/* c turns on named display c */
if( i < 0 || i >= DEBUG_MAX )
{
printf( "Invalid debug level '%s'.\n", s );
}
else if( *s == '+' )
{
globs.debug[i] = 1;
}
else if( i ) while( i )
{
globs.debug[i--] = 1;
}
else while( *s ) switch( *s++ )
{
case 'a': DEBUG_MAKE = DEBUG_MAKEQ = 1; break;
case 'c': DEBUG_CAUSES = 1; break;
case 'd': DEBUG_DEPENDS = 1; break;
case 'm': DEBUG_MAKEPROG = 1; break;
case 'x': DEBUG_EXEC = 1; break;
# ifdef OPT_RULE_PROFILING_EXT
case 'p': DEBUG_PROFILE_RULES = 1; break;
# endif
case '0': break;
default: printf( "Invalid debug flag '%c'.\n", s[-1] );
}
}
/* Set JAMDATE first */
{
char buf[ 128 ];
time_t clock;
time( &clock );
strcpy( buf, ctime( &clock ) );
/* Trim newline from date */
if( strlen( buf ) == 25 )
buf[ 24 ] = 0;
var_set( "JAMDATE", list_new( L0, buf, 0 ), VAR_SET );
}
/* And JAMUNAME */
# ifdef unix
{
struct utsname u;
if( uname( &u ) >= 0 )
{
LIST *l = L0;
l = list_new( l, u.machine, 0 );
l = list_new( l, u.version, 0 );
l = list_new( l, u.release, 0 );
l = list_new( l, u.nodename, 0 );
l = list_new( l, u.sysname, 0 );
var_set( "JAMUNAME", l, VAR_SET );
}
}
# endif /* unix */
/*
* Jam defined variables OS, OSPLAT
*/
var_defines( othersyms );
/* load up environment variables */
var_defines( (const char **)use_environ );
#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
/* define the variable JAM_TARGETS containing the targets specified on
the command line */
{
LIST *l = L0;
int i;
char **targets = argv;
int targetCount = argc;
if (targetCount == 0) {
targets = (char**)&all;
targetCount = 1;
}
for (i = 0; i < targetCount; i++)
l = list_new( l, targets[i], 0 );
var_set( "JAM_TARGETS", l, VAR_SET );
}
#endif
/* Load up variables set on command line. */
for( n = 0; s = getoptval( optv, 's', n ); n++ )
{
const char *symv[2];
symv[0] = s;
symv[1] = 0;
var_defines( symv );
}
/* Initialize built-in rules */
load_builtins();
/* Parse ruleset */
#ifdef OPT_JAMFILE_CACHE_EXT
jcache_init();
#endif
for( n = 0; s = getoptval( optv, 'f', n ); n++ )
parse_file( s );
if( !n )
parse_file( "+" );
#ifdef OPT_JAMFILE_CACHE_EXT
jcache_done();
#endif
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++;
}
#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
/* get value of variable JAM_TARGETS and build the targets */
{
LIST *l = var_get( "JAM_TARGETS" );
int targetCount = list_length(l);
char **targets;
int i;
if (targetCount == 0) {
/* No targets. Nothing to do. */
exit( EXITOK );
}
targets = malloc(targetCount * sizeof(char*));
if (!targets) {
printf( "Memory allocation failed!\n" );
exit( EXITBAD );
}
for (i = 0; i < targetCount; i++) {
targets[i] = (char*)l->string;
l = l->next;
}
argv = targets;
argc = targetCount;
}
#endif
/* Now make target */
if( !argc )
status |= make( 1, &all, anyhow );
else
status |= make( argc, (const char **)argv, anyhow );
/* Widely scattered cleanup */
var_done();
donerules();
donestamps();
donestr();
/* close cmdout */
if( globs.cmdout )
fclose( globs.cmdout );
return status ? EXITBAD : EXITOK;
}

528
jam/jam.h Normal file
View File

@@ -0,0 +1,528 @@
/*
* 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/22/01 (seiwald) - downshift paths on case-insensitive macintosh
* 03/23/01 (seiwald) - VMS C++ changes.
* 10/29/01 (brett) - More IA64 ifdefs for MS.
* 02/18/00 (belmonte)- Support for Cygwin.
* 09/12/00 (seiwald) - OSSYMS split to OSMAJOR/OSMINOR/OSPLAT
* 12/29/00 (seiwald) - OSVER dropped.
* 01/21/02 (seiwald) - new -q to quit quickly on build failure
* 03/16/02 (seiwald) - support for -g (reorder builds by source time)
* 03/20/02 (seiwald) - MINGW porting from Max Blagai
* 08/16/02 (seiwald) - BEOS porting from Ingo Weinhold
* 09/19/02 (seiwald) - new -d displays
* 11/05/02 (seiwald) - OSPLAT now set to sparc on solaris.
*/
/*
* VMS, OPENVMS
*/
# ifdef VMS
# define unlink remove
# include <types.h>
# include <file.h>
# include <stat.h>
# include <stdio.h>
# include <ctype.h>
# include <stdlib.h>
# include <signal.h>
# include <string.h>
# include <time.h>
# include <unixlib.h>
# 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 <fcntl.h>
# include <stdlib.h>
# include <stdio.h>
# include <ctype.h>
# include <malloc.h>
# include <memory.h>
# include <signal.h>
# include <string.h>
# include <time.h>
# 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 <fcntl.h>
# include <stdlib.h>
# include <stdio.h>
# include <ctype.h>
# include <malloc.h>
# include <memory.h>
# include <signal.h>
# include <string.h>
# include <time.h>
# 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 <fcntl.h>
# include <stdlib.h>
# include <stdio.h>
# include <ctype.h>
# include <malloc.h>
# include <signal.h>
# include <string.h>
# include <time.h>
# 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 <time.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <ctype.h>
# 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 <sys/types.h>
# include <sys/stat.h>
# ifndef OS_MPEIX
# include <sys/file.h>
# endif
# include <fcntl.h>
# include <stdio.h>
# include <ctype.h>
# include <signal.h>
# include <string.h>
# include <time.h>
# include <limits.h>
# ifndef OS_QNX
# include <memory.h>
# endif
# ifndef OS_ULTRIX
# include <stdlib.h>
# endif
# if !defined(OS_BSDI) && \
!defined(OS_FREEBSD) && \
!defined(OS_NEXT) && \
!defined(OS_MACHTEN) && \
!defined(OS_MACOSX) && \
!defined(OS_RHAPSODY) && \
!defined(OS_MVS)
# include <malloc.h>
# endif
# endif
/*
* OSPLAT definitions - suppressed when it's a one-of-a-kind
*/
# if defined( _M_PPC ) || \
defined( PPC ) || \
defined( ppc ) || \
defined( __powerpc__ ) || \
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_OS2 ) && \
!defined( OS_AS400 )
# define OSPLAT "OSPLAT=X86"
# endif
# endif
# ifdef __sparc__
# if !defined( OS_SUNOS )
# 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 20480 /* 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 15
/* Redefine DEBUG_MAX, if rule profiling support shall be compiled in. */
# ifdef OPT_RULE_PROFILING_EXT
# undef DEBUG_MAX
# define DEBUG_MAX 16
# endif
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 ] ) /* -da show actions when executed */
# define DEBUG_MAKEPROG ( globs.debug[ 3 ] ) /* -dm show progress of make0 */
# 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 */
# define DEBUG_MAKEQ ( globs.debug[ 11 ] ) /* -da show even quiet actions */
# define DEBUG_EXEC ( globs.debug[ 12 ] ) /* -dx show text of actions */
# define DEBUG_DEPENDS ( globs.debug[ 13 ] ) /* -dd show dependency graph */
# define DEBUG_CAUSES ( globs.debug[ 14 ] ) /* -dc show dependency graph */
# ifdef OPT_RULE_PROFILING_EXT
# define DEBUG_PROFILE_RULES ( globs.debug[ 15 ] ) /* -dp profile rules */
# endif

1317
jam/jambase.c Normal file

File diff suppressed because it is too large Load Diff

15
jam/jambase.h Normal file
View File

@@ -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[];

1411
jam/jamgram.c Normal file

File diff suppressed because it is too large Load Diff

54
jam/jamgram.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef YYSTYPE
#define YYSTYPE int
#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 BREAK_t 276
#define CASE_t 277
#define CONTINUE_t 278
#define DEFAULT_t 279
#define ELSE_t 280
#define EXISTING_t 281
#define FOR_t 282
#define IF_t 283
#define IGNORE_t 284
#define IN_t 285
#define INCLUDE_t 286
#define JUMPTOEOF_t 287
#define LOCAL_t 288
#define MAXLINE_t 289
#define ON_t 290
#define PIECEMEAL_t 291
#define QUIETLY_t 292
#define RETURN_t 293
#define RULE_t 294
#define SWITCH_t 295
#define TOGETHER_t 296
#define UPDATED_t 297
#define WHILE_t 298
#define _LBRACE_t 299
#define _BAR_t 300
#define _BARBAR_t 301
#define _RBRACE_t 302
#define ARG 303
#define STRING 304
extern YYSTYPE yylval;

358
jam/jamgram.y Normal file
View File

@@ -0,0 +1,358 @@
%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 BREAK_t
%token CASE_t
%token CONTINUE_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 JUMPTOEOF_t
%token LOCAL_t
%token MAXLINE_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 return LIST *.
* 01/22/01 (seiwald) - replace evaluate_if() with compile_eval()
* 01/24/01 (seiwald) - 'while' statement
* 03/23/01 (seiwald) - "[ on target rule ]" support
* 02/27/02 (seiwald) - un-break "expr : arg in list" syntax
* 03/02/02 (seiwald) - rules can be invoked via variable names
* 03/12/02 (seiwald) - set YYMAXDEPTH for big, right-recursive rules
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 06/21/02 (seiwald) - support for named parameters
* 10/22/02 (seiwald) - working return/break/continue statements
*/
%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 "variable.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 *, int *))0
# define P0 (PARSE *)0
# define S0 (char *)0
# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 )
# define pbreak( l,f ) parse_make( compile_break,l,P0,P0,S0,S0,f )
# 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,l,r ) parse_make( compile_setcomp,l,r,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 ); }
| JUMPTOEOF_t list _SEMIC_t
{ $$.parse = pbreak( $2.parse, JMP_EOF ); }
| 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 ); }
| BREAK_t list _SEMIC_t
{ $$.parse = pbreak( $2.parse, JMP_BREAK ); }
| CONTINUE_t list _SEMIC_t
{ $$.parse = pbreak( $2.parse, JMP_CONTINUE ); }
| RETURN_t list _SEMIC_t
{ $$.parse = pbreak( $2.parse, JMP_RETURN ); }
| 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 params _LBRACE_t block _RBRACE_t
{ $$.parse = psetc( $2.string, $3.parse, $5.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 = VAR_SET; }
| _PLUS_EQUALS_t
{ $$.number = VAR_APPEND; }
| _QUESTION_EQUALS_t
{ $$.number = VAR_DEFAULT; }
| DEFAULT_t _EQUALS_t
{ $$.number = VAR_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 ); }
;
/*
* params - optional parameter names to rule definition
* right-recursive rule so that params can be added in order.
*/
params : /* empty */
{ $$.parse = P0; }
| ARG _COLON_t params
{ $$.parse = psnode( $1.string, $3.parse ); }
| ARG
{ $$.parse = psnode( $1.string, P0 ); }
;
/*
* 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; }
| MAXLINE_t ARG
{ $$.number = atoi( $2.string ) * RULE_MAXLINE; }
;
/*
* bindlist - list of variable to bind for an action
*/
bindlist : /* empty */
{ $$.parse = pnull(); }
| BIND_t list
{ $$.parse = $2.parse; }
;

312
jam/jamgram.yy Normal file
View File

@@ -0,0 +1,312 @@
/*
* 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 return LIST *.
* 01/22/01 (seiwald) - replace evaluate_if() with compile_eval()
* 01/24/01 (seiwald) - 'while' statement
* 03/23/01 (seiwald) - "[ on target rule ]" support
* 02/27/02 (seiwald) - un-break "expr : arg in list" syntax
* 03/02/02 (seiwald) - rules can be invoked via variable names
* 03/12/02 (seiwald) - set YYMAXDEPTH for big, right-recursive rules
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 06/21/02 (seiwald) - support for named parameters
* 10/22/02 (seiwald) - working return/break/continue statements
*/
%token ARG STRING
%left `||` `|`
%left `&&` `&`
%left `=` `!=` `in`
%left `<` `<=` `>` `>=`
%left `!`
%{
#include "jam.h"
#include "lists.h"
#include "variable.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 *, int *))0
# define P0 (PARSE *)0
# define S0 (char *)0
# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 )
# define pbreak( l,f ) parse_make( compile_break,l,P0,P0,S0,S0,f )
# 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,l,r ) parse_make( compile_setcomp,l,r,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 ); }
| `jumptoeof` list `;`
{ $$.parse = pbreak( $2.parse, JMP_EOF ); }
| 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 ); }
| `break` list `;`
{ $$.parse = pbreak( $2.parse, JMP_BREAK ); }
| `continue` list `;`
{ $$.parse = pbreak( $2.parse, JMP_CONTINUE ); }
| `return` list `;`
{ $$.parse = pbreak( $2.parse, JMP_RETURN ); }
| `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 params `{` block `}`
{ $$.parse = psetc( $2.string, $3.parse, $5.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 = VAR_SET; }
| `+=`
{ $$.number = VAR_APPEND; }
| `?=`
{ $$.number = VAR_DEFAULT; }
| `default` `=`
{ $$.number = VAR_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 ); }
;
/*
* params - optional parameter names to rule definition
* right-recursive rule so that params can be added in order.
*/
params : /* empty */
{ $$.parse = P0; }
| ARG `:` params
{ $$.parse = psnode( $1.string, $3.parse ); }
| ARG
{ $$.parse = psnode( $1.string, P0 ); }
;
/*
* 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; }
| `maxline` ARG
{ $$.number = atoi( $2.string ) * RULE_MAXLINE; }
;
/*
* bindlist - list of variable to bind for an action
*/
bindlist : /* empty */
{ $$.parse = pnull(); }
| `bind` list
{ $$.parse = $2.parse; }
;

46
jam/jamgramtab.h Normal file
View File

@@ -0,0 +1,46 @@
{ "!", _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 },
{ "break", BREAK_t },
{ "case", CASE_t },
{ "continue", CONTINUE_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 },
{ "jumptoeof", JUMPTOEOF_t },
{ "local", LOCAL_t },
{ "maxline", MAXLINE_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 },

737
jam/jcache.c Normal file
View File

@@ -0,0 +1,737 @@
// jcache.c
#ifdef OPT_JAMFILE_CACHE_EXT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jam.h"
#include "jcache.h"
#include "filesys.h"
#include "hash.h"
#include "lists.h"
#include "newstr.h"
#include "pathsys.h"
#include "parse.h"
#include "rules.h"
#include "search.h"
#include "variable.h"
///////////////
// string_list
//
typedef struct string_list {
char** strings; // null terminated array of strings
int count; // number of strings in the array, not counting the
// terminating null
int capacity; // current capacity of the string array
int block_size; // granularity (number of entries) to be used for
// resizing the array
} string_list;
// string list prototypes
static string_list* new_string_list(int block_size);
static void delete_string_list(string_list* list);
static int resize_string_list(string_list *list, int size);
static int push_string(string_list *list, char *string);
static char* pop_string(string_list *list);
// file_read_line
/*! \brief Reads a line from the supplied file and writes it to the supplied
buffer.
If the line end in a LF, it is chopped off.
\param file The file.
\param value The pointer to where the read value shall be written.
\return \c ~0, if a number could be read, 0 otherwise.
*/
static
int
file_read_line(FILE *file, char *buffer, int bufferSize)
{
int len;
if (!fgets(buffer, bufferSize, file))
return 0;
len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
return 1;
}
// file_read_line_long
/*! \brief Reads a line from the supplied file and interprets it as long value.
This is almost equivalent to \code fscanf(file, "%ld", value) \endcode,
save that fscanf() seems to eat all following LFs, while this function
only reads one.
\param file The file.
\param value The pointer to where the read value shall be written.
\return \c ~0, if a number could be read, 0 otherwise.
*/
static
int
file_read_line_long(FILE *file, long *value)
{
char buffer[32];
int result;
result = file_read_line(file, buffer, sizeof(buffer));
if (!result)
return result;
if (sscanf(buffer, "%ld\n", value) != 1)
return 0;
return 1;
}
// new_string_list
/*! \brief Creates a new string_list.
\param block_size Granularity (number of entries) to be used for
resizing the string array.
\return Pointer to the newly allocated string_list, or 0 when out of
memory.
*/
static
string_list*
new_string_list(int block_size)
{
string_list *list = (string_list*)malloc(sizeof(string_list));
if (list) {
list->strings = 0;
list->count = 0;
list->capacity = 0;
if (block_size <= 0)
block_size = 5;
list->block_size = block_size;
if (!resize_string_list(list, 0)) {
free(list);
list = 0;
}
}
return list;
}
// delete_string_list
/*! \brief Deletes a string_list formerly allocated with new_string_list.
All strings the list contains are freed as well.
\param list The string_list to be deleted.
*/
static
void
delete_string_list(string_list* list)
{
if (list) {
if (list->strings) {
int i = 0;
for (i = 0; i < list->count; i++) {
if (list->strings[i])
free(list->strings[i]);
}
free(list->strings);
}
free(list);
}
}
// resize_string_list
/*! \brief Resizes the string array of a string_list.
\note This functions is for internal use only.
\param list The string_list to be resized.
\param size Number of entries the list shall be able to contain.
\return \c !0, if everything went fine, 0, if an error occured (out of
memory).
*/
static
int
resize_string_list(string_list *list, int size)
{
int result = 0;
if (list) {
// capacity must be at least size + 1 and a multiple of the block size
int newCapacity = (size + list->block_size)
/ list->block_size * list->block_size;
if (newCapacity == list->capacity)
result = !0;
else {
char** newStrings = (char**)realloc(list->strings,
newCapacity * sizeof(char*));
if (newStrings) {
result = !0;
list->strings = newStrings;
list->capacity = newCapacity;
}
}
}
return result;
}
// push_string
/*! \brief Appends a string to a string_list.
The list's string array is resized, if necessary and null terminated.
\param list The string_list.
\param string The string to be appended.
\return \c !0, if everything went fine, 0, if an error occured (out of
memory).
*/
static
int
push_string(string_list *list, char *string)
{
int result = 0;
if (list) {
result = resize_string_list(list, list->count + 1);
if (result) {
list->strings[list->count] = string;
list->count++;
list->strings[list->count] = 0; // null terminate
}
}
return result;
}
// pop_string
/*! \brief Removes the last string from a string_list.
The list's string array is resized, if necessary and null terminated.
The caller takes over ownership of the removed string and is responsible
for freeing it.
\param list The string_list.
\return The removed string, if everything went fine, 0 otherwise.
*/
static
char*
pop_string(string_list *list)
{
char* string = 0;
if (list && list->count > 0) {
list->count--;
string = list->strings[list->count];
list->strings[list->count] = 0;
resize_string_list(list, list->count);
}
return string;
}
///////////////////
// jamfile caching
//
// the jamfile cache
typedef struct jamfile_cache {
struct hash* entries; // hash table of jcache_entrys
string_list* filenames; // entry filenames
char* cache_file; // name of the cache file
} jamfile_cache;
// a cache entry for an include file
typedef struct jcache_entry {
char* filename; // name of the file
time_t time; // time stamp of the file
string_list* strings; // contents of the file
int used; // whether this cache entry has been used
} jcache_entry;
// pointer to the jamfile cache
static jamfile_cache* jamfileCache = 0;
// jamfile cache prototypes
static jamfile_cache* new_jamfile_cache(void);
static void delete_jamfile_cache(jamfile_cache* cache);
static int init_jcache_entry(jcache_entry* entry, char *filename, time_t time,
int used);
static void cleanup_jcache_entry(jcache_entry* entry);
static int add_jcache_entry(jamfile_cache* cache, jcache_entry* entry);
static jcache_entry* find_jcache_entry(jamfile_cache* cache, char* filename);
static string_list* read_file(const char *filename, string_list* list);
static int read_jcache(jamfile_cache* cache, char* filename);
static int write_jcache(jamfile_cache* cache);
static char* jcache_name(void);
static jamfile_cache* get_jcache(void);
// new_jamfile_cache
/*! \brief Creates a new jamfile_cache.
\return A pointer to the newly allocated jamfile_cache, or 0, if out of
memory.
*/
static
jamfile_cache*
new_jamfile_cache(void)
{
jamfile_cache *cache = (jamfile_cache*)malloc(sizeof(jamfile_cache));
if (cache) {
cache->entries = hashinit(sizeof(jcache_entry), "jcache");
cache->filenames = new_string_list(100);
cache->cache_file = 0;
if (!cache->entries || !cache->filenames) {
delete_jamfile_cache(cache);
cache = 0;
}
}
return cache;
}
// delete_jamfile_cache
/*! \brief Deletes a jamfile_cache formerly allocated with new_jamfile_cache.
\param cache The jamfile_cache to be deleted.
*/
static
void
delete_jamfile_cache(jamfile_cache* cache)
{
if (cache) {
if (cache->entries)
hashdone(cache->entries);
delete_string_list(cache->filenames);
free(cache->cache_file);
}
}
// init_jcache_entry
/*! \brief Initializes a pre-allocated jcache_entry.
\param entry The jcache_entry to be initialized.
\param filename The name of the include file to be associated with the
entry.
\param time The time stamp of the include file to be associated with the
entry.
\param used Whether or not the entry shall be marked used.
\return \c !0, if everything went fine, 0, if an error occured (out of
memory).
*/
static
int
init_jcache_entry(jcache_entry* entry, char *filename, time_t time, int used)
{
int result = 0;
if (entry) {
result = !0;
entry->filename = (char*)malloc(strlen(filename) + 1);
if (entry->filename)
strcpy(entry->filename, filename);
entry->time = time;
entry->strings = new_string_list(100);
entry->used = used;
// cleanup on error
if (!entry->filename || !entry->strings) {
cleanup_jcache_entry(entry);
result = 0;
}
}
return result;
}
// cleanup_jcache_entry
/*! \brief De-initializes a jcache_entry.
All resources associated with the entry, save the memory for the entry
structure itself, are freed.
\param entry The jcache_entry to be de-initialized.
*/
static
void
cleanup_jcache_entry(jcache_entry* entry)
{
if (entry) {
if (entry->filename)
free(entry->filename);
if (entry->strings)
delete_string_list(entry->strings);
}
}
// add_jcache_entry
/*! \brief Adds a jcache_entry to a jamfile_cache.
\param cache The jamfile_cache.
\param entry The jcache_entry to be added.
\return \c !0, if everything went fine, 0, if an error occured (out of
memory).
*/
static
int
add_jcache_entry(jamfile_cache* cache, jcache_entry* entry)
{
int result = 0;
if (cache && entry) {
result = push_string(cache->filenames, entry->filename);
if (result) {
result = hashenter(cache->entries, (HASHDATA**)&entry);
if (!result)
pop_string(cache->filenames);
}
}
return result;
}
// find_jcache_entry
/*! \brief Looks up jcache_entry in a jamfile_cache.
\param cache The jamfile_cache.
\param filename The name of the include file for whose jcache_entry shall
be retrieved.
\return A pointer to the found jcache_entry, or 0, if the cache does not
contain an entry for the specified filename.
*/
static
jcache_entry*
find_jcache_entry(jamfile_cache* cache, char* filename)
{
jcache_entry _entry;
jcache_entry* entry = &_entry;
entry->filename = filename;
if (!hashcheck(cache->entries, (HASHDATA**)&entry))
entry = 0;
return entry;
}
// read_file
/*! \brief Reads in a text file and returns its contents as a string_list.
The function strips leading white spaces from each line and omits empty
or comment lines.
If a string_list is supplied via \a list the file's content is appended
to this list, otherwise a new string_list is allocated.
\param filename The name of the file to be read in.
\param list Pointer to a pre-allocated string_list. May be 0.
\return A pointer to the string_list containing the contents of the file,
or 0, if an error occured.
*/
static
string_list*
read_file(const char *filename, string_list* list)
{
int result = 0;
FILE *file = 0;
string_list *allocatedList = 0;
// open file
if ((file = fopen(filename, "r")) != 0
&& (list || (list = allocatedList = new_string_list(100)) != 0)) {
// read the file
char buffer[513];
result = !0;
while (result && fgets(buffer, sizeof(buffer) - 1, file)) {
char* line = buffer;
int len = 0;
char *string = 0;
// skip leading white spaces
while (*line == ' ' || *line == '\t' || *line == '\n')
line++;
// make empty and comment lines simple new-line lines
if (!*line || *line == '#') {
line[0] = '\n';
line[1] = '\0';
}
len = strlen(line);
// make sure, the line ends in a LF
if (line[len - 1] != '\n') {
line[len] = '\n';
len++;
line[len] = '\0';
}
// copy it
string = (char*)malloc(len + 1);
if (string) {
strcpy(string, line);
result = push_string(list, string);
} else
result = 0;
}
// close the file
fclose(file);
} else
perror(filename);
// cleanup on error
if (!result) {
delete_string_list(allocatedList);
list = 0;
}
return list;
}
// read_jcache
/*! \brief Reads a jamfile_cache from a file.
Only cache entries for files, that don't have an entry in \a cache yet, are
added to it.
\param cache The jamfile_cache the cache stored in the file shall be added
to.
\param filename The name of the file containing the cache to be read.
\return \c !0, if everything went fine, 0, if an error occured.
*/
static
int
read_jcache(jamfile_cache* cache, char* filename)
{
int result = 0;
if (cache && filename) {
// open file
FILE *file = 0;
cache->cache_file = filename;
if ((file = fopen(filename, "r")) != 0) {
// read the file
char buffer[512];
long count = 0;
int i;
result = !0;
// read number of cache entries
result = file_read_line_long(file, &count);
// read the cache entries
for (i = 0; result && i < count; i++) {
char entryname[PATH_MAX];
long lineCount = 0;
time_t time = 0;
jcache_entry entry = { 0, 0, 0 };
// entry name, time and line count
if (file_read_line(file, entryname, sizeof(entryname))
&& strlen(entryname) > 0
&& file_read_line_long(file, &time)
&& file_read_line_long(file, &lineCount)
&& (init_jcache_entry(&entry, entryname, time, 0)) != 0) {
// read the lines
int j;
for (j = 0; result && j < lineCount; j++) {
if (fgets(buffer, sizeof(buffer), file)) {
char *string = (char*)malloc(strlen(buffer) + 1);
if (string) {
strcpy(string, buffer);
result = push_string(entry.strings, string);
} else
result = 0;
} else {
fprintf(stderr, "warning: Invalid jamfile cache: "
"Unexpected end of file.\n");
result = 0;
}
}
} else {
fprintf(stderr, "warning: Invalid jamfile cache: "
"Failed to read file info.\n");
result = 0;
}
if (result) {
// add only, if there's no entry for that file yet
if (find_jcache_entry(cache, entry.filename))
cleanup_jcache_entry(&entry);
else
result = add_jcache_entry(cache, &entry);
}
// cleanup on error
if (!result)
cleanup_jcache_entry(&entry);
}
// close the file
fclose(file);
} // else: Couldn't open cache file. Don't worry.
}
return result;
}
// write_jcache
/*! \brief Writes a jamfile_cache into a file.
\param cache The jamfile_cache that shall be stored in the file.
\param filename The name of the file the cache shall be stored in.
\return \c !0, if everything went fine, 0, if an error occured.
*/
static
int
write_jcache(jamfile_cache* cache)
{
int result = 0;
if (cache && cache->cache_file) {
// open file
FILE *file = 0;
if ((file = fopen(cache->cache_file, "w")) != 0) {
int count = cache->filenames->count;
int i;
// write number of cache entries
result = (fprintf(file, "%d\n", count) > 0);
// write the cache entries
for (i = 0; result && i < count; i++) {
char* entryname = cache->filenames->strings[i];
jcache_entry* entry = find_jcache_entry(cache, entryname);
// entry name, time and line count
if (!entry) {
result = 0;
} else if (!entry->strings || !entry->used) {
// just skip the entry, if it is not loaded or not used
} else if (fprintf(file, "%s\n", entryname) > 0
&& (fprintf(file, "%ld\n", entry->time) > 0)
&& (fprintf(file, "%d\n", entry->strings->count) > 0)) {
int j;
// the lines
for (j = 0; result && j < entry->strings->count; j++) {
result = (fprintf(file,
entry->strings->strings[j]) > 0);
}
} else
result = 0;
}
// close the file
fclose(file);
}
}
return result;
}
// jcache_name
/*! \brief Returns the name of the file containing the global jamfile_cache.
The returned filename is the path to the target stored in the jam variable
\c JCACHEFILE. The string does not need to be freed.
\return A pointer to the jamfile cache file, or 0, if the jam variable is
not set yet, or an error occured.
*/
static
char*
jcache_name(void)
{
static char* name = 0;
if (!name) {
LIST *jcachevar = var_get("JCACHEFILE");
if (jcachevar) {
TARGET *t = bindtarget( jcachevar->string );
pushsettings( t->settings );
t->boundname = search( t->name, &t->time );
popsettings( t->settings );
if (t->boundname) {
name = (char*)copystr(t->boundname);
}
}
}
return name;
}
// get_jcache
/*! \brief Returns a pointer to the global jamfile_cache.
The cache is being lazy-allocated.
\return A pointer to the global jamfile_cache, or 0, if an error occured.
*/
static
jamfile_cache*
get_jcache(void)
{
if (!jamfileCache)
jamfileCache = new_jamfile_cache();
if (jamfileCache && !jamfileCache->cache_file) {
char* filename = jcache_name();
if (filename)
read_jcache(jamfileCache, filename);
}
return jamfileCache;
}
// jcache_init
/*! \brief To be called before using the global jamfile_cache.
Does nothing currently. The global jamfile_cache is lazy-allocated by
get_jcache().
*/
void
jcache_init(void)
{
}
// jcache_done
/*! \brief To be called when done with the global jamfile_cache.
Writes the cache to the specified cache file.
*/
void
jcache_done(void)
{
jamfile_cache* cache = get_jcache();
if (cache) {
write_jcache(cache);
delete_jamfile_cache(cache);
jamfileCache = 0;
}
}
// jcache
/*! \brief Returns the contents of an include file as a null terminated string
array.
If the file is cached and the respective entry is not obsolete, the cached
string array is returned, otherwise the file is read in.
The caller must not free the returned string array or any of the contained
strings.
\param filename The name of the include file.
\return A pointer to a null terminated string array representing the
contents of the specified file, or 0, if an error occured.
*/
char**
jcache(char *_filename)
{
char** strings = 0;
jamfile_cache* cache = get_jcache();
time_t time;
// normalize the filename
char _normalizedPath[PATH_MAX];
char *filename = normalize_path(_filename, _normalizedPath,
sizeof(_normalizedPath));
if (!filename)
filename = _filename;
// get file time
if (!cache)
return 0;
if (file_time(filename, &time) == 0) {
// lookup file in cache
jcache_entry* entry = find_jcache_entry(cache, filename);
if (entry) {
// in cache
entry->used = !0;
if (entry->time == time && entry->strings) {
// up to date
strings = entry->strings->strings;
} else {
// obsolete
delete_string_list(entry->strings);
entry->strings = read_file(filename, 0);
entry->time = time;
strings = entry->strings->strings;
}
} else {
// not in cache
jcache_entry newEntry;
entry = &newEntry;
init_jcache_entry(entry, filename, time, !0);
if (read_file(filename, entry->strings)) {
if (add_jcache_entry(cache, entry))
strings = entry->strings->strings;
}
// cleanup on error
if (!strings)
cleanup_jcache_entry(entry);
}
} else
perror(filename);
return strings;
}
#endif // OPT_JAMFILE_CACHE_EXT

10
jam/jcache.h Normal file
View File

@@ -0,0 +1,10 @@
// jcache.h
#ifndef _JCACHE_H
#define _JCACHE_H
void jcache_init(void);
void jcache_done(void);
char** jcache(char* filename);
#endif // _JCACHE_H

289
jam/lists.c Normal file
View File

@@ -0,0 +1,289 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
* 12/09/02 (seiwald) - new list_printq() for writing lists to Jambase
*/
# include "jam.h"
# include "newstr.h"
# include "lists.h"
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,
const char *string,
int copy )
{
LIST *l;
if( DEBUG_LISTS )
printf( "list > %s <\n", string );
/* Copy/newstr as needed */
string = copy ? copystr( string ) : newstr( 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, nl->string, 1 );
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, l->string, 1 );
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_printq() - print a list of safely quoted strings to a file
*/
void
list_printq( FILE *out, LIST *l )
{
/* Dump each word, enclosed in "s */
/* Suitable for Jambase use. */
for( ; l; l = list_next( l ) )
{
const char *p = l->string;
const char *ep = p + strlen( p );
const char *op = p;
fputc( '\n', out );
fputc( '\t', out );
fputc( '"', out );
/* Any embedded "'s? Escape them */
while( p = (char *)memchr( op, '"', ep - op ) )
{
fwrite( op, p - op, 1, out );
fputc( '\\', out );
fputc( '"', out );
op = p + 1;
}
/* Write remainder */
fwrite( op, ep - op, 1, out );
fputc( '"', out );
fputc( ' ', out );
}
}
/*
* 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] );
}
}

84
jam/lists.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* 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_printq() - print a list of safely quoted strings to a file
* 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()
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
* 12/09/02 (seiwald) - new list_printq() for writing lists to Jambase
*/
/*
* LIST - list of strings
*/
typedef struct _list LIST;
struct _list {
LIST *next;
LIST *tail; /* only valid in head node */
const 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, const char *string, int copy );
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 );

534
jam/make.c Normal file
View File

@@ -0,0 +1,534 @@
/*
* 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)
* 07/17/02 (seiwald) - TEMPORARY sources for headers now get built
* 09/19/02 (seiwald) - new -d displays
* 09/23/02 (seiwald) - suppress "...using temp..." in default output
* 09/28/02 (seiwald) - make0() takes parent pointer; new -dc display
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
* 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
* 01/03/03 (seiwald) - T_FATE_NEWER once again gets set with missing parent
* 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
* 04/04/03 (seiwald) - fix INTERNAL node binding to avoid T_BIND_PARENTS
*/
# 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, TARGET *p, int depth,
COUNTS *counts, int anyhow );
static TARGETS *make0sort( TARGETS *c );
static const 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 */
"needtmp", /* T_FATE_NEEDTMP */
"old", /* T_FATE_OUTDATED */
"update", /* T_FATE_UPDATE */
"nofind", /* T_FATE_CANTFIND */
"nomake" /* T_FATE_CANTMAKE */
} ;
static const 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,
const char **targets,
int anyhow )
{
int i;
COUNTS counts[1];
int status = 0; /* 1 if anything fails */
#ifdef OPT_HEADER_CACHE_EXT
hcache_init();
#endif
memset( (char *)counts, 0, sizeof( *counts ) );
for( i = 0; i < n_targets; i++ )
{
TARGET *t = bindtarget( targets[i] );
make0( 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 );
}
#ifdef OPT_HEADER_CACHE_EXT
hcache_done();
#endif
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,
TARGET *p, /* parent */
int depth, /* for display purposes */
COUNTS *counts, /* for reporting */
int anyhow ) /* forcibly touch all (real) targets */
{
TARGETS *c, *d, *incs;
TARGET *ptime = t;
time_t last, leaf, hlast;
int fate;
const char *flag = "";
SETTINGS *s;
/*
* Step 1: initialize
*/
if( DEBUG_MAKEPROG )
printf( "make\t--\t%s%s\n", spaces( depth ), t->name );
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. */
s = copysettings( t->settings );
pushsettings( s );
/* 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;
}
/* INTERNAL, NOTFILE header nodes have the time of their parents */
if( p && t->flags & T_FLAG_INTERNAL )
ptime = p;
/* If temp file doesn't exist but parent does, use parent */
if( p && t->flags & T_FLAG_TEMP &&
t->binding == T_BIND_MISSING &&
p->binding != T_BIND_MISSING )
{
t->binding = T_BIND_PARENTS;
ptime = p;
}
/* Step 2c: If its a file, search for headers. */
if( t->binding == T_BIND_EXISTS )
headers( t );
/* Step 2d: reset "on target" variables */
popsettings( s );
freesettings( s );
/*
* Pause for a little progress reporting
*/
if( DEBUG_MAKEPROG )
{
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 & headers
*/
/* Step 3a: recursively make0() dependents */
for( c = t->depends; c; c = c->next )
{
int internal = t->flags & T_FLAG_INTERNAL;
if( DEBUG_DEPENDS )
printf( "%s \"%s\" : \"%s\" ;\n",
internal ? "Includes" : "Depends",
t->name, c->target->name );
/* Warn about circular deps, except for includes, */
/* which include each other alot. */
if( c->target->fate == T_FATE_INIT )
make0( c->target, ptime, depth + 1, counts, anyhow );
else if( c->target->fate == T_FATE_MAKING && !internal )
printf( "warning: %s depends on itself\n", c->target->name );
}
/* Step 3b: recursively make0() internal includes node */
if( t->includes )
make0( t->includes, p, depth + 1, counts, anyhow );
/* Step 3c: add dependents' includes to our direct dependencies */
incs = 0;
for( c = t->depends; c; c = c->next )
if( c->target->includes )
incs = targetentry( incs, c->target->includes );
t->depends = targetchain( t->depends, incs );
/*
* Step 4: compute time & fate
*/
/* Step 4a: pick up dependents' time and fate */
last = 0;
leaf = 0;
fate = T_FATE_STABLE;
for( c = t->depends; c; c = c->next )
{
/* If LEAVES has been applied, we only heed the timestamps of */
/* the leaf source nodes. */
leaf = max( leaf, c->target->leaf );
if( t->flags & T_FLAG_LEAVES )
{
last = leaf;
continue;
}
last = max( last, c->target->time );
fate = max( fate, c->target->fate );
}
/* Step 4b: pick up included headers time */
/*
* If a header is newer than a temp source that includes it,
* the temp source will need building.
*/
hlast = t->includes ? t->includes->time : 0;
/* Step 4c: handle NOUPDATE oddity */
/*
* If a NOUPDATE file exists, make dependents eternally old.
* Don't inherit our fate from our dependents. Decide fate
* based only upon other flags and our binding (done later).
*/
if( t->flags & T_FLAG_NOUPDATE )
{
last = 0;
t->time = 0;
fate = T_FATE_STABLE;
}
/* Step 4d: 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 temp's headers newer than parent, make temp.
If deliberately touched, make it.
If up-to-date temp file present, use it.
If target newer than non-notfile parent, mark target newer.
Otherwise, stable!
Note this block runs from least to most stable:
as we make it further down the list, the target's
fate is getting stabler.
*/
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 > p->time )
{
fate = T_FATE_NEEDTMP;
}
else if( t->binding == T_BIND_PARENTS && hlast > p->time )
{
fate = T_FATE_NEEDTMP;
}
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 && p &&
p->binding != T_BIND_UNBOUND && t->time > p->time )
{
fate = T_FATE_NEWER;
}
else
{
fate = T_FATE_STABLE;
}
/* Step 4e: 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->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 4f: 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 5: sort dependents by their update time.
*/
if( globs.newestfirst )
t->depends = make0sort( t->depends );
/*
* Step 6: a little harmless tabulating for tracing purposes
*/
/* Don't count or report interal includes nodes. */
if( t->flags & T_FLAG_INTERNAL )
return;
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 && p && t->time > p->time )
flag = "*";
if( DEBUG_MAKEPROG )
printf( "made%s\t%s\t%s%s\n",
flag, target_fate[ t->fate ],
spaces( depth ), t->name );
if( DEBUG_CAUSES &&
t->fate >= T_FATE_NEWER &&
t->fate <= T_FATE_MISSING )
printf( "%s %s\n", target_fate[ t->fate ], 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;
}

14
jam/make.h Normal file
View File

@@ -0,0 +1,14 @@
/*
* 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
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
int make( int n_targets, const char **targets, int anyhow );
int make1( TARGET *t );

671
jam/make1.c Normal file
View File

@@ -0,0 +1,671 @@
/*
* 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, 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.
* 02/07/01 (seiwald) - Fix jam -d0 return status.
* 01/21/02 (seiwald) - new -q to quit quickly on build failure
* 02/28/02 (seiwald) - don't delete 'actions updated' targets on failure
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 07/17/02 (seiwald) - TEMPORARY sources for headers now get built
* 09/23/02 (seiwald) - "...using temp..." only displayed on -da now.
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
*/
# 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,
int *missingTargets );
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;
/* 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( c = t->depends; 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;
const 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( c = t->depends; 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_MAKEQ )
printf( "...using %s...\n", t->name );
break;
case T_FATE_TOUCHED:
case T_FATE_MISSING:
case T_FATE_NEEDTMP:
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, maxline;
int missingTargets = 0;
int ruleFlags = rule->flags;
/* 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 , &missingTargets );
/* If a target is missing use all sources. */
if (missingTargets)
ruleFlags &= ~RULE_UPDATED;
ns = make1list( L0, a0->action->sources, ruleFlags, NULL );
if( ruleFlags & RULE_TOGETHER )
for( a1 = a0->next; a1; a1 = a1->next )
if( a1->action->rule == rule && !a1->action->running )
{
ns = make1list( ns, a1->action->sources, ruleFlags, NULL );
a1->action->running = 1;
}
/* If doing only updated (or existing) sources, but none have */
/* been updated (or exist), skip this action. */
if( !ns && ( ruleFlags & ( 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.
*
* Max line length is the action specific maxline or, if not
* given or bigger than MAXLINE, MAXLINE.
*/
start = 0;
chunk = length = list_length( ns );
maxline = ruleFlags / RULE_MAXLINE;
maxline = maxline && maxline < MAXLINE ? maxline : MAXLINE;
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 ),
maxline );
if( cmd )
{
/* It fit: chain it up. */
if( !cmds ) cmds = cmd;
else cmds->tail->next = cmd;
cmds->tail = cmd;
start += chunk;
}
else if( ( ruleFlags & 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,
int *missingTargets )
{
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 ( t->binding != T_BIND_EXISTS && missingTargets)
*missingTargets = 1;
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, t->boundname, 1 );
}
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, t->boundname, 1 );
}
/* 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 );
}

125
jam/mkjambase.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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.
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include <stdio.h>
# include <string.h>
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, "const 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;
}

98
jam/newstr.c Normal file
View File

@@ -0,0 +1,98 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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.
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "newstr.h"
# include "hash.h"
typedef const char *STRING;
static struct hash *strhash = 0;
static int strtotal = 0;
/*
* newstr() - return a malloc'ed copy of a string
*/
const char *
newstr( const 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()
*/
const char *
copystr( const char *s )
{
return s;
}
/*
* freestr() - free a string returned by newstr() or copystr()
*/
void
freestr( const char *s )
{
}
/*
* donestr() - free string tables
*/
void
donestr()
{
hashdone( strhash );
if( DEBUG_MEM )
printf( "%dK in strings\n", strtotal / 1024 );
}

16
jam/newstr.h Normal file
View File

@@ -0,0 +1,16 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* newstr.h - string manipulation routines
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
const char *newstr( const char *string );
const char *copystr( const char *s );
void freestr( const char *s );
void donestr();

105
jam/option.c Normal file
View File

@@ -0,0 +1,105 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* option.c - command line option processing
*
* {o >o
* \<>) "Process command line options as defined in <option.h>.
* 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."
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "option.h"
int
getoptions(
int argc,
char **argv,
const 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++ )
{
const 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
*/
const 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;
}

25
jam/option.h Normal file
View File

@@ -0,0 +1,25 @@
/*
* 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."
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
typedef struct option
{
char flag; /* filled in by getoption() */
const char *val; /* set to random address if true */
} option;
# define N_OPTS 256
int getoptions( int argc, char **argv, const char *opts, option *optv );
const char * getoptval( option *optv, char opt, int subopt );

123
jam/parse.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright 1993, 2000 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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 *.
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "scan.h"
# include "newstr.h"
# include "compile.h"
static PARSE *yypsave;
void
parse_file( const 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;
int jmp = 0; /* JMP_NONE */
/* $(<) 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. */
list_free( (*(p->func))( p, &l, &jmp ) );
parse_free( p );
if ( jmp == JMP_EOF )
break;
}
}
void
parse_save( PARSE *p )
{
yypsave = p;
}
PARSE *
parse_make(
LIST *(*func)( PARSE *p, LOL *args, int *jmp ),
PARSE *left,
PARSE *right,
PARSE *third,
const char *string,
const 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 );
}

44
jam/parse.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* 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
*
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
*/
/*
* parse tree node
*/
typedef struct _PARSE PARSE;
struct _PARSE {
LIST *(*func)( PARSE *p, LOL *args, int *jmp );
PARSE *left;
PARSE *right;
PARSE *third;
const char *string;
const char *string1;
int num;
int refs;
} ;
void parse_file( const char *f );
void parse_save( PARSE *p );
PARSE * parse_make(
LIST *(*func)( PARSE *p, LOL *args, int *jmp ),
PARSE *left,
PARSE *right,
PARSE *third,
const char *string,
const char *string1,
int num );
void parse_refer( PARSE *p );
void parse_free( PARSE *p );

5
jam/patchlevel.h Normal file
View File

@@ -0,0 +1,5 @@
/* Keep JAMVERSYM in sync with VERSION. */
/* It can be accessed as $(JAMVERSION) in the Jamfile. */
#define VERSION "2.5-haiku-20060813"
#define JAMVERSYM "JAMVERSION=2.5"

283
jam/pathmac.c Normal file
View File

@@ -0,0 +1,283 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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.
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "pathsys.h"
# ifdef OS_MAC
# define DELIM ':'
/*
* path_parse() - split a file name into dir/base/suffix/member
*/
void
path_parse(
const char *file,
PATHNAME *f )
{
const char *p, *q;
const char *end;
memset( (char *)f, 0, sizeof( *f ) );
/* Look for <grist> */
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(
const 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 */

52
jam/pathsys.h Normal file
View File

@@ -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.
*/
/*
* pathsys.h - PATHNAME struct
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
/*
* PATHNAME - a name of a file, broken into <grist>dir/base/suffix(member)
*
* <grist> 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 {
const 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( const char *file, PATHNAME *f );
void path_parent( PATHNAME *f );
char *normalize_path(const char *path, char *buffer, size_t bufferSize);

327
jam/pathunix.c Normal file
View File

@@ -0,0 +1,327 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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 off \ handling on UNIX, on by accident
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "pathsys.h"
# ifdef USE_PATHUNIX
/*
* path_parse() - split a file name into dir/base/suffix/member
*/
void
path_parse(
const char *file,
PATHNAME *f )
{
const char *p, *q;
const char *end;
memset( (char *)f, 0, sizeof( *f ) );
/* Look for <grist> */
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;
}
/*
* normalize_path() - normalize a path
*
* It doesn't really generate a unique representation of a path to an entry,
* but at least reduces the number of categories that represent the same
* entry. On error, or if the supplied buffer is too small, NULL is returned.
*/
char *
normalize_path(const char *path, char *buffer, size_t bufferSize)
{
// init cwd
static char _cwd[PATH_MAX];
static char *cwd = 0;
static size_t cwdLen = 0;
int pathLen = (path ? strlen(path) : 0);
int resultLen = 0;
int resolveDotDot = !0;
// init cwd
if (!cwd) {
cwd = getcwd(_cwd, PATH_MAX);
if (cwd)
cwdLen = strlen(cwd);
else
return 0;
}
// check length
if (cwdLen + pathLen + 2 > bufferSize)
return 0;
// construct result
if (pathLen > 0 && path[0] == PATH_DELIM) {
// absolute path: ignore cwd
buffer[0] = PATH_DELIM;
buffer[1] = '\0';
resultLen = 1;
path++;
pathLen--;
} else {
// relative path: copy cwd into result
memcpy(buffer, cwd, cwdLen + 1);
resultLen = cwdLen;
}
// append path componentwise to the result, skipping "." and empty
// components, and chopping off a component per ".."
while (pathLen > 0) {
// find component
char *separator = strchr(path, PATH_DELIM);
const char *component = path;
int componentLen = 0;
if (separator) {
componentLen = separator - path;
pathLen -= componentLen + 1;
path = separator + 1;
} else {
componentLen = pathLen;
path += componentLen;
pathLen = 0;
}
// handle found component
if (componentLen > 0) {
if (componentLen == 1 && component[0] == '.') {
// component is ".": skip
} else if (resolveDotDot && componentLen == 2 && component[0] == '.'
&& component[1] == '.') {
// component is "..": eat the last component of the result
char *lastSeparator = strrchr(buffer, PATH_DELIM);
if (lastSeparator) {
resultLen = lastSeparator - buffer;
if (resultLen == 0) {
// always leave at least the root
buffer[0] = PATH_DELIM;
resultLen = 1;
}
buffer[resultLen] = '\0';
} // else: not good
} else {
// normal component: append
if (resultLen < 1 || buffer[resultLen - 1] != PATH_DELIM)
buffer[resultLen++] = PATH_DELIM;
memcpy(buffer + resultLen, component, componentLen);
resultLen += componentLen;
buffer[resultLen] = '\0';
// After we found the first real path component, we don't
// resolve ".." anymore, as it could be a (sym)link, which
// could break the algorithm.
resolveDotDot = 0;
}
}
}
return buffer;
}
# endif /* unix, NT, OS/2, AmigaOS */

431
jam/pathvms.c Normal file
View File

@@ -0,0 +1,431 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "pathsys.h"
# ifdef OS_VMS
# define DEBUG
/*
* path_parse() - split a file name into dir/base/suffix/member
*/
void
path_parse(
const char *file,
PATHNAME *f )
{
const char *p, *q;
const char *end;
memset( (char *)f, 0, sizeof( *f ) );
/* Look for <grist> */
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(
const char *buf,
int len,
struct dirinf *i )
{
const 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( (char *)buf, ':', len ) )
{
i->dev.ptr = (char *)buf;
i->dev.len = p + 1 - buf;
i->dir.ptr = (char *)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 = (char *)buf;
i->dev.len = 0;
i->dir.ptr = (char *)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 */

1325
jam/regexp.c Normal file

File diff suppressed because it is too large Load Diff

29
jam/regexp.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Definitions etc. for regexp(3) routines.
*
* Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
* not the System V one.
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
#define NSUBEXP 10
typedef struct regexp {
const char *startp[NSUBEXP];
const 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( const char *exp );
int regexec( regexp *prog, const char *string );
void regerror( const 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

396
jam/rules.c Normal file
View File

@@ -0,0 +1,396 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* copytarget() - make a new target with the old target's name
* 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
* targetchain() - append two TARGET chains
* actionlist() - append to an ACTION chain
* addsettings() - add a deferred "set" command to a target
* copysettings() - copy a settings list for temp use
* 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)
* 06/21/02 (seiwald) - support for named parameters
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
* 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
* 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "variable.h"
# include "rules.h"
# include "newstr.h"
# include "hash.h"
static struct hash *rulehash = 0;
static struct hash *targethash = 0;
# ifdef OPT_RULE_PROFILING_EXT
static RULE *profiling_rule_list = 0;
# endif
/*
* bindrule() - return pointer to RULE, creating it if necessary
*/
RULE *
bindrule( const 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->params = L0;
r->flags = 0;
# ifdef OPT_RULE_PROFILING_EXT
if ( DEBUG_PROFILE_RULES )
{
r->invocations = 0;
r->invocation_time = 0;
r->next_profiling_rule = profiling_rule_list;
profiling_rule_list = r;
}
# endif
}
return r;
}
/*
* bindtarget() - return pointer to TARGET, creating it if necessary
*/
TARGET *
bindtarget( const char *targetname )
{
TARGET target, *t = &target;
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;
}
/*
* copytarget() - make a new target with the old target's name
*
* Not entered into hash table -- for internal nodes.
*/
TARGET *
copytarget( const TARGET *ot )
{
TARGET *t;
t = (TARGET *)malloc( sizeof( *t ) );
memset( (char *)t, '\0', sizeof( *t ) );
t->name = copystr( ot->name );
t->boundname = t->name;
t->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL;
return t;
}
/*
* touchtarget() - mark a target to simulate being new
*/
void
touchtarget( const 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;
}
/*
* targetchain() - append two TARGET chains
*
* Inputs:
* chain exisitng TARGETS to append to
* target new target to append
*/
TARGETS *
targetchain(
TARGETS *chain,
TARGETS *targets )
{
TARGETS *c;
if( !targets )
return chain;
else if( !chain )
return targets;
chain->tail->next = targets;
chain->tail = targets->tail;
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 setflag,
const 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 switch( setflag )
{
case VAR_SET:
/* Toss old, set new */
list_free( v->value );
v->value = value;
break;
case VAR_APPEND:
/* Append new to old */
v->value = list_append( v->value, value );
break;
case VAR_DEFAULT:
/* Toss new, old already set */
list_free( value );
break;
}
/* Return (new) head of list. */
return head;
}
/*
* copysettings() - copy a settings list for temp use
*
* When target-specific variables are pushed into place with pushsettings(),
* any global variables with the same name are swapped onto the target's
* SETTINGS chain. If that chain gets modified (by using the "on target"
* syntax), popsettings() would wrongly swap those modified values back
* as the new global values.
*
* copysettings() protects the target's SETTINGS chain by providing a
* copy of the chain to pass to pushsettings() and popsettings(), so that
* the target's original SETTINGS chain can be modified using the usual
* "on target" syntax.
*/
SETTINGS *
copysettings( SETTINGS *from )
{
SETTINGS *head = 0, *v;
for( ; from; from = from->next )
{
SETTINGS *v = (SETTINGS *)malloc( sizeof( *v ) );
v->symbol = copystr( from->symbol );
v->value = list_copy( 0, from->value );
v->next = head;
head = v;
}
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()
{
# ifdef OPT_RULE_PROFILING_EXT
if ( DEBUG_PROFILE_RULES )
{
RULE *rule;
printf("# invoked total time (us) rule\n");
printf("--------- --------------- "
"------------------------------------\n");
for (rule = profiling_rule_list;
rule;
rule = rule->next_profiling_rule)
{
printf("%9d %15lld %s\n", rule->invocations,
(long long)rule->invocation_time, rule->name);
}
}
# endif
hashdone( rulehash );
hashdone( targethash );
}

190
jam/rules.h Normal file
View File

@@ -0,0 +1,190 @@
/*
* 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.
* 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
* 06/21/02 (seiwald) - support for named parameters
* 07/17/02 (seiwald) - TEMPORARY sources for headers now get built
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
* 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
* 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
*/
# ifdef OPT_RULE_PROFILING_EXT
# include <stdint.h>
# endif
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 {
const char *name;
PARSE *procedure; /* parse tree from RULE */
const char *actions; /* command string from ACTIONS */
LIST *bindlist; /* variable to bind for actions */
LIST *params; /* bind args to local vars */
int flags; /* modifiers on ACTIONS */
# ifdef OPT_RULE_PROFILING_EXT
int invocations;
int64_t invocation_time;
RULE *next_profiling_rule;
# endif
# 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 */
# define RULE_MAXLINE 0x40 /* cmd specific maxline (last) */
} ;
/* 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;
const 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 {
const char *name;
const 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 */
# define T_FLAG_INTERNAL 0x40 /* internal INCLUDES node */
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 *depends; /* dependencies */
TARGET *includes; /* includes */
time_t time; /* update time */
time_t leaf; /* update time of leaf sources */
char fate; /* make0()'s diagnosis */
# 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_NEEDTMP 7 /* missing temp that must be rebuild */
# define T_FATE_OUTDATED 8 /* is out of date, needs updating */
# define T_FATE_UPDATE 9 /* deps updated, needs updating */
# define T_FATE_BROKEN 10 /* >= BROKEN ruins parents */
# define T_FATE_CANTFIND 10 /* no rules to make missing target */
# define T_FATE_CANTMAKE 11 /* 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( const char *rulename );
TARGET *bindtarget( const char *targetname );
TARGET *copytarget( const TARGET *t );
void touchtarget( const char *t );
TARGETS *targetlist( TARGETS *chain, LIST *targets );
TARGETS *targetentry( TARGETS *chain, TARGET *target );
TARGETS *targetchain( TARGETS *chain, TARGETS *targets );
ACTIONS *actionlist( ACTIONS *chain, ACTION *action );
SETTINGS *addsettings( SETTINGS *v, int setflag, const char *sym, LIST *val );
SETTINGS *copysettings( SETTINGS *v );
void pushsettings( SETTINGS *v );
void popsettings( SETTINGS *v );
void freesettings( SETTINGS *v );
void donerules();

421
jam/scan.c Normal file
View File

@@ -0,0 +1,421 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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.
* 01/10/01 (seiwald) - \ can now escape any whitespace char
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "scan.h"
# include "jamgram.h"
# include "jambase.h"
# include "jcache.h"
# include "newstr.h"
struct keyword {
const char *word;
int type;
} keywords[] = {
# include "jamgramtab.h"
{ 0, 0 }
} ;
struct include {
struct include *next; /* next serial include file */
const char *string; /* pointer into current line */
char **strings; /* for yyfparse() -- text to parse */
FILE *file; /* for yyfparse() -- file being read */
const 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( const 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( const 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 */
#ifdef OPT_JAMFILE_CACHE_EXT
if( !i->file )
{
if ( strcmp( i->fname, "-" ) )
{
i->strings = jcache((char*)i->fname);
if (!i->strings || !*i->strings)
goto next;
i->line++;
i->string = *(i->strings++);
return *i->string++;
}
else
{
i->file = stdin;
if( fgets( i->buf, sizeof( i->buf ), i->file ) )
{
i->line++;
i->string = i->buf;
return *i->string++;
}
}
}
#else
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++;
}
#endif
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;
}

54
jam/scan.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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.
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
/*
* YYSTYPE - value of a lexical token
*/
# define YYSTYPE YYSYMBOL
typedef struct _YYSTYPE {
int type;
const char *string;
PARSE *parse;
LIST *list;
int number;
} YYSTYPE;
extern YYSTYPE yylval;
void yymode( int n );
void yyerror( const char *s );
int yyanyerrors();
void yyfparse( const 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 */

87
jam/search.c Normal file
View File

@@ -0,0 +1,87 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* search.c - find a target along $(SEARCH) or $(LOCATE)
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "lists.h"
# include "search.h"
# include "timestamp.h"
# include "pathsys.h"
# include "variable.h"
# include "newstr.h"
const char *
search(
const 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 );
}

13
jam/search.h Normal file
View File

@@ -0,0 +1,13 @@
/*
* 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)
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
const char *search( const char *target, time_t *time );

205
jam/timestamp.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* timestamp.c - get the timestamp of a file or archive member
*
* 09/22/00 (seiwald) - downshift names on OS2, too
* 01/08/01 (seiwald) - closure param for file_dirscan/file_archscan
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "hash.h"
# include "filesys.h"
# include "pathsys.h"
# include "timestamp.h"
# include "newstr.h"
/*
* BINDING - all known files
*/
typedef struct _binding BINDING;
struct _binding {
const 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 *, const char *, int , time_t );
static const 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,
const 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 );
}

12
jam/timestamp.h Normal file
View File

@@ -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();

345
jam/variable.c Normal file
View File

@@ -0,0 +1,345 @@
/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* 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
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "variable.h"
# include "expand.h"
# include "hash.h"
# include "newstr.h"
static struct hash *varhash = 0;
/*
* VARIABLE - a user defined multi-value variable
*/
typedef struct _variable VARIABLE ;
struct _variable {
const char *symbol;
LIST *value;
} ;
static VARIABLE *var_enter( const char *symbol );
static void var_dump( const char *symbol, LIST *value, const 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( const char **e )
{
for( ; *e; e++ )
{
const 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;
const 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, buf, 0 );
}
l = list_new( l, pp, 0 );
/* 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(
const 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 = var_expand( L0, lastword, out, lol, 0 );
out = lastword;
while( l )
{
int so = strlen( l->string );
if( out + so >= oute )
return -1;
strcpy( out, l->string );
out += so;
/* Separate with space */
if( l = list_next( l ) )
*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( const 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(
const 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(
const 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( const 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(
const char *symbol,
LIST *value,
const char *what )
{
printf( "%s %s = ", what, symbol );
list_print( value );
printf( "\n" );
}
/*
* var_done() - free variable tables
*/
void
var_done()
{
hashdone( varhash );
}

27
jam/variable.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright 1993, 2000 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* variable.h - handle jam multi-element variables
*
* 11/04/02 (seiwald) - const-ing for string literals
*/
void var_defines( const char **e );
int var_string( const char *in, char *out, int outsize, LOL *lol );
LIST * var_get( const char *symbol );
void var_set( const char *symbol, LIST *value, int flag );
LIST * var_swap( const 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 */

88
jam/yyacc Executable file
View File

@@ -0,0 +1,88 @@
#!/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
#
# 03-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
# 03/19/02 (seiwald) - suffix symbols with _t to avoid conflicts
#
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/</_LANGLE/
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

View File

@@ -919,7 +919,7 @@ fi
PACKAGE=bfd
VERSION=2.15-beos-041202
VERSION=2.15-beos-060710
if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
{ echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; }

View File

@@ -1,7 +1,8 @@
# Makefile for program source directory in GNU NLS utilities package.
# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
# Copyright 2003, 2006 Free Software Foundation, Inc.
#
# This file file be copied and used freely without restrictions. It can
# This file may be copied and used freely without restrictions. It can
# be used in projects which are not available under the GNU Public License
# but which still want to provide support for the GNU gettext functionality.
# Please note that the actual code is *not* freely available.
@@ -109,6 +110,7 @@ $(srcdir)/stamp-cat-id: $(PACKAGE).pot
install: install-exec install-data
install-exec:
install-info:
install-html:
install-data: install-data-@USE_NLS@
install-data-no: all
install-data-yes: all
@@ -184,7 +186,7 @@ check: all
cat-id-tbl.o: ../intl/libgettext.h
dvi info tags TAGS ID:
html dvi pdf ps info tags TAGS ID:
mostlyclean:
rm -f core core.* *.pox $(PACKAGE).po *.old.po cat-id-tbl.tmp

View File

@@ -1,4 +1,5 @@
deffile.h
elf-hints-local.h
emultempl/armcoff.em
emultempl/pe.em
ldcref.c

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More