diff options
author | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
---|---|---|
committer | Patrick Simianer <simianer@cl.uni-heidelberg.de> | 2012-05-13 03:35:30 +0200 |
commit | 670a8f984fc6d8342180c59ae9e96b0b76f34d3d (patch) | |
tree | 9f2ce7eec1a77e56b3bb1ad0ad40f212d7a996b0 /jam-files/engine | |
parent | eb3ee28dc0eb1d3e5ed01ba0df843be329ae450d (diff) | |
parent | 2f64af3e06a518b93f7ca2c30a9d0aeb2c947031 (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'jam-files/engine')
110 files changed, 31951 insertions, 0 deletions
diff --git a/jam-files/engine/Jambase b/jam-files/engine/Jambase new file mode 100644 index 00000000..94f8fbde --- /dev/null +++ b/jam-files/engine/Jambase @@ -0,0 +1,2473 @@ +# +# /+\ +# +\ Copyright 1993, 2000 Christopher Seiwald. +# \+/ +# +# This file is part of Jam - see jam.c for Copyright information. +# + +# This file is ALSO: +# Copyright 2001-2004 David Abrahams. +# Copyright 2002-2004 Rene Rivera. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +if $(NT) +{ + SLASH ?= \\ ; +} +SLASH ?= / ; + + +# Glob for patterns in the directories starting from the given start directory, +# up to and including the root of the file-system. We stop globbing as soon as +# we find at least one match. +# +rule find-to-root ( dir : patterns + ) +{ + local globs = [ GLOB $(dir) : $(patterns) ] ; + while ! $(globs) && $(dir:P) != $(dir) + { + dir = $(dir:P) ; + globs = [ GLOB $(dir) : $(patterns) ] ; + } + return $(globs) ; +} + + +# This global will hold the location of the user's boost-build.jam file. +.boost-build-file = ; + +# This global will hold the location of the build system bootstrap file. +.bootstrap-file = ; + +# Remember the value of $(BOOST_BUILD_PATH) supplied to us by the user. +BOOST_BUILD_PATH.user-value = $(BOOST_BUILD_PATH) ; + +# On Unix only, when BOOST_BUILD_PATH is not supplied by the user, set it to a +# sensible default value. This allows Boost.Build to work without any +# environment variables, which is good in itself and also required by the Debian +# Policy. +if ! $(BOOST_BUILD_PATH) && $(UNIX) +{ + BOOST_BUILD_PATH = /usr/share/boost-build ; +} + + +rule _poke ( module-name ? : variables + : value * ) +{ + module $(<) + { + $(>) = $(3) ; + } +} + + +# This rule can be invoked from an optional user's boost-build.jam file to both +# indicate where to find the build system files, and to load them. The path +# indicated is relative to the location of the boost-build.jam file. +# +rule boost-build ( dir ? ) +{ + if $(.bootstrap-file) + { + ECHO "Error: Illegal attempt to re-bootstrap the build system by invoking" ; + ECHO ; + ECHO " 'boost-build" $(dir) ";'" ; + ECHO ; + EXIT "Please consult the documentation at 'http://www.boost.org'." ; + } + + # Add the given directory to the path so we can find the build system. If + # dir is empty, has no effect. + BOOST_BUILD_PATH = $(dir:R=$(.boost-build-file:D)) $(BOOST_BUILD_PATH) ; + + # We might have just modified the *global* value of BOOST_BUILD_PATH. The + # code that loads the rest of Boost.Build, in particular the site-config.jam + # and user-config.jam configuration files uses os.environ, so we need to + # update the value there. + _poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ; + + # Try to find the build system bootstrap file 'bootstrap.jam'. + local bootstrap-file = [ GLOB $(BOOST_BUILD_PATH) : bootstrap.jam ] ; + .bootstrap-file = $(bootstrap-file[1]) ; + + # There is no bootstrap.jam we can find, exit with an error. + if ! $(.bootstrap-file) + { + ECHO "Unable to load Boost.Build: could not find build system." ; + ECHO --------------------------------------------------------- ; + ECHO "$(.boost-build-file) attempted to load the build system by invoking" ; + ECHO ; + ECHO " 'boost-build" $(dir) ";'" ; + ECHO ; + ECHO "but we were unable to find \"bootstrap.jam\" in the specified directory" ; + ECHO "or in BOOST_BUILD_PATH (searching "$(BOOST_BUILD_PATH:J=", ")")." ; + ECHO ; + EXIT "Please consult the documentation at 'http://www.boost.org'." ; + } + + if [ MATCH .*(--debug-configuration).* : $(ARGV) ] + { + ECHO "notice: loading Boost.Build from" + [ NORMALIZE_PATH $(.bootstrap-file:D) ] ; + } + + # Load the build system, now that we know where to start from. + include $(.bootstrap-file) ; +} + + +if [ MATCH .*(b2).* : $(ARGV[1]:BL) ] + || [ MATCH .*(bjam).* : $(ARGV[1]:BL) ] + || $(BOOST_ROOT) # A temporary measure so Jam works with Boost.Build v1. +{ + # We attempt to load "boost-build.jam" by searching from the current + # invocation directory up to the root of the file-system. + # + # boost-build.jam is expected to invoke the "boost-build" rule to load the + # Boost.Build files. + + local search-path = $(BOOST_BUILD_PATH) $(BOOST_ROOT) ; + local self = [ SELF_PATH ] ; + local boost-build-relative = ../../share/boost-build ; + local self-based-path = [ NORMALIZE_PATH $(boost-build-relative:R=$(self)) ] ; + + local boost-build-files = + [ find-to-root [ PWD ] : boost-build.jam ] + [ GLOB $(self-based-path) : boost-build.jam ] + # Another temporary measure so Jam works with Boost.Build v1. + [ GLOB $(search-path) : boost-build.jam ] ; + + .boost-build-file = $(boost-build-files[1]) ; + + # There is no boost-build.jam we can find, exit with an error, and + # information. + if ! $(.boost-build-file) + { + ECHO "Unable to load Boost.Build: could not find \"boost-build.jam\"" ; + ECHO --------------------------------------------------------------- ; + + if ! [ MATCH .*(bjam).* : $(ARGV[1]:BL) ] + { + ECHO "BOOST_ROOT must be set, either in the environment, or " ; + ECHO "on the command-line with -sBOOST_ROOT=..., to the root" ; + ECHO "of the boost installation." ; + ECHO ; + } + + ECHO "Attempted search from" [ PWD ] "up to the root" ; + ECHO "at" $(self-based-path) ; + ECHO "and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: "$(search-path:J=", ")"." ; + EXIT "Please consult the documentation at 'http://www.boost.org'." ; + } + + if [ MATCH .*(--debug-configuration).* : $(ARGV) ] + { + ECHO "notice: found boost-build.jam at" + [ NORMALIZE_PATH $(.boost-build-file) ] ; + } + + # Now load the boost-build.jam to get the build system loaded. This + # incidentaly loads the users jamfile and attempts to build targets. + # + # We also set it up so we can tell whether we are loading the new V2 system + # or the the old V1 system. + include $(.boost-build-file) ; + + # Check that, at minimum, the bootstrap file was found. + if ! $(.bootstrap-file) + { + ECHO "Unable to load Boost.Build" ; + ECHO -------------------------- ; + ECHO "\"$(.boost-build-file)\" was found by searching from" [ PWD ] "up to the root" ; + ECHO "and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: "$(search-path:J=", ")"." ; + ECHO ; + ECHO "However, it failed to call the \"boost-build\" rule to indicate" ; + ECHO "the location of the build system." ; + ECHO ; + EXIT "Please consult the documentation at 'http://www.boost.org'." ; + } +} +else +{ + +# +# JAMBASE - jam 2.3 ruleset providing make(1)-like functionality +# +# Supports UNIX, NT, and VMS. +# +# 12/27/93 (seiwald) - purturb library sources with SOURCE_GRIST +# 04/18/94 (seiwald) - use '?=' when setting OS specific vars +# 04/21/94 (seiwald) - do RmTemps together +# 05/05/94 (seiwald) - all supported C compilers support -o: relegate +# RELOCATE as an option; set Ranlib to "" to disable it +# 06/01/94 (seiwald) - new 'actions existing' to do existing sources +# 08/25/94 (seiwald) - new ObjectCcFlags rule to append to per-target CCFLAGS +# 08/29/94 (seiwald) - new ObjectHdrs rule to append to per-target HDRS +# 09/19/94 (seiwald) - LinkLibraries and Undefs now append +# - Rule names downshifted. +# 10/06/94 (seiwald) - Dumb yyacc stuff moved into Jamfile. +# 10/14/94 (seiwald) - (Crude) support for .s, .C, .cc, .cpp, and .f files. +# 01/08/95 (seiwald) - Shell now handled with awk, not sed +# 01/09/95 (seiwald) - Install* now take dest directory as target +# 01/10/95 (seiwald) - All entries sorted. +# 01/10/95 (seiwald) - NT support moved in, with LauraW's help. +# 01/10/95 (seiwald) - VMS support moved in. +# 02/06/95 (seiwald) - ObjectC++Flags and SubDirC++Flags added. +# 02/07/95 (seiwald) - Iron out when HDRSEARCH uses "" or SEARCH_SOURCE. +# 02/08/95 (seiwald) - SubDir works on VMS. +# 02/14/95 (seiwald) - MkDir and entourage. +# 04/30/95 (seiwald) - Use install -c flag so that it copies, not moves. +# 07/10/95 (taylor) - Support for Microsoft C++. +# 11/21/96 (peterk) - Support for BeOS +# 07/19/99 (sickel) - Support for Mac OS X Server (and maybe client) +# 02/18/00 (belmonte)- Support for Cygwin. + +# Special targets defined in this file: +# +# all - parent of first, shell, files, lib, exe +# first - first dependency of 'all', for potential initialization +# shell - parent of all Shell targets +# files - parent of all File targets +# lib - parent of all Library targets +# exe - parent of all Main targets +# dirs - parent of all MkDir targets +# clean - removes all Shell, File, Library, and Main targets +# uninstall - removes all Install targets +# + +# Rules defined by this file: +# +# as obj.o : source.s ; .s -> .o +# Bulk dir : files ; populate directory with many files +# Cc obj.o : source.c ; .c -> .o +# C++ obj.o : source.cc ; .cc -> .o +# Clean clean : sources ; remove sources with 'jam clean' +# File dest : source ; copy file +# Fortran obj.o : source.f ; .f -> .o +# GenFile source.c : program args ; make custom file +# Hardlink target : source ; make link from source to target +# HdrRule source : headers ; handle #includes +# InstallInto dir : sources ; install any files +# InstallBin dir : sources ; install binaries +# InstallLib dir : sources ; install files +# InstallFile dir : sources ; install files +# InstallMan dir : sources ; install man pages +# InstallShell dir : sources ; install shell scripts +# Lex source.c : source.l ; .l -> .c +# Library lib : source ; archive library from compiled sources +# LibraryFromObjects lib : objects ; archive library from objects +# LinkLibraries images : libraries ; bag libraries onto Mains +# Main image : source ; link executable from compiled sources +# MainFromObjects image : objects ; link executable from objects +# MkDir dir ; make a directory, if not there +# Object object : source ; compile object from source +# ObjectCcFlags source : flags ; add compiler flags for object +# ObjectC++Flags source : flags ; add compiler flags for object +# ObjectHdrs source : dirs ; add include directories for object +# Objects sources ; compile sources +# RmTemps target : sources ; remove temp sources after target made +# Setuid images ; mark executables Setuid +# SubDir TOP d1 d2 ... ; start a subdirectory Jamfile +# SubDirCcFlags flags ; add compiler flags until next SubDir +# SubDirC++Flags flags ; add compiler flags until next SubDir +# SubDirHdrs dirs ; add include dirs until next SubDir +# SubInclude TOP d1 d2 ... ; include a subdirectory Jamfile +# Shell exe : source ; make a shell executable +# Undefines images : symbols ; save undef's for linking +# UserObject object : source ; handle unknown suffixes for Object +# Yacc source.c : source.y ; .y -> .c +# +# Utility rules that have no side effects (not supported): +# +# FAppendSuffix f1 f2 ... : $(SUF) ; return $(<) with suffixes +# FConcat value ... ; return contatenated values +# FDirName d1 d2 ... ; return path from root to dir +# FGrist d1 d2 ... ; return d1!d2!... +# FGristFiles value ; return $(value:G=$(SOURCE_GRIST)) +# FGristSourceFiles value ; return $(value:G=$(SOURCE_GRIST)) +# FRelPath d1 : d2 ; return rel path from d1 to d2 +# FSubDir d1 d2 ... ; return path to root +# + + +# Brief review of the jam language: +# +# Statements: +# rule RULE - statements to process a rule +# actions RULE - system commands to carry out target update +# +# Modifiers on actions: +# together - multiple instances of same rule on target get executed +# once with their sources ($(>)) concatenated +# updated - refers to updated sources ($(>)) only +# ignore - ignore return status of command +# quietly - don't trace its execution unless verbose +# piecemeal - iterate command each time with a small subset of $(>) +# existing - refers to currently existing sources ($(>)) only +# bind vars - subject to binding before expanding in actions +# +# Special rules: +# ALWAYS - always build a target +# DEPENDS - builds the dependency graph +# ECHO - blurt out targets on stdout +# EXIT - blurt out targets and exit +# INCLUDES - marks sources as headers for target (a codependency) +# NOCARE - don't panic if the target can't be built +# NOUPDATE - create the target if needed but never update it +# NOTFILE - ignore the timestamp of the target (it's not a file) +# TEMPORARY - target need not be present if sources haven't changed +# +# Special variables set by jam: +# $(<) - targets of a rule (to the left of the :) +# $(>) - sources of a rule (to the right of the :) +# $(xxx) - true on xxx (UNIX, VMS, NT, OS2, MAC) +# $(OS) - name of OS - varies wildly +# $(JAMVERSION) - version number (2.3) +# +# Special variables used by jam: +# SEARCH - where to find something (used during binding and actions) +# LOCATE - where to plop something not found with SEARCH +# HDRRULE - rule to call to handle include files +# HDRSCAN - egrep regex to extract include files +# +# Special targets: +# all - default if none given on command line +# + +# Initialize variables +# + +# +# OS specific variable settings +# +if $(NT) +{ + # the list of supported toolsets on Windows NT and Windows 95/98 + # + local SUPPORTED_TOOLSETS = "BORLANDC" "VC7" "VISUALC" "VISUALC16" "INTELC" "WATCOM" + "MINGW" "LCC" ; + + # this variable holds the current toolset + # + TOOLSET = "" ; + + # if the JAM_TOOLSET environment variable is defined, check that it is + # one of our supported values + # + if $(JAM_TOOLSET) + { + local t ; + + for t in $(SUPPORTED_TOOLSETS) + { + $(t) = $($(t):J=" ") ; # reconstitute paths with spaces in them + if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; } + } + + if ! $(TOOLSET) + { + ECHO "The JAM_TOOLSET environment variable is defined but its value" ; + ECHO "is invalid, please use one of the following:" ; + ECHO ; + + for t in $(SUPPORTED_TOOLSETS) { ECHO " " $(t) ; } + EXIT ; + } + } + + # if TOOLSET is empty, we'll try to detect the toolset from other + # environment variables to remain backwards compatible with Jam 2.3 + # + if ! $(TOOLSET) + { + if $(BCCROOT) + { + TOOLSET = BORLANDC ; + BORLANDC = $(BCCROOT:J=" ") ; + } + else if $(MSVC) + { + TOOLSET = VISUALC16 ; + VISUALC16 = $(MSVC:J=" ") ; + } + else if $(MSVCNT) + { + TOOLSET = VISUALC ; + VISUALC = $(MSVCNT:J=" ") ; + } + else if $(MSVCDir) + { + TOOLSET = VISUALC ; + VISUALC = $(MSVCDir:J=" ") ; + } + else if $(MINGW) + { + TOOLSET = MINGW ; + } + else + { + ECHO "Jam cannot be run because, either:" ; + ECHO " a. You didn't set BOOST_ROOT to indicate the root of your" ; + ECHO " Boost installation." ; + ECHO " b. You are trying to use stock Jam but didn't indicate which" ; + ECHO " compilation toolset to use. To do so, follow these simple" ; + ECHO " instructions:" ; + ECHO ; + ECHO " - define one of the following environment variable, with the" ; + ECHO " appropriate value according to this list:" ; + ECHO ; + ECHO " Variable Toolset Description" ; + ECHO ; + ECHO " BORLANDC Borland C++ BC++ install path" ; + ECHO " VISUALC Microsoft Visual C++ VC++ install path" ; + ECHO " VISUALC16 Microsoft Visual C++ 16 bit VC++ 16 bit install" ; + ECHO " INTELC Intel C/C++ IC++ install path" ; + ECHO " WATCOM Watcom C/C++ Watcom install path" ; + ECHO " MINGW MinGW (gcc) MinGW install path" ; + ECHO " LCC Win32-LCC LCC-Win32 install path" ; + ECHO ; + ECHO " - define the JAM_TOOLSET environment variable with the *name*" ; + ECHO " of the toolset variable you want to use." ; + ECHO ; + ECHO " e.g.: set VISUALC=C:\\Visual6" ; + ECHO " set JAM_TOOLSET=VISUALC" ; + EXIT ; + } + } + + CP ?= copy ; + RM ?= del /f/q ; + SLASH ?= \\ ; + SUFLIB ?= .lib ; + SUFOBJ ?= .obj ; + SUFEXE ?= .exe ; + + if $(TOOLSET) = BORLANDC + { + ECHO "Compiler is Borland C++" ; + + AR ?= tlib /C /P64 ; + CC ?= bcc32 ; + CCFLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus ; + C++ ?= bcc32 ; + C++FLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus -P ; + LINK ?= $(CC) ; + LINKFLAGS ?= $(CCFLAGS) ; + STDLIBPATH ?= $(BORLANDC)\\lib ; + STDHDRS ?= $(BORLANDC)\\include ; + NOARSCAN ?= true ; + } + else if $(TOOLSET) = VISUALC16 + { + ECHO "Compiler is Microsoft Visual C++ 16 bit" ; + + AR ?= lib /nologo ; + CC ?= cl /nologo ; + CCFLAGS ?= /D \"WIN\" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= $(CC) ; + LINKFLAGS ?= $(CCFLAGS) ; + LINKLIBS ?= + \"$(VISUALC16)\\lib\\mlibce.lib\" + \"$(VISUALC16)\\lib\\oldnames.lib\" + ; + LINKLIBS ?= ; + NOARSCAN ?= true ; + OPTIM ?= "" ; + STDHDRS ?= $(VISUALC16)\\include ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = VISUALC + { + ECHO "Compiler is Microsoft Visual C++" ; + + AR ?= lib ; + AS ?= masm386 ; + CC ?= cl /nologo ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= link /nologo ; + LINKFLAGS ?= "" ; + LINKLIBS ?= \"$(VISUALC)\\lib\\advapi32.lib\" + # $(VISUALC)\\lib\\libc.lib + # $(VISUALC)\\lib\\oldnames.lib + \"$(VISUALC)\\lib\\gdi32.lib\" + \"$(VISUALC)\\lib\\user32.lib\" + \"$(VISUALC)\\lib\\kernel32.lib\" ; + OPTIM ?= "" ; + STDHDRS ?= $(VISUALC)\\include ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = VC7 + { + ECHO "Compiler is Microsoft Visual C++ .NET" ; + + AR ?= lib ; + AS ?= masm386 ; + CC ?= cl /nologo ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= link /nologo ; + LINKFLAGS ?= "" ; + LINKLIBS ?= \"$(VISUALC)\\PlatformSDK\\lib\\advapi32.lib\" + # $(VISUALC)\\lib\\libc.lib + # $(VISUALC)\\lib\\oldnames.lib + \"$(VISUALC)\\PlatformSDK\\lib\\gdi32.lib\" + \"$(VISUALC)\\PlatformSDK\\lib\\user32.lib\" + \"$(VISUALC)\\PlatformSDK\\lib\\kernel32.lib\" ; + OPTIM ?= "" ; + STDHDRS ?= \"$(VISUALC)\\include\" + \"$(VISUALC)\\PlatformSDK\\include\" ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = INTELC + { + ECHO "Compiler is Intel C/C++" ; + + if ! $(VISUALC) + { + ECHO "As a special exception, when using the Intel C++ compiler, you need" ; + ECHO "to define the VISUALC environment variable to indicate the location" ; + ECHO "of your Visual C++ installation. Aborting.." ; + EXIT ; + } + + AR ?= lib ; + AS ?= masm386 ; + CC ?= icl /nologo ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= link /nologo ; + LINKFLAGS ?= "" ; + LINKLIBS ?= $(VISUALC)\\lib\\advapi32.lib + # $(VISUALC)\\lib\\libc.lib + # $(VISUALC)\\lib\\oldnames.lib + $(VISUALC)\\lib\\kernel32.lib + ; + OPTIM ?= "" ; + STDHDRS ?= $(INTELC)\include $(VISUALC)\\include ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = WATCOM + { + ECHO "Compiler is Watcom C/C++" ; + + AR ?= wlib ; + CC ?= wcc386 ; + CCFLAGS ?= /zq /DWIN32 /I$(WATCOM)\\h ; # zq=quiet + C++ ?= wpp386 ; + C++FLAGS ?= $(CCFLAGS) ; + CP ?= copy ; + DOT ?= . ; + DOTDOT ?= .. ; + LINK ?= wcl386 ; + LINKFLAGS ?= /zq ; # zq=quiet + LINKLIBS ?= ; + MV ?= move ; + NOARSCAN ?= true ; + OPTIM ?= ; + RM ?= del /f ; + SLASH ?= \\ ; + STDHDRS ?= $(WATCOM)\\h $(WATCOM)\\h\\nt ; + SUFEXE ?= .exe ; + SUFLIB ?= .lib ; + SUFOBJ ?= .obj ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = MINGW + { + ECHO "Compiler is GCC with Mingw" ; + + AR ?= ar -ru ; + CC ?= gcc ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= $(CC) ; + LINKFLAGS ?= "" ; + LINKLIBS ?= "" ; + OPTIM ?= ; + SUFOBJ = .o ; + SUFLIB = .a ; + SLASH = / ; +# NOARSCAN ?= true ; + } + else if $(TOOLSET) = LCC + { + ECHO "Compiler is Win32-LCC" ; + + AR ?= lcclib ; + CC ?= lcc ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= lcclnk ; + LINKFLAGS ?= "" ; + LINKLIBS ?= "" ; + OPTIM ?= ; + NOARSCAN = true ; + } + else + { +# +# XXX: We need better comments here !! +# + EXIT On NT, set BCCROOT, MSVCNT, MINGW or MSVC to the root of the + Borland or Microsoft directories. ; + } + +} +else if $(OS2) +{ + # the list of supported toolsets on Windows NT and Windows 95/98 + # + local SUPPORTED_TOOLSETS = "EMX" "WATCOM" ; + + # this variable holds the current toolset + # + TOOLSET = "" ; + + # if the JAM_TOOLSET environment variable is defined, check that it is + # one of our supported values + # + if $(JAM_TOOLSET) + { + local t ; + + for t in $(SUPPORTED_TOOLSETS) + { + $(t) = $($(t):J=" ") ; # reconstitute paths with spaces in them + if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; } + } + + if ! $(TOOLSET) + { + ECHO "The JAM_TOOLSET environment variable is defined but its value" ; + ECHO "is invalid, please use one of the following:" ; + ECHO ; + + for t in $(SUPPORTED_TOOLSETS) { ECHO " " $(t) ; } + EXIT ; + } + } + + # if TOOLSET is empty, we'll try to detect the toolset from other + # environment variables to remain backwards compatible with Jam 2.3 + # + if ! $(TOOLSET) + { + if $(watcom) + { + WATCOM = $(watcom:J=" ") ; + TOOLSET = WATCOM ; + } + else + { + ECHO "Jam cannot be run because you didn't indicate which compilation toolset" ; + ECHO "to use. To do so, follow these simple instructions:" ; + ECHO ; + ECHO " - define one of the following environment variable, with the" ; + ECHO " appropriate value according to this list:" ; + ECHO ; + ECHO " Variable Toolset Description" ; + ECHO ; + ECHO " WATCOM Watcom C/C++ Watcom install path" ; + ECHO " EMX EMX (gcc) EMX install path" ; + ECHO " VISUALAGE IBM Visual Age C/C++ VisualAge install path" ; + ECHO ; + ECHO " - define the JAM_TOOLSET environment variable with the *name*" ; + ECHO " of the toolset variable you want to use." ; + ECHO ; + ECHO " e.g.: set WATCOM=C:\WATCOM" ; + ECHO " set JAM_TOOLSET=WATCOM" ; + ECHO ; + EXIT ; + } + } + + RM = del /f ; + CP = copy ; + MV ?= move ; + DOT ?= . ; + DOTDOT ?= .. ; + SUFLIB ?= .lib ; + SUFOBJ ?= .obj ; + SUFEXE ?= .exe ; + + if $(TOOLSET) = WATCOM + { + AR ?= wlib ; + BINDIR ?= \\os2\\apps ; + CC ?= wcc386 ; + CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\h ; # zq=quiet + C++ ?= wpp386 ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= wcl386 ; + LINKFLAGS ?= /zq ; # zq=quiet + LINKLIBS ?= ; + NOARSCAN ?= true ; + OPTIM ?= ; + SLASH ?= \\ ; + STDHDRS ?= $(WATCOM)\\h ; + UNDEFFLAG ?= "/u _" ; + } + else if $(TOOLSET) = EMX + { + ECHO "Compiler is GCC-EMX" ; + AR ?= ar -ru ; + CC ?= gcc ; + CCFLAGS ?= "" ; + C++ ?= $(CC) ; + C++FLAGS ?= $(CCFLAGS) ; + LINK ?= $(CC) ; + LINKFLAGS ?= "" ; + LINKLIBS ?= "" ; + OPTIM ?= ; + SUFOBJ = .o ; + SUFLIB = .a ; + UNDEFFLAG ?= "-U" ; + SLASH = / ; +# NOARSCAN ?= true ; + } + else + { + # should never happen + EXIT "Sorry, but the $(JAM_TOOLSET) toolset isn't supported for now" ; + } +} +else if $(VMS) +{ + C++ ?= cxx ; + C++FLAGS ?= ; + CC ?= cc ; + CCFLAGS ?= ; + CHMOD ?= set file/prot= ; + CP ?= copy/replace ; + CRELIB ?= true ; + DOT ?= [] ; + DOTDOT ?= [-] ; + EXEMODE ?= (w:e) ; + FILEMODE ?= (w:r) ; + HDRS ?= ; + LINK ?= link ; + LINKFLAGS ?= "" ; + LINKLIBS ?= ; + MKDIR ?= create/dir ; + MV ?= rename ; + OPTIM ?= "" ; + RM ?= delete ; + RUNVMS ?= mcr ; + SHELLMODE ?= (w:er) ; + SLASH ?= . ; + STDHDRS ?= decc$library_include ; + SUFEXE ?= .exe ; + SUFLIB ?= .olb ; + SUFOBJ ?= .obj ; + + switch $(OS) + { + case OPENVMS : CCFLAGS ?= /stand=vaxc ; + case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ; + } +} +else if $(MAC) +{ + local OPT ; + + CW ?= "{CW}" ; + + MACHDRS ?= + "$(UMACHDRS):Universal:Interfaces:CIncludes" + "$(CW):MSL:MSL_C:MSL_Common:Include" + "$(CW):MSL:MSL_C:MSL_MacOS:Include" ; + + MACLIBS ?= + "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib" + "$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib" ; + + MPWLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib" ; + + MPWNLLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib" ; + + SIOUXHDRS ?= ; + + SIOUXLIBS ?= + "$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib" + "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib" ; + + C++ ?= mwcppc ; + C++FLAGS ?= -w off -nomapcr ; + CC ?= mwcppc ; + CCFLAGS ?= -w off -nomapcr ; + CP ?= duplicate -y ; + DOT ?= ":" ; + DOTDOT ?= "::" ; + HDRS ?= $(MACHDRS) $(MPWHDRS) ; + LINK ?= mwlinkppc ; + LINKFLAGS ?= -mpwtool -warn ; + LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ; + MKDIR ?= newfolder ; + MV ?= rename -y ; + NOARSCAN ?= true ; + OPTIM ?= ; + RM ?= delete -y ; + SLASH ?= ":" ; + STDHDRS ?= ; + SUFLIB ?= .lib ; + SUFOBJ ?= .o ; +} +else if $(OS) = BEOS && $(METROWERKS) +{ + AR ?= mwld -xml -o ; + BINDIR ?= /boot/apps ; + CC ?= mwcc ; + CCFLAGS ?= -nosyspath ; + C++ ?= $(CC) ; + C++FLAGS ?= -nosyspath ; + FORTRAN ?= "" ; + LIBDIR ?= /boot/develop/libraries ; + LINK ?= mwld ; + LINKFLAGS ?= "" ; + MANDIR ?= /boot/documentation/"Shell Tools"/HTML ; + NOARSCAN ?= true ; + STDHDRS ?= /boot/develop/headers/posix ; +} +else if $(OS) = BEOS +{ + BINDIR ?= /boot/apps ; + CC ?= gcc ; + C++ ?= $(CC) ; + FORTRAN ?= "" ; + LIBDIR ?= /boot/develop/libraries ; + LINK ?= gcc ; + LINKLIBS ?= -lnet ; + NOARSCAN ?= true ; + STDHDRS ?= /boot/develop/headers/posix ; +} +else if $(UNIX) +{ + switch $(OS) + { + case AIX : + LINKLIBS ?= -lbsd ; + + case AMIGA : + CC ?= gcc ; + YACC ?= "bison -y" ; + + case CYGWIN : + CC ?= gcc ; + CCFLAGS += -D__cygwin__ ; + LEX ?= flex ; + RANLIB ?= "" ; + SUFEXE ?= .exe ; + YACC ?= "bison -y" ; + + case DGUX : + RANLIB ?= "" ; + RELOCATE ?= true ; + + case HPUX : + YACC = ; + CFLAGS += -Ae ; + CCFLAGS += -Ae ; + RANLIB ?= "" ; + + case INTERIX : + CC ?= gcc ; + RANLIB ?= "" ; + + case IRIX : + RANLIB ?= "" ; + + case MPEIX : + CC ?= gcc ; + C++ ?= gcc ; + CCFLAGS += -D_POSIX_SOURCE ; + HDRS += /usr/include ; + RANLIB ?= "" ; + NOARSCAN ?= true ; + NOARUPDATE ?= true ; + + case MVS : + RANLIB ?= "" ; + + case NEXT : + AR ?= libtool -o ; + RANLIB ?= "" ; + + case MACOSX : + AR ?= libtool -o ; + C++ ?= c++ ; + MANDIR ?= /usr/local/share/man ; + RANLIB ?= "" ; + + case NCR : + RANLIB ?= "" ; + + case PTX : + RANLIB ?= "" ; + + case QNX : + AR ?= wlib ; + CC ?= cc ; + CCFLAGS ?= -Q ; # quiet + C++ ?= $(CC) ; + C++FLAGS ?= -Q ; # quiet + LINK ?= $(CC) ; + LINKFLAGS ?= -Q ; # quiet + NOARSCAN ?= true ; + RANLIB ?= "" ; + + case SCO : + RANLIB ?= "" ; + RELOCATE ?= true ; + + case SINIX : + RANLIB ?= "" ; + + case SOLARIS : + RANLIB ?= "" ; + AR ?= "/usr/ccs/bin/ar ru" ; + + case UNICOS : + NOARSCAN ?= true ; + OPTIM ?= -O0 ; + + case UNIXWARE : + RANLIB ?= "" ; + RELOCATE ?= true ; + } + + # UNIX defaults + + CCFLAGS ?= ; + C++FLAGS ?= $(CCFLAGS) ; + CHMOD ?= chmod ; + CHGRP ?= chgrp ; + CHOWN ?= chown ; + LEX ?= lex ; + LINKFLAGS ?= $(CCFLAGS) ; + LINKLIBS ?= ; + OPTIM ?= -O ; + RANLIB ?= ranlib ; + YACC ?= yacc ; + YACCFILES ?= y.tab ; + YACCFLAGS ?= -d ; +} + +# +# General defaults; a lot like UNIX +# + + AR ?= ar ru ; + AS ?= as ; + ASFLAGS ?= ; + AWK ?= awk ; + BINDIR ?= /usr/local/bin ; + C++ ?= cc ; + C++FLAGS ?= ; + CC ?= cc ; + CCFLAGS ?= ; + CP ?= cp -f ; + CRELIB ?= ; + DOT ?= . ; + DOTDOT ?= .. ; + EXEMODE ?= 711 ; + FILEMODE ?= 644 ; + FORTRAN ?= f77 ; + FORTRANFLAGS ?= ; + HDRS ?= ; + INSTALLGRIST ?= installed ; + JAMFILE ?= Jamfile ; + JAMRULES ?= Jamrules ; + LEX ?= ; + LIBDIR ?= /usr/local/lib ; + LINK ?= $(CC) ; + LINKFLAGS ?= ; + LINKLIBS ?= ; + LN ?= ln ; + MANDIR ?= /usr/local/man ; + MKDIR ?= mkdir ; + MV ?= mv -f ; + OPTIM ?= ; + RCP ?= rcp ; + RM ?= rm -f ; + RSH ?= rsh ; + SED ?= sed ; + SHELLHEADER ?= "#!/bin/sh" ; + SHELLMODE ?= 755 ; + SLASH ?= / ; + STDHDRS ?= /usr/include ; + SUFEXE ?= "" ; + SUFLIB ?= .a ; + SUFOBJ ?= .o ; + UNDEFFLAG ?= "-u _" ; + YACC ?= ; + YACCFILES ?= ; + YACCFLAGS ?= ; + + HDRPATTERN = + "^[ ]*#[ ]*include[ ]*[<\"]([^\">]*)[\">].*$" ; + + OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ; + + +# +# Base dependencies - first for "bootstrap" kinds of rules +# + +DEPENDS all : shell files lib exe obj ; +DEPENDS all shell files lib exe obj : first ; +NOTFILE all first shell files lib exe obj dirs clean uninstall ; +ALWAYS clean uninstall ; + +# +# Rules +# + +rule As +{ + DEPENDS $(<) : $(>) ; + ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ; +} + +rule Bulk +{ + local i ; + + for i in $(>) + { + File $(i:D=$(<)) : $(i) ; + } +} + +rule Cc +{ + local _h ; + + DEPENDS $(<) : $(>) ; + + # Just to clarify here: this sets the per-target CCFLAGS to + # be the current value of (global) CCFLAGS and SUBDIRCCFLAGS. + + CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) ; + + # If the compiler's -o flag doesn't work, relocate the .o + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + _h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ; + + if $(VMS) && $(_h) + { + SLASHINC on $(<) = "/inc=(" $(_h[1]) ,$(_h[2-]) ")" ; + } + else if $(MAC) && $(_h) + { + local _i _j ; + _j = $(_h[1]) ; + for _i in $(_h[2-]) + { + _j = $(_j),$(_i) ; + } + MACINC on $(<) = \"$(_j)\" ; + } +} + +rule C++ +{ + local _h ; + + DEPENDS $(<) : $(>) ; + C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) ; + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + _h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ; + + if $(VMS) && $(_h) + { + SLASHINC on $(<) = "/inc=(" $(_h[1]) ,$(_h[2-]) ")" ; + } + else if $(MAC) && $(_h) + { + local _i _j ; + _j = $(_h[1]) ; + for _i in $(_h[2-]) + { + _j = $(_j),$(_i) ; + } + MACINC on $(<) = \"$(_j)\" ; + } +} + +rule Chmod +{ + if $(CHMOD) { Chmod1 $(<) ; } +} + +rule File +{ + DEPENDS files : $(<) ; + DEPENDS $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MODE on $(<) = $(FILEMODE) ; + Chmod $(<) ; +} + +rule Fortran +{ + DEPENDS $(<) : $(>) ; +} + +rule GenFile +{ + local _t = [ FGristSourceFiles $(<) ] ; + local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ; + Depends $(_t) : $(_s) $(>[2-]) ; + GenFile1 $(_t) : $(_s) $(>[2-]) ; + Clean clean : $(_t) ; +} + +rule GenFile1 +{ + MakeLocate $(<) : $(LOCATE_SOURCE) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; +} + +rule HardLink +{ + DEPENDS files : $(<) ; + DEPENDS $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; +} + +rule HdrMacroFile +{ + # HdrMacroFile file ; + # + # this rule is used to indicate that a given file contains definitions + # for filename macros (e.g. "#define MYFILE_H <myfile.h>") that can + # later be used in #include statements in the rest of the source + # + # theses files must be parsed before any make is tried.. + # + HDRMACRO $(<) ; +} + +rule HdrRule +{ + # HdrRule source : headers ; + + # N.B. This rule is called during binding, potentially after + # the fate of many targets has been determined, and must be + # used with caution: don't add dependencies to unrelated + # targets, and don't set variables on $(<). + + # Tell Jam that anything depending on $(<) also depends on $(>), + # set SEARCH so Jam can find the headers, but then say we don't + # care if we can't actually find the headers (they may have been + # within ifdefs), + + local s ; + + if $(HDRGRIST) + { + s = $(>:G=$(HDRGRIST)) ; + } else { + s = $(>) ; + } + + INCLUDES $(<) : $(s) ; + SEARCH on $(s) = $(HDRSEARCH) ; + NOCARE $(s) ; + + # Propagate on $(<) to $(>) + + HDRSEARCH on $(s) = $(HDRSEARCH) ; + HDRSCAN on $(s) = $(HDRSCAN) ; + HDRRULE on $(s) = $(HDRRULE) ; + HDRGRIST on $(s) = $(HDRGRIST) ; +} + +rule InstallInto +{ + # InstallInto dir : sources ; + + local i t ; + + t = $(>:G=$(INSTALLGRIST)) ; + + # Arrange for jam install + # Arrange for jam uninstall + # sources are in SEARCH_SOURCE + # targets are in dir + + Depends install : $(t) ; + Clean uninstall : $(t) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MakeLocate $(t) : $(<) ; + + # For each source, make gristed target name + # and Install, Chmod, Chown, and Chgrp + + for i in $(>) + { + local tt = $(i:G=$(INSTALLGRIST)) ; + + Depends $(tt) : $(i) ; + Install $(tt) : $(i) ; + Chmod $(tt) ; + + if $(OWNER) && $(CHOWN) + { + Chown $(tt) ; + OWNER on $(tt) = $(OWNER) ; + } + + if $(GROUP) && $(CHGRP) + { + Chgrp $(tt) ; + GROUP on $(tt) = $(GROUP) ; + } + } +} + +rule InstallBin +{ + local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ; + + InstallInto $(<) : $(_t) ; + MODE on $(_t:G=installed) = $(EXEMODE) ; +} + +rule InstallFile +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=installed) = $(FILEMODE) ; +} + +rule InstallLib +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=installed) = $(FILEMODE) ; +} + +rule InstallMan +{ + # Really this just strips the . from the suffix + + local i s d ; + + for i in $(>) + { + switch $(i:S) + { + case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ; + case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ; + case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ; + case .n : s = n ; case .man : s = 1 ; + } + + d = man$(s) ; + + InstallInto $(d:R=$(<)) : $(i) ; + } + + MODE on $(>:G=installed) = $(FILEMODE) ; +} + +rule InstallShell +{ + InstallInto $(<) : $(>) ; + MODE on $(>:G=installed) = $(SHELLMODE) ; +} + +rule Lex +{ + LexMv $(<) : $(>) ; + DEPENDS $(<) : $(>) ; + MakeLocate $(<) : $(LOCATE_SOURCE) ; + Clean clean : $(<) ; +} + +rule Library +{ + LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ; + Objects $(>) ; +} + +rule LibraryFromObjects +{ + local _i _l _s ; + + # Add grist to file names + + _s = [ FGristFiles $(>) ] ; + _l = $(<:S=$(SUFLIB)) ; + + # library depends on its member objects + + if $(KEEPOBJS) + { + DEPENDS obj : $(_s) ; + } + else + { + DEPENDS lib : $(_l) ; + } + + # Set LOCATE for the library and its contents. The bound + # value shows up as $(NEEDLIBS) on the Link actions. + # For compatibility, we only do this if the library doesn't + # already have a path. + + if ! $(_l:D) + { + MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ; + } + + if $(NOARSCAN) + { + # If we can't scan the library to timestamp its contents, + # we have to just make the library depend directly on the + # on-disk object files. + + DEPENDS $(_l) : $(_s) ; + } + else + { + # If we can scan the library, we make the library depend + # on its members and each member depend on the on-disk + # object file. + + DEPENDS $(_l) : $(_l)($(_s:BS)) ; + + for _i in $(_s) + { + DEPENDS $(_l)($(_i:BS)) : $(_i) ; + } + } + + Clean clean : $(_l) ; + + if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; } + + Archive $(_l) : $(_s) ; + + if $(RANLIB) { Ranlib $(_l) ; } + + # If we can't scan the library, we have to leave the .o's around. + + if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; } +} + +rule Link +{ + MODE on $(<) = $(EXEMODE) ; + Chmod $(<) ; +} + +rule LinkLibraries +{ + # make library dependencies of target + # set NEEDLIBS variable used by 'actions Main' + + local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ; + + DEPENDS $(_t) : $(>:S=$(SUFLIB)) ; + NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ; +} + +rule Main +{ + MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ; + Objects $(>) ; +} + +rule MainFromObjects +{ + local _s _t ; + + # Add grist to file names + # Add suffix to exe + + _s = [ FGristFiles $(>) ] ; + _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ; + + if $(_t) != $(<) + { + DEPENDS $(<) : $(_t) ; + NOTFILE $(<) ; + } + + # make compiled sources a dependency of target + + DEPENDS exe : $(_t) ; + DEPENDS $(_t) : $(_s) ; + MakeLocate $(_t) : $(LOCATE_TARGET) ; + + Clean clean : $(_t) ; + + Link $(_t) : $(_s) ; +} + +rule MakeLocate +{ + if $(>) + { + LOCATE on $(<) = $(>) ; + Depends $(<) : $(>[1]) ; + MkDir $(>[1]) ; + } +} + +rule MkDir +{ + # If dir exists, don't update it + # Do this even for $(DOT). + + NOUPDATE $(<) ; + + if $(<) != $(DOT) && ! $($(<)-mkdir) + { + local s ; + + # Cheesy gate to prevent multiple invocations on same dir + # MkDir1 has the actions + # Arrange for jam dirs + + $(<)-mkdir = true ; + MkDir1 $(<) ; + Depends dirs : $(<) ; + + # Recursively make parent directories. + # $(<:P) = $(<)'s parent, & we recurse until root + + s = $(<:P) ; + + if $(NT) + { + switch $(s) + { + case *: : s = ; + case *:\\ : s = ; + } + } + + if $(s) && $(s) != $(<) + { + Depends $(<) : $(s) ; + MkDir $(s) ; + } + else if $(s) + { + NOTFILE $(s) ; + } + + } +} + +rule Object +{ + local h ; + + # locate object and search for source, if wanted + + Clean clean : $(<) ; + + MakeLocate $(<) : $(LOCATE_TARGET) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + + # Save HDRS for -I$(HDRS) on compile. + # We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers + # in the .c file's directory, but generated .c files (from + # yacc, lex, etc) are located in $(LOCATE_TARGET), possibly + # different from $(SEARCH_SOURCE). + + HDRS on $(<) = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ; + + # handle #includes for source: Jam scans for headers with + # the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE) + # with the scanned file as the target and the found headers + # as the sources. HDRSEARCH is the value of SEARCH used for + # the found header files. Finally, if jam must deal with + # header files of the same name in different directories, + # they can be distinguished with HDRGRIST. + + # $(h) is where cc first looks for #include "foo.h" files. + # If the source file is in a distant directory, look there. + # Else, look in "" (the current directory). + + if $(SEARCH_SOURCE) + { + h = $(SEARCH_SOURCE) ; + } + else + { + h = "" ; + } + + HDRRULE on $(>) = HdrRule ; + HDRSCAN on $(>) = $(HDRPATTERN) ; + HDRSEARCH on $(>) = $(HDRS) $(SUBDIRHDRS) $(h) $(STDHDRS) ; + HDRGRIST on $(>) = $(HDRGRIST) ; + + # if source is not .c, generate .c with specific rule + + switch $(>:S) + { + case .asm : As $(<) : $(>) ; + case .c : Cc $(<) : $(>) ; + case .C : C++ $(<) : $(>) ; + case .cc : C++ $(<) : $(>) ; + case .cpp : C++ $(<) : $(>) ; + case .f : Fortran $(<) : $(>) ; + case .l : Cc $(<) : $(<:S=.c) ; + Lex $(<:S=.c) : $(>) ; + case .s : As $(<) : $(>) ; + case .y : Cc $(<) : $(<:S=.c) ; + Yacc $(<:S=.c) : $(>) ; + case * : UserObject $(<) : $(>) ; + } +} + + +rule ObjectCcFlags +{ + CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ; +} + +rule ObjectC++Flags +{ + C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ; +} + +rule ObjectHdrs +{ + HDRS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ; +} + +rule Objects +{ + local _i ; + + for _i in [ FGristFiles $(<) ] + { + Object $(_i:S=$(SUFOBJ)) : $(_i) ; + DEPENDS obj : $(_i:S=$(SUFOBJ)) ; + } +} + +rule RmTemps +{ + TEMPORARY $(>) ; +} + +rule Setuid +{ + MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ; +} + +rule Shell +{ + DEPENDS shell : $(<) ; + DEPENDS $(<) : $(>) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + MODE on $(<) = $(SHELLMODE) ; + Clean clean : $(<) ; + Chmod $(<) ; +} + +rule SubDir +{ + local _r _s ; + + # + # SubDir TOP d1 [ ... ] + # + # This introduces a Jamfile that is part of a project tree + # rooted at $(TOP). It (only once) includes the project-specific + # rules file $(TOP)/Jamrules and then sets search & locate stuff. + # + # If the variable $(TOPRULES) is set (where TOP is the first arg + # to SubDir), that file is included instead of $(TOP)/Jamrules. + # + # d1 ... are the directory elements that lead to this directory + # from $(TOP). We construct the system dependent path from these + # directory elements in order to set search & locate stuff. + # + + if ! $($(<[1])) + { + if ! $(<[1]) + { + EXIT SubDir syntax error ; + } + + $(<[1]) = [ FSubDir $(<[2-]) ] ; + } + + # + # If $(TOP)/Jamrules hasn't been included, do so. + # + + if ! $($(<[1])-included) + { + # Gated entry. + + $(<[1])-included = TRUE ; + + # File is $(TOPRULES) or $(TOP)/Jamrules. + + _r = $($(<[1])RULES) ; + + if ! $(_r) + { + _r = $(JAMRULES:R=$($(<[1]))) ; + } + + # Include it. + + include $(_r) ; + } + + # Get path to current directory from root using SubDir. + # Save dir tokens for other potential uses. + + _s = [ FDirName $(<[2-]) ] ; + SUBDIR = $(_s:R=$($(<[1]))) ; + SUBDIR_TOKENS = $(<[2-]) ; + + # Now set up SEARCH_SOURCE, LOCATE_TARGET, SOURCE_GRIST + # These can be reset if needed. For example, if the source + # directory should not hold object files, LOCATE_TARGET can + # subsequently be redefined. + + SEARCH_SOURCE = $(SUBDIR) ; + LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ; + LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ; + SOURCE_GRIST = [ FGrist $(<[2-]) ] ; + + # Reset per-directory ccflags, hdrs + + SUBDIRCCFLAGS = ; + SUBDIRC++FLAGS = ; + SUBDIRHDRS = ; +} + +rule SubDirCcFlags +{ + SUBDIRCCFLAGS += $(<) ; +} + +rule SubDirC++Flags +{ + SUBDIRC++FLAGS += $(<) ; +} + +rule SubDirHdrs +{ + SUBDIRHDRS += $(<) ; +} + +rule SubInclude +{ + local _s ; + + # That's + # SubInclude TOP d1 [ d2 [ d3 [ d4 ] ] ] + # + # to include a subdirectory's Jamfile. + + if ! $($(<[1])) + { + EXIT Top level of source tree has not been set with $(<[1]) ; + } + + _s = [ FDirName $(<[2-]) ] ; + + include $(JAMFILE:D=$(_s):R=$($(<[1]))) ; +} + +rule Undefines +{ + UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ; +} + +rule UserObject +{ + EXIT "Unknown suffix on" $(>) "- see UserObject rule in Jamfile(5)." ; +} + +rule Yacc +{ + local _h ; + + _h = $(<:BS=.h) ; + + # Some places don't have a yacc. + + MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ; + + if $(YACC) + { + DEPENDS $(<) $(_h) : $(>) ; + Yacc1 $(<) $(_h) : $(>) ; + YaccMv $(<) $(_h) : $(>) ; + Clean clean : $(<) $(_h) ; + } + + # Make sure someone includes $(_h) else it will be a deadly independent + # target. + INCLUDES $(<) : $(_h) ; +} + +# +# Utility rules; no side effects on these. +# + +rule FGrist +{ + # Turn individual elements in $(<) into grist. + + local _g _i ; + + _g = $(<[1]) ; + + for _i in $(<[2-]) + { + _g = $(_g)!$(_i) ; + } + + return $(_g) ; +} + +rule FGristFiles +{ + if ! $(SOURCE_GRIST) + { + return $(<) ; + } + else + { + return $(<:G=$(SOURCE_GRIST)) ; + } +} + +rule FGristSourceFiles +{ + # Produce source file name name with grist in it, + # if SOURCE_GRIST is set. + + # Leave header files alone, because they have a global + # visibility. + + if ! $(SOURCE_GRIST) + { + return $(<) ; + } + else + { + local _i _o ; + + for _i in $(<) + { + switch $(_i) + { + case *.h : _o += $(_i) ; + case * : _o += $(_i:G=$(SOURCE_GRIST)) ; + } + } + + return $(_o) ; + } +} + +rule FConcat +{ + # Puts the variables together, removing spaces. + + local _t _r ; + + $(_r) = $(<[1]) ; + + for _t in $(<[2-]) + { + $(_r) = $(_r)$(_t) ; + } + + return $(_r) ; +} + +rule FSubDir +{ + local _i _d ; + + # If $(>) is the path to the current directory, compute the + # path (using ../../ etc) back to that root directory. + # Sets result in $(<) + + if ! $(<[1]) + { + _d = $(DOT) ; + } + else + { + _d = $(DOTDOT) ; + + for _i in $(<[2-]) + { + _d = $(_d:R=$(DOTDOT)) ; + } + } + + return $(_d) ; +} + +rule FDirName +{ + local _s _i ; + + # Turn individual elements in $(<) into a usable path. + + if ! $(<) + { + _s = $(DOT) ; + } + else if $(VMS) + { + # This handles the following cases: + # a -> [.a] + # a b c -> [.a.b.c] + # x: -> x: + # x: a -> x:[a] + # x:[a] b -> x:[a.b] + + switch $(<[1]) + { + case *:* : _s = $(<[1]) ; + case \\[*\\] : _s = $(<[1]) ; + case * : _s = [.$(<[1])] ; + } + + for _i in [.$(<[2-])] + { + _s = $(_i:R=$(_s)) ; + } + } + else if $(MAC) + { + _s = $(DOT) ; + + for _i in $(<) + { + _s = $(_i:R=$(_s)) ; + } + } + else + { + _s = $(<[1]) ; + + for _i in $(<[2-]) + { + _s = $(_i:R=$(_s)) ; + } + } + + return $(_s) ; +} + + +rule _makeCommon +{ + # strip common initial elements + + if $($(<)[1]) && $($(<)[1]) = $($(>)[1]) + { + $(<) = $($(<)[2-]) ; + $(>) = $($(>)[2-]) ; + _makeCommon $(<) : $(>) ; + } +} + + +rule FRelPath +{ + local _l _r ; + + # first strip off common parts + + _l = $(<) ; + _r = $(>) ; + + _makeCommon _l : _r ; + + # now make path to root and path down + + _l = [ FSubDir $(_l) ] ; + _r = [ FDirName $(_r) ] ; + + # Concatenate and save + + # XXX This should be better + + if $(_r) = $(DOT) { + return $(_l) ; + } else { + return $(_r:R=$(_l)) ; + } +} + +rule FAppendSuffix +{ + # E.g., "FAppendSuffix yacc lex foo.bat : $(SUFEXE) ;" + # returns (yacc,lex,foo.bat) on Unix and + # (yacc.exe,lex.exe,foo.bat) on NT. + + if $(>) + { + local _i _o ; + + for _i in $(<) + { + if $(_i:S) + { + _o += $(_i) ; + } + else + { + _o += $(_i:S=$(>)) ; + } + } + return $(_o) ; + } + else + { + return $(<) ; + } +} + +rule unmakeDir +{ + if $(>[1]:D) && $(>[1]:D) != $(>[1]) && $(>[1]:D) != \\\\ + { + unmakeDir $(<) : $(>[1]:D) $(>[1]:BS) $(>[2-]) ; + } + else + { + $(<) = $(>) ; + } +} + + +rule FConvertToSlashes +{ + local _d, _s, _i ; + + unmakeDir _d : $(<) ; + + _s = $(_d[1]) ; + for _i in $(_d[2-]) + { + _s = $(_s)/$(_i) ; + } + return $(_s) ; +} + + +# +# Actions +# + +# +# First the defaults +# + +actions updated together piecemeal Archive +{ + $(AR) $(<) $(>) +} + +actions As +{ + $(AS) $(ASFLAGS) -I$(HDRS) -o $(<) $(>) +} + +actions C++ +{ + $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>) +} + +actions Cc +{ + $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>) +} + +actions Chgrp +{ + $(CHGRP) $(GROUP) $(<) +} + +actions Chmod1 +{ + $(CHMOD) $(MODE) $(<) +} + +actions Chown +{ + $(CHOWN) $(OWNER) $(<) +} + +actions piecemeal together existing Clean +{ + $(RM) $(>) +} + +actions File +{ + $(CP) $(>) $(<) +} + +actions GenFile1 +{ + $(>[1]) $(<) $(>[2-]) +} + +actions Fortran +{ + $(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>) +} + +actions HardLink +{ + $(RM) $(<) && $(LN) $(>) $(<) +} + +actions Install +{ + $(CP) $(>) $(<) +} + +actions Lex +{ + $(LEX) $(>) +} + +actions LexMv +{ + $(MV) lex.yy.c $(<) +} + +actions Link bind NEEDLIBS +{ + $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) +} + +actions MkDir1 +{ + $(MKDIR) $(<) +} + +actions together Ranlib +{ + $(RANLIB) $(<) +} + +actions quietly updated piecemeal together RmTemps +{ + $(RM) $(>) +} + +actions Shell +{ + $(AWK) ' + NR == 1 { print "$(SHELLHEADER)" } + NR == 1 && /^[#:]/ { next } + /^##/ { next } + { print } + ' < $(>) > $(<) +} + +actions Yacc1 +{ + $(YACC) $(YACCFLAGS) $(>) +} + +actions YaccMv +{ + $(MV) $(YACCFILES).c $(<[1]) + $(MV) $(YACCFILES).h $(<[2]) +} + +# +# RELOCATE - for compilers with broken -o flags +# + +if $(RELOCATE) +{ + actions C++ + { + $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) $(>) + } + + actions Cc + { + $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) $(>) + } + + actions ignore CcMv + { + [ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<) + } +} + +# +# NOARUPDATE - can't update an archive +# + +if $(NOARUPDATE) +{ + actions Archive + { + $(AR) $(<) $(>) + } +} + +# +# NT specific actions +# + +if $(NT) +{ + if $(TOOLSET) = VISUALC || $(TOOLSET) = VC7 || $(TOOLSET) = INTELC + { + actions updated together piecemeal Archive + { + if exist $(<) set _$(<:B)_=$(<) + $(AR) /out:$(<) %_$(<:B)_% $(>) + } + + actions As + { + $(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul; + } + + actions Cc + { + $(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) $(>) + } + + actions C++ + { + $(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) /Tp$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + } + else if $(TOOLSET) = VISUALC16 + { + actions updated together piecemeal Archive + { + $(AR) $(<) -+$(>) + } + + actions Cc + { + $(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) $(>) + } + + actions C++ + { + $(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /Tp$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + } + else if $(TOOLSET) = BORLANDC + { + actions updated together piecemeal Archive + { + $(AR) $(<) -+$(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>) + } + + actions C++ + { + $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + + actions Cc + { + $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + + } + else if $(TOOLSET) = MINGW + { + actions together piecemeal Archive + { + $(AR) $(<) $(>:T) + } + + actions Cc + { + $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + + actions C++ + { + $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + } + else if $(TOOLSET) = WATCOM + { + actions together piecemeal Archive + { + $(AR) $(<) +-$(>) + } + + actions Cc + { + $(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>) + } + + actions C++ + { + $(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + + actions Shell + { + $(CP) $(>) $(<) + } + } + else if $(TOOLSET) = LCC + { + actions together piecemeal Archive + { + $(AR) /out:$(<) $(>) + } + + actions Cc + { + $(CC) $(CCFLAGS) $(OPTIM) -Fo$(<) -I$(HDRS) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + + actions Shell + { + $(CP) $(>) $(<) + } + } +} + +# +# OS2 specific actions +# + +else if $(OS2) +{ + if $(TOOLSET) = WATCOM + { + actions together piecemeal Archive + { + $(AR) $(<) +-$(>) + } + + actions Cc + { + $(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>) + } + + actions C++ + { + $(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS) + } + + actions Shell + { + $(CP) $(>) $(<) + } + } + else if $(TOOLSET) = EMX + { + actions together piecemeal Archive + { + $(AR) $(<) $(>:T) + } + + actions Cc + { + $(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + + actions C++ + { + $(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>) + } + } +} + +# +# VMS specific actions +# + +else if $(VMS) +{ + actions updated together piecemeal Archive + { + lib/replace $(<) $(>[1]) ,$(>[2-]) + } + + actions Cc + { + $(CC)/obj=$(<) $(CCFLAGS) $(OPTIM) $(SLASHINC) $(>) + } + + actions C++ + { + $(C++)/obj=$(<) $(C++FLAGS) $(OPTIM) $(SLASHINC) $(>) + } + + actions piecemeal together existing Clean + { + $(RM) $(>[1]);* ,$(>[2-]);* + } + + actions together quietly CreLib + { + if f$search("$(<)") .eqs. "" then lib/create $(<) + } + + actions GenFile1 + { + mcr $(>[1]) $(<) $(>[2-]) + } + + actions Link bind NEEDLIBS + { + $(LINK)/exe=$(<) $(LINKFLAGS) $(>[1]) ,$(>[2-]) ,$(NEEDLIBS)/lib ,$(LINKLIBS) + } + + actions quietly updated piecemeal together RmTemps + { + $(RM) $(>[1]);* ,$(>[2-]);* + } + + actions Shell + { + $(CP) $(>) $(<) + } +} + +# +# Mac specifc actions +# + +else if $(MAC) +{ + actions together Archive + { + $(LINK) -library -o $(<) $(>) + } + + actions Cc + { + set -e MWCincludes $(MACINC) + $(CC) -o $(<) $(CCFLAGS) $(OPTIM) $(>) + } + + actions C++ + { + set -e MWCincludes $(MACINC) + $(CC) -o $(<) $(C++FLAGS) $(OPTIM) $(>) + } + + actions Link bind NEEDLIBS + { + $(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) "$(LINKLIBS)" + } +} + +# +# Backwards compatibility with jam 1, where rules were uppercased. +# + +rule BULK { Bulk $(<) : $(>) ; } +rule FILE { File $(<) : $(>) ; } +rule HDRRULE { HdrRule $(<) : $(>) ; } +rule INSTALL { Install $(<) : $(>) ; } +rule LIBRARY { Library $(<) : $(>) ; } +rule LIBS { LinkLibraries $(<) : $(>) ; } +rule LINK { Link $(<) : $(>) ; } +rule MAIN { Main $(<) : $(>) ; } +rule SETUID { Setuid $(<) ; } +rule SHELL { Shell $(<) : $(>) ; } +rule UNDEFINES { Undefines $(<) : $(>) ; } + +# Old INSTALL* didn't take dest directory. + +rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; } +rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; } +rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; } + +# Compatibility with jam 2.2. + +rule addDirName { $(<) += [ FDirName $(>) ] ; } +rule makeDirName { $(<) = [ FDirName $(>) ] ; } +rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; } +rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; } +rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; } + +# +# Now include the user's Jamfile. +# + +{ + if $(JAMFILE) { include $(JAMFILE) ; } +} + +} diff --git a/jam-files/engine/boost-jam.spec b/jam-files/engine/boost-jam.spec new file mode 100644 index 00000000..bc572fc9 --- /dev/null +++ b/jam-files/engine/boost-jam.spec @@ -0,0 +1,64 @@ +Name: boost-jam +Version: 3.1.19 +Summary: Build tool +Release: 1 +Source: %{name}-%{version}.tgz + +License: Boost Software License, Version 1.0 +Group: Development/Tools +URL: http://www.boost.org +Packager: Rene Rivera <grafik@redshift-software.com> +BuildRoot: /var/tmp/%{name}-%{version}.root + +%description +Boost Jam is a build tool based on FTJam, which in turn is based on +Perforce Jam. It contains significant improvements made to facilitate +its use in the Boost Build System, but should be backward compatible +with Perforce Jam. + +Authors: + Perforce Jam : Cristopher Seiwald + FT Jam : David Turner + Boost Jam : David Abrahams + +Copyright: + /+\ + +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + \+/ + 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. + +Also: + Copyright 2001-2006 David Abrahams. + Copyright 2002-2006 Rene Rivera. + Copyright 2003-2006 Vladimir Prus. + + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +%prep +%setup -n %{name}-%{version} + +%build +LOCATE_TARGET=bin ./build.sh $BOOST_JAM_TOOLSET + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT%{_bindir} +mkdir -p $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version} +install -m 755 bin/bjam $RPM_BUILD_ROOT%{_bindir}/bjam-%{version} +ln -sf bjam-%{version} $RPM_BUILD_ROOT%{_bindir}/bjam +cp -R *.html *.png *.css LICENSE*.txt images jam $RPM_BUILD_ROOT%{_docdir}/%{name}-%{version} + +find $RPM_BUILD_ROOT -name CVS -type d -exec rm -r {} \; + +%files +%defattr(-,root,root) +%attr(755,root,root) /usr/bin/* +%doc %{_docdir}/%{name}-%{version} + + +%clean +rm -rf $RPM_BUILD_ROOT diff --git a/jam-files/engine/boost-no-inspect b/jam-files/engine/boost-no-inspect new file mode 100644 index 00000000..8a06f3a7 --- /dev/null +++ b/jam-files/engine/boost-no-inspect @@ -0,0 +1 @@ +this really out of our hands, so tell inspect to ignore directory
\ No newline at end of file diff --git a/jam-files/engine/build.bat b/jam-files/engine/build.bat new file mode 100644 index 00000000..f927b769 --- /dev/null +++ b/jam-files/engine/build.bat @@ -0,0 +1,532 @@ +@ECHO OFF + +REM ~ Copyright 2002-2007 Rene Rivera. +REM ~ Distributed under the Boost Software License, Version 1.0. +REM ~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +setlocal +goto Start + + +:Set_Error +color 00 +goto :eof + + +:Clear_Error +ver >nul +goto :eof + + +:Error_Print +REM Output an error message and set the errorlevel to indicate failure. +setlocal +ECHO ### +ECHO ### %1 +ECHO ### +ECHO ### You can specify the toolset as the argument, i.e.: +ECHO ### .\build.bat msvc +ECHO ### +ECHO ### Toolsets supported by this script are: borland, como, gcc, gcc-nocygwin, +ECHO ### intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10 +ECHO ### +call :Set_Error +endlocal +goto :eof + + +:Test_Path +REM Tests for the given file(executable) presence in the directories in the PATH +REM environment variable. Additionaly sets FOUND_PATH to the path of the +REM found file. +call :Clear_Error +setlocal +set test=%~$PATH:1 +endlocal +if not errorlevel 1 set FOUND_PATH=%~dp$PATH:1 +goto :eof + + +:Test_Option +REM Tests whether the given string is in the form of an option: "--*" +call :Clear_Error +setlocal +set test=%1 +if not defined test ( + call :Set_Error + goto Test_Option_End +) +set test=###%test%### +set test=%test:"###=% +set test=%test:###"=% +set test=%test:###=% +if not "-" == "%test:~1,1%" call :Set_Error +:Test_Option_End +endlocal +goto :eof + + +:Test_Empty +REM Tests whether the given string is not empty +call :Clear_Error +setlocal +set test=%1 +if not defined test ( + call :Clear_Error + goto Test_Empty_End +) +set test=###%test%### +set test=%test:"###=% +set test=%test:###"=% +set test=%test:###=% +if not "" == "%test%" call :Set_Error +:Test_Empty_End +endlocal +goto :eof + + +:Call_If_Exists +if EXIST %1 call %* +goto :eof + + +:Guess_Toolset +REM Try and guess the toolset to bootstrap the build with... +REM Sets BOOST_JAM_TOOLSET to the first found toolset. +REM May also set BOOST_JAM_TOOLSET_ROOT to the +REM location of the found toolset. + +call :Clear_Error +call :Test_Empty %ProgramFiles% +if not errorlevel 1 set ProgramFiles=C:\Program Files + +call :Clear_Error +if NOT "_%VS100COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc10" + set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio 10.0\VC\VCVARSALL.BAT" ( + set "BOOST_JAM_TOOLSET=vc10" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 10.0\VC\" + goto :eof) +call :Clear_Error +if NOT "_%VS90COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc9" + set "BOOST_JAM_TOOLSET_ROOT=%VS90COMNTOOLS%..\..\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio 9.0\VC\VCVARSALL.BAT" ( + set "BOOST_JAM_TOOLSET=vc9" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 9.0\VC\" + goto :eof) +call :Clear_Error +if NOT "_%VS80COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc8" + set "BOOST_JAM_TOOLSET_ROOT=%VS80COMNTOOLS%..\..\VC\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio 8\VC\VCVARSALL.BAT" ( + set "BOOST_JAM_TOOLSET=vc8" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio 8\VC\" + goto :eof) +call :Clear_Error +if NOT "_%VS71COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET=vc7" + set "BOOST_JAM_TOOLSET_ROOT=%VS71COMNTOOLS%\..\..\VC7\" + goto :eof) +call :Clear_Error +if NOT "_%VCINSTALLDIR%_" == "__" ( + REM %VCINSTALLDIR% is also set for VC9 (and probably VC8) + set "BOOST_JAM_TOOLSET=vc7" + set "BOOST_JAM_TOOLSET_ROOT=%VCINSTALLDIR%\VC7\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio .NET 2003\VC7\bin\VCVARS32.BAT" ( + set "BOOST_JAM_TOOLSET=vc7" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio .NET 2003\VC7\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio .NET\VC7\bin\VCVARS32.BAT" ( + set "BOOST_JAM_TOOLSET=vc7" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio .NET\VC7\" + goto :eof) +call :Clear_Error +if NOT "_%MSVCDir%_" == "__" ( + set "BOOST_JAM_TOOLSET=msvc" + set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual Studio\VC98\bin\VCVARS32.BAT" ( + set "BOOST_JAM_TOOLSET=msvc" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual Studio\VC98\" + goto :eof) +call :Clear_Error +if EXIST "%ProgramFiles%\Microsoft Visual C++\VC98\bin\VCVARS32.BAT" ( + set "BOOST_JAM_TOOLSET=msvc" + set "BOOST_JAM_TOOLSET_ROOT=%ProgramFiles%\Microsoft Visual C++\VC98\" + goto :eof) +call :Clear_Error +call :Test_Path cl.exe +if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET=msvc" + set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\" + goto :eof) +call :Clear_Error +call :Test_Path vcvars32.bat +if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET=msvc" + call "%FOUND_PATH%VCVARS32.BAT" + set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\" + goto :eof) +call :Clear_Error +if EXIST "C:\Borland\BCC55\Bin\bcc32.exe" ( + set "BOOST_JAM_TOOLSET=borland" + set "BOOST_JAM_TOOLSET_ROOT=C:\Borland\BCC55\" + goto :eof) +call :Clear_Error +call :Test_Path bcc32.exe +if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET=borland" + set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\" + goto :eof) +call :Clear_Error +call :Test_Path icl.exe +if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET=intel-win32" + set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\" + goto :eof) +call :Clear_Error +if EXIST "C:\MinGW\bin\gcc.exe" ( + set "BOOST_JAM_TOOLSET=mingw" + set "BOOST_JAM_TOOLSET_ROOT=C:\MinGW\" + goto :eof) +call :Clear_Error +if NOT "_%CWFolder%_" == "__" ( + set "BOOST_JAM_TOOLSET=metrowerks" + set "BOOST_JAM_TOOLSET_ROOT=%CWFolder%\" + goto :eof ) +call :Clear_Error +call :Test_Path mwcc.exe +if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET=metrowerks" + set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\..\" + goto :eof) +call :Clear_Error +call :Error_Print "Could not find a suitable toolset." +goto :eof + + +:Guess_Yacc +REM Tries to find bison or yacc in common places so we can build the grammar. +call :Clear_Error +call :Test_Path yacc.exe +if not errorlevel 1 ( + set "YACC=yacc -d" + goto :eof) +call :Clear_Error +call :Test_Path bison.exe +if not errorlevel 1 ( + set "YACC=bison -d --yacc" + goto :eof) +call :Clear_Error +if EXIST "C:\Program Files\GnuWin32\bin\bison.exe" ( + set "YACC=C:\Program Files\GnuWin32\bin\bison.exe" -d --yacc + goto :eof) +call :Clear_Error +call :Error_Print "Could not find Yacc to build the Jam grammar." +goto :eof + + +:Start +set BOOST_JAM_TOOLSET= +set BOOST_JAM_ARGS= + +REM If no arguments guess the toolset; +REM or if first argument is an option guess the toolset; +REM otherwise the argument is the toolset to use. +call :Clear_Error +call :Test_Empty %1 +if not errorlevel 1 ( + call :Guess_Toolset + if not errorlevel 1 ( goto Setup_Toolset ) else ( goto Finish ) +) + +call :Clear_Error +call :Test_Option %1 +if not errorlevel 1 ( + call :Guess_Toolset + if not errorlevel 1 ( goto Setup_Toolset ) else ( goto Finish ) +) + +call :Clear_Error +set BOOST_JAM_TOOLSET=%1 +shift +goto Setup_Toolset + + +:Setup_Toolset +REM Setup the toolset command and options. This bit of code +REM needs to be flexible enough to handle both when +REM the toolset was guessed at and found, or when the toolset +REM was indicated in the command arguments. +REM NOTE: The strange multiple "if ?? == _toolset_" tests are that way +REM because in BAT variables are subsituted only once during a single +REM command. A complete "if ... ( commands ) else ( commands )" +REM is a single command, even though it's in multiple lines here. +:Setup_Args +call :Clear_Error +call :Test_Empty %1 +if not errorlevel 1 goto Config_Toolset +call :Clear_Error +call :Test_Option %1 +if errorlevel 1 ( + set BOOST_JAM_ARGS=%BOOST_JAM_ARGS% %1 + shift + goto Setup_Args +) +:Config_Toolset +if NOT "_%BOOST_JAM_TOOLSET%_" == "_metrowerks_" goto Skip_METROWERKS +if NOT "_%CWFolder%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%CWFolder%\" + ) +set "PATH=%BOOST_JAM_TOOLSET_ROOT%Other Metrowerks Tools\Command Line Tools;%PATH%" +set "BOOST_JAM_CC=mwcc -runtime ss -cwd include -DNT -lkernel32.lib -ladvapi32.lib -luser32.lib" +set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe" +set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe" +set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe" +set "_known_=1" +:Skip_METROWERKS +if NOT "_%BOOST_JAM_TOOLSET%_" == "_msvc_" goto Skip_MSVC +if NOT "_%MSVCDir%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%MSVCDir%\" + ) +call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%bin\VCVARS32.BAT" +if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) +set "BOOST_JAM_CC=cl /nologo /GZ /Zi /MLd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_MSVC +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc7_" goto Skip_VC7 +if NOT "_%VS71COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS71COMNTOOLS%..\..\VC7\" + ) +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%bin\VCVARS32.BAT" +if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if "_%VCINSTALLDIR%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) ) +set "BOOST_JAM_CC=cl /nologo /GZ /Zi /MLd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_VC7 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc8_" goto Skip_VC8 +if NOT "_%VS80COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS80COMNTOOLS%..\..\VC\" + ) +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS% +if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if "_%VCINSTALLDIR%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) ) +set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_VC8 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc9_" goto Skip_VC9 +if NOT "_%VS90COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS90COMNTOOLS%..\..\VC\" + ) +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS% +if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if "_%VCINSTALLDIR%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) ) +set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_VC9 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_vc10_" goto Skip_VC10 +if NOT "_%VS100COMNTOOLS%_" == "__" ( + set "BOOST_JAM_TOOLSET_ROOT=%VS100COMNTOOLS%..\..\VC\" + ) +if "_%VCINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%VCVARSALL.BAT" %BOOST_JAM_ARGS% +if NOT "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if "_%VCINSTALLDIR%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) ) +set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_VC10 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_borland_" goto Skip_BORLAND +if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + call :Test_Path bcc32.exe ) +if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + if not errorlevel 1 ( + set "BOOST_JAM_TOOLSET_ROOT=%FOUND_PATH%..\" + ) ) +if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%Bin;%PATH%" + ) +set "BOOST_JAM_CC=bcc32 -WC -w- -q -I%BOOST_JAM_TOOLSET_ROOT%Include -L%BOOST_JAM_TOOLSET_ROOT%Lib /DNT -nbootstrap" +set "BOOST_JAM_OPT_JAM=-ejam0" +set "BOOST_JAM_OPT_MKJAMBASE=-emkjambasejam0" +set "BOOST_JAM_OPT_YYACC=-eyyacc0" +set "_known_=1" +:Skip_BORLAND +if NOT "_%BOOST_JAM_TOOLSET%_" == "_como_" goto Skip_COMO +set "BOOST_JAM_CC=como -DNT" +set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe" +set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe" +set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe" +set "_known_=1" +:Skip_COMO +if NOT "_%BOOST_JAM_TOOLSET%_" == "_gcc_" goto Skip_GCC +set "BOOST_JAM_CC=gcc -DNT" +set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe" +set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe" +set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe" +set "_known_=1" +:Skip_GCC +if NOT "_%BOOST_JAM_TOOLSET%_" == "_gcc-nocygwin_" goto Skip_GCC_NOCYGWIN +set "BOOST_JAM_CC=gcc -DNT -mno-cygwin" +set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe" +set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe" +set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe" +set "_known_=1" +:Skip_GCC_NOCYGWIN +if NOT "_%BOOST_JAM_TOOLSET%_" == "_intel-win32_" goto Skip_INTEL_WIN32 +set "BOOST_JAM_CC=icl -DNT /nologo kernel32.lib advapi32.lib user32.lib" +set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" +set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" +set "BOOST_JAM_OPT_YYACC=/Febootstrap\yyacc0" +set "_known_=1" +:Skip_INTEL_WIN32 +if NOT "_%BOOST_JAM_TOOLSET%_" == "_mingw_" goto Skip_MINGW +if not "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( + set "PATH=%BOOST_JAM_TOOLSET_ROOT%bin;%PATH%" + ) +set "BOOST_JAM_CC=gcc -DNT" +set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe" +set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe" +set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe" +set "_known_=1" +:Skip_MINGW +call :Clear_Error +if "_%_known_%_" == "__" ( + call :Error_Print "Unknown toolset: %BOOST_JAM_TOOLSET%" +) +if errorlevel 1 goto Finish + +echo ### +echo ### Using '%BOOST_JAM_TOOLSET%' toolset. +echo ### + +set YYACC_SOURCES=yyacc.c +set MKJAMBASE_SOURCES=mkjambase.c +set BJAM_SOURCES= +set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c debug.c execnt.c expand.c filent.c glob.c hash.c +set BJAM_SOURCES=%BJAM_SOURCES% hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c +set BJAM_SOURCES=%BJAM_SOURCES% newstr.c option.c output.c parse.c pathunix.c regexp.c +set BJAM_SOURCES=%BJAM_SOURCES% rules.c scan.c search.c subst.c timestamp.c variable.c modules.c +set BJAM_SOURCES=%BJAM_SOURCES% strings.c filesys.c builtins.c md5.c pwd.c class.c w32_getreg.c native.c +set BJAM_SOURCES=%BJAM_SOURCES% modules/set.c modules/path.c modules/regex.c +set BJAM_SOURCES=%BJAM_SOURCES% modules/property-set.c modules/sequence.c modules/order.c + +set BJAM_UPDATE= +:Check_Update +call :Test_Empty %1 +if not errorlevel 1 goto Check_Update_End +call :Clear_Error +setlocal +set test=%1 +set test=###%test%### +set test=%test:"###=% +set test=%test:###"=% +set test=%test:###=% +if "%test%" == "--update" set BJAM_UPDATE=update +endlocal +shift +if not "_%BJAM_UPDATE%_" == "_update_" goto Check_Update +:Check_Update_End +if "_%BJAM_UPDATE%_" == "_update_" ( + if not exist ".\bootstrap\jam0.exe" ( + set BJAM_UPDATE= + ) +) + +@echo ON +@if "_%BJAM_UPDATE%_" == "_update_" goto Skip_Bootstrap +if exist bootstrap rd /S /Q bootstrap +md bootstrap +@if not exist jamgram.y goto Bootstrap_GrammarPrep +@if not exist jamgramtab.h goto Bootstrap_GrammarPrep +@goto Skip_GrammarPrep +:Bootstrap_GrammarPrep +%BOOST_JAM_CC% %BOOST_JAM_OPT_YYACC% %YYACC_SOURCES% +@if not exist ".\bootstrap\yyacc0.exe" goto Skip_GrammarPrep +.\bootstrap\yyacc0 jamgram.y jamgramtab.h jamgram.yy +:Skip_GrammarPrep +@if not exist jamgram.c goto Bootstrap_GrammarBuild +@if not exist jamgram.h goto Bootstrap_GrammarBuild +@goto Skip_GrammarBuild +:Bootstrap_GrammarBuild +@echo OFF +if "_%YACC%_" == "__" ( + call :Guess_Yacc +) +if errorlevel 1 goto Finish +@echo ON +%YACC% jamgram.y +@if errorlevel 1 goto Finish +del /f jamgram.c +rename y.tab.c jamgram.c +del /f jamgram.h +rename y.tab.h jamgram.h +:Skip_GrammarBuild +@echo ON +@if exist jambase.c goto Skip_Jambase +%BOOST_JAM_CC% %BOOST_JAM_OPT_MKJAMBASE% %MKJAMBASE_SOURCES% +@if not exist ".\bootstrap\mkjambase0.exe" goto Skip_Jambase +.\bootstrap\mkjambase0 jambase.c Jambase +:Skip_Jambase +%BOOST_JAM_CC% %BOOST_JAM_OPT_JAM% %BJAM_SOURCES% +:Skip_Bootstrap +@if not exist ".\bootstrap\jam0.exe" goto Skip_Jam +@if "_%BJAM_UPDATE%_" == "_update_" goto Skip_Clean +.\bootstrap\jam0 -f build.jam --toolset=%BOOST_JAM_TOOLSET% "--toolset-root=%BOOST_JAM_TOOLSET_ROOT% " clean +:Skip_Clean +@set args=%* +@echo OFF +:Set_Args +setlocal +call :Test_Empty %args% +if not errorlevel 1 goto Set_Args_End +set test=###%args:~0,2%### +set test=%test:"###=% +set test=%test:###"=% +set test=%test:###=% +set test=%test:~0,1% +if "-" == "%test%" goto Set_Args_End +endlocal +set args=%args:~1% +goto Set_Args +:Set_Args_End +@echo ON +.\bootstrap\jam0 -f build.jam --toolset=%BOOST_JAM_TOOLSET% "--toolset-root=%BOOST_JAM_TOOLSET_ROOT% " %args% +:Skip_Jam + +:Finish diff --git a/jam-files/engine/build.jam b/jam-files/engine/build.jam new file mode 100644 index 00000000..266b07a1 --- /dev/null +++ b/jam-files/engine/build.jam @@ -0,0 +1,1070 @@ +#~ Copyright 2002-2007 Rene Rivera. +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Clean env vars of any "extra" empty values. +for local v in ARGV CC CFLAGS LIBS +{ + local values ; + for local x in $($(v)) + { + if $(x) != "" + { + values += $(x) ; + } + } + $(v) = $(values) ; +} + +# Platform related specifics. +if $(OS) = NT { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; } +else if $(OS) = OS2 { rule .path { return "$(<:J=\\)" ; } ./ = "/" ; } +else if $(OS) = VMS { rule .path { return "[.$(<:J=/)]" ; } } +else if $(OS) = MAC { rule .path { return ":$(<:J=\:)" ; } } +else { rule .path { return "$(<:J=/)" ; } } +if $(OS) = VMS { . = "_" ; } +else { . = "." ; } +./ ?= "" ; + +# Info about what we are building. +_VERSION_ = 3 1 19 ; +NAME = boost-jam ; +VERSION = $(_VERSION_:J=$(.)) ; +RELEASE = 1 ; +LICENSE = LICENSE_1_0 ; + +# Generate development debug binaries? +if --debug in $(ARGV) +{ + debug = true ; +} + +if --profile in $(ARGV) +{ + profile = true ; +} + +# Attempt to generate and/or build the grammar? +if --grammar in $(ARGV) +{ + grammar = true ; +} + +# Do we need to add a default build type argument? +if ! ( --release in $(ARGV) ) && + ! ( --debug in $(ARGV) ) && + ! ( --profile in $(ARGV) ) +{ + ARGV += --release ; +} + +# Enable, and configure, Python hooks. +with-python = ; +python-location = [ MATCH --with-python=(.*) : $(ARGV) ] ; +if $(python-location) +{ + with-python = true ; +} +if $(with-python) +{ + if $(OS) = NT + { + --python-include = [ .path $(python-location) include ] ; + --python-lib = ; + for local v in 26 25 24 23 22 + { + --python-lib ?= + [ GLOB [ .path $(python-location) libs ] : "python$(v).lib" ] + [ GLOB $(python-location) [ .path $(python-location) libs ] + $(Path) $(PATH) $(path) : "python$(v).dll" ] + ; + if ! $(--python-lib[2]) + { + --python-lib = ; + } + } + --python-lib = $(--python-lib[1]) ; + } + else if $(OS) = MACOSX + { + --python-include = [ .path $(python-location) Headers ] ; + --python-lib = $(python-location) Python ; + } + else + { + --python-include = ; + --python-lib = ; + for local v in 2.6 2.5 2.4 2.3 2.2 + { + local inc = [ GLOB [ .path $(python-location) include ] : python$(v) ] ; + local lib = [ GLOB [ .path $(python-location) lib ] : libpython$(v)* ] ; + if $(inc) && $(lib) + { + --python-include ?= $(inc) ; + --python-lib ?= $(lib[1]:D) python$(v) ; + } + } + } +} + +# Boehm GC? +if --gc in $(ARGV) +{ + --boehm-gc = true ; +} +if $(--boehm-gc) +{ + --extra-include += [ .path [ PWD ] "boehm_gc" "include" ] ; +} + +# Duma? +if --duma in $(ARGV) +{ + --duma = true ; +} +if $(--duma) +{ + --extra-include += [ .path [ PWD ] "duma" ] ; +} + +# An explicit root for the toolset? (trim spaces) +toolset-root = [ MATCH --toolset-root=(.*) : $(ARGV) ] ; +{ + local t = [ MATCH "[ ]*(.*)" : $(toolset-root:J=" ") ] ; + toolset-root = ; + while $(t) + { + t = [ MATCH "([^ ]+)([ ]*)(.*)" : $(t) ] ; + toolset-root += $(t[1]) ; + if $(t[3]) { toolset-root += $(t[2]) ; } + t = $(t[3]) ; + } + toolset-root = $(toolset-root:J="") ; +} + +# Configure the implemented toolsets. These are minimal +# commands and options to compile the full Jam. When +# adding new toolsets make sure to add them to the +# "known" list also. + +rule toolset ( name command .type ? : opt.out + : opt.define * : flags * : linklibs * ) +{ + .type ?= "" ; + tool.$(name)$(.type).cc ?= $(command) ; + tool.$(name)$(.type).opt.out ?= $(opt.out) ; + tool.$(name)$(.type).opt.define ?= $(opt.define) ; + tool.$(name)$(.type).flags ?= $(flags) ; + tool.$(name)$(.type).linklibs ?= $(linklibs) ; + if ! $(name) in $(toolsets) { toolsets += $(name) ; } +} + +rule if-os ( os + : yes-opt * : no-opt * ) + { if $(os) in $(OS) { return $(yes-opt) ; } else { return $(no-opt) ; } } + +rule opt ( type : yes-opt * : no-opt * ) + { if $(type) in $(ARGV) { return $(yes-opt) ; } else { return $(no-opt) ; } } + +## HP-UX aCC compiler +toolset acc cc : "-o " : -D + : -Ae + [ opt --release : -s -O3 ] + [ opt --debug : -g -pg ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Borland C++ 5.5.x +toolset borland bcc32 : -e -n : /D + : -WC -w- -q "-I$(toolset-root)Include" "-L$(toolset-root)Lib" + [ opt --release : -O2 -vi -w-inl ] + [ opt --debug : -v -Od -vi- ] + -I$(--python-include) -I$(--extra-include) + : $(--python-lib[1]) ; +## Generic Unix cc +if ! $(CC) { CC = cc ; } +toolset cc $(CC) : "-o " : -D + : $(CFLAGS) + [ opt --release : -s -O ] + [ opt --debug : -g ] + -I$(--python-include) -I$(--extra-include) + : $(LIBS) -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Comeau C/C++ 4.x +toolset como como : "-o " : -D + : --c + [ opt --release : --inlining ] + [ opt --debug : --no_inlining ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Clang Linux 2.8+ +toolset clang clang : "-o " : -D + : -Wno-unused -Wno-format + [ opt --release : -Os ] + [ opt --debug : -g -O0 -fno-inline ] + [ opt --profile : -finline-functions -g ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## MacOSX Darwin, using GCC 2.9.x, 3.x +toolset darwin cc : "-o " : -D + : + [ opt --release : -Wl,-x -O3 -finline-functions ] + [ opt --debug : -g -O0 -fno-inline -pg ] + [ opt --profile : -Wl,-x -O3 -finline-functions -g -pg ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## GCC 2.x, 3.x, 4.x +toolset gcc gcc : "-o " : -D + : -pedantic -fno-strict-aliasing + [ opt --release : [ opt --symbols : -g : -s ] -O3 ] + [ opt --debug : -g -O0 -fno-inline ] + -I$(--python-include) -I$(--extra-include) -Wno-long-long + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## GCC 2.x, 3.x on CYGWIN but without cygwin1.dll +toolset gcc-nocygwin gcc : "-o " : -D + : -s -O3 -mno-cygwin + [ opt --release : -finline-functions ] + [ opt --debug : -s -O3 -fno-inline -pg ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Intel C/C++ for Darwin +toolset intel-darwin icc : "-o " : -D + : + [ opt --release : -O3 ] + [ opt --debug : -g -O0 -p ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Intel C/C++ for Linux +toolset intel-linux icc : "-o " : -D + : + [ opt --release : -Xlinker -s -O3 ] + [ opt --debug : -g -O0 -p ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Intel C/C++ for Win32 +toolset intel-win32 icl : /Fe : -D + : /nologo + [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /GB ] + [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## KCC ? +toolset kcc KCC : "-o " : -D + : + [ opt --release : -s +K2 ] + [ opt --debug : -g +K0 ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Borland Kylix +toolset kylix bc++ : -o : -D + : -tC -q + [ opt --release : -O2 -vi -w-inl ] + [ opt --debug : -v -Od -vi- ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Metrowerks CodeWarrior 8.x +{ + # Even though CW can compile all files at once, it crashes if it tries in the bjam case. + local mwcc = ; if $(OS) = NT { mwcc = mwcc ; } else { mwcc = mwc$(OSPLAT:L) ; } + mwcc ?= mwcc ; + toolset metrowerks $(mwcc) : "-o " : -D + : -c -lang c -subsystem console -cwd include + [ opt --release : -runtime ss -opt full -inline all ] + [ opt --debug : -runtime ssd -opt none -inline off ] + -I$(--python-include) -I$(--extra-include) ; + toolset metrowerks $(mwcc) .link : "-o " : + : -subsystem console -lkernel32.lib -ladvapi32.lib -luser32.lib + [ opt --release : -runtime ss ] + [ opt --debug : -runtime ssd ] + : $(--python-lib[1]) ; +} +## MINGW GCC +toolset mingw gcc : "-o " : -D + : + [ opt --release : -s -O3 -finline-functions ] + [ opt --debug : -g -O0 -fno-inline -pg ] + -I$(--python-include) -I$(--extra-include) + : $(--python-lib[2]) ; +## MIPS Pro +toolset mipspro cc : "-o " : -D + : + [ opt --release : -s -O3 -g0 -INLINE:none ] + [ opt --debug : -g -O0 -INLINE ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Microsoft Visual Studio C++ 6.x +toolset msvc cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /ML /O2 /Ob2 /Gy /GF /GA /GB ] + [ opt --debug : /MLd /DEBUG /Z7 /Od /Ob0 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## QNX 6.x GCC 3.x/2.95.3 +toolset qcc qcc : "-o " : -D + : -Wc,-pedantic -Wc,-fno-strict-aliasing + [ opt --release : [ opt --symbols : -g ] -O3 -Wc,-finline-functions ] + [ opt --debug : -g -O0 -Wc,-fno-inline ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Qlogic Pathscale 2.4 +toolset pathscale pathcc : "-o " : -D + : + [ opt --release : -s -Ofast -O3 ] + [ opt --debug : -g ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Portland Group Pgi 6.2 +toolset pgi pgcc : "-o " : -D + : + [ opt --release : -s -O3 ] + [ opt --debug : -g ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Sun Workshop 6 C++ +toolset sun cc : "-o " : -D + : + [ opt --release : -s -fast -xO4 ] + [ opt --debug : -g ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Sun Workshop 6 C++ (old alias) +toolset sunpro cc : "-o " : -D + : + [ opt --release : -s -fast -xO4 ] + [ opt --debug : -g ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## Compaq Alpha CXX +toolset tru64cxx cc : "-o " : -D + : + [ opt --release : -s -O5 -inline speed ] + [ opt --debug : -g -O0 -pg ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) ; +## IBM VisualAge C++ +toolset vacpp xlc : "-o " : -D + : + [ opt --release : -s -O3 -qstrict -qinline ] + [ opt --debug : -g -qNOOPTimize -qnoinline -pg ] + -I$(--python-include) -I$(--extra-include) + : -L$(--python-lib[1]) -l$(--python-lib[2]) [ if-os AIX : -bmaxdata:0x40000000 ] ; +## Microsoft Visual C++ .NET 7.x +toolset vc7 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /ML /O2 /Ob2 /Gy /GF /GA /GB ] + [ opt --debug : /MLd /DEBUG /Z7 /Od /Ob0 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## Microsoft Visual C++ 2005 +toolset vc8 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ] + [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## Microsoft Visual C++ 2008 +toolset vc9 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ] + [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## Microsoft Visual C++ 2010 +toolset vc10 cl : /Fe /Fe /Fd /Fo : -D + : /nologo + [ opt --release : /MT /O2 /Ob2 /Gy /GF /GA /wd4996 ] + [ opt --debug : /MTd /DEBUG /Z7 /Od /Ob0 /wd4996 ] + -I$(--python-include) -I$(--extra-include) + : kernel32.lib advapi32.lib user32.lib $(--python-lib[1]) ; +## VMS/OpenVMS DEC C +toolset vmsdecc cc : /OBJECT= : "/DEFINES=(" "," ")" + : /STANDARD=VAXC /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES + [ opt --release : /OPTIMIZE /NODEBUG ] + [ opt --debug : /NOOPTIMIZE /DEBUG ] + ; +toolset vmsdecc link .link : /EXECUTABLE= : + : /NOMAP + [ opt --release : /NODEBUG ] + [ opt --debug : /DEBUG ] + ; + +# First set the build commands and options according to the +# preset toolset. +toolset = [ MATCH --toolset=(.*) : $(ARGV) ] ; +if ! $(toolset) +{ + # For some reason, the following test does not catch empty toolset. + ECHO "###" ; + ECHO "###" No toolset specified. Please use --toolset option. ; + ECHO "###" ; + ECHO "###" Known toolsets are: $(toolsets:J=", ") ; + EXIT "###" ; +} +if ! $(toolset) in $(toolsets) +{ + ECHO "###" ; + ECHO "###" Unknown toolset: $(toolset) ; + ECHO "###" ; + ECHO "###" Known toolsets are: $(toolsets:J=", ") ; + EXIT "###" ; +} +--cc = $(tool.$(toolset).cc) ; +if $(tool.$(toolset).opt.out[2]) +{ + if $(tool.$(toolset).opt.out[1]) = $(tool.$(toolset).opt.out[2]) + { + --out = $(tool.$(toolset).opt.out[1]) ; + --dir = $(tool.$(toolset).opt.out[3-]) ; + } + else + { + --bin = $(tool.$(toolset).opt.out[1]) ; + --dir = $(tool.$(toolset).opt.out[2-]) ; + } +} +else +{ + --out = $(tool.$(toolset).opt.out) ; +} +--def = $(tool.$(toolset).opt.define) ; +--flags = $(tool.$(toolset).flags) ; +--defs = $(tool.$(toolset).defines) ; +--libs = $(tool.$(toolset).linklibs) ; +if $(tool.$(toolset).link.cc) +{ + --link = $(tool.$(toolset).link.cc) ; + if $(tool.$(toolset).link.opt.out[2]) + { + if $(tool.$(toolset).link.opt.out[1]) = $(tool.$(toolset).link.opt.out[2]) + { + --link-out = $(tool.$(toolset).link.opt.out[1]) ; + --link-dir = $(tool.$(toolset).link.opt.out[3-]) ; + } + else + { + --link-bin = $(tool.$(toolset).link.opt.out[1]) ; + --link-dir = $(tool.$(toolset).link.opt.out[2-]) ; + } + } + else + { + --link-out = $(tool.$(toolset).link.opt.out) ; + } + --link-def = $(tool.$(toolset).link.opt.define) ; + --link-flags = $(tool.$(toolset).link.flags) ; + --link-defs = $(tool.$(toolset).link.defines) ; + --link-libs = $(tool.$(toolset).link.linklibs) ; +} + +# Put executables in platform-specific subdirectory. +locate-target = $(LOCATE_TARGET) ; +if $(OS) = VMS +{ + locate-target ?= bin$(.)vms ; + platform = vms ; +} +else if $(OS) = MAC +{ + locate-target ?= bin$(.)$(OS:L)$(OSPLAT:L) ; + platform = $(OS:L)$(OSPLAT:L) ; +} +else if $(OSPLAT) +{ + locate-target ?= bin$(.)$(OS:L)$(OSPLAT:L) ; + platform = $(OS:L)$(OSPLAT:L) ; +} +else +{ + locate-target ?= bin$(.)$(OS:L) ; + platform = $(OS:L) ; +} +if $(debug) +{ + locate-target = [ .path $(locate-target)$(.)debug ] ; +} +if $(profile) +{ + locate-target = [ .path $(locate-target)$(.)profile ] ; +} +else +{ + locate-target = [ .path $(locate-target) ] ; +} + +if --show-locate-target in $(ARGV) +{ + ECHO $(locate-target) ; +} + +# We have some different files for UNIX, VMS, and NT. +jam.source = + command.c compile.c debug.c expand.c glob.c + hash.c hcache.c headers.c hdrmacro.c + jam.c jambase.c jamgram.c + lists.c make.c make1.c mem.c newstr.c + option.c output.c parse.c regexp.c rules.c + scan.c search.c subst.c w32_getreg.c + timestamp.c variable.c modules.c strings.c filesys.c + builtins.c pwd.c class.c native.c md5.c modules/set.c + modules/path.c modules/regex.c modules/property-set.c + modules/sequence.c modules/order.c + ; +if $(OS) = NT +{ + jam.source += execnt.c filent.c pathunix.c ; +} +else if $(OS) = OS2 +{ + jam.source += execunix.c fileos2.c pathunix.c ; +} +else if $(OS) = VMS +{ + jam.source += execvms.c filevms.c pathvms.c ; +} +else if $(OS) = MAC +{ + jam.source += execmac.c filemac.c pathmac.c ; +} +else +{ + jam.source += execunix.c fileunix.c pathunix.c ; +} + +# Debug assertions, or not. +if ! $(debug) || --noassert in $(ARGV) +{ + --defs += NDEBUG ; +} + +# Enable some optional features. +--defs += OPT_HEADER_CACHE_EXT ; +--defs += OPT_GRAPH_DEBUG_EXT ; +--defs += OPT_SEMAPHORE ; +--defs += OPT_AT_FILES ; +--defs += OPT_DEBUG_PROFILE ; + +# Bug fixes +--defs += OPT_FIX_TARGET_VARIABLES_EXT ; +#~ --defs += OPT_NO_EXTERNAL_VARIABLE_SPLIT ; + +# Improvements +--defs += OPT_IMPROVED_PATIENCE_EXT ; + +# Use Boehm GC memory allocator? +if $(--boehm-gc) +{ + --defs += OPT_BOEHM_GC ; + if $(debug) + { + --defs += GC_DEBUG ; + } +} + +if $(--duma) +{ + --defs += OPT_DUMA ; +} + +if ( $(OS) = NT ) && ! NT in $(--defs) +{ + --defs += NT ; +} +if $(OS) = VMS +{ + --defs += VMS ; +} +--defs += YYSTACKSIZE=5000 ; + +if $(with-python) +{ + --defs += HAVE_PYTHON ; +} + +if $(debug) +{ + --defs += BJAM_NEWSTR_NO_ALLOCATE ; +} + + +# The basic symbolic targets... +NOTFILE all clean dist ; +ALWAYS clean ; + +# Utility rules and actions... +rule .clean +{ + [DELETE] clean : $(<) ; +} +if $(OS) = NT { actions piecemeal together existing [DELETE] { + del /F /Q "$(>)" +} } +if $(UNIX) = true { actions piecemeal together existing [DELETE] { + rm -f "$(>)" +} } +if $(OS) = VMS { actions piecemeal together existing [DELETE] { + DELETE $(>[--2]:J=";*, ") $(>[-1]);* +} } +if $(OS) = NT { + --chmod+w = "attrib -r " ; +} +if $(UNIX) = true { + --chmod+w = "chmod +w " ; +} +if $(OS) = VMS { + --chmod+w = "SET FILE/PROT=(S:RWED) " ; +} + +rule .mkdir +{ + NOUPDATE $(<) ; + if $(<:P) { DEPENDS $(<) : $(<:P) ; .mkdir $(<:P) ; } + if ! $(md<$(<)>) { [MKDIR] $(<) ; md<$(<)> = - ; } +} +if $(OS) = NT { actions [MKDIR] { + md "$(<)" +} } +if $(UNIX) = true { actions [MKDIR] { + mkdir "$(<)" +} } +if $(OS) = VMS { actions [MKDIR] { + CREATE/DIR $(<J=", ") +} } + +rule .exe +{ + local exe = $(<) ; + if $(OS) = NT || ( $(UNIX) = true && $(OS) = CYGWIN ) || $(OS) = VMS { exe = $(exe:S=.exe) ; } + LOCATE on $(exe) = $(locate-target) ; + DEPENDS all : $(exe) ; + .mkdir $(locate-target) ; + if $(--link) + { + local objs = ; + for local s in $(>) + { + # Translate any subdir elements into a simple file name. + local o = [ MATCH "([^/]+)[/]?(.+)" : $(s) ] ; + o = $(o:J=_) ; + o = $(o:S=.o) ; + objs += $(o) ; + LOCATE on $(o) = $(locate-target) ; + DEPENDS $(exe) : $(o) ; + DEPENDS $(o) : $(s) ; + DEPENDS $(o) : $(locate-target) ; + [COMPILE] $(o) : $(s) ; + .clean $(o) ; + } + DEPENDS $(exe) : $(objs) ; + DEPENDS $(exe) : $(locate-target) ; + [COMPILE.LINK] $(exe) : $(objs) ; + .clean $(exe) ; + } + else + { + DEPENDS $(exe) : $(>) ; + DEPENDS $(exe) : $(locate-target) ; + [COMPILE] $(exe) : $(>) ; + .clean $(exe) ; + } + return $(exe) ; +} +if ! $(--def[2]) { actions [COMPILE] { + "$(--cc)" "$(--bin)$(<:D=)" "$(--dir)$(<:D)$(./)" $(--out)$(<) "$(--def)$(--defs)" "$(--flags)" "$(--libs)" "$(>)" +} } +else { actions [COMPILE] { + "$(--cc)" "$(--bin)$(<:D=)" "$(--dir)$(<:D)$(./)" $(--out)$(<) "$(--def[1])$(--defs:J=$(--def[2]))$(--def[3])" "$(--flags)" "$(--libs)" "$(>)" +} } +if $(OS) = VMS { actions [COMPILE.LINK] { + "$(--link)" $(--link-bin)$(<:D=) $(--link-dir)$(<:D)$(./) $(--link-out)$(<) $(--link-def)$(--link-defs) $(--link-flags) "$(--link-libs)" $(>J=", ") +} } +else { actions [COMPILE.LINK] { + "$(--link)" "$(--link-bin)$(<:D=)" "$(--link-dir)$(<:D)$(./)" "$(--link-out)$(<)" "$(--link-def)$(--link-defs)" "$(--link-flags)" "$(--link-libs)" "$(>)" +} } + +rule .link +{ + DEPENDS all : $(<) ; + DEPENDS $(<) : $(>) ; + [LINK] $(<) : $(>) ; + .clean $(<) ; +} +if $(OS) = NT { actions [LINK] { + copy "$(>)" "$(<)" +} } +if $(UNIX) = true { actions [LINK] { + ln -fs "$(>)" "$(<)" +} } +if $(OS) = VMS { actions [LINK] { + COPY/REPLACE $(>) $(<) +} } + +rule .copy +{ + DEPENDS all : $(<) ; + DEPENDS $(<) : $(>) ; + [COPY] $(<) : $(>) ; + .clean $(<) ; +} + +# Will be redefined later. +actions [COPY] +{ +} + + +rule .move +{ + DEPENDS $(<) : $(>) ; + [MOVE] $(<) : $(>) ; +} +if $(OS) = NT { actions [MOVE] { + del /f "$(<)" + rename "$(>)" "$(<)" +} } +if $(UNIX) = true { actions [MOVE] { + mv -f "$(>)" "$(<)" +} } +if $(OS) = VMS { actions [MOVE] { + RENAME "$(>)" "$(<)" +} } + +# Generate the grammar tokens table, and the real yacc grammar. +rule .yyacc +{ + local exe = [ .exe yyacc : yyacc.c ] ; + NOUPDATE $(exe) ; + DEPENDS $(<) : $(exe) $(>) ; + LEAVES $(<) ; + yyacc.exe on $(<) = $(exe:R=$(locate-target)) ; + [YYACC] $(<) : $(>) ; +} +actions [YYACC] { + $(--chmod+w)$(<[1]) + $(--chmod+w)$(<[2]) + "$(yyacc.exe)" "$(<)" "$(>)" +} +if $(grammar) +{ + .yyacc jamgram.y jamgramtab.h : jamgram.yy ; +} +else if $(debug) +{ + .exe yyacc : yyacc.c ; +} + +# How to build the grammar. +if $(OS) = NT +{ + SUFEXE = .exe ; + # try some other likely spellings... + PATH ?= $(Path) ; + PATH ?= $(path) ; +} +SUFEXE ?= "" ; + +yacc ?= [ GLOB $(PATH) : yacc$(SUFEXE) ] ; +yacc ?= [ GLOB $(PATH) : bison$(SUFEXE) ] ; +yacc ?= [ GLOB "$(ProgramFiles:J= )\\GnuWin32\\bin" "C:\\Program Files\\GnuWin32\\bin" : bison$(SUFEXE) ] ; +yacc = $(yacc[1]) ; +switch $(yacc:D=:S=) +{ + case bison : yacc += -d --yacc ; + case yacc : yacc += -d ; +} +if $(debug) && $(yacc) +{ + yacc += -t -v ; +} +yacc += $(YACCFLAGS) ; + +rule .yacc +{ + DEPENDS $(<) : $(>) ; + LEAVES $(<) ; + [YACC] $(<) : $(>) ; +} +if $(OS) = NT { actions [YACC] { + "$(yacc)" "$(>)" + if not errorlevel 1 ( + del /f "$(<[1])" + rename y.tab$(<[1]:S) "$(<[1])" + del /f $(<[2]) + rename y.tab$(<[2]:S) "$(<[2])" + ) else set _error_ = +} } +if $(UNIX) = true { actions [YACC] { + if ` "$(yacc)" "$(>)" ` ; then + mv -f y.tab$(<[1]:S) "$(<[1])" + mv -f y.tab$(<[2]:S) "$(<[2])" + else + exit 1 + fi +} } +if $(OS) = VMS { actions [YACC] { + IF "$(yacc)" $(>) + THEN + RENAME y_tab$(<[1]:S) $(<[1]) + RENAME y_tab$(<[2]:S) $(<[2]) + ENDIF +} } +if $(grammar) && ! $(yacc) +{ + EXIT "Could not find the 'yacc' tool, and therefore can not build the grammar." ; +} +if $(grammar) && $(yacc) +{ + .yacc jamgram.c jamgram.h : jamgram.y ; +} + +# How to build the compiled in jambase. +rule .mkjambase +{ + local exe = [ .exe mkjambase : mkjambase.c ] ; + DEPENDS $(<) : $(exe) $(>) ; + LEAVES $(<) ; + mkjambase.exe on $(<) = $(exe:R=$(locate-target)) ; + [MKJAMBASE] $(<) : $(>) ; +} +actions [MKJAMBASE] { + $(--chmod+w)$(<) + $(mkjambase.exe) "$(<)" "$(>)" +} +if $(debug) +{ + .mkjambase jambase.c : Jambase ; +} + +# How to build Jam. +rule .jam +{ + $(>).exe = [ .exe $(>) : $(jam.source) ] ; + DEPENDS all : $($(>).exe) ; + + # Make a copy under the old name. + $(<).exe = $(<:S=$($(>).exe:S)) ; + LOCATE on $($(<).exe) = $(locate-target) ; + .copy $($(<).exe) : $($(>).exe) ; + DEPENDS all : $($(<).exe) ; +} +.jam bjam : b2 ; + + +# Scan sources for header dependencies. +# WARNING: Yes those are *REAL TABS* below. DO NOT CHANGE, +# under any circumstances, to spaces!! And the tabs +# indenting this are so that if someone is in the mood to +# replace tabs they hit this comment, and hopefully notice +# their error. +rule .scan +{ + HDRRULE on $(<:D=) = .hdr.scan ; + HDRSCAN on $(<:D=) = "^[ ]*#[ ]*include[ ]*([<\"][^\">]*[\">]).*$" ; +} +rule .hdr.scan +{ + local hdrs = [ GLOB . : $(>:D=) ] ; + INCLUDES $(<:D=) : $(hdrs:D=) ; + HDRRULE on $(>:D=) = .hdr.scan ; + HDRSCAN on $(>:D=) = "^[ ]*#[ ]*include[ ]*([<\"][^\">]*[\">]).*$" ; +} +.scan [ GLOB . : *.c ] ; + +# Distribution making from here on out. Assumes that +# the docs are already built as html at ../doc/html. If +# they aren't, then the docs are not included in the dist +# archive. +dist.license = + [ GLOB . : $(LICENSE).txt ] + ; +dist.license = $(dist.license:D=) + [ GLOB [ .path .. .. .. ] : $(LICENSE).txt ] + [ GLOB [ .path .. boost ] : $(LICENSE).txt ] ; +dist.docs = + [ GLOB . : *.png *.css *.html ] + ; +dist.docs = $(dist.docs:D=) + [ GLOB [ .path images ] : *.png ] + [ GLOB [ .path jam ] : *.html ] + ; +dist.source = + [ GLOB . : *.c *.h ] + ; +dist.source = $(dist.source:D=) + $(dist.license[1]) + $(dist.docs) + build.jam build.bat build.sh build_vms.com + Jambase + jamgram.y jamgram.yy + [ .path modules set.c ] + [ .path modules path.c ] + [ .path modules regex.c ] + [ .path modules property-set.c ] + [ .path modules sequence.c ] + [ .path modules order.c ] + [ GLOB [ .path boehm_gc ] : * ] + [ GLOB [ .path boehm_gc include ] : * ] + [ GLOB [ .path boehm_gc include private ] : * ] + [ GLOB [ .path boehm_gc cord ] : * ] + [ GLOB [ .path boehm_gc Mac_files ] : * ] + [ GLOB [ .path boehm_gc tests ] : * ] + [ GLOB [ .path boehm_gc doc ] : * ] + ; +dist.bin = + bjam + ; +dist.bin = + $(dist.license[1]) + $(dist.bin:S=$(bjam.exe:S)) + ; + +if $(OS) = NT +{ + zip ?= [ GLOB "$(ProgramFiles:J= )\\7-ZIP" "C:\\Program Files\\7-ZIP" : "7z.exe" ] ; + zip ?= [ GLOB "$(ProgramFiles:J= )\\7-ZIP" "C:\\Program Files\\7-ZIP" : "7zn.exe" ] ; + zip ?= [ GLOB $(PATH) : zip.exe ] ; + zip ?= zip ; + zip = $(zip[1]) ; + switch $(zip:D=:S=) + { + case 7z* : zip += a -r -tzip -mx=9 ; + case zip : zip += -9r ; + } + actions piecemeal [PACK] { + "$(zip)" "$(<)" "$(>)" + } + actions piecemeal [ZIP] { + "$(zip)" "$(<)" "$(>)" + } + actions piecemeal [COPY] { + copy /Y "$(>)" "$(<)" >NUL: + } +} +if $(UNIX) = true +{ + tar ?= [ GLOB $(PATH) : star bsdtar tar ] ; + tar = $(tar[1]) ; + switch $(tar:D=:S=) + { + case star : tar += -c artype=pax -D -d -to-stdout ; + case * : tar += -c -f - ; + } + actions [PACK] { + "$(tar)" "$(>)" | gzip -c9 > "$(<)" + } + #~ actions [PACK] { + #~ tar cf "$(<:S=.tar)" "$(>)" + #~ } + actions [ZIP] { + gzip -c9 "$(>)" > "$(<)" + } + actions [COPY] { + cp -Rpf "$(>)" "$(<)" + } +} + +# The single binary, compressed. +rule .binary +{ + local zip = ; + if $(OS) = NT { zip = $($(<).exe:S=.zip) ; } + if $(UNIX) = true { zip = $($(<).exe:S=.tgz) ; } + zip = $(zip:S=)-$(VERSION)-$(RELEASE)-$(platform)$(zip:S) ; + DEPENDS $(zip) : $($(<).exe) ; + DEPENDS dist : $(zip) ; + #~ LOCATE on $(zip) = $(locate-target) ; + if $(OS) = NT { [ZIP] $(zip) : $($(<).exe) ; } + if $(UNIX) = true { [PACK] $(zip) : $($(<).exe) ; } + .clean $(zip) ; +} + +# Package some file. +rule .package ( dst-dir : src-files + ) +{ + local dst-files ; + local src-files-actual ; + for local src-path in $(src-files) + { + if ! [ GLOB $(src-path:P) : $(src-path:B) ] || [ CHECK_IF_FILE $(src-path) ] + { + local src-subdir = $(src-path:D) ; + local src-file = $(src-path) ; + while $(src-subdir:D) { src-subdir = $(src-subdir:D) ; } + if $(src-subdir) = ".." + { + src-file = $(src-file:D=) ; + } + dst-files += $(src-file:R=$(dst-dir)) ; + src-files-actual += $(src-path) ; + } + } + + local pack = ; + if $(OS) = NT { pack = $(dst-dir).zip ; } + if $(UNIX) = true { pack = $(dst-dir).tgz ; } + + DEPENDS dist : $(pack) ; + DEPENDS $(pack) : $(dst-files) ; + + local dst-files-queue = $(dst-files) ; + for local src-path in $(src-files-actual) + { + local dst-file = $(dst-files-queue[1]) ; + dst-files-queue = $(dst-files-queue[2-]) ; + DEPENDS $(dst-file) : $(src-path) $(dst-file:D) ; + .mkdir $(dst-file:D) ; + + [COPY] $(dst-file) : $(src-path) ; + .clean $(dst-file) ; + } + + [PACK] $(pack) : $(dst-files) ; + .clean $(pack) ; +} + +# RPM distro file. +rpm-tool = [ GLOB $(PATH) : "rpmbuild" ] ; +rpm-tool ?= [ GLOB $(PATH) : "rpm" ] ; +rpm-tool = $(rpm-tool[1]) ; +rule .rpm ( name : source ) +{ + local rpm-arch = ; + switch $(OSPLAT) + { + case X86 : rpm-arch ?= i386 ; + case PPC : rpm-arch ?= ppc ; + case AXP : rpm-arch ?= alpha ; + # no guaranty for these: + case IA64 : rpm-arch ?= ia64 ; + case ARM : rpm-arch ?= arm ; + case SPARC : rpm-arch ?= sparc ; + case * : rpm-arch ?= other ; + } + local target = $(name)-rpm ; + NOTFILE $(target) ; + DEPENDS dist : $(target) ; + DEPENDS $(target) : $(name).$(rpm-arch).rpm $(name).src.rpm ; + DEPENDS $(name).$(rpm-arch).rpm : $(source) ; + DEPENDS $(name).src.rpm : $(name).$(rpm-arch).rpm ; + docs on $(target) = $(dist.docs:J=" ") ; + arch on $(target) = $(rpm-arch) ; + if $(rpm-arch) = ppc { target-opt on $(target) = --target= ; } + else { target-opt on $(target) = "--target " ; } + [RPM] $(target) : $(source) ; + .clean $(name).$(rpm-arch).rpm $(name).src.rpm ; +} +actions [RPM] { + set -e + export BOOST_JAM_TOOLSET="$(toolset)" + $(rpm-tool) -ta $(target-opt)$(arch) $(>) | tee rpm.out + cp `grep -e '^Wrote:' rpm.out | sed 's/^Wrote: //'` . + rm -f rpm.out +} + +# The distribution targets. Don't bother with the targets if +# distribution build not requested. +if dist in $(ARGV) +{ + #~ .binary bjam ; + .package $(NAME)-$(VERSION) : $(dist.source) ; + .package $(NAME)-$(VERSION)-$(RELEASE)-$(platform) : $(dist.bin) ; + if $(rpm-tool) + { + #~ .rpm $(NAME)-$(VERSION)-$(RELEASE) : $(NAME)-$(VERSION).tgz ; + } +} diff --git a/jam-files/engine/build.sh b/jam-files/engine/build.sh new file mode 100755 index 00000000..f1fb806d --- /dev/null +++ b/jam-files/engine/build.sh @@ -0,0 +1,303 @@ +#!/bin/sh + +#~ Copyright 2002-2005 Rene Rivera. +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Reset the toolset. +BOOST_JAM_TOOLSET= + +# Run a command, and echo before doing so. Also checks the exit +# status and quits if there was an error. +echo_run () +{ + echo "$@" + $@ + r=$? + if test $r -ne 0 ; then + exit $r + fi +} + +# Print an error message, and exit with a status of 1. +error_exit () +{ + echo "###" + echo "###" "$@" + echo "###" + echo "### You can specify the toolset as the argument, i.e.:" + echo "### ./build.sh gcc" + echo "###" + echo "### Toolsets supported by this script are:" + echo "### acc, como, darwin, gcc, intel-darwin, intel-linux, kcc, kylix," + echo "### mipspro, mingw(msys), pathscale, pgi, qcc, sun, sunpro, tru64cxx, vacpp" + echo "###" + echo "### A special toolset; cc, is available which is used as a fallback" + echo "### when a more specific toolset is not found and the cc command is" + echo "### detected. The 'cc' toolset will use the CC, CFLAGS, and LIBS" + echo "### envrironment variables, if present." + echo "###" + exit 1 +} + +# Check that a command is in the PATH. +test_path () +{ + if `command -v command 1>/dev/null 2>/dev/null`; then + command -v $1 1>/dev/null 2>/dev/null + else + hash $1 1>/dev/null 2>/dev/null + fi +} + +# Check that the OS name, as returned by "uname", is as given. +test_uname () +{ + if test_path uname; then + test `uname` = $* + fi +} + +# Try and guess the toolset to bootstrap the build with... +Guess_Toolset () +{ + if test -r /mingw/bin/gcc ; then + BOOST_JAM_TOOLSET=mingw + BOOST_JAM_TOOLSET_ROOT=/mingw/ + elif test_uname Darwin ; then BOOST_JAM_TOOLSET=darwin + elif test_uname IRIX ; then BOOST_JAM_TOOLSET=mipspro + elif test_uname IRIX64 ; then BOOST_JAM_TOOLSET=mipspro + elif test_uname OSF1 ; then BOOST_JAM_TOOLSET=tru64cxx + elif test_uname QNX && test_path qcc ; then BOOST_JAM_TOOLSET=qcc + elif test_path gcc ; then BOOST_JAM_TOOLSET=gcc + elif test_path icc ; then BOOST_JAM_TOOLSET=intel-linux + elif test -r /opt/intel/cc/9.0/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET=intel-linux + BOOST_JAM_TOOLSET_ROOT=/opt/intel/cc/9.0 + elif test -r /opt/intel_cc_80/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET=intel-linux + BOOST_JAM_TOOLSET_ROOT=/opt/intel_cc_80 + elif test -r /opt/intel/compiler70/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET=intel-linux + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler70/ia32/ + elif test -r /opt/intel/compiler60/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET=intel-linux + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler60/ia32/ + elif test -r /opt/intel/compiler50/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET=intel-linux + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler50/ia32/ + elif test_path pgcc ; then BOOST_JAM_TOOLSET=pgi + elif test_path pathcc ; then BOOST_JAM_TOOLSET=pathscale + elif test_path xlc ; then BOOST_JAM_TOOLSET=vacpp + elif test_path como ; then BOOST_JAM_TOOLSET=como + elif test_path KCC ; then BOOST_JAM_TOOLSET=kcc + elif test_path bc++ ; then BOOST_JAM_TOOLSET=kylix + elif test_path aCC ; then BOOST_JAM_TOOLSET=acc + elif test_uname HP-UX ; then BOOST_JAM_TOOLSET=acc + elif test -r /opt/SUNWspro/bin/cc ; then + BOOST_JAM_TOOLSET=sunpro + BOOST_JAM_TOOLSET_ROOT=/opt/SUNWspro/ + # Test for "cc" as the default fallback. + elif test_path $CC ; then BOOST_JAM_TOOLSET=cc + elif test_path cc ; then + BOOST_JAM_TOOLSET=cc + CC=cc + fi + if test "$BOOST_JAM_TOOLSET" = "" ; then + error_exit "Could not find a suitable toolset." + fi +} + +# The one option we support in the invocation +# is the name of the toolset to force building +# with. +case "$1" in + --guess-toolset) Guess_Toolset ; echo "$BOOST_JAM_TOOLSET" ; exit 1 ;; + -*) Guess_Toolset ;; + ?*) BOOST_JAM_TOOLSET=$1 ; shift ;; + *) Guess_Toolset ;; +esac +BOOST_JAM_OPT_JAM="-o bootstrap/jam0" +BOOST_JAM_OPT_MKJAMBASE="-o bootstrap/mkjambase0" +BOOST_JAM_OPT_YYACC="-o bootstrap/yyacc0" +case $BOOST_JAM_TOOLSET in + mingw) + if test -r ${BOOST_JAM_TOOLSET_ROOT}bin/gcc ; then + export PATH=${BOOST_JAM_TOOLSET_ROOT}bin:$PATH + fi + BOOST_JAM_CC="gcc -DNT" + ;; + + gcc) + BOOST_JAM_CC=gcc + ;; + + darwin) + BOOST_JAM_CC=cc + ;; + + intel-darwin) + BOOST_JAM_CC=icc + ;; + + intel-linux) + if test -r /opt/intel/cc/9.0/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET_ROOT=/opt/intel/cc/9.0/ + elif test -r /opt/intel_cc_80/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET_ROOT=/opt/intel_cc_80/ + elif test -r /opt/intel/compiler70/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler70/ia32/ + elif test -r /opt/intel/compiler60/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler60/ia32/ + elif test -r /opt/intel/compiler50/ia32/bin/iccvars.sh ; then + BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler50/ia32/ + fi + if test -r ${BOOST_JAM_TOOLSET_ROOT}bin/iccvars.sh ; then + # iccvars doesn't change LD_RUN_PATH. We adjust LD_RUN_PATH + # here in order not to have to rely on ld.so.conf knowing the + # icc library directory. We do this before running iccvars.sh + # in order to allow a user to add modifications to LD_RUN_PATH + # in iccvars.sh. + if test -z "${LD_RUN_PATH}"; then + LD_RUN_PATH="${BOOST_JAM_TOOLSET_ROOT}lib" + else + LD_RUN_PATH="${BOOST_JAM_TOOLSET_ROOT}lib:${LD_RUN_PATH}" + fi + export LD_RUN_PATH + . ${BOOST_JAM_TOOLSET_ROOT}bin/iccvars.sh + fi + BOOST_JAM_CC=icc + ;; + + vacpp) + BOOST_JAM_CC=xlc + ;; + + como) + BOOST_JAM_CC="como --c" + ;; + + kcc) + BOOST_JAM_CC=KCC + ;; + + kylix) + BOOST_JAM_CC=bc++ + ;; + + mipspro) + BOOST_JAM_CC=cc + ;; + + pathscale) + BOOST_JAM_CC=pathcc + ;; + + pgi) + BOOST_JAM_CC=pgcc + ;; + + sun*) + if test -z "${BOOST_JAM_TOOLSET_ROOT}" -a -r /opt/SUNWspro/bin/cc ; then + BOOST_JAM_TOOLSET_ROOT=/opt/SUNWspro/ + fi + if test -r "${BOOST_JAM_TOOLSET_ROOT}bin/cc" ; then + PATH=${BOOST_JAM_TOOLSET_ROOT}bin:${PATH} + export PATH + fi + BOOST_JAM_CC=cc + ;; + + clang*) + BOOST_JAM_CC="clang -Wno-unused -Wno-format" + BOOST_JAM_TOOLSET=clang + ;; + + tru64cxx) + BOOST_JAM_CC=cc + ;; + + acc) + BOOST_JAM_CC="cc -Ae" + ;; + + cc) + if test -z "$CC" ; then CC=cc ; fi + BOOST_JAM_CC=$CC + BOOST_JAM_OPT_JAM="$BOOST_JAM_OPT_JAM $CFLAGS $LIBS" + BOOST_JAM_OPT_MKJAMBASE="$BOOST_JAM_OPT_MKJAMBASE $CFLAGS $LIBS" + BOOST_JAM_OPT_YYACC="$BOOST_JAM_OPT_YYACC $CFLAGS $LIBS" + ;; + + qcc) + BOOST_JAM_CC=qcc + ;; + + *) + error_exit "Unknown toolset: $BOOST_JAM_TOOLSET" + ;; +esac + +echo "###" +echo "### Using '$BOOST_JAM_TOOLSET' toolset." +echo "###" + +YYACC_SOURCES="yyacc.c" +MKJAMBASE_SOURCES="mkjambase.c" +BJAM_SOURCES="\ + command.c compile.c debug.c expand.c glob.c hash.c\ + hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c\ + newstr.c option.c output.c parse.c pathunix.c pathvms.c regexp.c\ + rules.c scan.c search.c subst.c timestamp.c variable.c modules.c\ + strings.c filesys.c builtins.c pwd.c class.c native.c md5.c w32_getreg.c\ + modules/set.c modules/path.c modules/regex.c modules/property-set.c\ + modules/sequence.c modules/order.c" +case $BOOST_JAM_TOOLSET in + mingw) + BJAM_SOURCES="${BJAM_SOURCES} execnt.c filent.c" + ;; + + *) + BJAM_SOURCES="${BJAM_SOURCES} execunix.c fileunix.c" + ;; +esac + +BJAM_UPDATE= +if test "$1" = "--update" -o "$2" = "--update" -o "$3" = "--update" -o "$4" = "--update" ; then + BJAM_UPDATE="update" +fi +if test "${BJAM_UPDATE}" = "update" -a ! -x "./bootstrap/jam0" ; then + BJAM_UPDATE= +fi + +if test "${BJAM_UPDATE}" != "update" ; then + echo_run rm -rf bootstrap + echo_run mkdir bootstrap + if test ! -r jamgram.y -o ! -r jamgramtab.h ; then + echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_YYACC} ${YYACC_SOURCES} + if test -x "./bootstrap/yyacc0" ; then + echo_run ./bootstrap/yyacc0 jamgram.y jamgramtab.h jamgram.yy + fi + fi + if test ! -r jamgram.c -o ! -r jamgram.h ; then + if test_path yacc ; then YACC="yacc -d" + elif test_path bison ; then YACC="bison -y -d --yacc" + fi + echo_run $YACC jamgram.y + mv -f y.tab.c jamgram.c + mv -f y.tab.h jamgram.h + fi + if test ! -r jambase.c ; then + echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_MKJAMBASE} ${MKJAMBASE_SOURCES} + if test -x "./bootstrap/mkjambase0" ; then + echo_run ./bootstrap/mkjambase0 jambase.c Jambase + fi + fi + echo_run ${BOOST_JAM_CC} ${BOOST_JAM_OPT_JAM} ${BJAM_SOURCES} +fi +if test -x "./bootstrap/jam0" ; then + if test "${BJAM_UPDATE}" != "update" ; then + echo_run ./bootstrap/jam0 -f build.jam --toolset=$BOOST_JAM_TOOLSET "--toolset-root=$BOOST_JAM_TOOLSET_ROOT" clean + fi + echo_run ./bootstrap/jam0 -f build.jam --toolset=$BOOST_JAM_TOOLSET "--toolset-root=$BOOST_JAM_TOOLSET_ROOT" "$@" +fi diff --git a/jam-files/engine/build_vms.com b/jam-files/engine/build_vms.com new file mode 100644 index 00000000..965b6342 --- /dev/null +++ b/jam-files/engine/build_vms.com @@ -0,0 +1,105 @@ +$ ! Copyright 2002-2003 Rene Rivera, Johan Nilsson. +$ ! Distributed under the Boost Software License, Version 1.0. +$ ! (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +$ ! +$ ! bootstrap build script for Jam +$ ! +$ SAY :== WRITE SYS$OUTPUT +$ ! +$ ON WARNING THEN CONTINUE +$ ! +$ IF "" .NES. F$SEARCH("[.bootstrap_vms]*.*") +$ THEN +$ SAY "Cleaning previous boostrap files..." +$ ! +$ SET FILE/PROTECTION=(S:RWED) [.bootstrap_vms]*.*;* +$ DELETE [.bootstrap_vms]*.*;* +$ ENDIF +$ ! +$ IF "" .NES. F$SEARCH("bootstrap_vms.dir") +$ THEN +$ SAY "Removing previous boostrap directory..." +$ ! +$ SET FILE/PROT=(S:RWED) bootstrap_vms.dir +$ DELETE bootstrap_vms.dir; +$ ENDIF +$ ! +$ SAY "Creating boostrap directory..." +$ ! +$ CREATE/DIR [.bootstrap_vms] +$ ! +$ SAY "Building bootstrap jam..." +$ ! +$ CC_FLAGS = "/DEFINE=VMS /STANDARD=VAXC /PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES " +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]builtins.obj builtins.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]command.obj command.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]compile.obj compile.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]execvms.obj execvms.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]expand.obj expand.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]filesys.obj filesys.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]filevms.obj filevms.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]glob.obj glob.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]hash.obj hash.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]hdrmacro.obj hdrmacro.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]headers.obj headers.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jam.obj jam.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jambase.obj jambase.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]jamgram.obj jamgram.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]lists.obj lists.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]make.obj make.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]make1.obj make1.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]modules.obj modules.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]newstr.obj newstr.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]option.obj option.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]parse.obj parse.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]pathvms.obj pathvms.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]pwd.obj pwd.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]regexp.obj regexp.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]rules.obj rules.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]scan.obj scan.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]search.obj search.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]strings.obj strings.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]subst.obj subst.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]timestamp.obj timestamp.c +$ cc 'CC_FLAGS /OBJECT=[.bootstrap_vms]variable.obj variable.c +$ link - + /EXECUTABLE=[.bootstrap_vms]jam0.exe - + [.bootstrap_vms]builtins.obj, - + [.bootstrap_vms]command.obj, - + [.bootstrap_vms]compile.obj, - + [.bootstrap_vms]execvms.obj, - + [.bootstrap_vms]expand.obj, - + [.bootstrap_vms]filesys.obj, - + [.bootstrap_vms]filevms.obj, - + [.bootstrap_vms]glob.obj, - + [.bootstrap_vms]hash.obj, - + [.bootstrap_vms]hdrmacro.obj, - + [.bootstrap_vms]headers.obj, - + [.bootstrap_vms]jam.obj, - + [.bootstrap_vms]jambase.obj, - + [.bootstrap_vms]jamgram.obj, - + [.bootstrap_vms]lists.obj, - + [.bootstrap_vms]make.obj, - + [.bootstrap_vms]make1.obj, - + [.bootstrap_vms]modules.obj, - + [.bootstrap_vms]newstr.obj, - + [.bootstrap_vms]option.obj, - + [.bootstrap_vms]parse.obj, - + [.bootstrap_vms]pathvms.obj, - + [.bootstrap_vms]pwd.obj, - + [.bootstrap_vms]regexp.obj, - + [.bootstrap_vms]rules.obj, - + [.bootstrap_vms]scan.obj, - + [.bootstrap_vms]search.obj, - + [.bootstrap_vms]strings.obj, - + [.bootstrap_vms]subst.obj, - + [.bootstrap_vms]timestamp.obj, - + [.bootstrap_vms]variable.obj +$ ! +$ SAY "Cleaning any previous build..." +$ ! +$ MCR [.bootstrap_vms]jam0.exe -f build.jam --toolset=vmsdecc clean +$ ! +$ SAY "Building Boost.Jam..." +$ ! +$ MCR [.bootstrap_vms]jam0.exe -f build.jam --toolset=vmsdecc diff --git a/jam-files/engine/builtins.c b/jam-files/engine/builtins.c new file mode 100644 index 00000000..b28a484e --- /dev/null +++ b/jam-files/engine/builtins.c @@ -0,0 +1,2310 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "builtins.h" +#include "rules.h" +#include "filesys.h" +#include "newstr.h" +#include "regexp.h" +#include "frames.h" +#include "hash.h" +#include "strings.h" +#include "pwd.h" +#include "pathsys.h" +#include "make.h" +#include "hdrmacro.h" +#include "compile.h" +#include "native.h" +#include "variable.h" +#include "timestamp.h" +#include "md5.h" +#include <ctype.h> + +#if defined(USE_EXECUNIX) +# include <sys/types.h> +# include <sys/wait.h> +#else +/* + NT does not have wait() and associated macros, it uses the return value + of system() instead. Status code group are documented at + http://msdn.microsoft.com/en-gb/library/ff565436.aspx +*/ +# define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) +# define WEXITSTATUS(w)(w) +#endif + +/* + * builtins.c - builtin jam rules + * + * External routines: + * + * load_builtin() - define builtin rules + * + * Internal routines: + * + * builtin_depends() - DEPENDS/INCLUDES rule. + * builtin_echo() - ECHO rule. + * builtin_exit() - EXIT rule. + * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule. + * builtin_glob() - GLOB rule. + * builtin_match() - MATCH rule. + * + * 01/10/01 (seiwald) - split from compile.c + */ + + +/* + * compile_builtin() - define builtin rules + */ + +#define P0 (PARSE *)0 +#define C0 (char *)0 + +#if defined( OS_NT ) || defined( OS_CYGWIN ) + LIST * builtin_system_registry ( PARSE *, FRAME * ); + LIST * builtin_system_registry_names( PARSE *, FRAME * ); +#endif + +int glob( char * s, char * c ); + +void backtrace ( FRAME * ); +void backtrace_line ( FRAME * ); +void print_source_line( PARSE * ); + + +RULE * bind_builtin( char * name, LIST * (* f)( PARSE *, FRAME * ), int flags, char * * args ) +{ + argument_list* arg_list = 0; + + if ( args ) + { + arg_list = args_new(); + lol_build( arg_list->data, args ); + } + + return new_rule_body( root_module(), name, arg_list, + parse_make( f, P0, P0, P0, C0, C0, flags ), 1 ); +} + + +RULE * duplicate_rule( char * name, RULE * other ) +{ + return import_rule( other, root_module(), name ); +} + + +void load_builtins() +{ + duplicate_rule( "Always", + bind_builtin( "ALWAYS", + builtin_flags, T_FLAG_TOUCHED, 0 ) ); + + duplicate_rule( "Depends", + bind_builtin( "DEPENDS", + builtin_depends, 0, 0 ) ); + + duplicate_rule( "echo", + duplicate_rule( "Echo", + bind_builtin( "ECHO", + builtin_echo, 0, 0 ) ) ); + + { + char * args[] = { "message", "*", ":", "result-value", "?", 0 }; + duplicate_rule( "exit", + duplicate_rule( "Exit", + bind_builtin( "EXIT", + builtin_exit, 0, args ) ) ); + } + + { + char * args[] = { "directories", "*", ":", "patterns", "*", ":", "case-insensitive", "?", 0 }; + duplicate_rule( "Glob", + bind_builtin( "GLOB", builtin_glob, 0, args ) ); + } + + { + char * args[] = { "patterns", "*", 0 }; + bind_builtin( "GLOB-RECURSIVELY", + builtin_glob_recursive, 0, args ); + } + + duplicate_rule( "Includes", + bind_builtin( "INCLUDES", + builtin_depends, 1, 0 ) ); + + { + char * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", 0 }; + bind_builtin( "REBUILDS", + builtin_rebuilds, 0, args ); + } + + duplicate_rule( "Leaves", + bind_builtin( "LEAVES", + builtin_flags, T_FLAG_LEAVES, 0 ) ); + + duplicate_rule( "Match", + bind_builtin( "MATCH", + builtin_match, 0, 0 ) ); + + { + char * args[] = { "string", ":", "delimiters" }; + bind_builtin( "SPLIT_BY_CHARACTERS", + builtin_split_by_characters, 0, 0 ); + } + + duplicate_rule( "NoCare", + bind_builtin( "NOCARE", + builtin_flags, T_FLAG_NOCARE, 0 ) ); + + duplicate_rule( "NOTIME", + duplicate_rule( "NotFile", + bind_builtin( "NOTFILE", + builtin_flags, T_FLAG_NOTFILE, 0 ) ) ); + + duplicate_rule( "NoUpdate", + bind_builtin( "NOUPDATE", + builtin_flags, T_FLAG_NOUPDATE, 0 ) ); + + duplicate_rule( "Temporary", + bind_builtin( "TEMPORARY", + builtin_flags, T_FLAG_TEMP, 0 ) ); + + bind_builtin( "ISFILE", + builtin_flags, T_FLAG_ISFILE, 0 ); + + duplicate_rule( "HdrMacro", + bind_builtin( "HDRMACRO", + builtin_hdrmacro, 0, 0 ) ); + + /* FAIL_EXPECTED is used to indicate that the result of a target build + * action should be inverted (ok <=> fail) this can be useful when + * performing test runs from Jamfiles. + */ + bind_builtin( "FAIL_EXPECTED", + builtin_flags, T_FLAG_FAIL_EXPECTED, 0 ); + + bind_builtin( "RMOLD", + builtin_flags, T_FLAG_RMOLD, 0 ); + + { + char * args[] = { "targets", "*", 0 }; + bind_builtin( "UPDATE", + builtin_update, 0, args ); + } + + { + char * args[] = { "targets", "*", + ":", "log", "?", + ":", "ignore-minus-n", "?", + ":", "ignore-minus-q", "?", 0 }; + bind_builtin( "UPDATE_NOW", + builtin_update_now, 0, args ); + } + + { + char * args[] = { "string", "pattern", "replacements", "+", 0 }; + duplicate_rule( "subst", + bind_builtin( "SUBST", + builtin_subst, 0, args ) ); + } + + { + char * args[] = { "module", "?", 0 }; + bind_builtin( "RULENAMES", + builtin_rulenames, 0, args ); + } + + + { + char * args[] = { "module", "?", 0 }; + bind_builtin( "VARNAMES", + builtin_varnames, 0, args ); + } + + { + char * args[] = { "module", "?", 0 }; + bind_builtin( "DELETE_MODULE", + builtin_delete_module, 0, args ); + } + + { + char * args[] = { "source_module", "?", + ":", "source_rules", "*", + ":", "target_module", "?", + ":", "target_rules", "*", + ":", "localize", "?", 0 }; + bind_builtin( "IMPORT", + builtin_import, 0, args ); + } + + { + char * args[] = { "module", "?", ":", "rules", "*", 0 }; + bind_builtin( "EXPORT", + builtin_export, 0, args ); + } + + { + char * args[] = { "levels", "?", 0 }; + bind_builtin( "CALLER_MODULE", + builtin_caller_module, 0, args ); + } + + { + char * args[] = { "levels", "?", 0 }; + bind_builtin( "BACKTRACE", + builtin_backtrace, 0, args ); + } + + { + char * args[] = { 0 }; + bind_builtin( "PWD", + builtin_pwd, 0, args ); + } + + { + char * args[] = { "target", "*", ":", "path", "*", 0 }; + bind_builtin( "SEARCH_FOR_TARGET", + builtin_search_for_target, 0, args ); + } + + { + char * args[] = { "modules_to_import", "+", ":", "target_module", "?", 0 }; + bind_builtin( "IMPORT_MODULE", + builtin_import_module, 0, args ); + } + + { + char * args[] = { "module", "?", 0 }; + bind_builtin( "IMPORTED_MODULES", + builtin_imported_modules, 0, args ); + } + + { + char * args[] = { "instance_module", ":", "class_module", 0 }; + bind_builtin( "INSTANCE", + builtin_instance, 0, args ); + } + + { + char * args[] = { "sequence", "*", 0 }; + bind_builtin( "SORT", + builtin_sort, 0, args ); + } + + { + char * args[] = { "path_parts", "*", 0 }; + bind_builtin( "NORMALIZE_PATH", + builtin_normalize_path, 0, args ); + } + + { + char * args[] = { "args", "*", 0 }; + bind_builtin( "CALC", + builtin_calc, 0, args ); + } + + { + char * args[] = { "module", ":", "rule", 0 }; + bind_builtin( "NATIVE_RULE", + builtin_native_rule, 0, args ); + } + + { + char * args[] = { "module", ":", "rule", ":", "version", 0 }; + bind_builtin( "HAS_NATIVE_RULE", + builtin_has_native_rule, 0, args ); + } + + { + char * args[] = { "module", "*", 0 }; + bind_builtin( "USER_MODULE", + builtin_user_module, 0, args ); + } + + { + char * args[] = { 0 }; + bind_builtin( "NEAREST_USER_LOCATION", + builtin_nearest_user_location, 0, args ); + } + + { + char * args[] = { "file", 0 }; + bind_builtin( "CHECK_IF_FILE", + builtin_check_if_file, 0, args ); + } + +#ifdef HAVE_PYTHON + { + char * args[] = { "python-module", ":", "function", ":", + "jam-module", ":", "rule-name", 0 }; + bind_builtin( "PYTHON_IMPORT_RULE", + builtin_python_import_rule, 0, args ); + } +#endif + +# if defined( OS_NT ) || defined( OS_CYGWIN ) + { + char * args[] = { "key_path", ":", "data", "?", 0 }; + bind_builtin( "W32_GETREG", + builtin_system_registry, 0, args ); + } + + { + char * args[] = { "key_path", ":", "result-type", 0 }; + bind_builtin( "W32_GETREGNAMES", + builtin_system_registry_names, 0, args ); + } +# endif + + { + char * args[] = { "command", ":", "*", 0 }; + duplicate_rule( "SHELL", + bind_builtin( "COMMAND", + builtin_shell, 0, args ) ); + } + + { + char * args[] = { "string", 0 }; + bind_builtin( "MD5", + builtin_md5, 0, args ) ; + } + + { + char * args[] = { "name", ":", "mode", 0 }; + bind_builtin( "FILE_OPEN", + builtin_file_open, 0, args ); + } + + { + char * args[] = { "string", ":", "width", 0 }; + bind_builtin( "PAD", + builtin_pad, 0, args ); + } + + { + char * args[] = { "targets", "*", 0 }; + bind_builtin( "PRECIOUS", + builtin_precious, 0, args ); + } + + { + char * args [] = { 0 }; + bind_builtin( "SELF_PATH", builtin_self_path, 0, args ); + } + + { + char * args [] = { "path", 0 }; + bind_builtin( "MAKEDIR", builtin_makedir, 0, args ); + } + + /* Initialize builtin modules. */ + init_set(); + init_path(); + init_regex(); + init_property_set(); + init_sequence(); + init_order(); +} + + +/* + * builtin_calc() - CALC rule. + * + * The CALC rule performs simple mathematical operations on two arguments. + */ + +LIST * builtin_calc( PARSE * parse, FRAME * frame ) +{ + LIST * arg = lol_get( frame->args, 0 ); + + LIST * result = 0; + long lhs_value; + long rhs_value; + long result_value; + char buffer [ 16 ]; + char const * lhs; + char const * op; + char const * rhs; + + if ( arg == 0 ) return L0; + lhs = arg->string; + + arg = list_next( arg ); + if ( arg == 0 ) return L0; + op = arg->string; + + arg = list_next( arg ); + if ( arg == 0 ) return L0; + rhs = arg->string; + + lhs_value = atoi( lhs ); + rhs_value = atoi( rhs ); + + if ( strcmp( "+", op ) == 0 ) + { + result_value = lhs_value + rhs_value; + } + else if ( strcmp( "-", op ) == 0 ) + { + result_value = lhs_value - rhs_value; + } + else + { + return L0; + } + + sprintf( buffer, "%ld", result_value ); + result = list_new( result, newstr( buffer ) ); + return result; +} + + +/* + * builtin_depends() - DEPENDS/INCLUDES rule. + * + * The DEPENDS/INCLUDES builtin rule appends each of the listed sources on the + * dependency/includes list of each of the listed targets. It binds both the + * targets and sources as TARGETs. + */ + +LIST * builtin_depends( PARSE * parse, FRAME * frame ) +{ + LIST * targets = lol_get( frame->args, 0 ); + LIST * sources = lol_get( frame->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->includes->original_target = t; + } + t = t->includes; + } + + t->depends = targetlist( t->depends, sources ); + } + + /* Enter reverse links */ + for ( l = sources; l; l = list_next( l ) ) + { + TARGET * s = bindtarget( l->string ); + s->dependants = targetlist( s->dependants, targets ); + } + + return L0; +} + + +/* + * builtin_rebuilds() - REBUILDS rule. + * + * The REBUILDS builtin rule appends each of the listed rebuild-targets in its + * 2nd argument on the rebuilds list of each of the listed targets in its first + * argument. + */ + +LIST * builtin_rebuilds( PARSE * parse, FRAME * frame ) +{ + LIST * targets = lol_get( frame->args, 0 ); + LIST * rebuilds = lol_get( frame->args, 1 ); + LIST * l; + + for ( l = targets; l; l = list_next( l ) ) + { + TARGET * t = bindtarget( l->string ); + t->rebuilds = targetlist( t->rebuilds, rebuilds ); + } + + 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, FRAME * frame ) +{ + list_print( lol_get( frame->args, 0 ) ); + printf( "\n" ); + fflush( stdout ); + 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, FRAME * frame ) +{ + list_print( lol_get( frame->args, 0 ) ); + printf( "\n" ); + if ( lol_get( frame->args, 1 ) ) + { + exit( atoi( lol_get( frame->args, 1 )->string ) ); + } + else + { + 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, FRAME * frame ) +{ + LIST * l = lol_get( frame->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; + LIST * case_insensitive; +}; + + +static void downcase_inplace( char * p ) +{ + for ( ; *p; ++p ) + *p = tolower( *p ); +} + + +static void builtin_glob_back +( + void * closure, + char * file, + int status, + time_t time +) +{ + PROFILE_ENTER( BUILTIN_GLOB_BACK ); + + struct globbing * globbing = (struct globbing *)closure; + LIST * l; + PATHNAME f; + string buf[ 1 ]; + + /* Null out directory for matching. We wish we had file_dirscan() pass up a + * PATHNAME. + */ + path_parse( file, &f ); + f.f_dir.len = 0; + + /* For globbing, we unconditionally ignore current and parent directory + * items. Since they items always exist, there is no reason why caller of + * GLOB would want to see them. We could also change file_dirscan(), but + * then paths with embedded "." and ".." would not work anywhere. + */ + if ( !strcmp( f.f_base.ptr, "." ) || !strcmp( f.f_base.ptr, ".." ) ) + { + PROFILE_EXIT( BUILTIN_GLOB_BACK ); + return; + } + + string_new( buf ); + path_build( &f, buf, 0 ); + + if ( globbing->case_insensitive ) + downcase_inplace( buf->value ); + + for ( l = globbing->patterns; l; l = l->next ) + { + if ( !glob( l->string, buf->value ) ) + { + globbing->results = list_new( globbing->results, newstr( file ) ); + break; + } + } + + string_free( buf ); + + PROFILE_EXIT( BUILTIN_GLOB_BACK ); +} + + +static LIST * downcase_list( LIST * in ) +{ + LIST * result = 0; + + string s[ 1 ]; + string_new( s ); + + while ( in ) + { + string_copy( s, in->string ); + downcase_inplace( s->value ); + result = list_append( result, list_new( 0, newstr( s->value ) ) ); + in = in->next; + } + + string_free( s ); + return result; +} + + +LIST * builtin_glob( PARSE * parse, FRAME * frame ) +{ + LIST * l = lol_get( frame->args, 0 ); + LIST * r = lol_get( frame->args, 1 ); + + struct globbing globbing; + + globbing.results = L0; + globbing.patterns = r; + + globbing.case_insensitive +# if defined( OS_NT ) || defined( OS_CYGWIN ) + = l; /* Always case-insensitive if any files can be found. */ +# else + = lol_get( frame->args, 2 ); +# endif + + if ( globbing.case_insensitive ) + globbing.patterns = downcase_list( r ); + + for ( ; l; l = list_next( l ) ) + file_dirscan( l->string, builtin_glob_back, &globbing ); + + if ( globbing.case_insensitive ) + list_free( globbing.patterns ); + + return globbing.results; +} + + +static int has_wildcards( char const * str ) +{ + size_t const index = strcspn( str, "[]*?" ); + return str[ index ] == '\0' ? 0 : 1; +} + + +/* + * If 'file' exists, append 'file' to 'list'. Returns 'list'. + */ + +static LIST * append_if_exists( LIST * list, char * file ) +{ + time_t time; + timestamp( file, &time ); + return time > 0 + ? list_new( list, newstr( file ) ) + : list; +} + + +LIST * glob1( char * dirname, char * pattern ) +{ + LIST * plist = list_new( L0, pattern ); + struct globbing globbing; + + globbing.results = L0; + globbing.patterns = plist; + + globbing.case_insensitive +# if defined( OS_NT ) || defined( OS_CYGWIN ) + = plist; /* always case-insensitive if any files can be found */ +# else + = L0; +# endif + + if ( globbing.case_insensitive ) + globbing.patterns = downcase_list( plist ); + + file_dirscan( dirname, builtin_glob_back, &globbing ); + + if ( globbing.case_insensitive ) + list_free( globbing.patterns ); + + list_free( plist ); + + return globbing.results; +} + + +LIST * glob_recursive( char * pattern ) +{ + LIST * result = L0; + + /* Check if there's metacharacters in pattern */ + if ( !has_wildcards( pattern ) ) + { + /* No metacharacters. Check if the path exists. */ + result = append_if_exists(result, pattern); + } + else + { + /* Have metacharacters in the pattern. Split into dir/name. */ + PATHNAME path[ 1 ]; + path_parse( pattern, path ); + + if ( path->f_dir.ptr ) + { + LIST * dirs = L0; + string dirname[ 1 ]; + string basename[ 1 ]; + string_new( dirname ); + string_new( basename ); + + string_append_range( dirname, path->f_dir.ptr, + path->f_dir.ptr + path->f_dir.len ); + + path->f_grist.ptr = 0; + path->f_grist.len = 0; + path->f_dir.ptr = 0; + path->f_dir.len = 0; + path_build( path, basename, 0 ); + + dirs = has_wildcards( dirname->value ) + ? glob_recursive( dirname->value ) + : list_new( dirs, dirname->value ); + + if ( has_wildcards( basename->value ) ) + { + for ( ; dirs; dirs = dirs->next ) + result = list_append( result, glob1( dirs->string, + basename->value ) ); + } + else + { + string file_string[ 1 ]; + string_new( file_string ); + + /* No wildcard in basename. */ + for ( ; dirs; dirs = dirs->next ) + { + path->f_dir.ptr = dirs->string; + path->f_dir.len = strlen( dirs->string ); + path_build( path, file_string, 0 ); + + result = append_if_exists( result, file_string->value ); + + string_truncate( file_string, 0 ); + } + + string_free( file_string ); + } + + string_free( dirname ); + string_free( basename ); + } + else + { + /** No directory, just a pattern. */ + result = list_append( result, glob1( ".", pattern ) ); + } + } + + return result; +} + + +LIST * builtin_glob_recursive( PARSE * parse, FRAME * frame ) +{ + LIST * result = L0; + LIST * l = lol_get( frame->args, 0 ); + for ( ; l; l = l->next ) + result = list_append( result, glob_recursive( l->string ) ); + return result; +} + + +/* + * builtin_match() - MATCH rule, regexp matching. + */ + +LIST * builtin_match( PARSE * parse, FRAME * frame ) +{ + LIST * l; + LIST * r; + LIST * result = 0; + + string buf[ 1 ]; + string_new( buf ); + + /* For each pattern */ + + for ( l = lol_get( frame->args, 0 ); l; l = l->next ) + { + /* Result is cached and intentionally never freed. */ + regexp * re = regex_compile( l->string ); + + /* For each string to match against. */ + for ( r = lol_get( frame->args, 1 ); r; r = r->next ) + { + if ( regexec( re, r->string ) ) + { + int i; + int 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 ) + { + string_append_range( buf, re->startp[ i ], re->endp[ i ] ); + result = list_new( result, newstr( buf->value ) ); + string_truncate( buf, 0 ); + } + } + } + } + + string_free( buf ); + return result; +} + +LIST * builtin_split_by_characters( PARSE * parse, FRAME * frame ) +{ + LIST * l1 = lol_get( frame->args, 0 ); + LIST * l2 = lol_get( frame->args, 1 ); + + LIST * result = 0; + + char* s = strdup (l1->string); + char* delimiters = l2->string; + char* t; + + t = strtok (s, delimiters); + while (t) + { + result = list_new(result, newstr(t)); + t = strtok (NULL, delimiters); + } + + free (s); + + return result; +} + +LIST * builtin_hdrmacro( PARSE * parse, FRAME * frame ) +{ + LIST * l = lol_get( frame->args, 0 ); + + for ( ; l; l = list_next( l ) ) + { + TARGET * t = bindtarget( l->string ); + + /* Scan file for header filename macro definitions. */ + if ( DEBUG_HEADER ) + printf( "scanning '%s' for header file macro definitions\n", + l->string ); + + macro_headers( t ); + } + + return L0; +} + + +/* + * builtin_rulenames() - RULENAMES ( MODULE ? ). + * + * Returns a list of the non-local rule names in the given MODULE. If MODULE is + * not supplied, returns the list of rule names in the global module. + */ + +static void add_rule_name( void * r_, void * result_ ) +{ + RULE * r = (RULE *)r_; + LIST * * result = (LIST * *)result_; + if ( r->exported ) + *result = list_new( *result, copystr( r->name ) ); +} + + +LIST * builtin_rulenames( PARSE * parse, FRAME * frame ) +{ + LIST * arg0 = lol_get( frame->args, 0 ); + LIST * result = L0; + module_t * source_module = bindmodule( arg0 ? arg0->string : 0 ); + + if ( source_module->rules ) + hashenumerate( source_module->rules, add_rule_name, &result ); + return result; +} + + +/* + * builtin_varnames() - VARNAMES ( MODULE ? ). + * + * Returns a list of the variable names in the given MODULE. If MODULE is not + * supplied, returns the list of variable names in the global module. + */ + +/* helper function for builtin_varnames(), below. Used with hashenumerate, will + * prepend the key of each element to the list + */ +static void add_hash_key( void * np, void * result_ ) +{ + LIST * * result = (LIST * *)result_; + *result = list_new( *result, copystr( *(char * *)np ) ); +} + + +static struct hash * get_running_module_vars() +{ + struct hash * dummy; + struct hash * vars = NULL; + /* Get the global variables pointer (that of the currently running module). + */ + var_hash_swap( &vars ); + dummy = vars; + /* Put the global variables pointer in its right place. */ + var_hash_swap( &dummy ); + return vars; +} + + +LIST * builtin_varnames( PARSE * parse, FRAME * frame ) +{ + LIST * arg0 = lol_get( frame->args, 0 ); + LIST * result = L0; + module_t * source_module = bindmodule( arg0 ? arg0->string : 0 ); + + /* The running module _always_ has its 'variables' member set to NULL due to + * the way enter_module() and var_hash_swap() work. + */ + struct hash * vars = source_module == frame->module + ? get_running_module_vars() + : source_module->variables; + + if ( vars ) + hashenumerate( vars, add_hash_key, &result ); + return result; +} + + +/* + * builtin_delete_module() - MODULE ?. + * + * Clears all rules and variables from the given module. + */ + +LIST * builtin_delete_module( PARSE * parse, FRAME * frame ) +{ + LIST * arg0 = lol_get( frame->args, 0 ); + LIST * result = L0; + module_t * source_module = bindmodule( arg0 ? arg0->string : 0 ); + delete_module( source_module ); + return result; +} + + +static void unknown_rule( FRAME * frame, char * key, char * module_name, char * rule_name ) +{ + backtrace_line( frame->prev ); + printf( "%s error: rule \"%s\" unknown in module \"%s\"\n", key, rule_name, module_name ); + backtrace( frame->prev ); + exit( 1 ); +} + + +/* + * builtin_import() - IMPORT + * ( + * SOURCE_MODULE ? : + * SOURCE_RULES * : + * TARGET_MODULE ? : + * TARGET_RULES * : + * LOCALIZE ? + * ) + * + * The IMPORT rule imports rules from the SOURCE_MODULE into the TARGET_MODULE + * as local rules. If either SOURCE_MODULE or TARGET_MODULE is not supplied, it + * refers to the global module. SOURCE_RULES specifies which rules from the + * SOURCE_MODULE to import; TARGET_RULES specifies the names to give those rules + * in TARGET_MODULE. If SOURCE_RULES contains a name which doesn't correspond to + * a rule in SOURCE_MODULE, or if it contains a different number of items than + * TARGET_RULES, an error is issued. If LOCALIZE is specified, the rules will be + * executed in TARGET_MODULE, with corresponding access to its module local + * variables. + */ + +LIST * builtin_import( PARSE * parse, FRAME * frame ) +{ + LIST * source_module_list = lol_get( frame->args, 0 ); + LIST * source_rules = lol_get( frame->args, 1 ); + LIST * target_module_list = lol_get( frame->args, 2 ); + LIST * target_rules = lol_get( frame->args, 3 ); + LIST * localize = lol_get( frame->args, 4 ); + + module_t * target_module = + bindmodule( target_module_list ? target_module_list->string : 0 ); + module_t * source_module = + bindmodule( source_module_list ? source_module_list->string : 0 ); + + LIST * source_name; + LIST * target_name; + + for ( source_name = source_rules, target_name = target_rules; + source_name && target_name; + source_name = list_next( source_name ), + target_name = list_next( target_name ) ) + { + RULE r_; + RULE * r = &r_; + RULE * imported; + r_.name = source_name->string; + + if ( !source_module->rules || + !hashcheck( source_module->rules, (HASHDATA * *)&r ) ) + unknown_rule( frame, "IMPORT", source_module->name, r_.name ); + + imported = import_rule( r, target_module, target_name->string ); + if ( localize ) + imported->module = target_module; + /* This rule is really part of some other module. Just refer to it here, + * but do not let it out. + */ + imported->exported = 0; + } + + if ( source_name || target_name ) + { + backtrace_line( frame->prev ); + printf( "import error: length of source and target rule name lists don't match!\n" ); + printf( " source: " ); + list_print( source_rules ); + printf( "\n target: " ); + list_print( target_rules ); + printf( "\n" ); + backtrace( frame->prev ); + exit( 1 ); + } + + return L0; +} + + +/* + * builtin_export() - EXPORT ( MODULE ? : RULES * ). + * + * The EXPORT rule marks RULES from the SOURCE_MODULE as non-local (and thus + * exportable). If an element of RULES does not name a rule in MODULE, an error + * is issued. + */ + +LIST * builtin_export( PARSE * parse, FRAME * frame ) +{ + LIST * module_list = lol_get( frame->args, 0 ); + LIST * rules = lol_get( frame->args, 1 ); + module_t * m = bindmodule( module_list ? module_list->string : 0 ); + + for ( ; rules; rules = list_next( rules ) ) + { + RULE r_; + RULE * r = &r_; + r_.name = rules->string; + + if ( !m->rules || !hashcheck( m->rules, (HASHDATA * *)&r ) ) + unknown_rule( frame, "EXPORT", m->name, r_.name ); + + r->exported = 1; + } + return L0; +} + + +/* + * get_source_line() - Retrieve the file and line number that should be + * indicated for a given procedure in debug output or an error backtrace. + */ + +static void get_source_line( PARSE * procedure, char * * file, int * line ) +{ + if ( procedure ) + { + char * f = procedure->file; + int l = procedure->line; + if ( !strcmp( f, "+" ) ) + { + f = "jambase.c"; + l += 3; + } + *file = f; + *line = l; + } + else + { + *file = "(builtin)"; + *line = -1; + } +} + + +void print_source_line( PARSE * p ) +{ + char * file; + int line; + + get_source_line( p, &file, &line ); + if ( line < 0 ) + printf( "(builtin):" ); + else + printf( "%s:%d:", file, line ); +} + + +/* + * backtrace_line() - print a single line of error backtrace for the given + * frame. + */ + +void backtrace_line( FRAME * frame ) +{ + if ( frame == 0 ) + { + printf( "(no frame):" ); + } + else + { + print_source_line( frame->procedure ); + printf( " in %s\n", frame->rulename ); + } +} + + +/* + * backtrace() - Print the entire backtrace from the given frame to the Jambase + * which invoked it. + */ + +void backtrace( FRAME * frame ) +{ + if ( !frame ) return; + while ( ( frame = frame->prev ) ) + backtrace_line( frame ); +} + + +/* + * builtin_backtrace() - A Jam version of the backtrace function, taking no + * arguments and returning a list of quadruples: FILENAME LINE MODULE. RULENAME + * describing each frame. Note that the module-name is always followed by a + * period. + */ + +LIST * builtin_backtrace( PARSE * parse, FRAME * frame ) +{ + LIST * levels_arg = lol_get( frame->args, 0 ); + int levels = levels_arg ? atoi( levels_arg->string ) : ( (unsigned int)(-1) >> 1 ) ; + + LIST * result = L0; + for ( ; ( frame = frame->prev ) && levels ; --levels ) + { + char * file; + int line; + char buf[32]; + get_source_line( frame->procedure, &file, &line ); + sprintf( buf, "%d", line ); + result = list_new( result, newstr( file ) ); + result = list_new( result, newstr( buf ) ); + result = list_new( result, newstr( frame->module->name ) ); + result = list_new( result, newstr( frame->rulename ) ); + } + return result; +} + + +/* + * builtin_caller_module() - CALLER_MODULE ( levels ? ) + * + * If levels is not supplied, returns the name of the module of the rule which + * called the one calling this one. If levels is supplied, it is interpreted as + * an integer specifying a number of additional levels of call stack to traverse + * in order to locate the module in question. If no such module exists, returns + * the empty list. Also returns the empty list when the module in question is + * the global module. This rule is needed for implementing module import + * behavior. + */ + +LIST * builtin_caller_module( PARSE * parse, FRAME * frame ) +{ + LIST * levels_arg = lol_get( frame->args, 0 ); + int levels = levels_arg ? atoi( levels_arg->string ) : 0 ; + + int i; + for ( i = 0; ( i < levels + 2 ) && frame->prev; ++i ) + frame = frame->prev; + + if ( frame->module == root_module() ) + return L0; + + { + LIST * result; + string name; + string_copy( &name, frame->module->name ); + string_pop_back( &name ); + result = list_new( L0, newstr(name.value) ); + string_free( &name ); + return result; + } +} + + +/* + * Return the current working directory. + * + * Usage: pwd = [ PWD ] ; + */ + +LIST * builtin_pwd( PARSE * parse, FRAME * frame ) +{ + return pwd(); +} + + +/* + * Adds targets to the list of target that jam will attempt to update. + */ + +LIST * builtin_update( PARSE * parse, FRAME * frame ) +{ + LIST * result = list_copy( L0, targets_to_update() ); + LIST * arg1 = lol_get( frame->args, 0 ); + clear_targets_to_update(); + for ( ; arg1; arg1 = list_next( arg1 ) ) + mark_target_for_updating( newstr( arg1->string ) ); + return result; +} + +extern int anyhow; +int last_update_now_status; + +/* Takes a list of target names as first argument, and immediately + updates them. + Second parameter, if specified, if the descriptor (converted to a string) + of a log file where all build output is redirected. + Third parameter, if non-empty, specifies that the -n option should have + no effect -- that is, all out-of-date targets should be rebuild. +*/ +LIST * builtin_update_now( PARSE * parse, FRAME * frame ) +{ + LIST * targets = lol_get( frame->args, 0 ); + LIST * log = lol_get( frame->args, 1 ); + LIST * force = lol_get (frame->args, 2); + LIST * continue_ = lol_get(frame->args, 3); + int status = 0; + int original_stdout; + int original_stderr; + int n; + int targets_count; + const char** targets2; + int i; + int original_noexec; + int original_quitquick; + + + if (log) + { + int fd = atoi(log->string); + /* Redirect stdout and stderr, temporary, to the log file. */ + original_stdout = dup (0); + original_stderr = dup (1); + dup2 (fd, 0); + dup2 (fd, 1); + } + + if (force) + { + original_noexec = globs.noexec; + globs.noexec = 0; + original_quitquick = globs.quitquick; + globs.quitquick = 0; + } + + if (continue_) + { + original_quitquick = globs.quitquick; + globs.quitquick = 0; + } + + targets_count = list_length( targets ); + targets2 = (const char * *)BJAM_MALLOC( targets_count * sizeof( char * ) ); + for (i = 0 ; targets; targets = list_next( targets ) ) + targets2[ i++ ] = targets->string; + status |= make( targets_count, targets2, anyhow); + free( targets ); + + if (force) + { + globs.noexec = original_noexec; + globs.quitquick = original_quitquick; + } + + if (continue_) + { + globs.quitquick = original_quitquick; + } + + if (log) + { + /* Flush whatever stdio might have buffered, while descriptions + 0 and 1 still refer to the log file. */ + fflush (stdout); + fflush (stderr); + dup2 (original_stdout, 0); + dup2 (original_stderr, 1); + close (original_stdout); + close (original_stderr); + } + + last_update_now_status = status; + + if (status == 0) + return list_new (L0, newstr ("ok")); + else + return L0; +} + +LIST * builtin_search_for_target( PARSE * parse, FRAME * frame ) +{ + LIST * arg1 = lol_get( frame->args, 0 ); + LIST * arg2 = lol_get( frame->args, 1 ); + TARGET * t = search_for_target( arg1->string, arg2 ); + return list_new( L0, t->name ); +} + + +LIST * builtin_import_module( PARSE * parse, FRAME * frame ) +{ + LIST * arg1 = lol_get( frame->args, 0 ); + LIST * arg2 = lol_get( frame->args, 1 ); + module_t * m = arg2 ? bindmodule( arg2->string ) : root_module(); + import_module( arg1, m ); + return L0; +} + + +LIST * builtin_imported_modules( PARSE * parse, FRAME * frame ) +{ + LIST * arg0 = lol_get( frame->args, 0 ); + return imported_modules( bindmodule( arg0 ? arg0->string : 0 ) ); +} + + +LIST * builtin_instance( PARSE * parse, FRAME * frame ) +{ + LIST * arg1 = lol_get( frame->args, 0 ); + LIST * arg2 = lol_get( frame->args, 1 ); + module_t * const instance = bindmodule( arg1->string ); + module_t * const class_module = bindmodule( arg2->string ); + instance->class_module = class_module; + return L0; +} + + +LIST * builtin_sort( PARSE * parse, FRAME * frame ) +{ + LIST * arg1 = lol_get( frame->args, 0 ); + return list_sort( arg1 ); +} + + +LIST * builtin_normalize_path( PARSE * parse, FRAME * frame ) +{ + LIST * arg = lol_get( frame->args, 0 ); + + /* First, we iterate over all '/'-separated elements, starting from the end + * of string. If we see a '..', we remove a previous path elements. If we + * see '.', we remove it. The removal is done by overwriting data using '\1' + * in the string. After the whole string has been processed, we do a second + * pass, removing all the entered '\1' characters. + */ + + string in[ 1 ]; + string out[ 1 ]; + /* Last character of the part of string still to be processed. */ + char * end; + /* Working pointer. */ + char * current; + /* Number of '..' elements seen and not processed yet. */ + int dotdots = 0; + int rooted = 0; + char * result = 0; + + /* Make a copy of input: we should not change it. Prepend a '/' before it as + * a guard for the algorithm later on and remember whether it was originally + * rooted or not. + */ + string_new( in ); + string_push_back( in, '/' ); + for ( ; arg; arg = list_next( arg ) ) + { + if ( arg->string[ 0 ] != '\0' ) + { + if ( in->size == 1 ) + rooted = ( ( arg->string[ 0 ] == '/' ) || + ( arg->string[ 0 ] == '\\' ) ); + else + string_append( in, "/" ); + string_append( in, arg->string ); + } + } + + /* Convert \ into /. On Windows, paths using / and \ are equivalent, and we + * want this function to obtain a canonic representation. + */ + for ( current = in->value, end = in->value + in->size; + current < end; ++current ) + if ( *current == '\\' ) + *current = '/'; + + /* Now we remove any extra path elements by overwriting them with '\1' + * characters and cound how many more unused '..' path elements there are + * remaining. Note that each remaining path element with always starts with + * a '/' character. + */ + for ( end = in->value + in->size - 1; end >= in->value; ) + { + /* Set 'current' to the next occurence of '/', which always exists. */ + for ( current = end; *current != '/'; --current ); + + if ( current == end ) + { + /* Found a trailing or duplicate '/'. Remove it. */ + *current = '\1'; + } + else if ( ( end - current == 1 ) && ( *(current + 1) == '.' ) ) + { + /* Found '/.'. Remove them all. */ + *current = '\1'; + *(current + 1) = '\1'; + } + else if ( ( end - current == 2 ) && ( *(current + 1) == '.' ) && ( *(current + 2) == '.' ) ) + { + /* Found '/..'. Remove them all. */ + *current = '\1'; + *(current + 1) = '\1'; + *(current + 2) = '\1'; + ++dotdots; + } + else if ( dotdots ) + { + memset( current, '\1', end - current + 1 ); + --dotdots; + } + end = current - 1; + } + + string_new( out ); + + /* Now we know that we need to add exactly dotdots '..' path elements to the + * front and that our string is either empty or has a '/' as its first + * significant character. If we have any dotdots remaining then the passed + * path must not have been rooted or else it is invalid we return an empty + * list. + */ + if ( dotdots ) + { + if ( rooted ) return L0; + do + string_append( out, "/.." ); + while ( --dotdots ); + } + + /* Now we actually remove all the path characters marked for removal. */ + for ( current = in->value; *current; ++current ) + if ( *current != '\1' ) + string_push_back( out, *current ); + + /* Here we know that our string contains no '\1' characters and is either + * empty or has a '/' as its initial character. If the original path was not + * rooted and we have a non-empty path we need to drop the initial '/'. If + * the original path was rooted and we have an empty path we need to add + * back the '/'. + */ + result = newstr( out->size ? out->value + !rooted : ( rooted ? "/" : "." ) ); + + string_free( out ); + string_free( in ); + + return list_new( 0, result ); +} + + +LIST * builtin_native_rule( PARSE * parse, FRAME * frame ) +{ + LIST * module_name = lol_get( frame->args, 0 ); + LIST * rule_name = lol_get( frame->args, 1 ); + + module_t * module = bindmodule( module_name->string ); + + native_rule_t n; + native_rule_t * np = &n; + n.name = rule_name->string; + if ( module->native_rules && hashcheck( module->native_rules, (HASHDATA * *)&np ) ) + { + new_rule_body( module, np->name, np->arguments, np->procedure, 1 ); + } + else + { + backtrace_line( frame->prev ); + printf( "error: no native rule \"%s\" defined in module \"%s\"\n", + n.name, module->name ); + backtrace( frame->prev ); + exit( 1 ); + } + return L0; +} + + +LIST * builtin_has_native_rule( PARSE * parse, FRAME * frame ) +{ + LIST * module_name = lol_get( frame->args, 0 ); + LIST * rule_name = lol_get( frame->args, 1 ); + LIST * version = lol_get( frame->args, 2 ); + + module_t * module = bindmodule( module_name->string ); + + native_rule_t n; + native_rule_t * np = &n; + n.name = rule_name->string; + if ( module->native_rules && hashcheck( module->native_rules, (HASHDATA * *)&np ) ) + { + int expected_version = atoi( version->string ); + if ( np->version == expected_version ) + return list_new( 0, newstr( "true" ) ); + } + return L0; +} + + +LIST * builtin_user_module( PARSE * parse, FRAME * frame ) +{ + LIST * module_name = lol_get( frame->args, 0 ); + for ( ; module_name; module_name = module_name->next ) + { + module_t * m = bindmodule( module_name->string ); + m->user_module = 1; + } + return L0; +} + + +LIST * builtin_nearest_user_location( PARSE * parse, FRAME * frame ) +{ + FRAME * nearest_user_frame = + frame->module->user_module ? frame : frame->prev_user; + if ( !nearest_user_frame ) + return L0; + + { + LIST * result = 0; + char * file; + int line; + char buf[32]; + + get_source_line( nearest_user_frame->procedure, &file, &line ); + sprintf( buf, "%d", line ); + result = list_new( result, newstr( file ) ); + result = list_new( result, newstr( buf ) ); + return result; + } +} + + +LIST * builtin_check_if_file( PARSE * parse, FRAME * frame ) +{ + LIST * name = lol_get( frame->args, 0 ); + return file_is_file( name->string ) == 1 + ? list_new( 0, newstr( "true" ) ) + : L0 ; +} + + +LIST * builtin_md5( PARSE * parse, FRAME * frame ) +{ + LIST * l = lol_get( frame->args, 0 ); + char* s = l->string; + + md5_state_t state; + md5_byte_t digest[16]; + char hex_output[16*2 + 1]; + + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)s, strlen(s)); + md5_finish(&state, digest); + + for (di = 0; di < 16; ++di) + sprintf(hex_output + di * 2, "%02x", digest[di]); + + return list_new (0, newstr(hex_output)); +} + +LIST *builtin_file_open( PARSE *parse, FRAME *frame ) +{ + char* name = lol_get(frame->args, 0)->string; + char* mode = lol_get(frame->args, 1)->string; + int fd; + char buffer[sizeof("4294967295")]; + + if (strcmp(mode, "w") == 0) + { + fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + else + { + fd = open(name, O_RDONLY); + } + + if (fd != -1) + { + sprintf(buffer, "%d", fd); + return list_new(L0, newstr(buffer)); + } + else + { + return L0; + } +} + +LIST *builtin_pad( PARSE *parse, FRAME *frame ) +{ + char *string = lol_get(frame->args, 0)->string; + char *width_s = lol_get(frame->args, 1)->string; + + int current = strlen (string); + int desired = atoi(width_s); + if (current >= desired) + return list_new (L0, string); + else + { + char *buffer = malloc (desired + 1); + int i; + LIST *result; + + strcpy (buffer, string); + for (i = current; i < desired; ++i) + buffer[i] = ' '; + buffer[desired] = '\0'; + result = list_new (L0, newstr (buffer)); + free (buffer); + return result; + } +} + +LIST *builtin_precious( PARSE *parse, FRAME *frame ) +{ + LIST* targets = lol_get(frame->args, 0); + + for ( ; targets; targets = list_next( targets ) ) + { + TARGET* t = bindtarget (targets->string); + t->flags |= T_FLAG_PRECIOUS; + } + + return L0; +} + +LIST *builtin_self_path( PARSE *parse, FRAME *frame ) +{ + extern char *saved_argv0; + char *p = executable_path (saved_argv0); + if (p) + { + LIST* result = list_new (0, newstr (p)); + free(p); + return result; + } + else + { + return L0; + } +} + +LIST *builtin_makedir( PARSE *parse, FRAME *frame ) +{ + LIST *path = lol_get(frame->args, 0); + + if (file_mkdir(path->string) == 0) + { + LIST *result = list_new (0, newstr(path->string)); + return result; + } + else + { + return L0; + } +} + +#ifdef HAVE_PYTHON + +LIST * builtin_python_import_rule( PARSE * parse, FRAME * frame ) +{ + static int first_time = 1; + char * python_module = lol_get( frame->args, 0 )->string; + char * python_function = lol_get( frame->args, 1 )->string; + char * jam_module = lol_get( frame->args, 2 )->string; + char * jam_rule = lol_get( frame->args, 3 )->string; + + PyObject * pName; + PyObject * pModule; + PyObject * pDict; + PyObject * pFunc; + + if ( first_time ) + { + /* At the first invocation, we add the value of the global + * EXTRA_PYTHONPATH to the sys.path Python variable. + */ + LIST * extra = 0; + module_t * outer_module = frame->module; + + first_time = 0; + + if ( outer_module != root_module() ) + { + exit_module( outer_module ); + enter_module( root_module() ); + } + + extra = var_get( "EXTRA_PYTHONPATH" ); + + if ( outer_module != root_module() ) + { + exit_module( root_module() ); + enter_module( outer_module ); + } + + for ( ; extra; extra = extra->next ) + { + string buf[ 1 ]; + string_new( buf ); + string_append( buf, "import sys\nsys.path.append(\"" ); + string_append( buf, extra->string ); + string_append( buf, "\")\n" ); + PyRun_SimpleString( buf->value ); + string_free( buf ); + } + } + + pName = PyString_FromString( python_module ); + pModule = PyImport_Import( pName ); + Py_DECREF( pName ); + + if ( pModule != NULL ) + { + pDict = PyModule_GetDict( pModule ); + pFunc = PyDict_GetItemString( pDict, python_function ); + + if ( pFunc && PyCallable_Check( pFunc ) ) + { + module_t * m = bindmodule( jam_module ); + RULE * r = bindrule( jam_rule, m ); + + /* Make pFunc owned. */ + Py_INCREF( pFunc ); + + r->python_function = pFunc; + } + else + { + if ( PyErr_Occurred() ) + PyErr_Print(); + fprintf( stderr, "Cannot find function \"%s\"\n", python_function ); + } + Py_DECREF( pModule ); + } + else + { + PyErr_Print(); + fprintf( stderr, "Failed to load \"%s\"\n", python_module ); + } + return L0; + +} + +#endif + +void lol_build( LOL * lol, char * * elements ) +{ + LIST * l = L0; + lol_init( lol ); + + while ( elements && *elements ) + { + if ( !strcmp( *elements, ":" ) ) + { + lol_add( lol, l ); + l = L0 ; + } + else + { + l = list_new( l, newstr( *elements ) ); + } + ++elements; + } + + if ( l != L0 ) + lol_add( lol, l ); +} + + +#ifdef HAVE_PYTHON + +/* + * Calls the bjam rule specified by name passed in 'args'. The name is looked up + * in the context of bjam's 'python_interface' module. Returns the list of + * string retured by the rule. + */ + +PyObject* bjam_call( PyObject * self, PyObject * args ) +{ + FRAME inner[ 1 ]; + LIST * result; + PARSE * p; + char * rulename; + + /* Build up the list of arg lists. */ + frame_init( inner ); + inner->prev = 0; + inner->prev_user = 0; + inner->module = bindmodule( "python_interface" ); + inner->procedure = 0; + + /* Extract the rule name and arguments from 'args'. */ + + /* PyTuple_GetItem returns borrowed reference. */ + rulename = PyString_AsString( PyTuple_GetItem( args, 0 ) ); + { + int i = 1; + int size = PyTuple_Size( args ); + for ( ; i < size; ++i ) + { + PyObject * a = PyTuple_GetItem( args, i ); + if ( PyString_Check( a ) ) + { + lol_add( inner->args, list_new( 0, newstr( + PyString_AsString( a ) ) ) ); + } + else if ( PySequence_Check( a ) ) + { + LIST * l = 0; + int s = PySequence_Size( a ); + int i = 0; + for ( ; i < s; ++i ) + { + /* PySequence_GetItem returns new reference. */ + PyObject * e = PySequence_GetItem( a, i ); + char * s = PyString_AsString( e ); + if ( !s ) + { + printf( "Invalid parameter type passed from Python\n" ); + exit( 1 ); + } + l = list_new( l, newstr( s ) ); + Py_DECREF( e ); + } + lol_add( inner->args, l ); + } + } + } + + result = evaluate_rule( rulename, inner ); + + frame_free( inner ); + + /* Convert the bjam list into a Python list result. */ + { + PyObject * pyResult = PyList_New( list_length( result ) ); + int i = 0; + while ( result ) + { + PyList_SetItem( pyResult, i, PyString_FromString( result->string ) ); + result = list_next( result ); + i += 1; + } + list_free( result ); + return pyResult; + } +} + + +/* + * Accepts four arguments: + * - module name + * - rule name, + * - Python callable. + * - (optional) bjam language function signature. + * Creates a bjam rule with the specified name in the specified module, which will + * invoke the Python callable. + */ + +PyObject * bjam_import_rule( PyObject * self, PyObject * args ) +{ + char * module; + char * rule; + PyObject * func; + PyObject * bjam_signature = NULL; + module_t * m; + RULE * r; + + if ( !PyArg_ParseTuple( args, "ssO|O:import_rule", + &module, &rule, &func, &bjam_signature ) ) + return NULL; + + if ( !PyCallable_Check( func ) ) + { + PyErr_SetString( PyExc_RuntimeError, + "Non-callable object passed to bjam.import_rule" ); + return NULL; + } + + m = bindmodule( *module ? module : 0 ); + r = bindrule( rule, m ); + + /* Make pFunc owned. */ + Py_INCREF( func ); + + r->python_function = func; + r->arguments = 0; + + if (bjam_signature) + { + argument_list * arg_list = args_new(); + Py_ssize_t i; + + Py_ssize_t s = PySequence_Size (bjam_signature); + for (i = 0; i < s; ++i) + { + PyObject* v = PySequence_GetItem (bjam_signature, i); + lol_add(arg_list->data, list_from_python (v)); + Py_DECREF(v); + } + r->arguments = arg_list; + } + + Py_INCREF( Py_None ); + return Py_None; +} + + +/* + * Accepts four arguments: + * - an action name + * - an action body + * - a list of variable that will be bound inside the action + * - integer flags. + * Defines an action on bjam side. + */ + +PyObject * bjam_define_action( PyObject * self, PyObject * args ) +{ + char * name; + char * body; + module_t * m; + PyObject * bindlist_python; + int flags; + LIST * bindlist = L0; + int n; + int i; + + if ( !PyArg_ParseTuple( args, "ssO!i:define_action", &name, &body, + &PyList_Type, &bindlist_python, &flags ) ) + return NULL; + + n = PyList_Size( bindlist_python ); + for ( i = 0; i < n; ++i ) + { + PyObject * next = PyList_GetItem( bindlist_python, i ); + if ( !PyString_Check( next ) ) + { + PyErr_SetString( PyExc_RuntimeError, + "bind list has non-string type" ); + return NULL; + } + bindlist = list_new( bindlist, PyString_AsString( next ) ); + } + + new_rule_actions( root_module(), name, newstr( body ), bindlist, flags ); + + Py_INCREF( Py_None ); + return Py_None; +} + + +/* + * Returns the value of a variable in root Jam module. + */ + +PyObject * bjam_variable( PyObject * self, PyObject * args ) +{ + char * name; + LIST * value; + PyObject * result; + int i; + + if ( !PyArg_ParseTuple( args, "s", &name ) ) + return NULL; + + enter_module( root_module() ); + value = var_get( name ); + exit_module( root_module() ); + + result = PyList_New( list_length( value ) ); + for ( i = 0; value; value = list_next( value ), ++i ) + PyList_SetItem( result, i, PyString_FromString( value->string ) ); + + return result; +} + + +PyObject * bjam_backtrace( PyObject * self, PyObject * args ) +{ + PyObject * result = PyList_New( 0 ); + struct frame * f = frame_before_python_call; + + for ( ; f = f->prev; ) + { + PyObject * tuple = PyTuple_New( 4 ); + char * file; + int line; + char buf[ 32 ]; + + get_source_line( f->procedure, &file, &line ); + sprintf( buf, "%d", line ); + + /* PyTuple_SetItem steals reference. */ + PyTuple_SetItem( tuple, 0, PyString_FromString( file ) ); + PyTuple_SetItem( tuple, 1, PyString_FromString( buf ) ); + PyTuple_SetItem( tuple, 2, PyString_FromString( f->module->name ) ); + PyTuple_SetItem( tuple, 3, PyString_FromString( f->rulename ) ); + + PyList_Append( result, tuple ); + Py_DECREF( tuple ); + } + return result; +} + +PyObject * bjam_caller( PyObject * self, PyObject * args ) +{ + PyObject *result = PyString_FromString( + frame_before_python_call->prev->module->name); + return result; +} + +#endif /* #ifdef HAVE_PYTHON */ + + +#ifdef HAVE_POPEN + +#if defined(_MSC_VER) || defined(__BORLANDC__) + #define popen windows_popen_wrapper + #define pclose _pclose + + /* + * This wrapper is a workaround for a funny _popen() feature on Windows + * where it eats external quotes in some cases. The bug seems to be related + * to the quote stripping functionality used by the Windows cmd.exe + * interpreter when its /S is not specified. + * + * Cleaned up quote from the cmd.exe help screen as displayed on Windows XP + * SP3: + * + * 1. If all of the following conditions are met, then quote characters on + * the command line are preserved: + * + * - no /S switch + * - exactly two quote characters + * - no special characters between the two quote characters, where + * special is one of: &<>()@^| + * - there are one or more whitespace characters between the two quote + * characters + * - the string between the two quote characters is the name of an + * executable file. + * + * 2. Otherwise, old behavior is to see if the first character is a quote + * character and if so, strip the leading character and remove the last + * quote character on the command line, preserving any text after the + * last quote character. + * + * This causes some commands containing quotes not to be executed correctly. + * For example: + * + * "\Long folder name\aaa.exe" --name="Jurko" --no-surname + * + * would get its outermost quotes stripped and would be executed as: + * + * \Long folder name\aaa.exe" --name="Jurko --no-surname + * + * which would report an error about '\Long' not being a valid command. + * + * cmd.exe help seems to indicate it would be enough to add an extra space + * character in front of the command to avoid this but this does not work, + * most likely due to the shell first stripping all leading whitespace + * characters from the command. + * + * Solution implemented here is to quote the whole command in case it + * contains any quote characters. Note thought this will not work correctly + * should Windows ever 'fix' this feature. + * (03.06.2008.) (Jurko) + */ + static FILE * windows_popen_wrapper( char * command, char * mode ) + { + int extra_command_quotes_needed = ( strchr( command, '"' ) != 0 ); + string quoted_command; + FILE * result; + + if ( extra_command_quotes_needed ) + { + string_new( "ed_command ); + string_append( "ed_command, "\"" ); + string_append( "ed_command, command ); + string_append( "ed_command, "\"" ); + command = quoted_command.value; + } + + result = _popen( command, "r" ); + + if ( extra_command_quotes_needed ) + string_free( "ed_command ); + + return result; + } +#endif + + +static char * rtrim(char *s) +{ + char *p = s; + while(*p) ++p; + for(--p; p >= s && isspace(*p); *p-- = 0); + return s; +} + +LIST * builtin_shell( PARSE * parse, FRAME * frame ) +{ + LIST * command = lol_get( frame->args, 0 ); + LIST * result = 0; + string s; + int ret; + char buffer[ 1024 ]; + FILE * p = NULL; + int exit_status = -1; + int exit_status_opt = 0; + int no_output_opt = 0; + int strip_eol_opt = 0; + + /* Process the variable args options. */ + { + int a = 1; + LIST * arg = lol_get( frame->args, a ); + while ( arg ) + { + if ( strcmp( "exit-status", arg->string ) == 0 ) + { + exit_status_opt = 1; + } + else if ( strcmp( "no-output", arg->string ) == 0 ) + { + no_output_opt = 1; + } + else if ( strcmp("strip-eol", arg->string) == 0 ) + { + strip_eol_opt = 1; + } + arg = lol_get( frame->args, ++a ); + } + } + + /* The following fflush() call seems to be indicated as a workaround for a + * popen() bug on POSIX implementations related to synhronizing input + * stream positions for the called and the calling process. + */ + fflush( NULL ); + + p = popen( command->string, "r" ); + if ( p == NULL ) + return L0; + + string_new( &s ); + + while ( ( ret = fread( buffer, sizeof( char ), sizeof( buffer ) - 1, p ) ) > 0 ) + { + buffer[ret] = 0; + if ( !no_output_opt ) + { + if ( strip_eol_opt ) + rtrim(buffer); + string_append( &s, buffer ); + } + } + + exit_status = pclose( p ); + + /* The command output is returned first. */ + result = list_new( L0, newstr( s.value ) ); + string_free( &s ); + + /* The command exit result next. */ + if ( exit_status_opt ) + { + if ( WIFEXITED(exit_status) ) + exit_status = WEXITSTATUS(exit_status); + else + exit_status = -1; + sprintf( buffer, "%d", exit_status ); + result = list_new( result, newstr( buffer ) ); + } + + return result; +} + +#else /* #ifdef HAVE_POPEN */ + +LIST * builtin_shell( PARSE * parse, FRAME * frame ) +{ + return L0; +} + +#endif /* #ifdef HAVE_POPEN */ diff --git a/jam-files/engine/builtins.h b/jam-files/engine/builtins.h new file mode 100644 index 00000000..5fed07c9 --- /dev/null +++ b/jam-files/engine/builtins.h @@ -0,0 +1,69 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#ifndef JAM_BUILTINS_H +# define JAM_BUILTINS_H + +# include "frames.h" + +/* + * builtins.h - compile parsed jam statements + */ + +void load_builtins(); +void init_set(); +void init_path(); +void init_regex(); +void init_property_set(); +void init_sequence(); +void init_order(); + +LIST *builtin_calc( PARSE *parse, FRAME *args ); +LIST *builtin_depends( PARSE *parse, FRAME *args ); +LIST *builtin_rebuilds( PARSE *parse, FRAME *args ); +LIST *builtin_echo( PARSE *parse, FRAME *args ); +LIST *builtin_exit( PARSE *parse, FRAME *args ); +LIST *builtin_flags( PARSE *parse, FRAME *args ); +LIST *builtin_glob( PARSE *parse, FRAME *args ); +LIST *builtin_glob_recursive( PARSE *parse, FRAME *frame ); +LIST *builtin_subst( PARSE *parse, FRAME *args ); +LIST *builtin_match( PARSE *parse, FRAME *args ); +LIST *builtin_split_by_characters( PARSE *parse, FRAME *args ); +LIST *builtin_hdrmacro( PARSE *parse, FRAME *args ); +LIST *builtin_rulenames( PARSE *parse, FRAME *args ); +LIST *builtin_varnames( PARSE *parse, FRAME *args ); +LIST *builtin_delete_module( PARSE *parse, FRAME *args ); +LIST *builtin_import( PARSE *parse, FRAME *args ); +LIST *builtin_export( PARSE *parse, FRAME *args ); +LIST *builtin_caller_module( PARSE *parse, FRAME *args ); +LIST *builtin_backtrace( PARSE *parse, FRAME *args ); +LIST *builtin_pwd( PARSE *parse, FRAME *args ); +LIST *builtin_update( PARSE *parse, FRAME *args ); +LIST *builtin_update_now( PARSE *parse, FRAME *args ); +LIST *builtin_search_for_target( PARSE *parse, FRAME *args ); +LIST *builtin_import_module( PARSE *parse, FRAME *args ); +LIST *builtin_imported_modules( PARSE *parse, FRAME *frame ); +LIST *builtin_instance( PARSE *parse, FRAME *frame ); +LIST *builtin_sort( PARSE *parse, FRAME *frame ); +LIST *builtin_normalize_path( PARSE *parse, FRAME *frame ); +LIST *builtin_native_rule( PARSE *parse, FRAME *frame ); +LIST *builtin_has_native_rule( PARSE *parse, FRAME *frame ); +LIST *builtin_user_module( PARSE *parse, FRAME *frame ); +LIST *builtin_nearest_user_location( PARSE *parse, FRAME *frame ); +LIST *builtin_check_if_file( PARSE *parse, FRAME *frame ); +LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame ); +LIST *builtin_shell( PARSE *parse, FRAME *frame ); +LIST *builtin_md5( PARSE *parse, FRAME *frame ); +LIST *builtin_file_open( PARSE *parse, FRAME *frame ); +LIST *builtin_pad( PARSE *parse, FRAME *frame ); +LIST *builtin_precious( PARSE *parse, FRAME *frame ); +LIST *builtin_self_path( PARSE *parse, FRAME *frame ); +LIST *builtin_makedir( PARSE *parse, FRAME *frame ); + +void backtrace( FRAME *frame ); +extern int last_update_now_status; + +#endif diff --git a/jam-files/engine/bump_version.py b/jam-files/engine/bump_version.py new file mode 100644 index 00000000..9423c4c7 --- /dev/null +++ b/jam-files/engine/bump_version.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +# This script is used to bump version of bjam. It takes a single argument, e.g +# +# ./bump_version.py 3.1.9 +# +# and updates all necessary files. For the time being, it's assumes presense +# of 'perl' executable and Debian-specific 'dch' executable. +# + + +import os +import os.path +import re +import string +import sys + +srcdir = os.path.abspath(os.path.dirname(__file__ )) +docdir = os.path.abspath(os.path.join(srcdir,"..","doc")) + +def edit(file,replacements): + print " '%s'..." %(file) + text = open(file,'r').read() + while len(replacements) > 0: + #~ print " '%s' ==> '%s'" % (replacements[0],replacements[1]) + text = re.compile(replacements[0],re.M).subn(replacements[1],text)[0] + replacements = replacements[2:] + #~ print text + open(file,'w').write(text) + +def make_edits(version): + edit(os.path.join(srcdir,"boost-jam.spec"), [ + '^Version:.*$','Version: %s' % string.join(version, "."), + ]) + + edit(os.path.join(srcdir,"build.jam"), [ + '^_VERSION_ = .* ;$','_VERSION_ = %s %s %s ;' % (version[0], version[1], version[2]), + ]) + + edit(os.path.join(docdir,"bjam.qbk"), [ + '\[version.*\]','[version: %s]' % string.join(version, '.'), + '\[def :version:.*\]','[def :version: %s]' % string.join(version, '.'), + ]) + + edit(os.path.join(srcdir,"patchlevel.h"), [ + '^#define VERSION_MAJOR .*$', + '#define VERSION_MAJOR %s' % (version[0]), + '^#define VERSION_MINOR .*$', + '#define VERSION_MINOR %s' % (version[1]), + '^#define VERSION_PATCH .*$', + '#define VERSION_PATCH %s' % (version[2]), + '^#define VERSION_MAJOR_SYM .*$', + '#define VERSION_MAJOR_SYM "0%s"' % (version[0]), + '^#define VERSION_MINOR_SYM .*$', + '#define VERSION_MINOR_SYM "%s"' % (version[1]), + '^#define VERSION_PATCH_SYM .*$', + '#define VERSION_PATCH_SYM "%s"' % (version[2]), + '^#define VERSION .*$', + '#define VERSION "%s"' % string.join(version, '.'), + '^#define JAMVERSYM .*$', + '#define JAMVERSYM "JAMVERSION=%s.%s"' % (version[0],version[1]), + ]) + +def main(): + + if len(sys.argv) < 2: + print "Expect new version as argument" + sys.exit(1) + + version = string.split(sys.argv[1], ".") + print "Setting version to", version + make_edits(version) + +if __name__ == '__main__': + main() + +#~ Copyright 2006 Rene Rivera. +#~ Copyright 2005-2006 Vladimir Prus. +#~ Distributed under the Boost Software License, Version 1.0. +#~ (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) diff --git a/jam-files/engine/class.c b/jam-files/engine/class.c new file mode 100644 index 00000000..ff4ec568 --- /dev/null +++ b/jam-files/engine/class.c @@ -0,0 +1,141 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "class.h" +#include "strings.h" +#include "variable.h" +#include "frames.h" +#include "rules.h" +#include "newstr.h" + +#include "hash.h" + + +static struct hash * classes = 0; + + +static void check_defined( LIST * class_names ) +{ + for ( ; class_names; class_names = class_names->next ) + { + char * * p = &class_names->string; + if ( !hashcheck( classes, (HASHDATA * *)&p ) ) + { + printf( "Class %s is not defined\n", class_names->string ); + abort(); + } + } +} + + +static char * class_module_name( char * declared_name ) +{ + string name[ 1 ]; + char * result; + + string_new( name ); + string_append( name, "class@" ); + string_append( name, declared_name ); + + result = newstr( name->value ); + string_free( name ); + + return result; +} + + +struct import_base_data +{ + char * base_name; + module_t * base_module; + module_t * class_module; +}; + + +static void import_base_rule( void * r_, void * d_ ) +{ + RULE * r = (RULE *)r_; + RULE * ir1; + RULE * ir2; + struct import_base_data * d = (struct import_base_data *)d_; + string qualified_name[ 1 ]; + + string_new ( qualified_name ); + string_append ( qualified_name, d->base_name ); + string_push_back( qualified_name, '.' ); + string_append ( qualified_name, r->name ); + + ir1 = import_rule( r, d->class_module, r->name ); + ir2 = import_rule( r, d->class_module, qualified_name->value ); + + /* Copy 'exported' flag. */ + ir1->exported = ir2->exported = r->exported; + + /* If we are importing a class method, localize it. */ + if ( ( r->module == d->base_module ) || ( r->module->class_module && + ( r->module->class_module == d->base_module ) ) ) + ir1->module = ir2->module = d->class_module; + + string_free( qualified_name ); +} + + +/* + * For each exported rule 'n', declared in class module for base, imports that + * rule in 'class' as 'n' and as 'base.n'. Imported rules are localized and + * marked as exported. + */ + +static void import_base_rules( module_t * class, char * base ) +{ + module_t * base_module = bindmodule( class_module_name( base ) ); + struct import_base_data d; + d.base_name = base; + d.base_module = base_module; + d.class_module = class; + + if ( base_module->rules ) + hashenumerate( base_module->rules, import_base_rule, &d ); + + import_module( imported_modules( base_module ), class ); +} + + +char * make_class_module( LIST * xname, LIST * bases, FRAME * frame ) +{ + char * name = class_module_name( xname->string ); + char * * pp = &xname->string; + module_t * class_module = 0; + module_t * outer_module = frame->module; + + if ( !classes ) + classes = hashinit( sizeof( char * ), "classes" ); + + if ( hashcheck( classes, (HASHDATA * *)&pp ) ) + { + printf( "Class %s already defined\n", xname->string ); + abort(); + } + else + { + hashenter( classes, (HASHDATA * *)&pp ); + } + check_defined( bases ); + + class_module = bindmodule( name ); + + exit_module( outer_module ); + enter_module( class_module ); + + var_set( "__name__", xname, VAR_SET ); + var_set( "__bases__", bases, VAR_SET ); + + exit_module( class_module ); + enter_module( outer_module ); + + for ( ; bases; bases = bases->next ) + import_base_rules( class_module, bases->string ); + + return name; +} diff --git a/jam-files/engine/class.h b/jam-files/engine/class.h new file mode 100644 index 00000000..f7faeff6 --- /dev/null +++ b/jam-files/engine/class.h @@ -0,0 +1,13 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#ifndef CLASS_H_VP_2003_08_01 +#define CLASS_H_VP_2003_08_01 + +#include "lists.h" +#include "frames.h" + +char* make_class_module(LIST* xname, LIST* bases, FRAME* frame); + +#endif diff --git a/jam-files/engine/command.c b/jam-files/engine/command.c new file mode 100644 index 00000000..d2ea0681 --- /dev/null +++ b/jam-files/engine/command.c @@ -0,0 +1,100 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * command.c - maintain lists of commands + */ + +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "variable.h" +#include "rules.h" + +#include "command.h" +#include <limits.h> +#include <string.h> + + +/* + * cmd_new() - return a new CMD or 0 if too many args + */ + +CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell ) +{ + CMD * cmd = (CMD *)BJAM_MALLOC( sizeof( CMD ) ); + /* Lift line-length limitation entirely when JAMSHELL is just "%". */ + int no_limit = ( shell && !strcmp(shell->string,"%") && !list_next(shell) ); + int max_line = MAXLINE; + int allocated = -1; + + cmd->rule = rule; + cmd->shell = shell; + cmd->next = 0; + + lol_init( &cmd->args ); + lol_add( &cmd->args, targets ); + lol_add( &cmd->args, sources ); + cmd->buf = 0; + + do + { + BJAM_FREE( cmd->buf ); /* free any buffer from previous iteration */ + + cmd->buf = (char*)BJAM_MALLOC_ATOMIC( max_line + 1 ); + + if ( cmd->buf == 0 ) + break; + + allocated = var_string( rule->actions->command, cmd->buf, max_line, &cmd->args ); + + max_line = max_line * 2; + } + while ( ( allocated < 0 ) && ( max_line < INT_MAX / 2 ) ); + + if ( !no_limit ) + { + /* Bail if the result will not fit in MAXLINE. */ + char * s = cmd->buf; + while ( *s ) + { + size_t l = strcspn( s, "\n" ); + + if ( l > MAXLINE ) + { + /* We do not free targets/sources/shell if bailing. */ + cmd_free( cmd ); + return 0; + } + + s += l; + if ( *s ) + ++s; + } + } + + return cmd; +} + + +/* + * cmd_free() - free a CMD + */ + +void cmd_free( CMD * cmd ) +{ + lol_free( &cmd->args ); + list_free( cmd->shell ); + BJAM_FREE( cmd->buf ); + BJAM_FREE( (char *)cmd ); +} diff --git a/jam-files/engine/command.h b/jam-files/engine/command.h new file mode 100644 index 00000000..ddd38e68 --- /dev/null +++ b/jam-files/engine/command.h @@ -0,0 +1,61 @@ +/* + * 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; /* actual commands */ +}; + +CMD * cmd_new +( + RULE * rule, /* rule (referenced) */ + LIST * targets, /* $(<) (freed) */ + LIST * sources, /* $(>) (freed) */ + LIST * shell /* $(SHELL) (freed) */ +); + +void cmd_free( CMD * ); + +#define cmd_next( c ) ( ( c )->next ) diff --git a/jam-files/engine/compile.c b/jam-files/engine/compile.c new file mode 100644 index 00000000..2c049aae --- /dev/null +++ b/jam-files/engine/compile.c @@ -0,0 +1,1424 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# 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 "make.h" +# include "search.h" +# include "hdrmacro.h" +# include "hash.h" +# include "modules.h" +# include "strings.h" +# include "builtins.h" +# include "class.h" + +# include <assert.h> +# include <string.h> +# include <stdarg.h> + +/* + * compile.c - compile parsed jam statements + * + * External routines: + * + * compile_append() - append list results of two statements + * compile_eval() - evaluate if to determine which leg to compile + * compile_foreach() - compile the "for x in y" statement + * compile_if() - compile 'if' rule + * compile_while() - compile 'while' 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 + * + * builtin_depends() - DEPENDS/INCLUDES rule + * builtin_echo() - ECHO rule + * builtin_exit() - EXIT rule + * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule + * + * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of + * the awkward sounding "settings". + * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 05/13/94 (seiwald) - include files are now bound as targets, and thus + * can make use of $(SEARCH) + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/22/95 (seiwald) - Exit rule. + * 02/02/95 (seiwald) - Always rule; LEAVES rule. + * 02/14/95 (seiwald) - NoUpdate rule. + * 09/11/00 (seiwald) - new evaluate_rule() for headers(). + * 09/11/00 (seiwald) - compile_xxx() now return LIST *. + * New compile_append() and compile_list() in + * support of building lists here, rather than + * in jamgram.yy. + * 01/10/00 (seiwald) - built-ins split out to builtin.c. + */ + +static void debug_compile( int which, char *s, FRAME* frame ); +int glob( char *s, char *c ); +/* Internal functions from builtins.c */ +void backtrace( FRAME *frame ); +void backtrace_line( FRAME *frame ); +void print_source_line( PARSE* p ); + +struct frame * frame_before_python_call; + +void frame_init( FRAME* frame ) +{ + frame->prev = 0; + frame->prev_user = 0; + lol_init(frame->args); + frame->module = root_module(); + frame->rulename = "module scope"; + frame->procedure = 0; +} + + +void frame_free( FRAME* frame ) +{ + lol_free( frame->args ); +} + + +/* + * 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, FRAME * frame ) +{ + /* Append right to left. */ + return list_append( + parse_evaluate( parse->left, frame ), + parse_evaluate( parse->right, frame ) ); +} + + +/* + * compile_eval() - evaluate if to determine which leg to compile + * + * Returns: + * list if expression true - compile 'then' clause + * L0 if expression false - compile 'else' clause + */ + +static int lcmp( LIST * t, LIST * s ) +{ + int status = 0; + + while ( !status && ( t || s ) ) + { + char *st = t ? t->string : ""; + char *ss = s ? s->string : ""; + + status = strcmp( st, ss ); + + t = t ? list_next( t ) : t; + s = s ? list_next( s ) : s; + } + + return status; +} + +LIST * compile_eval( PARSE * parse, FRAME * frame ) +{ + LIST * ll; + LIST * lr; + LIST * s; + LIST * t; + int status = 0; + + /* Short circuit lr eval for &&, ||, and 'in'. */ + + ll = parse_evaluate( parse->left, frame ); + 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_evaluate( parse->right, frame ); + } + + /* 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", frame ); + list_print( ll ); + printf( "(%d) ", status ); + list_print( lr ); + printf( "\n" ); + } + + /* Find something to return. */ + /* In odd circumstances (like "" = "") */ + /* we'll have to return a new string. */ + + if ( !status ) t = 0; + else if ( ll ) t = ll, ll = 0; + else if ( lr ) t = lr, lr = 0; + else t = list_new( L0, newstr( "1" ) ); + + if ( ll ) list_free( ll ); + if ( lr ) list_free( lr ); + return t; +} + + +/* + * compile_foreach() - compile the "for x in y" statement + * + * Compile_foreach() resets the given variable name to each specified + * value, executing the commands enclosed in braces for each iteration. + * + * parse->string index variable + * parse->left variable values + * parse->right rule to compile + */ + +LIST * compile_foreach( PARSE * parse, FRAME * frame ) +{ + LIST * nv = parse_evaluate( parse->left, frame ); + LIST * l; + SETTINGS * s = 0; + + if ( parse->num ) + { + s = addsettings( s, VAR_SET, parse->string, L0 ); + pushsettings( s ); + } + + /* Call var_set to reset $(parse->string) for each val. */ + + for ( l = nv; l; l = list_next( l ) ) + { + LIST * val = list_new( L0, copystr( l->string ) ); + var_set( parse->string, val, VAR_SET ); + list_free( parse_evaluate( parse->right, frame ) ); + } + + if ( parse->num ) + { + popsettings( s ); + freesettings( s ); + } + + list_free( nv ); + + return L0; +} + +/* + * compile_if() - compile 'if' rule + * + * parse->left condition tree + * parse->right then tree + * parse->third else tree + */ + +LIST * compile_if( PARSE * p, FRAME * frame ) +{ + LIST * l = parse_evaluate( p->left, frame ); + if ( l ) + { + list_free( l ); + return parse_evaluate( p->right, frame ); + } + return parse_evaluate( p->third, frame ); +} + + +LIST * compile_while( PARSE * p, FRAME * frame ) +{ + LIST * r = 0; + LIST * l; + while ( ( l = parse_evaluate( p->left, frame ) ) ) + { + list_free( l ); + if ( r ) list_free( r ); + r = parse_evaluate( p->right, frame ); + } + return r; +} + + +/* + * 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, FRAME * frame ) +{ + LIST * nt = parse_evaluate( parse->left, frame ); + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "include", frame); + list_print( nt ); + printf( "\n" ); + } + + if ( nt ) + { + TARGET * t = bindtarget( nt->string ); + + /* DWA 2001/10/22 - Perforce Jam cleared the arguments here, which + * prevents an included file from being treated as part of the body of a + * rule. I did not see any reason to do that, so I lifted the + * restriction. + */ + + /* Bind the include file under the influence of */ + /* "on-target" variables. Though they are targets, */ + /* include files are not built with make(). */ + + pushsettings( t->settings ); + /* We don't expect that file to be included is generated by some + action. Therefore, pass 0 as third argument. + If the name resolves to directory, let it error out. */ + t->boundname = search( t->name, &t->time, 0, 0 ); + popsettings( t->settings ); + + parse_file( t->boundname, frame ); + } + + list_free( nt ); + + return L0; +} + +static LIST* evaluate_in_module ( char* module_name, PARSE * p, FRAME* frame) +{ + LIST* result; + + module_t* outer_module = frame->module; + frame->module = module_name ? bindmodule( module_name ) : root_module(); + + if ( outer_module != frame->module ) + { + exit_module( outer_module ); + enter_module( frame->module ); + } + + result = parse_evaluate( p, frame ); + + if ( outer_module != frame->module ) + { + exit_module( frame->module ); + enter_module( outer_module ); + frame->module = outer_module; + } + + return result; +} + + +LIST * compile_module( PARSE * p, FRAME * frame ) +{ + /* Here we are entering a module declaration block. */ + LIST * module_name = parse_evaluate( p->left, frame ); + LIST * result = evaluate_in_module( module_name ? module_name->string : 0, + p->right, frame ); + list_free( module_name ); + return result; +} + + +LIST * compile_class( PARSE * p, FRAME * frame ) +{ + /** Todo: check for empty class name. + Check for class redeclaration. */ + + char * class_module = 0; + + LIST * name = parse_evaluate( p->left->right, frame ); + LIST * bases = 0; + + if ( p->left->left ) + bases = parse_evaluate( p->left->left->right, frame ); + + class_module = make_class_module( name, bases, frame ); + evaluate_in_module( class_module, p->right, frame ); + + return L0; +} + + +/* + * compile_list() - expand and return a list. + * + * parse->string - character string to expand. + */ + +LIST * compile_list( PARSE * parse, FRAME * frame ) +{ + /* s is a copyable string */ + char * s = parse->string; + return var_expand( L0, s, s + strlen( s ), frame->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, FRAME * frame ) +{ + LIST * l; + SETTINGS * s = 0; + LIST * nt = parse_evaluate( parse->left, frame ); + LIST * ns = parse_evaluate( parse->right, frame ); + LIST * result; + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "local", frame ); + list_print( nt ); + printf( " = " ); + list_print( ns ); + printf( "\n" ); + } + + /* Initial value is ns. */ + for ( l = nt; l; l = list_next( l ) ) + s = addsettings( s, VAR_SET, 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_evaluate( parse->third, frame ); + popsettings( s ); + + freesettings( s ); + + return result; +} + + +/* + * compile_null() - do nothing -- a stub for parsing. + */ + +LIST * compile_null( PARSE * parse, FRAME * frame ) +{ + return L0; +} + + +/* + * compile_on() - run rule under influence of on-target variables + * + * parse->left list of files to include (can only do 1). + * parse->right rule to run. + * + * EXPERIMENTAL! + */ + +LIST * compile_on( PARSE * parse, FRAME * frame ) +{ + LIST * nt = parse_evaluate( parse->left, frame ); + LIST * result = 0; + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "on", frame ); + list_print( nt ); + printf( "\n" ); + } + + if ( nt ) + { + TARGET * t = bindtarget( nt->string ); + pushsettings( t->settings ); + result = parse_evaluate( parse->right, frame ); + popsettings( t->settings ); + } + + list_free( nt ); + + return result; +} + + +/* + * compile_rule() - compile a single user defined rule. + * + * parse->string name of user defined rule. + * parse->left parameters (list of lists) to rule, recursing left. + * + * Wrapped around evaluate_rule() so that headers() can share it. + */ + +LIST * compile_rule( PARSE * parse, FRAME * frame ) +{ + FRAME inner[ 1 ]; + LIST * result; + PARSE * p; + + /* Build up the list of arg lists. */ + frame_init( inner ); + inner->prev = frame; + inner->prev_user = frame->module->user_module ? frame : frame->prev_user; + inner->module = frame->module; /* This gets fixed up in evaluate_rule(), below. */ + inner->procedure = parse; + /* Special-case LOL of length 1 where the first list is totally empty. + This is created when calling functions with no parameters, due to + the way jam grammar is written. This is OK when one jam function + calls another, but really not good when Jam function calls Python. */ + if ( parse->left->left == NULL && parse->left->right->func == compile_null) + ; + else + for ( p = parse->left; p; p = p->left ) + lol_add( inner->args, parse_evaluate( p->right, frame ) ); + + /* And invoke the rule. */ + result = evaluate_rule( parse->string, inner ); + frame_free( inner ); + return result; +} + + +static void argument_error( char * message, RULE * rule, FRAME * frame, LIST* arg ) +{ + LOL * actual = frame->args; + assert( frame->procedure != 0 ); + backtrace_line( frame->prev ); + printf( "*** argument error\n* rule %s ( ", frame->rulename ); + lol_print( rule->arguments->data ); + printf( " )\n* called with: ( " ); + lol_print( actual ); + printf( " )\n* %s %s\n", message, arg ? arg->string : "" ); + print_source_line( rule->procedure ); + printf( "see definition of rule '%s' being called\n", rule->name ); + backtrace( frame->prev ); + exit( 1 ); +} + + +/* Define delimiters for type check elements in argument lists (and return type + * specifications, eventually). + */ +# define TYPE_OPEN_DELIM '[' +# define TYPE_CLOSE_DELIM ']' + +/* + * is_type_name() - true iff the given string represents a type check + * specification. + */ + +static int is_type_name( char * s ) +{ + return ( s[ 0 ] == TYPE_OPEN_DELIM ) && + ( s[ strlen( s ) - 1 ] == TYPE_CLOSE_DELIM ); +} + + +/* + * arg_modifier - if the next element of formal is a single character, return + * that; return 0 otherwise. Used to extract "*+?" modifiers * from argument + * lists. + */ + +static char arg_modifier( LIST * formal ) +{ + if ( formal->next ) + { + char * next = formal->next->string; + if ( next && ( next[ 0 ] != 0 ) && ( next[ 1 ] == 0 ) ) + return next[ 0 ]; + } + return 0; +} + + +/* + * type_check() - checks that each element of values satisfies the requirements + * of type_name. + * + * caller - the frame of the rule calling the rule whose arguments are + * being checked + * + * called - the rule being called + * + * arg_name - a list element containing the name of the argument being + * checked + */ + +static void type_check +( + char * type_name, + LIST * values, + FRAME * caller, + RULE * called, + LIST * arg_name +) +{ + static module_t * typecheck = 0; + + /* If nothing to check, bail now. */ + if ( !values || !type_name ) + return; + + if ( !typecheck ) + typecheck = bindmodule( ".typecheck" ); + + /* If the checking rule can not be found, also bail. */ + { + RULE checker_, *checker = &checker_; + + checker->name = type_name; + if ( !typecheck->rules || !hashcheck( typecheck->rules, (HASHDATA * *)&checker ) ) + return; + } + + exit_module( caller->module ); + + while ( values != 0 ) + { + LIST *error; + FRAME frame[1]; + frame_init( frame ); + frame->module = typecheck; + frame->prev = caller; + frame->prev_user = caller->module->user_module ? caller : caller->prev_user; + + enter_module( typecheck ); + /* Prepare the argument list */ + lol_add( frame->args, list_new( L0, values->string ) ); + error = evaluate_rule( type_name, frame ); + + exit_module( typecheck ); + + if ( error ) + argument_error( error->string, called, caller, arg_name ); + + frame_free( frame ); + values = values->next; + } + + enter_module( caller->module ); +} + +/* + * collect_arguments() - local argument checking and collection + */ +static SETTINGS * +collect_arguments( RULE* rule, FRAME* frame ) +{ + SETTINGS *locals = 0; + + LOL * all_actual = frame->args; + LOL * all_formal = rule->arguments ? rule->arguments->data : 0; + if ( all_formal ) /* Nothing to set; nothing to check */ + { + int max = all_formal->count > all_actual->count + ? all_formal->count + : all_actual->count; + + int n; + for ( n = 0; n < max ; ++n ) + { + LIST *actual = lol_get( all_actual, n ); + char *type_name = 0; + + LIST *formal; + for ( formal = lol_get( all_formal, n ); formal; formal = formal->next ) + { + char* name = formal->string; + + if ( is_type_name(name) ) + { + if ( type_name ) + argument_error( "missing argument name before type name:", rule, frame, formal ); + + if ( !formal->next ) + argument_error( "missing argument name after type name:", rule, frame, formal ); + + type_name = formal->string; + } + else + { + LIST* value = 0; + char modifier; + LIST* arg_name = formal; /* hold the argument name for type checking */ + int multiple = 0; + + /* Stop now if a variable number of arguments are specified */ + if ( name[0] == '*' && name[1] == 0 ) + return locals; + + modifier = arg_modifier( formal ); + + if ( !actual && modifier != '?' && modifier != '*' ) + argument_error( "missing argument", rule, frame, formal ); + + switch ( modifier ) + { + case '+': + case '*': + value = list_copy( 0, actual ); + multiple = 1; + actual = 0; + /* skip an extra element for the modifier */ + formal = formal->next; + break; + case '?': + /* skip an extra element for the modifier */ + formal = formal->next; + /* fall through */ + default: + if ( actual ) /* in case actual is missing */ + { + value = list_new( 0, actual->string ); + actual = actual->next; + } + } + + locals = addsettings(locals, VAR_SET, name, value); + locals->multiple = multiple; + type_check( type_name, value, frame, rule, arg_name ); + type_name = 0; + } + } + + if ( actual ) + { + argument_error( "extra argument", rule, frame, actual ); + } + } + } + return locals; +} + +RULE * +enter_rule( char *rulename, module_t *target_module ); + +#ifdef HAVE_PYTHON + +static int python_instance_number = 0; + + +/* Given a Python object, return a string to use in Jam + code instead of said object. + If the object is string, use the string value + If the object implemenets __jam_repr__ method, use that. + Otherwise return 0. + + The result value is newstr-ed. */ +char *python_to_string(PyObject* value) +{ + if (PyString_Check(value)) + { + return newstr(PyString_AsString(value)); + } + else + { + /* See if this is an instance that defines special __jam_repr__ + method. */ + if (PyInstance_Check(value) + && PyObject_HasAttrString(value, "__jam_repr__")) + { + PyObject* repr = PyObject_GetAttrString(value, "__jam_repr__"); + if (repr) + { + PyObject* arguments2 = PyTuple_New(0); + PyObject* value2 = PyObject_Call(repr, arguments2, 0); + Py_DECREF(repr); + Py_DECREF(arguments2); + if (PyString_Check(value2)) + { + return newstr(PyString_AsString(value2)); + } + Py_DECREF(value2); + } + } + return 0; + } +} + +static LIST* +call_python_function(RULE* r, FRAME* frame) +{ + LIST * result = 0; + PyObject * arguments = 0; + PyObject * kw = NULL; + int i ; + PyObject * py_result; + + if (r->arguments) + { + SETTINGS * args; + + arguments = PyTuple_New(0); + kw = PyDict_New(); + + for (args = collect_arguments(r, frame); args; args = args->next) + { + PyObject *key = PyString_FromString(args->symbol); + PyObject *value = 0; + if (args->multiple) + value = list_to_python(args->value); + else { + if (args->value) + value = PyString_FromString(args->value->string); + } + + if (value) + PyDict_SetItem(kw, key, value); + Py_DECREF(key); + Py_XDECREF(value); + } + } + else + { + arguments = PyTuple_New( frame->args->count ); + for ( i = 0; i < frame->args->count; ++i ) + { + PyObject * arg = PyList_New(0); + LIST* l = lol_get( frame->args, i); + + for ( ; l; l = l->next ) + { + PyObject * v = PyString_FromString(l->string); + PyList_Append( arg, v ); + Py_DECREF(v); + } + /* Steals reference to 'arg' */ + PyTuple_SetItem( arguments, i, arg ); + } + } + + frame_before_python_call = frame; + py_result = PyObject_Call( r->python_function, arguments, kw ); + Py_DECREF(arguments); + Py_XDECREF(kw); + if ( py_result != NULL ) + { + if ( PyList_Check( py_result ) ) + { + int size = PyList_Size( py_result ); + int i; + for ( i = 0; i < size; ++i ) + { + PyObject * item = PyList_GetItem( py_result, i ); + char *s = python_to_string (item); + if (!s) { + fprintf( stderr, "Non-string object returned by Python call.\n" ); + } else { + result = list_new (result, s); + } + } + } + else if ( py_result == Py_None ) + { + result = L0; + } + else + { + char *s = python_to_string(py_result); + if (s) + result = list_new(0, s); + else + /* We have tried all we could. Return empty list. There are + cases, e.g. feature.feature function that should return + value for the benefit of Python code and which also can be + called by Jam code, where no sensible value can be + returned. We cannot even emit a warning, since there will + be a pile of them. */ + result = L0; + } + + Py_DECREF( py_result ); + } + else + { + PyErr_Print(); + fprintf(stderr,"Call failed\n"); + } + + return result; +} + + +module_t * python_module() +{ + static module_t * python = 0; + if ( !python ) + python = bindmodule("__python__"); + return python; +} + +#endif + + +/* + * evaluate_rule() - execute a rule invocation. + */ + +LIST * +evaluate_rule( + char * rulename, + FRAME * frame ) +{ + LIST * result = L0; + RULE * rule; + profile_frame prof[1]; + module_t * prev_module = frame->module; + + LIST * l; + { + LOL arg_context_, * arg_context = &arg_context_; + if ( !frame->prev ) + lol_init(arg_context); + else + arg_context = frame->prev->args; + l = var_expand( L0, rulename, rulename+strlen(rulename), arg_context, 0 ); + } + + if ( !l ) + { + backtrace_line( frame->prev ); + printf( "warning: rulename %s expands to empty string\n", rulename ); + backtrace( frame->prev ); + return result; + } + + rulename = l->string; + rule = bindrule( l->string, frame->module ); + +#ifdef HAVE_PYTHON + if ( rule->python_function ) + { + /* The below messing with modules is due to the way modules are + * implemented in Jam. Suppose we are in module M1 now. The global + * variable map actually holds 'M1' variables, and M1->variables hold + * global variables. + * + * If we call Python right away, Python calls back Jam and then Jam + * does 'module M1 { }' then Jam will try to swap the current global + * variables with M1->variables. The result will be that global + * variables map will hold global variables, and any variable settings + * we do will go to the global module, not M1. + * + * By restoring basic state, where the global variable map holds global + * variable, we make sure any future 'module M1' entry will work OK. + */ + + LIST * result; + module_t * m = python_module(); + + frame->module = m; + + exit_module( prev_module ); + enter_module( m ); + + result = call_python_function( rule, frame ); + + exit_module( m ); + enter_module ( prev_module ); + + return result; + } +#endif + + /* Drop the rule name. */ + l = list_pop_front( l ); + + /* Tack the rest of the expansion onto the front of the first argument. */ + frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) ); + + if ( DEBUG_COMPILE ) + { + /* Try hard to indicate in which module the rule is going to execute. */ + if ( rule->module != frame->module + && rule->procedure != 0 && strcmp( rulename, rule->procedure->rulename ) ) + { + char buf[256] = ""; + strncat( buf, rule->module->name, sizeof( buf ) - 1 ); + strncat( buf, rule->name, sizeof( buf ) - 1 ); + debug_compile( 1, buf, frame ); + } + else + { + debug_compile( 1, rulename, frame ); + } + + lol_print( frame->args ); + printf( "\n" ); + } + + if ( rule->procedure && rule->module != prev_module ) + { + /* Propagate current module to nested rule invocations. */ + frame->module = rule->module; + + /* Swap variables. */ + exit_module( prev_module ); + enter_module( rule->module ); + } + + /* Record current rule name in frame. */ + if ( rule->procedure ) + { + frame->rulename = rulename; + /* And enter record profile info. */ + if ( DEBUG_PROFILE ) + profile_enter( rule->procedure->rulename, prof ); + } + + /* Check traditional targets $(<) and sources $(>). */ + if ( !rule->actions && !rule->procedure ) + { + backtrace_line( frame->prev ); + printf( "rule %s unknown in module %s\n", rule->name, frame->module->name ); + backtrace( frame->prev ); + exit( 1 ); + } + + /* 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 *)BJAM_MALLOC( sizeof( ACTION ) ); + memset( (char *)action, '\0', sizeof( *action ) ); + + action->rule = rule; + action->targets = targetlist( (TARGETS *)0, lol_get( frame->args, 0 ) ); + action->sources = targetlist( (TARGETS *)0, lol_get( frame->args, 1 ) ); + + /* If we have a group of targets all being built using the same action + * then we must not allow any of them to be used as sources unless they + * had all already been built in the first place or their joined action + * has had a chance to finish its work and build all of them anew. + * + * Without this it might be possible, in case of a multi-process build, + * for their action, triggered by buiding one of the targets, to still + * be running when another target in the group reports as done in order + * to avoid triggering the same action again and gets used prematurely. + * + * As a quick-fix to achieve this effect we make all the targets list + * each other as 'included targets'. More precisely, we mark the first + * listed target as including all the other targets in the list and vice + * versa. This makes anyone depending on any of those targets implicitly + * depend on all of them, thus making sure none of those targets can be + * used as sources until all of them have been built. Note that direct + * dependencies could not have been used due to the 'circular + * dependency' issue. + * + * TODO: Although the current implementation solves the problem of one + * of the targets getting used before its action completes its work it + * also forces the action to run whenever any of the targets in the + * group is not up to date even though some of them might not actually + * be used by the targets being built. We should see how we can + * correctly recognize such cases and use that to avoid running the + * action if possible and not rebuild targets not actually depending on + * targets that are not up to date. + * + * TODO: Using the 'include' feature might have side-effects due to + * interaction with the actual 'inclusion scanning' system. This should + * be checked. + */ + if ( action->targets ) + { + TARGET * t0 = action->targets->target; + for ( t = action->targets->next; t; t = t->next ) + { + target_include( t->target, t0 ); + target_include( t0, t->target ); + } + } + + /* 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. + * parse_refer()/parse_free() call pair added to ensure rule not freed + * during use. + */ + if ( rule->procedure ) + { + SETTINGS * local_args = collect_arguments( rule, frame ); + PARSE * parse = rule->procedure; + parse_refer( parse ); + + pushsettings( local_args ); + result = parse_evaluate( parse, frame ); + popsettings( local_args ); + freesettings( local_args ); + + parse_free( parse ); + } + + if ( frame->module != prev_module ) + { + exit_module( frame->module ); + enter_module( prev_module ); + } + + if ( DEBUG_PROFILE && rule->procedure ) + profile_exit( prof ); + + if ( DEBUG_COMPILE ) + debug_compile( -1, 0, frame); + + return result; +} + + +/* + * Call the given rule with the specified parameters. The parameters should be + * of type LIST* and end with a NULL pointer. This differs from 'evaluate_rule' + * in that frame for the called rule is prepared inside 'call_rule'. + * + * This function is useful when a builtin rule (in C) wants to call another rule + * which might be implemented in Jam. + */ + +LIST * call_rule( char * rulename, FRAME * caller_frame, ... ) +{ + va_list va; + LIST * result; + + FRAME inner[1]; + frame_init( inner ); + inner->prev = caller_frame; + inner->prev_user = caller_frame->module->user_module ? + caller_frame : caller_frame->prev_user; + inner->module = caller_frame->module; + inner->procedure = 0; + + va_start( va, caller_frame ); + for ( ; ; ) + { + LIST * l = va_arg( va, LIST* ); + if ( !l ) + break; + lol_add( inner->args, l ); + } + va_end( va ); + + result = evaluate_rule( rulename, inner ); + + frame_free( inner ); + + 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, FRAME * frame ) +{ + /* Ignore result from first statement; return the 2nd. */ + /* Optimize recursion on the right by looping. */ + do list_free( parse_evaluate( parse->left, frame ) ); + while ( ( parse = parse->right )->func == compile_rules ); + return parse_evaluate( parse, frame ); +} + + +/* + * assign_var_mode() - convert ASSIGN_XXX compilation flag into corresponding + * VAR_XXX variable set flag. + */ + +static int assign_var_mode( int parsenum, char const * * tracetext ) +{ + char const * trace; + int setflag; + switch ( parsenum ) + { + case ASSIGN_SET : setflag = VAR_SET ; trace = "=" ; break; + case ASSIGN_APPEND : setflag = VAR_APPEND ; trace = "+="; break; + case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break; + default: setflag = VAR_SET ; trace = "" ; break; + } + if ( tracetext ) + *tracetext = trace ; + return setflag; +} + +/* + * compile_set() - compile the "set variable" statement + * + * parse->left variable names + * parse->right variable values + * parse->num ASSIGN_SET/APPEND/DEFAULT + */ + +LIST * compile_set( PARSE * parse, FRAME * frame ) +{ + LIST * nt = parse_evaluate( parse->left, frame ); + LIST * ns = parse_evaluate( parse->right, frame ); + LIST * l; + char const * trace; + int setflag = assign_var_mode( parse->num, &trace ); + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "set", frame ); + list_print( nt ); + printf( " %s ", trace ); + list_print( ns ); + printf( "\n" ); + } + + /* Call var_set to set variable. var_set keeps ns, so need to copy it. */ + for ( l = nt; l; l = list_next( l ) ) + var_set( l->string, list_copy( L0, ns ), setflag ); + list_free( nt ); + return ns; +} + + +/* + * compile_setcomp() - support for `rule` - save parse tree. + * + * parse->string rule name + * parse->left rules for rule + * parse->right optional list-of-lists describing arguments + */ + +LIST * compile_setcomp( PARSE * parse, FRAME * frame ) +{ + argument_list * arg_list = 0; + + /* Create new LOL describing argument requirements if supplied. */ + if ( parse->right ) + { + PARSE * p; + arg_list = args_new(); + for ( p = parse->right; p; p = p->left ) + lol_add( arg_list->data, parse_evaluate( p->right, frame ) ); + } + + new_rule_body( frame->module, parse->string, arg_list, parse->left, !parse->num ); + 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 transferred directly + * to the rule flags (as defined in rules.h). + */ + +LIST * compile_setexec( PARSE * parse, FRAME * frame ) +{ + LIST * bindlist = parse_evaluate( parse->left, frame ); + new_rule_actions( frame->module, parse->string, parse->string1, bindlist, parse->num ); + return L0; +} + + +/* + * compile_settings() - compile the "on =" (set variable on exec) statement. + * + * parse->left variable names + * parse->right target name + * parse->third variable value + * parse->num ASSIGN_SET/APPEND + */ + +LIST * compile_settings( PARSE * parse, FRAME * frame ) +{ + LIST * nt = parse_evaluate( parse->left, frame ); + LIST * ns = parse_evaluate( parse->third, frame ); + LIST * targets = parse_evaluate( parse->right, frame ); + LIST * ts; + char const * trace; + int setflag = assign_var_mode( parse->num, &trace ); + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "set", frame ); + list_print( nt ); + printf( " on " ); + list_print( targets ); + printf( " %s ", trace ); + 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, setflag, 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, FRAME * frame ) +{ + LIST * nt = parse_evaluate( parse->left, frame ); + LIST * result = 0; + + if ( DEBUG_COMPILE ) + { + debug_compile( 0, "switch", frame ); + 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_evaluate( parse, frame ); + break; + } + } + + list_free( nt ); + return result; +} + + +/* + * debug_compile() - printf with indent to show rule expansion. + */ + +static void debug_compile( int which, char * s, FRAME * frame ) +{ + static int level = 0; + static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; + + if ( which >= 0 ) + { + int i; + + print_source_line( frame->procedure ); + + i = ( level + 1 ) * 2; + while ( i > 35 ) + { + fputs( indent, stdout ); + i -= 35; + } + + printf( "%*.*s ", i, i, indent ); + } + + if ( s ) + printf( "%s ", s ); + + level += which; +} diff --git a/jam-files/engine/compile.h b/jam-files/engine/compile.h new file mode 100644 index 00000000..7d5191f0 --- /dev/null +++ b/jam-files/engine/compile.h @@ -0,0 +1,82 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef COMPILE_DWA20011022_H +# define COMPILE_DWA20011022_H + +# include "frames.h" +# include "parse.h" +# include "regexp.h" + +/* + * compile.h - compile parsed jam statements + */ + +void compile_builtins(); + +LIST *compile_append( PARSE *parse, FRAME *frame ); +LIST *compile_foreach( PARSE *parse, FRAME *frame ); +LIST *compile_if( PARSE *parse, FRAME *frame ); +LIST *compile_eval( PARSE *parse, FRAME *args ); +LIST *compile_include( PARSE *parse, FRAME *frame ); +LIST *compile_list( PARSE *parse, FRAME *frame ); +LIST *compile_local( PARSE *parse, FRAME *frame ); +LIST *compile_module( PARSE *parse, FRAME *frame ); +LIST *compile_class( PARSE *parse, FRAME *frame ); +LIST *compile_null( PARSE *parse, FRAME *frame ); +LIST *compile_on( PARSE *parse, FRAME *frame ); +LIST *compile_rule( PARSE *parse, FRAME *frame ); +LIST *compile_rules( PARSE *parse, FRAME *frame ); +LIST *compile_set( PARSE *parse, FRAME *frame ); +LIST *compile_setcomp( PARSE *parse, FRAME *frame ); +LIST *compile_setexec( PARSE *parse, FRAME *frame ); +LIST *compile_settings( PARSE *parse, FRAME *frame ); +LIST *compile_switch( PARSE *parse, FRAME *frame ); +LIST *compile_while( PARSE *parse, FRAME *frame ); + +LIST *evaluate_rule( char *rulename, FRAME *frame ); +LIST *call_rule( char *rulename, FRAME* caller_frame, ...); + +regexp* regex_compile( const char* pattern ); + +/* Flags for compile_set(), etc */ + +# define ASSIGN_SET 0x00 /* = assign variable */ +# define ASSIGN_APPEND 0x01 /* += append variable */ +# define ASSIGN_DEFAULT 0x02 /* set only if unset */ + +/* Flags for compile_setexec() */ + +# define EXEC_UPDATED 0x01 /* executes updated */ +# define EXEC_TOGETHER 0x02 /* executes together */ +# define EXEC_IGNORE 0x04 /* executes ignore */ +# define EXEC_QUIETLY 0x08 /* executes quietly */ +# define EXEC_PIECEMEAL 0x10 /* executes piecemeal */ +# define EXEC_EXISTING 0x20 /* executes existing */ + +/* 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 */ + +#endif + diff --git a/jam-files/engine/debian/changelog b/jam-files/engine/debian/changelog new file mode 100644 index 00000000..29084289 --- /dev/null +++ b/jam-files/engine/debian/changelog @@ -0,0 +1,72 @@ +bjam (3.1.12-1) unstable; urgency=low + + * New upstream release. + + -- Rene Rivera <grafik@redshift-software.com> Sat, 01 Oct 2005 00:00:00 +0000 + +bjam (3.1.11-1) unstable; urgency=low + + * New upstream release. + + -- Rene Rivera <grafik@redshift-software.com> Sat, 30 Apr 2005 00:00:00 +0000 + +bjam (3.1.10-1) unstable; urgency=low + + * New upstream release. + + -- Rene Rivera <grafik@redshift-software.com> Tue, 1 Jun 2004 05:42:35 +0000 + +bjam (3.1.9-2) unstable; urgency=low + + * Use default value of BOOST_BUILD_PATH is not is set in environment. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Wed, 17 Dec 2003 16:44:35 +0300 + +bjam (3.1.9-1) unstable; urgency=low + + * Implement NATIVE_FILE builtin and several native rules. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Thu, 11 Dec 2003 13:15:26 +0300 + +bjam (3.1.8-1) unstable; urgency=low + + * New upstream release. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Tue, 4 Nov 2003 20:50:43 +0300 + +bjam (3.1.7-1) unstable; urgency=low + + * New upstream release. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Thu, 11 Sep 2003 10:45:44 +0400 + +bjam (3.1.6-1) unstable; urgency=low + + * New upstream release. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Tue, 1 Jul 2003 09:12:18 +0400 + +bjam (3.1.5-1) unstable; urgency=low + + * New upstream release. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Mon, 19 May 2003 14:05:13 +0400 + +bjam (3.1.3-2) unstable; urgency=low + + * Changed Debian package to be similar to Jam's package. + + -- Vladimir Prus <ghost@cs.msu.su> Thu, 10 Oct 2002 18:43:26 +0400 + +bjam (3.1.3-1) unstable; urgency=low + + * New upstream release. + + -- Vladimir Prus <ghost@zigzag.lvk.cs.msu.su> Fri, 4 Oct 2002 18:16:54 +0400 + +bjam (3.1.2-1) unstable; urgency=low + + * Initial Release. + + -- Vladimir Prus <ghost@cs.msu.su> Wed, 14 Aug 2002 14:08:00 +0400 + diff --git a/jam-files/engine/debian/control b/jam-files/engine/debian/control new file mode 100644 index 00000000..c7f15193 --- /dev/null +++ b/jam-files/engine/debian/control @@ -0,0 +1,16 @@ +Source: bjam +Section: devel +Priority: optional +Maintainer: Vladimir Prus <ghost@cs.msu.su> +Build-Depends: debhelper (>> 3.0.0), docbook-to-man, bison +Standards-Version: 3.5.2 + +Package: bjam +Architecture: any +Depends: ${shlibs:Depends} +Description: Build tool + Boost.Jam is a portable build tool with its own interpreted language, which + allows to implement rather complex logic in a readable way and without + resorting to external programs. It is a descendant of Jam/MR tool modified to + suit the needs of Boost.Build. In particular, modules and rule parameters + were added, as well as several new builtins. diff --git a/jam-files/engine/debian/copyright b/jam-files/engine/debian/copyright new file mode 100644 index 00000000..f72e4e3a --- /dev/null +++ b/jam-files/engine/debian/copyright @@ -0,0 +1,25 @@ +This package was debianized by Vladimir Prus <ghost@cs.msu.su> on +Wed, 17 July 2002, 19:27:00 +0400. + +Copyright: + + /+\ + +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + \+/ + + This is Release 2.4 of Jam/MR, a make-like program. + + License is hereby granted to use this software and distribute it + freely, as long as this copyright notice is retained and modifications + are clearly marked. + + ALL WARRANTIES ARE HEREBY DISCLAIMED. + +Some portions are also: + + Copyright 2001-2006 David Abrahams. + Copyright 2002-2006 Rene Rivera. + Copyright 2003-2006 Vladimir Prus. + + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) diff --git a/jam-files/engine/debian/jam.man.sgml b/jam-files/engine/debian/jam.man.sgml new file mode 100644 index 00000000..ee21d4d8 --- /dev/null +++ b/jam-files/engine/debian/jam.man.sgml @@ -0,0 +1,236 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + --> + + <!ENTITY dhfirstname "<firstname>Yann</firstname>"> + <!ENTITY dhsurname "<surname>Dirson</surname>"> + <!-- Please adjust the date whenever revising the manpage. --> + <!ENTITY dhdate "<date>mai 23, 2001</date>"> + <!ENTITY dhemail "<email>dirson@debian.org</email>"> + <!ENTITY dhusername "Yann Dirson"> + <!ENTITY dhpackage "jam"> + + <!ENTITY debian "<productname>Debian GNU/Linux</productname>"> + <!ENTITY gnu "<acronym>GNU</acronym>"> +]> + +<refentry> + <refentryinfo> + <address> + &dhemail; + </address> + <author> + &dhfirstname; + &dhsurname; + </author> + <copyright> + <year>2001</year> + <holder>&dhusername;</holder> + </copyright> + &dhdate; + </refentryinfo> + + <refmeta> + <refentrytitle>JAM</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>Jam/MR</refname> + <refpurpose>Make(1) Redux</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>jam</command> + + <arg><option>-a</option></arg> + <arg><option>-n</option></arg> + <arg><option>-v</option></arg> + + <arg><option>-d <replaceable/debug/</option></arg> + <arg><option>-f <replaceable/jambase/</option></arg> + <arg><option>-j <replaceable/jobs/</option></arg> + <arg><option>-o <replaceable/actionsfile/</option></arg> + <arg><option>-s <replaceable/var/=<replaceable/value/</option></arg> + <arg><option>-t <replaceable/target/</option></arg> + + <arg repeat><option><replaceable/target/</option></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>DESCRIPTION</title> + + <para>Jam is a program construction tool, like make(1).</para> + + <para>Jam recursively builds target files from source files, using + dependency information and updating actions expressed in the + Jambase file, which is written in jam's own interpreted language. + The default Jambase is compiled into jam and provides a + boilerplate for common use, relying on a user-provide file + "Jamfile" to enumerate actual targets and sources.</para> + </refsect1> + + <refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term><option/-a/</term> + <listitem> + <para>Build all targets anyway, even if they are up-to-date.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-d <replaceable/n/</option></term> + <listitem> + <para>Enable cummulative debugging levels from 1 to + <replaceable/n/. Interesting values are: + + <glosslist> + <glossentry><glossterm/1/ <glossdef><simpara/Show + actions (the default)/</glossdef></glossentry> + + <glossentry><glossterm/2/ <glossdef><simpara/Show + "quiet" actions and display all action + text/</glossdef></glossentry> + + <glossentry><glossterm/3/ <glossdef><simpara>Show + dependency analysis, and target/source + timestamps/paths</simpara></glossdef></glossentry> + + <glossentry><glossterm/4/ <glossdef><simpara/Show shell + arguments/</glossdef></glossentry> + + <glossentry><glossterm/5/ <glossdef><simpara/Show rule + invocations and variable + expansions/</glossdef></glossentry> + + <glossentry><glossterm/6/ <glossdef><simpara>Show + directory/header file/archive + scans</simpara></glossdef></glossentry> + + <glossentry><glossterm/7/ <glossdef><simpara/Show + variable settings/</glossdef></glossentry> + + <glossentry><glossterm/8/ <glossdef><simpara/Show + variable fetches/</glossdef></glossentry> + + <glossentry><glossterm/9/ <glossdef><simpara/Show + variable manipulation, scanner + tokens/</glossdef></glossentry> + </glosslist> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-d +<replaceable/n/</option></term> + <listitem> + <para>Enable debugging level <replaceable/n/.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option/-d 0/</term> + <listitem> + <para>Turn off all debugging levels. Only errors are not + suppressed.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-f <replaceable/jambase/</option></term> + <listitem> + <para>Read <replaceable/jambase/ instead of using the + built-in Jambase. Only one <option/-f/ flag is permitted, + but the <replaceable/jambase/ may explicitly include other + files.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-j <replaceable/n/</option></term> + <listitem> + <para>Run up to <replaceable/n/ shell commands concurrently + (UNIX and NT only). The default is 1.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option/-n/</term> + <listitem> + <para>Don't actually execute the updating actions, but do + everything else. This changes the debug level default to + <option/-d2/.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-o <replaceable/file/</option></term> + <listitem> + <para>Write the updating actions to the specified file + instead of running them (or outputting them, as on the + Mac).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-s <replaceable/var/=<replaceable/value/</option></term> + <listitem> + <para>Set the variable <replaceable/var/ to + <replaceable/value/, overriding both internal variables and + variables imported from the environment. </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>-t <replaceable/target/</option></term> + <listitem> + <para>Rebuild <replaceable/target/ and everything that + depends on it, even if it is up-to-date.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option/-v/</term> + <listitem> + <para>Print the version of jam and exit.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>SEE ALSO</title> + + <para>Jam is documented fully in HTML pages available on Debian + systems from + <filename>/usr/share/doc/jam/Jam.html</filename>.</para> + </refsect1> + + <refsect1> + <title>AUTHOR</title> + + <para>This manual page was created by &dhusername; &dhemail; from + the <filename/Jam.html/ documentation, for the &debian; system + (but may be used by others).</para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +sgml-omittag:t +sgml-shorttag:t +End: +--> diff --git a/jam-files/engine/debian/rules b/jam-files/engine/debian/rules new file mode 100755 index 00000000..756052a3 --- /dev/null +++ b/jam-files/engine/debian/rules @@ -0,0 +1,73 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. +# GNU copyright 2001 by Yann Dirson. + +# This is the debian/rules file for packages jam and ftjam +# It should be usable with both packages without any change + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatability version to use. +export DH_COMPAT=3 + +topdir=$(shell pwd) + +jam=bjam +binname=bjam + +build: build-stamp +build-stamp: debian/jam.1 + dh_testdir + + ./build.sh + + touch build-stamp + +%.1: %.man.sgml + /usr/bin/docbook-to-man $< > $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + rm -rf bin.* + rm -f jam0 debian/jam.1 + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + install -d ${topdir}/debian/${jam}/usr/bin + install -m755 bin.linuxx86/bjam ${topdir}/debian/${jam}/usr/bin/ + install -d ${topdir}/debian/${jam}/usr/share/man/man1/ + install -m644 debian/jam.1 ${topdir}/debian/${jam}/usr/share/man/man1/${binname}.1 + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installdocs README RELNOTES Jambase *.html +# dh_installemacsen +# dh_undocumented + dh_installchangelogs + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/jam-files/engine/debug.c b/jam-files/engine/debug.c new file mode 100644 index 00000000..7290555a --- /dev/null +++ b/jam-files/engine/debug.c @@ -0,0 +1,132 @@ +/* + Copyright Rene Rivera 2005. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include "jam.h" + +#include "hash.h" + +#include <time.h> +#include <assert.h> + + +static profile_frame * profile_stack = 0; +static struct hash * profile_hash = 0; +static profile_info profile_other = { "[OTHER]", 0, 0, 0, 0, 0 }; +static profile_info profile_total = { "[TOTAL]", 0, 0, 0, 0, 0 }; + + +profile_frame * profile_init( char * rulename, profile_frame * frame ) +{ + if ( DEBUG_PROFILE ) profile_enter( rulename, frame ); + return frame; +} + + +void profile_enter( char * rulename, profile_frame * frame ) +{ + if ( DEBUG_PROFILE ) + { + clock_t start = clock(); + profile_info info; + profile_info * p = &info; + + if ( !rulename ) p = &profile_other; + + if ( !profile_hash && rulename ) + profile_hash = hashinit( sizeof( profile_info ), "profile" ); + + info.name = rulename; + + if ( rulename && hashenter( profile_hash, (HASHDATA * *)&p ) ) + p->cumulative = p->net = p->num_entries = p->stack_count = p->memory = 0; + + ++p->num_entries; + ++p->stack_count; + + frame->info = p; + + frame->caller = profile_stack; + profile_stack = frame; + + frame->entry_time = clock(); + frame->overhead = 0; + frame->subrules = 0; + + /* caller pays for the time it takes to play with the hash table */ + if ( frame->caller ) + frame->caller->overhead += frame->entry_time - start; + } +} + + +void profile_memory( long mem ) +{ + if ( DEBUG_PROFILE ) + if ( profile_stack && profile_stack->info ) + profile_stack->info->memory += mem; +} + + +void profile_exit( profile_frame * frame ) +{ + if ( DEBUG_PROFILE ) + { + /* Cumulative time for this call. */ + clock_t t = clock() - frame->entry_time - frame->overhead; + /* If this rule is already present on the stack, don't add the time for + * this instance. + */ + if ( frame->info->stack_count == 1 ) + frame->info->cumulative += t; + /* Net time does not depend on presense of the same rule in call stack. + */ + frame->info->net += t - frame->subrules; + + if ( frame->caller ) + { + /* Caller's cumulative time must account for this overhead. */ + frame->caller->overhead += frame->overhead; + frame->caller->subrules += t; + } + /* Pop this stack frame. */ + --frame->info->stack_count; + profile_stack = frame->caller; + } +} + + +static void dump_profile_entry( void * p_, void * ignored ) +{ + profile_info * p = (profile_info *)p_; + unsigned long mem_each = ( p->memory / ( p->num_entries ? p->num_entries : 1 ) ); + double cumulative = p->cumulative; + double net = p->net; + double q = p->net; + q /= ( p->num_entries ? p->num_entries : 1 ); + cumulative /= CLOCKS_PER_SEC; + net /= CLOCKS_PER_SEC; + q /= CLOCKS_PER_SEC; + if ( !ignored ) + { + profile_total.cumulative += p->net; + profile_total.memory += p->memory; + } + printf( "%10ld %12.6f %12.6f %12.8f %10ld %10ld %s\n", p->num_entries, + cumulative, net, q, p->memory, mem_each, p->name ); +} + + +void profile_dump() +{ + if ( profile_hash ) + { + printf( "%10s %12s %12s %12s %10s %10s %s\n", "--count--", "--gross--", + "--net--", "--each--", "--mem--", "--each--", "--name--" ); + hashenumerate( profile_hash, dump_profile_entry, 0 ); + dump_profile_entry( &profile_other, 0 ); + dump_profile_entry( &profile_total, (void *)1 ); + } +} diff --git a/jam-files/engine/debug.h b/jam-files/engine/debug.h new file mode 100644 index 00000000..115a8873 --- /dev/null +++ b/jam-files/engine/debug.h @@ -0,0 +1,54 @@ +/* + Copyright Rene Rivera 2005. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BJAM_DEBUG_H +#define BJAM_DEBUG_H + +#include "jam.h" +#include <time.h> + + +struct profile_info +{ + /* name of rule being called */ + char* name; + /* cumulative time spent in rule */ + clock_t cumulative; + /* time spent in rule proper */ + clock_t net; + /* number of time rule was entered */ + unsigned long num_entries; + /* number of the times this function is present in stack */ + unsigned long stack_count; + /* bytes of memory allocated by the call */ + unsigned long memory; +}; +typedef struct profile_info profile_info; + +struct profile_frame +{ + /* permanent storage where data accumulates */ + profile_info* info; + /* overhead for profiling in this call */ + clock_t overhead; + /* time of last entry to rule */ + clock_t entry_time; + /* stack frame of caller */ + struct profile_frame* caller; + /* time spent in subrules */ + clock_t subrules; +}; +typedef struct profile_frame profile_frame; + +profile_frame * profile_init( char * rulename, profile_frame * frame ); +void profile_enter( char* rulename, profile_frame * frame ); +void profile_memory( long mem ); +void profile_exit( profile_frame * frame ); +void profile_dump(); + +#define PROFILE_ENTER( scope ) profile_frame PROF_ ## scope, *PROF_ ## scope ## _p = profile_init( #scope, &PROF_ ## scope ) +#define PROFILE_EXIT( scope ) profile_exit( PROF_ ## scope ## _p ) + +#endif diff --git a/jam-files/engine/execcmd.h b/jam-files/engine/execcmd.h new file mode 100644 index 00000000..67f2b839 --- /dev/null +++ b/jam-files/engine/execcmd.h @@ -0,0 +1,45 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * execcmd.h - execute a shell script. + * + * Defines the interface to be implemented in platform specific implementation + * modules. + * + * 05/04/94 (seiwald) - async multiprocess interface + */ + +#ifndef EXECCMD_H +#define EXECCMD_H + +#include <time.h> + +typedef struct timing_info +{ + double system; + double user; + time_t start; + time_t end; +} timing_info; + +void exec_cmd +( + char * string, + void (* func)( void * closure, int status, timing_info *, char *, char * ), + void * closure, + LIST * shell, + char * action, + char * target +); + +int exec_wait(); + +#define EXEC_CMD_OK 0 +#define EXEC_CMD_FAIL 1 +#define EXEC_CMD_INTR 2 + +#endif diff --git a/jam-files/engine/execmac.c b/jam-files/engine/execmac.c new file mode 100644 index 00000000..2ddddedd --- /dev/null +++ b/jam-files/engine/execmac.c @@ -0,0 +1,69 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#include "jam.h" +#include "lists.h" +#include "execcmd.h" +#include <errno.h> + +#ifdef OS_MAC + +/* + * execunix.c - execute a shell script on UNIX + * + * If $(JAMSHELL) is defined, uses that to formulate execvp(). + * The default is: + * + * /bin/sh -c % + * + * Each word must be an individual element in a jam variable value. + * + * In $(JAMSHELL), % expands to the command string and ! expands to + * the slot number (starting at 1) for multiprocess (-j) invocations. + * If $(JAMSHELL) doesn't include a %, it is tacked on as the last + * argument. + * + * Don't just set JAMSHELL to /bin/sh - it won't work! + * + * External routines: + * exec_cmd() - launch an async command execution. + * exec_wait() - 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 + */ + + +/* + * exec_cmd() - launch an async command execution. + */ + +void exec_cmd +( + char * string, + void (* func)( void * closure, int status, timing_info *, char *, char * ), + void * closure, + LIST * shell +) +{ + printf( "%s", string ); + (*func)( closure, EXEC_CMD_OK ); +} + +/* + * exec_wait() - wait and drive at most one execution completion. + */ + +int exec_wait() +{ + return 0; +} + +#endif /* OS_MAC */ diff --git a/jam-files/engine/execnt.c b/jam-files/engine/execnt.c new file mode 100644 index 00000000..76420451 --- /dev/null +++ b/jam-files/engine/execnt.c @@ -0,0 +1,1296 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Copyright 2007 Rene Rivera. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "jam.h" +#include "lists.h" +#include "execcmd.h" +#include "pathsys.h" +#include "string.h" +#include "output.h" +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> +#include <math.h> + +#ifdef USE_EXECNT + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include <tlhelp32.h> + +/* + * execnt.c - execute a shell command on Windows NT + * + * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). + * The default is: + * + * /bin/sh -c % [ on UNIX/AmigaOS ] + * cmd.exe /c % [ on Windows NT ] + * + * 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: + * exec_cmd() - launch an async command execution. + * exec_wait() - 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 + */ + +/* get the maximum command line length according to the OS */ +int maxline(); + +/* delete and argv list */ +static void free_argv(char**); +/* Convert a command string into arguments for spawnvp. */ +static char** string_to_args(const char*); +/* bump intr to note command interruption */ +static void onintr(int); +/* If the command is suitable for execution via spawnvp */ +long can_spawn(char*); +/* Add two 64-bit unsigned numbers, h1l1 and h2l2 */ +static FILETIME add_64( + unsigned long h1, unsigned long l1, + unsigned long h2, unsigned long l2); +static FILETIME add_FILETIME(FILETIME t1, FILETIME t2); +static FILETIME negate_FILETIME(FILETIME t); +/* Convert a FILETIME to a number of seconds */ +static double filetime_seconds(FILETIME t); +/* record the timing info for the process */ +static void record_times(HANDLE, timing_info*); +/* calc the current running time of an *active* process */ +static double running_time(HANDLE); +/* */ +DWORD get_process_id(HANDLE); +/* terminate the given process, after terminating all its children */ +static void kill_process_tree(DWORD, HANDLE); +/* waits for a command to complete or for the given timeout, whichever is first */ +static int try_wait(int timeoutMillis); +/* reads any pending output for running commands */ +static void read_output(); +/* checks if a command ran out of time, and kills it */ +static int try_kill_one(); +/* */ +static double creation_time(HANDLE); +/* Recursive check if first process is parent (directly or indirectly) of +the second one. */ +static int is_parent_child(DWORD, DWORD); +/* */ +static void close_alert(HANDLE); +/* close any alerts hanging around */ +static void close_alerts(); + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static int intr = 0; +static int cmdsrunning = 0; +static void (* istat)( int ); + + +/* The list of commands we run. */ +static struct +{ + string action; /* buffer to hold action */ + string target; /* buffer to hold target */ + string command; /* buffer to hold command being invoked */ + + /* Temporary batch file used to execute the action when needed. */ + char * tempfile_bat; + + /* Pipes for communicating with the child process. Parent reads from (0), + * child writes to (1). + */ + HANDLE pipe_out[ 2 ]; + HANDLE pipe_err[ 2 ]; + + string buffer_out; /* buffer to hold stdout, if any */ + string buffer_err; /* buffer to hold stderr, if any */ + + PROCESS_INFORMATION pi; /* running process information */ + DWORD exit_code; /* executed command's exit code */ + int exit_reason; /* reason why a command completed */ + + /* Function called when the command completes. */ + void (* func)( void * closure, int status, timing_info *, char *, char * ); + + /* Opaque data passed back to the 'func' callback called when the command + * completes. + */ + void * closure; +} +cmdtab[ MAXJOBS ] = { { 0 } }; + + +/* + * Execution unit tests. + */ + +void execnt_unit_test() +{ +#if !defined( NDEBUG ) + /* vc6 preprocessor is broken, so assert with these strings gets confused. + * Use a table instead. + */ + typedef struct test { char * command; int result; } test; + test tests[] = { + { "x", 0 }, + { "x\n ", 0 }, + { "x\ny", 1 }, + { "x\n\n y", 1 }, + { "echo x > foo.bar", 1 }, + { "echo x < foo.bar", 1 }, + { "echo x \">\" foo.bar", 0 }, + { "echo x \"<\" foo.bar", 0 }, + { "echo x \\\">\\\" foo.bar", 1 }, + { "echo x \\\"<\\\" foo.bar", 1 } }; + int i; + for ( i = 0; i < sizeof( tests ) / sizeof( *tests ); ++i ) + assert( !can_spawn( tests[ i ].command ) == tests[ i ].result ); + + { + char * long_command = BJAM_MALLOC_ATOMIC( MAXLINE + 10 ); + assert( long_command != 0 ); + memset( long_command, 'x', MAXLINE + 9 ); + long_command[ MAXLINE + 9 ] = 0; + assert( can_spawn( long_command ) == MAXLINE + 9 ); + BJAM_FREE( long_command ); + } + + { + /* Work around vc6 bug; it doesn't like escaped string + * literals inside assert + */ + char * * argv = string_to_args(" \"g++\" -c -I\"Foobar\"" ); + char const expected[] = "-c -I\"Foobar\""; + + assert( !strcmp( argv[ 0 ], "g++" ) ); + assert( !strcmp( argv[ 1 ], expected ) ); + free_argv( argv ); + } +#endif +} + + +/* + * exec_cmd() - launch an async command execution. + */ + +void exec_cmd +( + char * command, + void (* func)( void * closure, int status, timing_info *, char * invoked_command, char * command_output ), + void * closure, + LIST * shell, + char * action, + char * target +) +{ + int slot; + int raw_cmd = 0 ; + char * argv_static[ MAXARGC + 1 ]; /* +1 for NULL */ + char * * argv = argv_static; + char * p; + char * command_orig = command; + + /* Check to see if we need to hack around the line-length limitation. Look + * for a JAMSHELL setting of "%", indicating that the command should be + * invoked directly. + */ + if ( shell && !strcmp( shell->string, "%" ) && !list_next( shell ) ) + { + raw_cmd = 1; + shell = 0; + } + + /* Find a slot in the running commands table for this one. */ + for ( slot = 0; slot < MAXJOBS; ++slot ) + if ( !cmdtab[ slot ].pi.hProcess ) + break; + if ( slot == MAXJOBS ) + { + printf( "no slots for child!\n" ); + exit( EXITBAD ); + } + + /* Compute the name of a temp batch file, for possible use. */ + if ( !cmdtab[ slot ].tempfile_bat ) + { + char const * tempdir = path_tmpdir(); + DWORD procID = GetCurrentProcessId(); + + /* SVA - allocate 64 bytes extra just to be safe. */ + cmdtab[ slot ].tempfile_bat = BJAM_MALLOC_ATOMIC( strlen( tempdir ) + 64 ); + + sprintf( cmdtab[ slot ].tempfile_bat, "%s\\jam%d-%02d.bat", + tempdir, procID, slot ); + } + + /* Trim leading, -ending- white space */ + while ( *( command + 1 ) && isspace( *command ) ) + ++command; + + /* Write to .BAT file unless the line would be too long and it meets the + * other spawnability criteria. + */ + if ( raw_cmd && ( can_spawn( command ) >= MAXLINE ) ) + { + if ( DEBUG_EXECCMD ) + printf("Executing raw command directly\n"); + } + else + { + FILE * f = 0; + int tries = 0; + raw_cmd = 0; + + /* Write command to bat file. For some reason this open can fail + * intermitently. But doing some retries works. Most likely this is due + * to a previously existing file of the same name that happens to be + * opened by an active virus scanner. Pointed out and fixed by Bronek + * Kozicki. + */ + for ( ; !f && ( tries < 4 ); ++tries ) + { + f = fopen( cmdtab[ slot ].tempfile_bat, "w" ); + if ( !f && ( tries < 4 ) ) Sleep( 250 ); + } + if ( !f ) + { + printf( "failed to write command file!\n" ); + exit( EXITBAD ); + } + fputs( command, f ); + fclose( f ); + + command = cmdtab[ slot ].tempfile_bat; + + if ( DEBUG_EXECCMD ) + { + if ( shell ) + printf( "using user-specified shell: %s", shell->string ); + else + printf( "Executing through .bat file\n" ); + } + } + + /* Formulate argv; If shell was defined, be prepared for % and ! subs. + * Otherwise, use stock cmd.exe. + */ + 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 ] = command; ++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++ ] = command; + + argv[ i ] = 0; + } + else if ( raw_cmd ) + { + argv = string_to_args( command ); + } + else + { + argv[ 0 ] = "cmd.exe"; + argv[ 1 ] = "/Q/C"; /* anything more is non-portable */ + argv[ 2 ] = command; + argv[ 3 ] = 0; + } + + /* Catch interrupts whenever commands are running. */ + if ( !cmdsrunning++ ) + istat = signal( SIGINT, onintr ); + + /* Start the command. */ + { + SECURITY_ATTRIBUTES sa + = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 }; + SECURITY_DESCRIPTOR sd; + STARTUPINFO si + = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + string cmd; + + /* Init the security data. */ + InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ); + SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE ); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = TRUE; + + /* Create the stdout, which is also the merged out + err, pipe. */ + if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ 0 ], + &cmdtab[ slot ].pipe_out[ 1 ], &sa, 0 ) ) + { + perror( "CreatePipe" ); + exit( EXITBAD ); + } + + /* Create the stdout, which is also the merged out+err, pipe. */ + if ( globs.pipe_action == 2 ) + { + if ( !CreatePipe( &cmdtab[ slot ].pipe_err[ 0 ], + &cmdtab[ slot ].pipe_err[ 1 ], &sa, 0 ) ) + { + perror( "CreatePipe" ); + exit( EXITBAD ); + } + } + + /* Set handle inheritance off for the pipe ends the parent reads from. */ + SetHandleInformation( cmdtab[ slot ].pipe_out[ 0 ], HANDLE_FLAG_INHERIT, 0 ); + if ( globs.pipe_action == 2 ) + SetHandleInformation( cmdtab[ slot ].pipe_err[ 0 ], HANDLE_FLAG_INHERIT, 0 ); + + /* Hide the child window, if any. */ + si.dwFlags |= STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + /* Set the child outputs to the pipes. */ + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdOutput = cmdtab[ slot ].pipe_out[ 1 ]; + if ( globs.pipe_action == 2 ) + { + /* Pipe stderr to the action error output. */ + si.hStdError = cmdtab[ slot ].pipe_err[ 1 ]; + } + else if ( globs.pipe_action == 1 ) + { + /* Pipe stderr to the console error output. */ + si.hStdError = GetStdHandle( STD_ERROR_HANDLE ); + } + else + { + /* Pipe stderr to the action merged output. */ + si.hStdError = cmdtab[ slot ].pipe_out[ 1 ]; + } + + /* Let the child inherit stdin, as some commands assume it's available. */ + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + + /* Save the operation for exec_wait() to find. */ + cmdtab[ slot ].func = func; + cmdtab[ slot ].closure = closure; + if ( action && target ) + { + string_copy( &cmdtab[ slot ].action, action ); + string_copy( &cmdtab[ slot ].target, target ); + } + else + { + string_free( &cmdtab[ slot ].action ); + string_new ( &cmdtab[ slot ].action ); + string_free( &cmdtab[ slot ].target ); + string_new ( &cmdtab[ slot ].target ); + } + string_copy( &cmdtab[ slot ].command, command_orig ); + + /* Put together the command we run. */ + { + char * * argp = argv; + string_new( &cmd ); + string_copy( &cmd, *(argp++) ); + while ( *argp ) + { + string_push_back( &cmd, ' ' ); + string_append( &cmd, *(argp++) ); + } + } + + /* Create output buffers. */ + string_new( &cmdtab[ slot ].buffer_out ); + string_new( &cmdtab[ slot ].buffer_err ); + + /* Run the command by creating a sub-process for it. */ + if ( + ! CreateProcess( + NULL , /* application name */ + cmd.value , /* command line */ + NULL , /* process attributes */ + NULL , /* thread attributes */ + TRUE , /* inherit handles */ + CREATE_NEW_PROCESS_GROUP, /* create flags */ + NULL , /* env vars, null inherits env */ + NULL , /* current dir, null is our */ + /* current dir */ + &si , /* startup info */ + &cmdtab[ slot ].pi /* child process info, if created */ + ) + ) + { + perror( "CreateProcess" ); + exit( EXITBAD ); + } + + /* Clean up temporary stuff. */ + string_free( &cmd ); + } + + /* Wait until we are under the limit of concurrent commands. Do not trust + * globs.jobs alone. + */ + while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) ) + if ( !exec_wait() ) + break; + + if ( argv != argv_static ) + free_argv( argv ); +} + + +/* + * exec_wait() + * * wait and drive at most one execution completion. + * * waits for one command to complete, while processing the i/o for all + * ongoing commands. + * + * Returns 0 if called when there were no more commands being executed or 1 + * otherwise. + */ + +int exec_wait() +{ + int i = -1; + + /* Handle naive make1() which does not know if cmds are running. */ + if ( !cmdsrunning ) + return 0; + + /* Wait for a command to complete, while snarfing up any output. */ + do + { + /* Check for a complete command, briefly. */ + i = try_wait(500); + /* Read in the output of all running commands. */ + read_output(); + /* Close out pending debug style dialogs. */ + close_alerts(); + /* Check if a command ran out of time. */ + if ( i < 0 ) i = try_kill_one(); + } + while ( i < 0 ); + + /* We have a command... process it. */ + --cmdsrunning; + { + timing_info time; + int rstat; + + /* The time data for the command. */ + record_times( cmdtab[ i ].pi.hProcess, &time ); + + /* Clear the temp file. */ + if ( cmdtab[ i ].tempfile_bat ) + { + unlink( cmdtab[ i ].tempfile_bat ); + BJAM_FREE( cmdtab[ i ].tempfile_bat ); + cmdtab[ i ].tempfile_bat = NULL; + } + + /* Find out the process exit code. */ + GetExitCodeProcess( cmdtab[ i ].pi.hProcess, &cmdtab[ i ].exit_code ); + + /* The dispossition of the command. */ + if ( intr ) + rstat = EXEC_CMD_INTR; + else if ( cmdtab[ i ].exit_code != 0 ) + rstat = EXEC_CMD_FAIL; + else + rstat = EXEC_CMD_OK; + + /* Output the action block. */ + out_action( + cmdtab[ i ].action.size > 0 ? cmdtab[ i ].action.value : 0, + cmdtab[ i ].target.size > 0 ? cmdtab[ i ].target.value : 0, + cmdtab[ i ].command.size > 0 ? cmdtab[ i ].command.value : 0, + cmdtab[ i ].buffer_out.size > 0 ? cmdtab[ i ].buffer_out.value : 0, + cmdtab[ i ].buffer_err.size > 0 ? cmdtab[ i ].buffer_err.value : 0, + cmdtab[ i ].exit_reason ); + + /* Call the callback, may call back to jam rule land. Assume -p0 in + * effect so only pass buffer containing merged output. + */ + (*cmdtab[ i ].func)( + cmdtab[ i ].closure, + rstat, + &time, + cmdtab[ i ].command.value, + cmdtab[ i ].buffer_out.value ); + + /* Clean up the command data, process, etc. */ + string_free( &cmdtab[ i ].action ); string_new( &cmdtab[ i ].action ); + string_free( &cmdtab[ i ].target ); string_new( &cmdtab[ i ].target ); + string_free( &cmdtab[ i ].command ); string_new( &cmdtab[ i ].command ); + if ( cmdtab[ i ].pi.hProcess ) { CloseHandle( cmdtab[ i ].pi.hProcess ); cmdtab[ i ].pi.hProcess = 0; } + if ( cmdtab[ i ].pi.hThread ) { CloseHandle( cmdtab[ i ].pi.hThread ); cmdtab[ i ].pi.hThread = 0; } + if ( cmdtab[ i ].pipe_out[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 0 ] ); cmdtab[ i ].pipe_out[ 0 ] = 0; } + if ( cmdtab[ i ].pipe_out[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_out[ 1 ] ); cmdtab[ i ].pipe_out[ 1 ] = 0; } + if ( cmdtab[ i ].pipe_err[ 0 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 0 ] ); cmdtab[ i ].pipe_err[ 0 ] = 0; } + if ( cmdtab[ i ].pipe_err[ 1 ] ) { CloseHandle( cmdtab[ i ].pipe_err[ 1 ] ); cmdtab[ i ].pipe_err[ 1 ] = 0; } + string_free( &cmdtab[ i ].buffer_out ); string_new( &cmdtab[ i ].buffer_out ); + string_free( &cmdtab[ i ].buffer_err ); string_new( &cmdtab[ i ].buffer_err ); + cmdtab[ i ].exit_code = 0; + cmdtab[ i ].exit_reason = EXIT_OK; + } + + return 1; +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void free_argv( char * * args ) +{ + BJAM_FREE( args[ 0 ] ); + BJAM_FREE( args ); +} + + +/* + * For more details on Windows cmd.exe shell command-line length limitations see + * the following MSDN article: + * http://support.microsoft.com/default.aspx?scid=kb;en-us;830473 + */ + +int maxline() +{ + OSVERSIONINFO os_info; + os_info.dwOSVersionInfoSize = sizeof( os_info ); + GetVersionEx( &os_info ); + + if ( os_info.dwMajorVersion >= 5 ) return 8191; /* XP > */ + if ( os_info.dwMajorVersion == 4 ) return 2047; /* NT 4.x */ + return 996; /* NT 3.5.1 */ +} + + +/* + * Convert a command string into arguments for spawnvp(). The original code, + * inherited from ftjam, tried to break up every argument on the command-line, + * dealing with quotes, but that is really a waste of time on Win32, at least. + * It turns out that all you need to do is get the raw path to the executable in + * the first argument to spawnvp(), and you can pass all the rest of the + * command-line arguments to spawnvp() in one, un-processed string. + * + * New strategy: break the string in at most one place. + */ + +static char * * string_to_args( char const * string ) +{ + int src_len; + int in_quote; + char * line; + char const * src; + char * dst; + char * * argv; + + /* Drop leading and trailing whitespace if any. */ + while ( isspace( *string ) ) + ++string; + + src_len = strlen( string ); + while ( ( src_len > 0 ) && isspace( string[ src_len - 1 ] ) ) + --src_len; + + /* Copy the input string into a buffer we can modify. */ + line = (char *)BJAM_MALLOC_ATOMIC( src_len + 1 ); + if ( !line ) + return 0; + + /* Allocate the argv array. + * element 0: stores the path to the executable + * element 1: stores the command-line arguments to the executable + * element 2: NULL terminator + */ + argv = (char * *)BJAM_MALLOC( 3 * sizeof( char * ) ); + if ( !argv ) + { + BJAM_FREE( line ); + return 0; + } + + /* Strip quotes from the first command-line argument and find where it ends. + * Quotes are illegal in Win32 pathnames, so we do not need to worry about + * preserving escaped quotes here. Spaces can not be escaped in Win32, only + * enclosed in quotes, so removing backslash escapes is also a non-issue. + */ + in_quote = 0; + for ( src = string, dst = line ; *src; ++src ) + { + if ( *src == '"' ) + in_quote = !in_quote; + else if ( !in_quote && isspace( *src ) ) + break; + else + *dst++ = *src; + } + *dst++ = 0; + argv[ 0 ] = line; + + /* Skip whitespace in src. */ + while ( isspace( *src ) ) + ++src; + + argv[ 1 ] = dst; + + /* Copy the rest of the arguments verbatim. */ + src_len -= src - string; + + /* Use strncat() because it appends a trailing nul. */ + *dst = 0; + strncat( dst, src, src_len ); + + argv[ 2 ] = 0; + + return argv; +} + + +static void onintr( int disp ) +{ + ++intr; + printf( "...interrupted\n" ); +} + + +/* + * can_spawn() - If the command is suitable for execution via spawnvp(), return + * a number >= the number of characters it would occupy on the command-line. + * Otherwise, return zero. + */ + +long can_spawn( char * command ) +{ + char * p; + char inquote = 0; + + /* Move to the first non-whitespace. */ + command += strspn( command, " \t" ); + + p = command; + + /* Look for newlines and unquoted i/o redirection. */ + do + { + p += strcspn( p, "'\n\"<>|" ); + + switch ( *p ) + { + case '\n': + /* Skip over any following spaces. */ + while ( isspace( *p ) ) + ++p; + /* Must use a .bat file if there is anything significant following + * the newline. + */ + if ( *p ) + return 0; + break; + + case '"': + case '\'': + if ( ( p > command ) && ( p[ -1 ] != '\\' ) ) + { + if ( inquote == *p ) + inquote = 0; + else if ( inquote == 0 ) + inquote = *p; + } + ++p; + break; + + case '<': + case '>': + case '|': + if ( !inquote ) + return 0; + ++p; + break; + } + } + while ( *p ); + + /* Return the number of characters the command will occupy. */ + return p - command; +} + + +/* 64-bit arithmetic helpers. */ + +/* Compute the carry bit from the addition of two 32-bit unsigned numbers. */ +#define add_carry_bit( a, b ) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 ) + +/* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2. */ +#define add_64_hi( h1, l1, h2, l2 ) ((h1) + (h2) + add_carry_bit(l1, l2)) + + +/* + * Add two 64-bit unsigned numbers, h1l1 and h2l2. + */ + +static FILETIME add_64 +( + unsigned long h1, unsigned long l1, + unsigned long h2, unsigned long l2 +) +{ + FILETIME result; + result.dwLowDateTime = l1 + l2; + result.dwHighDateTime = add_64_hi( h1, l1, h2, l2 ); + return result; +} + + +static FILETIME add_FILETIME( FILETIME t1, FILETIME t2 ) +{ + return add_64( t1.dwHighDateTime, t1.dwLowDateTime, t2.dwHighDateTime, + t2.dwLowDateTime ); +} + + +static FILETIME negate_FILETIME( FILETIME t ) +{ + /* 2s complement negation */ + return add_64( ~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1 ); +} + + +/* + * Convert a FILETIME to a number of seconds. + */ + +static double filetime_seconds( FILETIME t ) +{ + return t.dwHighDateTime * ( (double)( 1UL << 31 ) * 2.0 * 1.0e-7 ) + t.dwLowDateTime * 1.0e-7; +} + + +/* + * What should be a simple conversion, turns out to be horribly complicated by + * the defficiencies of MSVC and the Win32 API. + */ + +static time_t filetime_dt( FILETIME t_utc ) +{ + static int calc_time_diff = 1; + static double time_diff; + if ( calc_time_diff ) + { + struct tm t0_; + FILETIME f0_local; + FILETIME f0_; + SYSTEMTIME s0_; + GetSystemTime( &s0_ ); + t0_.tm_year = s0_.wYear-1900; + t0_.tm_mon = s0_.wMonth-1; + t0_.tm_wday = s0_.wDayOfWeek; + t0_.tm_mday = s0_.wDay; + t0_.tm_hour = s0_.wHour; + t0_.tm_min = s0_.wMinute; + t0_.tm_sec = s0_.wSecond; + t0_.tm_isdst = 0; + SystemTimeToFileTime( &s0_, &f0_local ); + LocalFileTimeToFileTime( &f0_local, &f0_ ); + time_diff = filetime_seconds( f0_ ) - (double)mktime( &t0_ ); + calc_time_diff = 0; + } + return ceil( filetime_seconds( t_utc ) - time_diff ); +} + + +static void record_times( HANDLE process, timing_info * time ) +{ + FILETIME creation; + FILETIME exit; + FILETIME kernel; + FILETIME user; + if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) ) + { + time->system = filetime_seconds( kernel ); + time->user = filetime_seconds( user ); + time->start = filetime_dt ( creation ); + time->end = filetime_dt ( exit ); + } +} + + +#define IO_BUFFER_SIZE ( 16 * 1024 ) + +static char ioBuffer[ IO_BUFFER_SIZE + 1 ]; + + +static void read_pipe +( + HANDLE in, /* the pipe to read from */ + string * out +) +{ + DWORD bytesInBuffer = 0; + DWORD bytesAvailable = 0; + + do + { + /* check if we have any data to read */ + if ( !PeekNamedPipe( in, ioBuffer, IO_BUFFER_SIZE, &bytesInBuffer, &bytesAvailable, NULL ) ) + bytesAvailable = 0; + + /* read in the available data */ + if ( bytesAvailable > 0 ) + { + /* we only read in the available bytes, to avoid blocking */ + if ( ReadFile( in, ioBuffer, + bytesAvailable <= IO_BUFFER_SIZE ? bytesAvailable : IO_BUFFER_SIZE, + &bytesInBuffer, NULL ) ) + { + if ( bytesInBuffer > 0 ) + { + /* Clean up some illegal chars. */ + int i; + for ( i = 0; i < bytesInBuffer; ++i ) + { + if ( ( (unsigned char)ioBuffer[ i ] < 1 ) ) + ioBuffer[ i ] = '?'; + } + /* Null, terminate. */ + ioBuffer[ bytesInBuffer ] = '\0'; + /* Append to the output. */ + string_append( out, ioBuffer ); + /* Subtract what we read in. */ + bytesAvailable -= bytesInBuffer; + } + else + { + /* Likely read a error, bail out. */ + bytesAvailable = 0; + } + } + else + { + /* Definitely read a error, bail out. */ + bytesAvailable = 0; + } + } + } + while ( bytesAvailable > 0 ); +} + + +static void read_output() +{ + int i; + for ( i = 0; i < globs.jobs && i < MAXJOBS; ++i ) + { + /* Read stdout data. */ + if ( cmdtab[ i ].pipe_out[ 0 ] ) + read_pipe( cmdtab[ i ].pipe_out[ 0 ], & cmdtab[ i ].buffer_out ); + /* Read stderr data. */ + if ( cmdtab[ i ].pipe_err[ 0 ] ) + read_pipe( cmdtab[ i ].pipe_err[ 0 ], & cmdtab[ i ].buffer_err ); + } +} + + +/* + * Waits for a single child process command to complete, or the timeout, + * whichever comes first. Returns the index of the completed command in the + * cmdtab array, or -1. + */ + +static int try_wait( int timeoutMillis ) +{ + int i; + int num_active; + int wait_api_result; + HANDLE active_handles[ MAXJOBS ]; + int active_procs[ MAXJOBS ]; + + /* Prepare a list of all active processes to wait for. */ + for ( num_active = 0, i = 0; i < globs.jobs; ++i ) + { + if ( cmdtab[ i ].pi.hProcess ) + { + active_handles[ num_active ] = cmdtab[ i ].pi.hProcess; + active_procs[ num_active ] = i; + ++num_active; + } + } + + /* Wait for a child to complete, or for our timeout window to expire. */ + wait_api_result = WaitForMultipleObjects( num_active, active_handles, + FALSE, timeoutMillis ); + if ( ( WAIT_OBJECT_0 <= wait_api_result ) && + ( wait_api_result < WAIT_OBJECT_0 + num_active ) ) + { + /* Rerminated process detected - return its index. */ + return active_procs[ wait_api_result - WAIT_OBJECT_0 ]; + } + + /* Timeout. */ + return -1; +} + + +static int try_kill_one() +{ + /* Only need to check if a timeout was specified with the -l option. */ + if ( globs.timeout > 0 ) + { + int i; + for ( i = 0; i < globs.jobs; ++i ) + { + double t = running_time( cmdtab[ i ].pi.hProcess ); + if ( t > (double)globs.timeout ) + { + /* The job may have left an alert dialog around, try and get rid + * of it before killing + */ + close_alert( cmdtab[ i ].pi.hProcess ); + /* We have a "runaway" job, kill it. */ + kill_process_tree( 0, cmdtab[ i ].pi.hProcess ); + /* And return it marked as a timeout. */ + cmdtab[ i ].exit_reason = EXIT_TIMEOUT; + return i; + } + } + } + return -1; +} + + +static void close_alerts() +{ + /* We only attempt this every 5 seconds, or so, because it is not a cheap + * operation, and we will catch the alerts eventually. This check uses + * floats as some compilers define CLOCKS_PER_SEC as a float or double. + */ + if ( ( (float)clock() / (float)( CLOCKS_PER_SEC * 5 ) ) < ( 1.0 / 5.0 ) ) + { + int i; + for ( i = 0; i < globs.jobs; ++i ) + close_alert( cmdtab[ i ].pi.hProcess ); + } +} + + +/* + * Calc the current running time of an *active* process. + */ + +static double running_time( HANDLE process ) +{ + FILETIME creation; + FILETIME exit; + FILETIME kernel; + FILETIME user; + FILETIME current; + if ( GetProcessTimes( process, &creation, &exit, &kernel, &user ) ) + { + /* Compute the elapsed time. */ + GetSystemTimeAsFileTime( ¤t ); + return filetime_seconds( add_FILETIME( current, + negate_FILETIME( creation ) ) ); + } + return 0.0; +} + + +/* It is just stupidly silly that one has to do this. */ +typedef struct PROCESS_BASIC_INFORMATION__ +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG AffinityMask; + LONG BasePriority; + ULONG UniqueProcessId; + ULONG InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION_; +typedef LONG (__stdcall * NtQueryInformationProcess__)( + HANDLE ProcessHandle, + LONG ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength); +static NtQueryInformationProcess__ NtQueryInformationProcess_ = NULL; +static HMODULE NTDLL_ = NULL; +DWORD get_process_id( HANDLE process ) +{ + PROCESS_BASIC_INFORMATION_ pinfo; + if ( !NtQueryInformationProcess_ ) + { + if ( ! NTDLL_ ) + NTDLL_ = GetModuleHandleA( "ntdll" ); + if ( NTDLL_ ) + NtQueryInformationProcess_ + = (NtQueryInformationProcess__)GetProcAddress( NTDLL_, "NtQueryInformationProcess" ); + } + if ( NtQueryInformationProcess_ ) + { + LONG r = (*NtQueryInformationProcess_)( process, + /* ProcessBasicInformation == */ 0, &pinfo, + sizeof( PROCESS_BASIC_INFORMATION_ ), NULL ); + return pinfo.UniqueProcessId; + } + return 0; +} + + +/* + * Not really optimal, or efficient, but it is easier this way, and it is not + * like we are going to be killing thousands, or even tens of processes. + */ + +static void kill_process_tree( DWORD pid, HANDLE process ) +{ + HANDLE process_snapshot_h = INVALID_HANDLE_VALUE; + if ( !pid ) + pid = get_process_id( process ); + process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + + if ( INVALID_HANDLE_VALUE != process_snapshot_h ) + { + BOOL ok = TRUE; + PROCESSENTRY32 pinfo; + pinfo.dwSize = sizeof( PROCESSENTRY32 ); + for ( + ok = Process32First( process_snapshot_h, &pinfo ); + ok == TRUE; + ok = Process32Next( process_snapshot_h, &pinfo ) ) + { + if ( pinfo.th32ParentProcessID == pid ) + { + /* Found a child, recurse to kill it and anything else below it. + */ + HANDLE ph = OpenProcess( PROCESS_ALL_ACCESS, FALSE, + pinfo.th32ProcessID ); + if ( NULL != ph ) + { + kill_process_tree( pinfo.th32ProcessID, ph ); + CloseHandle( ph ); + } + } + } + CloseHandle( process_snapshot_h ); + } + /* Now that the children are all dead, kill the root. */ + TerminateProcess( process, -2 ); +} + + +static double creation_time( HANDLE process ) +{ + FILETIME creation; + FILETIME exit; + FILETIME kernel; + FILETIME user; + FILETIME current; + return GetProcessTimes( process, &creation, &exit, &kernel, &user ) + ? filetime_seconds( creation ) + : 0.0; +} + + +/* + * Recursive check if first process is parent (directly or indirectly) of the + * second one. Both processes are passed as process ids, not handles. Special + * return value 2 means that the second process is smss.exe and its parent + * process is System (first argument is ignored). + */ + +static int is_parent_child( DWORD parent, DWORD child ) +{ + HANDLE process_snapshot_h = INVALID_HANDLE_VALUE; + + if ( !child ) + return 0; + if ( parent == child ) + return 1; + + process_snapshot_h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + if ( INVALID_HANDLE_VALUE != process_snapshot_h ) + { + BOOL ok = TRUE; + PROCESSENTRY32 pinfo; + pinfo.dwSize = sizeof( PROCESSENTRY32 ); + for ( + ok = Process32First( process_snapshot_h, &pinfo ); + ok == TRUE; + ok = Process32Next( process_snapshot_h, &pinfo ) ) + { + if ( pinfo.th32ProcessID == child ) + { + /* Unfortunately, process ids are not really unique. There might + * be spurious "parent and child" relationship match between two + * non-related processes if real parent process of a given + * process has exited (while child process kept running as an + * "orphan") and the process id of such parent process has been + * reused by internals of the operating system when creating + * another process. + * + * Thus additional check is needed - process creation time. This + * check may fail (i.e. return 0) for system processes due to + * insufficient privileges, and that is OK. + */ + double tchild = 0.0; + double tparent = 0.0; + HANDLE hchild = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pinfo.th32ProcessID ); + CloseHandle( process_snapshot_h ); + + /* csrss.exe may display message box like following: + * xyz.exe - Unable To Locate Component + * This application has failed to start because + * boost_foo-bar.dll was not found. Re-installing the + * application may fix the problem + * This actually happens when starting test process that depends + * on a dynamic library which failed to build. We want to + * automatically close these message boxes even though csrss.exe + * is not our child process. We may depend on the fact that (in + * all current versions of Windows) csrss.exe is directly child + * of the smss.exe process, which in turn is directly child of + * the System process, which always has process id == 4. This + * check must be performed before comparison of process creation + * times. + */ + if ( !stricmp( pinfo.szExeFile, "csrss.exe" ) && + ( is_parent_child( parent, pinfo.th32ParentProcessID ) == 2 ) ) + return 1; + if ( !stricmp( pinfo.szExeFile, "smss.exe" ) && + ( pinfo.th32ParentProcessID == 4 ) ) + return 2; + + if ( hchild ) + { + HANDLE hparent = OpenProcess( PROCESS_QUERY_INFORMATION, + FALSE, pinfo.th32ParentProcessID ); + if ( hparent ) + { + tchild = creation_time( hchild ); + tparent = creation_time( hparent ); + CloseHandle( hparent ); + } + CloseHandle( hchild ); + } + + /* Return 0 if one of the following is true: + * 1. we failed to read process creation time + * 2. child was created before alleged parent + */ + if ( ( tchild == 0.0 ) || ( tparent == 0.0 ) || + ( tchild < tparent ) ) + return 0; + + return is_parent_child( parent, pinfo.th32ParentProcessID ) & 1; + } + } + + CloseHandle( process_snapshot_h ); + } + + return 0; +} + +typedef struct PROCESS_HANDLE_ID { HANDLE h; DWORD pid; } PROCESS_HANDLE_ID; + + +/* + * This function is called by the operating system for each topmost window. + */ + +BOOL CALLBACK close_alert_window_enum( HWND hwnd, LPARAM lParam ) +{ + char buf[ 7 ] = { 0 }; + PROCESS_HANDLE_ID p = *( (PROCESS_HANDLE_ID *)lParam ); + DWORD pid = 0; + DWORD tid = 0; + + /* We want to find and close any window that: + * 1. is visible and + * 2. is a dialog and + * 3. is displayed by any of our child processes + */ + if ( !IsWindowVisible( hwnd ) ) + return TRUE; + + if ( !GetClassNameA( hwnd, buf, sizeof( buf ) ) ) + return TRUE; /* Failed to read class name; presume it is not a dialog. */ + + if ( strcmp( buf, "#32770" ) ) + return TRUE; /* Not a dialog */ + + /* GetWindowThreadProcessId() returns 0 on error, otherwise thread id of + * window message pump thread. + */ + tid = GetWindowThreadProcessId( hwnd, &pid ); + + if ( tid && is_parent_child( p.pid, pid ) ) + { + /* Ask really nice. */ + PostMessageA( hwnd, WM_CLOSE, 0, 0 ); + /* Now wait and see if it worked. If not, insist. */ + if ( WaitForSingleObject( p.h, 200 ) == WAIT_TIMEOUT ) + { + PostThreadMessageA( tid, WM_QUIT, 0, 0 ); + WaitForSingleObject( p.h, 300 ); + } + + /* Done, we do not want to check any other window now. */ + return FALSE; + } + + return TRUE; +} + + +static void close_alert( HANDLE process ) +{ + DWORD pid = get_process_id( process ); + /* If process already exited or we just can not get its process id, do not + * go any further. + */ + if ( pid ) + { + PROCESS_HANDLE_ID p; + p.h = process; + p.pid = pid; + EnumWindows( &close_alert_window_enum, (LPARAM)&p ); + } +} + +#endif /* USE_EXECNT */ diff --git a/jam-files/engine/execunix.c b/jam-files/engine/execunix.c new file mode 100644 index 00000000..ef9dba00 --- /dev/null +++ b/jam-files/engine/execunix.c @@ -0,0 +1,569 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * Copyright 2007 Noel Belcourt. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#include "jam.h" +#include "lists.h" +#include "execcmd.h" +#include "output.h" +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> /* needed for vfork(), _exit() prototypes */ +#include <sys/resource.h> +#include <sys/times.h> +#include <sys/wait.h> + +#if defined(sun) || defined(__sun) || defined(linux) + #include <wait.h> +#endif + +#ifdef USE_EXECUNIX + +#include <sys/times.h> + +#if defined(__APPLE__) + #define NO_VFORK +#endif + +#ifdef NO_VFORK + #define vfork() fork() +#endif + + +/* + * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS + * + * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). + * The default is: + * + * /bin/sh -c % [ on UNIX/AmigaOS ] + * cmd.exe /c % [ on OS2/WinNT ] + * + * Each word must be an individual element in a jam variable value. + * + * In $(JAMSHELL), % expands to the command string and ! expands to the slot + * number (starting at 1) for multiprocess (-j) invocations. If $(JAMSHELL) does + * not include a %, it is tacked on as the last argument. + * + * Do not just set JAMSHELL to /bin/sh or cmd.exe - it will not work! + * + * External routines: + * exec_cmd() - launch an async command execution. + * exec_wait() - wait and drive at most one execution completion. + * + * Internal routines: + * onintr() - bump intr to note command interruption. + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 05/04/94 (seiwald) - async multiprocess interface + * 01/22/95 (seiwald) - $(JAMSHELL) support + * 06/02/97 (gsar) - full async multiprocess support for Win32 + */ + +static clock_t tps = 0; +static struct timeval tv; +static int select_timeout = 0; +static int intr = 0; +static int cmdsrunning = 0; +static struct tms old_time; + +#define OUT 0 +#define ERR 1 + +static struct +{ + int pid; /* on win32, a real process handle */ + int fd[2]; /* file descriptors for stdout and stderr */ + FILE *stream[2]; /* child's stdout (0) and stderr (1) file stream */ + clock_t start_time; /* start time of child process */ + int exit_reason; /* termination status */ + int action_length; /* length of action string */ + int target_length; /* length of target string */ + char *action; /* buffer to hold action and target invoked */ + char *target; /* buffer to hold action and target invoked */ + char *command; /* buffer to hold command being invoked */ + char *buffer[2]; /* buffer to hold stdout and stderr, if any */ + void (*func)( void *closure, int status, timing_info*, char *, char * ); + void *closure; + time_t start_dt; /* start of command timestamp */ +} cmdtab[ MAXJOBS ] = {{0}}; + +/* + * onintr() - bump intr to note command interruption + */ + +void onintr( int disp ) +{ + ++intr; + printf( "...interrupted\n" ); +} + + +/* + * exec_cmd() - launch an async command execution. + */ + +void exec_cmd +( + char * string, + void (*func)( void *closure, int status, timing_info*, char *, char * ), + void * closure, + LIST * shell, + char * action, + char * target +) +{ + static int initialized = 0; + int out[2]; + int err[2]; + int slot; + int len; + char * argv[ MAXARGC + 1 ]; /* +1 for NULL */ + + /* 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 ); + } + + /* 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 + { + argv[ 0 ] = "/bin/sh"; + argv[ 1 ] = "-c"; + argv[ 2 ] = string; + argv[ 3 ] = 0; + } + + /* Increment jobs running. */ + ++cmdsrunning; + + /* Save off actual command string. */ + cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC( strlen( string ) + 1 ); + strcpy( cmdtab[ slot ].command, string ); + + /* Initialize only once. */ + if ( !initialized ) + { + times( &old_time ); + initialized = 1; + } + + /* Create pipes from child to parent. */ + { + if ( pipe( out ) < 0 ) + exit( EXITBAD ); + + if ( pipe( err ) < 0 ) + exit( EXITBAD ); + } + + /* Start the command */ + + cmdtab[ slot ].start_dt = time(0); + + if ( 0 < globs.timeout ) + { + /* + * Handle hung processes by manually tracking elapsed time and signal + * process when time limit expires. + */ + struct tms buf; + cmdtab[ slot ].start_time = times( &buf ); + + /* Make a global, only do this once. */ + if ( tps == 0 ) tps = sysconf( _SC_CLK_TCK ); + } + + if ( ( cmdtab[ slot ].pid = vfork() ) == 0 ) + { + int pid = getpid(); + + close( out[0] ); + close( err[0] ); + + dup2( out[1], STDOUT_FILENO ); + + if ( globs.pipe_action == 0 ) + dup2( out[1], STDERR_FILENO ); + else + dup2( err[1], STDERR_FILENO ); + + close( out[1] ); + close( err[1] ); + + /* Make this process a process group leader so that when we kill it, all + * child processes of this process are terminated as well. We use + * killpg(pid, SIGKILL) to kill the process group leader and all its + * children. + */ + if ( 0 < globs.timeout ) + { + struct rlimit r_limit; + r_limit.rlim_cur = globs.timeout; + r_limit.rlim_max = globs.timeout; + setrlimit( RLIMIT_CPU, &r_limit ); + } + setpgid( pid,pid ); + execvp( argv[0], argv ); + perror( "execvp" ); + _exit( 127 ); + } + else if ( cmdtab[ slot ].pid == -1 ) + { + perror( "vfork" ); + exit( EXITBAD ); + } + + setpgid( cmdtab[ slot ].pid, cmdtab[ slot ].pid ); + + /* close write end of pipes */ + close( out[1] ); + close( err[1] ); + + /* set both file descriptors to non-blocking */ + fcntl(out[0], F_SETFL, O_NONBLOCK); + fcntl(err[0], F_SETFL, O_NONBLOCK); + + /* child writes stdout to out[1], parent reads from out[0] */ + cmdtab[ slot ].fd[ OUT ] = out[0]; + cmdtab[ slot ].stream[ OUT ] = fdopen( cmdtab[ slot ].fd[ OUT ], "rb" ); + if ( cmdtab[ slot ].stream[ OUT ] == NULL ) + { + perror( "fdopen" ); + exit( EXITBAD ); + } + + /* child writes stderr to err[1], parent reads from err[0] */ + if (globs.pipe_action == 0) + { + close(err[0]); + } + else + { + cmdtab[ slot ].fd[ ERR ] = err[0]; + cmdtab[ slot ].stream[ ERR ] = fdopen( cmdtab[ slot ].fd[ ERR ], "rb" ); + if ( cmdtab[ slot ].stream[ ERR ] == NULL ) + { + perror( "fdopen" ); + exit( EXITBAD ); + } + } + + /* Ensure enough room for rule and target name. */ + if ( action && target ) + { + len = strlen( action ) + 1; + if ( cmdtab[ slot ].action_length < len ) + { + BJAM_FREE( cmdtab[ slot ].action ); + cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC( len ); + cmdtab[ slot ].action_length = len; + } + strcpy( cmdtab[ slot ].action, action ); + len = strlen( target ) + 1; + if ( cmdtab[ slot ].target_length < len ) + { + BJAM_FREE( cmdtab[ slot ].target ); + cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC( len ); + cmdtab[ slot ].target_length = len; + } + strcpy( cmdtab[ slot ].target, target ); + } + else + { + BJAM_FREE( cmdtab[ slot ].action ); + BJAM_FREE( cmdtab[ slot ].target ); + cmdtab[ slot ].action = 0; + cmdtab[ slot ].target = 0; + cmdtab[ slot ].action_length = 0; + cmdtab[ slot ].target_length = 0; + } + + /* Save the operation for exec_wait() to find. */ + cmdtab[ slot ].func = func; + cmdtab[ slot ].closure = closure; + + /* Wait until we are under the limit of concurrent commands. Do not trust + * globs.jobs alone. + */ + while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) ) + if ( !exec_wait() ) + break; +} + + +/* Returns 1 if file is closed, 0 if descriptor is still live. + * + * i is index into cmdtab + * + * s (stream) indexes: + * - cmdtab[ i ].stream[ s ] + * - cmdtab[ i ].buffer[ s ] + * - cmdtab[ i ].fd [ s ] + */ + +int read_descriptor( int i, int s ) +{ + int ret; + int len; + char buffer[BUFSIZ]; + + while ( 0 < ( ret = fread( buffer, sizeof(char), BUFSIZ-1, cmdtab[ i ].stream[ s ] ) ) ) + { + buffer[ret] = 0; + if ( !cmdtab[ i ].buffer[ s ] ) + { + /* Never been allocated. */ + cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 ); + memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 ); + } + else + { + /* Previously allocated. */ + char * tmp = cmdtab[ i ].buffer[ s ]; + len = strlen( tmp ); + cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( len + ret + 1 ); + memcpy( cmdtab[ i ].buffer[ s ], tmp, len ); + memcpy( cmdtab[ i ].buffer[ s ] + len, buffer, ret + 1 ); + BJAM_FREE( tmp ); + } + } + + return feof(cmdtab[ i ].stream[ s ]); +} + + +void close_streams( int i, int s ) +{ + /* Close the stream and pipe descriptor. */ + fclose(cmdtab[ i ].stream[ s ]); + cmdtab[ i ].stream[ s ] = 0; + + close(cmdtab[ i ].fd[ s ]); + cmdtab[ i ].fd[ s ] = 0; +} + + +void populate_file_descriptors( int * fmax, fd_set * fds) +{ + int i, fd_max = 0; + struct tms buf; + clock_t current = times( &buf ); + select_timeout = globs.timeout; + + /* Compute max read file descriptor for use in select. */ + FD_ZERO(fds); + for ( i = 0; i < globs.jobs; ++i ) + { + if ( 0 < cmdtab[ i ].fd[ OUT ] ) + { + fd_max = fd_max < cmdtab[ i ].fd[ OUT ] ? cmdtab[ i ].fd[ OUT ] : fd_max; + FD_SET(cmdtab[ i ].fd[ OUT ], fds); + } + if ( globs.pipe_action != 0 ) + { + if (0 < cmdtab[ i ].fd[ ERR ]) + { + fd_max = fd_max < cmdtab[ i ].fd[ ERR ] ? cmdtab[ i ].fd[ ERR ] : fd_max; + FD_SET(cmdtab[ i ].fd[ ERR ], fds); + } + } + + if (globs.timeout && cmdtab[ i ].pid) { + clock_t consumed = (current - cmdtab[ i ].start_time) / tps; + clock_t process_timesout = globs.timeout - consumed; + if (0 < process_timesout && process_timesout < select_timeout) { + select_timeout = process_timesout; + } + if ( globs.timeout <= consumed ) + { + killpg( cmdtab[ i ].pid, SIGKILL ); + cmdtab[ i ].exit_reason = EXIT_TIMEOUT; + } + } + } + *fmax = fd_max; +} + + +/* + * exec_wait() - wait and drive at most one execution completion. + */ + +int exec_wait() +{ + int i; + int ret; + int fd_max; + int pid; + int status; + int finished; + int rstat; + timing_info time_info; + fd_set fds; + struct tms new_time; + + /* Handle naive make1() which does not know if commands are running. */ + if ( !cmdsrunning ) + return 0; + + /* Process children that signaled. */ + finished = 0; + while ( !finished && cmdsrunning ) + { + /* Compute max read file descriptor for use in select(). */ + populate_file_descriptors( &fd_max, &fds ); + + if ( 0 < globs.timeout ) + { + /* Force select() to timeout so we can terminate expired processes. + */ + tv.tv_sec = select_timeout; + tv.tv_usec = 0; + + /* select() will wait until: i/o on a descriptor, a signal, or we + * time out. + */ + ret = select( fd_max + 1, &fds, 0, 0, &tv ); + } + else + { + /* select() will wait until i/o on a descriptor or a signal. */ + ret = select( fd_max + 1, &fds, 0, 0, 0 ); + } + + if ( 0 < ret ) + { + for ( i = 0; i < globs.jobs; ++i ) + { + int out = 0; + int err = 0; + if ( FD_ISSET( cmdtab[ i ].fd[ OUT ], &fds ) ) + out = read_descriptor( i, OUT ); + + if ( ( globs.pipe_action != 0 ) && + ( FD_ISSET( cmdtab[ i ].fd[ ERR ], &fds ) ) ) + err = read_descriptor( i, ERR ); + + /* If feof on either descriptor, then we are done. */ + if ( out || err ) + { + /* Close the stream and pipe descriptors. */ + close_streams( i, OUT ); + if ( globs.pipe_action != 0 ) + close_streams( i, ERR ); + + /* Reap the child and release resources. */ + pid = waitpid( cmdtab[ i ].pid, &status, 0 ); + + if ( pid == cmdtab[ i ].pid ) + { + finished = 1; + pid = 0; + cmdtab[ i ].pid = 0; + + /* Set reason for exit if not timed out. */ + if ( WIFEXITED( status ) ) + { + cmdtab[ i ].exit_reason = 0 == WEXITSTATUS( status ) + ? EXIT_OK + : EXIT_FAIL; + } + + /* Print out the rule and target name. */ + out_action( cmdtab[ i ].action, cmdtab[ i ].target, + cmdtab[ i ].command, cmdtab[ i ].buffer[ OUT ], + cmdtab[ i ].buffer[ ERR ], cmdtab[ i ].exit_reason + ); + + times( &new_time ); + + time_info.system = (double)( new_time.tms_cstime - old_time.tms_cstime ) / CLOCKS_PER_SEC; + time_info.user = (double)( new_time.tms_cutime - old_time.tms_cutime ) / CLOCKS_PER_SEC; + time_info.start = cmdtab[ i ].start_dt; + time_info.end = time( 0 ); + + old_time = new_time; + + /* Drive the completion. */ + --cmdsrunning; + + if ( intr ) + rstat = EXEC_CMD_INTR; + else if ( status != 0 ) + rstat = EXEC_CMD_FAIL; + else + rstat = EXEC_CMD_OK; + + /* Assume -p0 in effect so only pass buffer[ 0 ] + * containing merged output. + */ + (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, + &time_info, cmdtab[ i ].command, + cmdtab[ i ].buffer[ 0 ] ); + + BJAM_FREE( cmdtab[ i ].buffer[ OUT ] ); + cmdtab[ i ].buffer[ OUT ] = 0; + + BJAM_FREE( cmdtab[ i ].buffer[ ERR ] ); + cmdtab[ i ].buffer[ ERR ] = 0; + + BJAM_FREE( cmdtab[ i ].command ); + cmdtab[ i ].command = 0; + + cmdtab[ i ].func = 0; + cmdtab[ i ].closure = 0; + cmdtab[ i ].start_time = 0; + } + else + { + printf( "unknown pid %d with errno = %d\n", pid, errno ); + exit( EXITBAD ); + } + } + } + } + } + + return 1; +} + +# endif /* USE_EXECUNIX */ diff --git a/jam-files/engine/execvms.c b/jam-files/engine/execvms.c new file mode 100644 index 00000000..729917d3 --- /dev/null +++ b/jam-files/engine/execvms.c @@ -0,0 +1,161 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#include "jam.h" +#include "lists.h" +#include "execcmd.h" + +#ifdef OS_VMS + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <iodef.h> +#include <ssdef.h> +#include <descrip.h> +#include <dvidef.h> +#include <clidef.h> + +/* + * 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 do not 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) - do not put -'s between "'s + */ + +#define WRTLEN 240 + +#define MIN( a, b ) ((a) < (b) ? (a) : (b)) + +/* 1 for the @ and 4 for the .com */ + +char tempnambuf[ L_tmpnam + 1 + 4 ] = { 0 }; + + +void exec_cmd +( + char * string, + void (* func)( void * closure, int status, timing_info *, char *, char * ), + void * closure, + LIST * shell, + char * rule_name, + char * target +) +{ + char * s; + char * e; + cahr * 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 exec_wait() +{ + return 0; +} + +# endif /* VMS */ diff --git a/jam-files/engine/expand.c b/jam-files/engine/expand.c new file mode 100644 index 00000000..d8e58827 --- /dev/null +++ b/jam-files/engine/expand.c @@ -0,0 +1,733 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "variable.h" +# include "expand.h" +# include "pathsys.h" +# include "newstr.h" +# include <assert.h> +# include <stdlib.h> +# include <limits.h> + +# ifdef OS_CYGWIN +# include <sys/cygwin.h> +# include <windows.h> +# endif + +/* + * expand.c - expand a buffer, given variable values + * + * External routines: + * + * var_expand() - variable-expand input string into list of strings + * + * Internal routines: + * + * var_edit_parse() - parse : modifiers into PATHNAME structure. + * var_edit_file() - copy input target name to output, modifying filename. + * var_edit_shift() - do upshift/downshift mods. + * + * 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X) + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval + */ + +typedef struct +{ + PATHNAME f; /* :GDBSMR -- pieces */ + char parent; /* :P -- go to parent directory */ + char filemods; /* one of the above applied */ + char downshift; /* :L -- downshift result */ + char upshift; /* :U -- upshift result */ + char to_slashes; /* :T -- convert "\" to "/" */ + char to_windows; /* :W -- convert cygwin to native paths */ + PATHPART empty; /* :E -- default for empties */ + PATHPART join; /* :J -- join list with char */ +} VAR_EDITS ; + +static void var_edit_parse( char * mods, VAR_EDITS * edits ); +static void var_edit_file ( char * in, string * out, VAR_EDITS * edits ); +static void var_edit_shift( string * out, VAR_EDITS * edits ); + +#define MAGIC_COLON '\001' +#define MAGIC_LEFT '\002' +#define MAGIC_RIGHT '\003' + + +/* + * var_expand() - variable-expand input string into list of strings. + * + * Would just copy input to output, performing variable expansion, except that + * since variables can contain multiple values the result of variable expansion + * may contain multiple values (a list). Properly performs "product" operations + * that occur in "$(var1)xxx$(var2)" or even "$($(var2))". + * + * Returns a newly created list. + */ + +LIST * var_expand( LIST * l, char * in, char * end, LOL * lol, int cancopyin ) +{ + char out_buf[ MAXSYM ]; + string buf[ 1 ]; + string out1[ 1 ]; /* temporary buffer */ + size_t prefix_length; + char * out; + 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 a lot of cases: $(<) and $(>). */ + if + ( + ( in[ 0 ] == '$' ) && + ( in[ 1 ] == '(' ) && + ( in[ 3 ] == ')' ) && + ( in[ 4 ] == '\0' ) + ) + { + switch ( in[ 2 ] ) + { + case '<': return list_copy( l, lol_get( lol, 0 ) ); + case '>': return list_copy( l, lol_get( lol, 1 ) ); + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return list_copy( l, lol_get( lol, in[ 2 ] - '1' ) ); + } + } + else if ( in[0] == '$' && in[1] == '(' && in[2] == '1' && in[4] == ')' && + in[5] == '\0') { + + switch( in[3] ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return list_copy( l, lol_get( lol, in[3]-'0'+10-1 ) ); + } + } + + /* Expand @() files, to single item plus accompanying file. */ + if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) && ( *( end - 1 ) == ')' ) ) + { + /* We try the expansion until it fits within the propective output + * buffer. + */ + char * at_buf = 0; + int at_size = MAXJPATH; + int at_len = 0; + do + { + BJAM_FREE( at_buf ); + at_buf = (char *)BJAM_MALLOC_ATOMIC( at_size + 1 ); + at_len = var_string( in, at_buf, at_size, lol ); + at_size *= 2; + } + while ( ( at_len < 0 ) && ( at_size < INT_MAX / 2 ) ); + /* Return the result as a single item list. */ + if ( at_len > 0 ) + { + LIST * r; + string_copy( buf, at_buf ); + r = list_new( l, newstr( buf->value ) ); + string_free( buf ); + BJAM_FREE( at_buf ); + return r; + } + BJAM_FREE( at_buf ); + } + + /* Just try simple copy of in to out. */ + while ( in < end ) + if ( ( *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 copystr() to put it on the new list. Otherwise, we use the slower + * newstr(). + */ + if ( cancopyin ) + return list_new( l, copystr( inp ) ); + + { + LIST * r; + string_new( buf ); + string_append_range( buf, inp, end ); + r = list_new( l, newstr( buf->value ) ); + string_free( buf ); + return r; + } + +expand: + string_new( buf ); + string_append_range( buf, inp, in - 1 ); /* Copy the part before '$'. */ + /* + * 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; + inp = ++in; /* Skip over the '('. */ + + while ( ( in < end ) && depth ) + { + switch ( *in++ ) + { + case '(': ++depth; break; + case ')': --depth; break; + } + } + + /* + * Input so far (ignore blanks): + * + * stuff-in-outbuf $(variable) remainder + * ^ ^ ^ + * inp in end + */ + prefix_length = buf->size; + string_append_range( buf, inp, in - 1 ); + + out = buf->value + prefix_length; + for ( ov = out; ov < buf->value + buf->size; ++ov ) + { + switch ( *ov ) + { + case ':': *ov = MAGIC_COLON; break; + case '[': *ov = MAGIC_LEFT ; break; + case ']': *ov = MAGIC_RIGHT; break; + } + } + + /* + * 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 will 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 = 0; + LIST * evalue = 0; + char * colon; + char * bracket; + string variable[1]; + char * varname; + int sub1 = 0; + int sub2 = -1; + VAR_EDITS edits; + + /* Look for a : modifier in the variable name. Must copy into + * varname so we can modify it. + */ + string_copy( variable, vars->string ); + varname = variable->value; + + if ( ( colon = strchr( varname, MAGIC_COLON ) ) ) + { + string_truncate( variable, colon - varname ); + var_edit_parse( colon + 1, &edits ); + } + + /* Look for [x-y] subscripting. sub1 and sub2 are x and y. */ + if ( ( bracket = strchr( varname, MAGIC_LEFT ) ) ) + { + /* Make all syntax errors in [] subscripting result in the same + * behavior: silenty return an empty expansion (by setting sub2 + * = 0). Brute force parsing; May get moved into yacc someday. + */ + + char * s = bracket + 1; + + string_truncate( variable, bracket - varname ); + + do /* so we can use "break" */ + { + /* Allow negative indexes. */ + if ( !isdigit( *s ) && ( *s != '-' ) ) + { + sub2 = 0; + break; + } + sub1 = atoi( s ); + + /* Skip over the first symbol, which is either a digit or dash. */ + ++s; + while ( isdigit( *s ) ) ++s; + + if ( *s == MAGIC_RIGHT ) + { + sub2 = sub1; + break; + } + + if ( *s != '-' ) + { + sub2 = 0; + break; + } + + ++s; + + if ( *s == MAGIC_RIGHT ) + { + sub2 = -1; + break; + } + + if ( !isdigit( *s ) && ( *s != '-' ) ) + { + sub2 = 0; + break; + } + + /* First, compute the index of the last element. */ + sub2 = atoi( s ); + while ( isdigit( *++s ) ); + + if ( *s != MAGIC_RIGHT ) + sub2 = 0; + + } while ( 0 ); + + /* Anything but the end of the string, or the colon introducing + * a modifier is a syntax error. + */ + ++s; + if ( *s && ( *s != MAGIC_COLON ) ) + sub2 = 0; + + *bracket = '\0'; + } + + /* Get variable value, with special handling for $(<), $(>), $(n). + */ + if ( !varname[1] ) + { + if ( varname[0] == '<' ) + value = lol_get( lol, 0 ); + else if ( varname[0] == '>' ) + value = lol_get( lol, 1 ); + else if ( ( varname[0] >= '1' ) && ( varname[0] <= '9' ) ) + value = lol_get( lol, varname[0] - '1' ); + else if( varname[0] == '1' && varname[1] >= '0' && + varname[1] <= '9' && !varname[2] ) + value = lol_get( lol, varname[1] - '0' + 10 - 1 ); + } + + if ( !value ) + value = var_get( varname ); + + /* Handle negitive indexes: part two. */ + { + int length = list_length( value ); + + if ( sub1 < 0 ) + sub1 = length + sub1; + else + sub1 -= 1; + + if ( sub2 < 0 ) + sub2 = length + 1 + sub2 - sub1; + else + sub2 -= sub1; + /* The "sub2 < 0" test handles the semantic error of sub2 < + * sub1. + */ + if ( sub2 < 0 ) + sub2 = 0; + } + + /* The fast path: $(x) - just copy the variable value. This is only + * an optimization. + */ + if ( ( out == out_buf ) && !bracket && !colon && ( in == end ) ) + { + string_free( variable ); + l = list_copy( l, value ); + continue; + } + + /* Handle start subscript. */ + while ( ( sub1 > 0 ) && value ) + --sub1, value = list_next( value ); + + /* Empty w/ :E=default?. */ + if ( !value && colon && edits.empty.ptr ) + evalue = value = list_new( L0, newstr( edits.empty.ptr ) ); + + /* For each variable value. */ + string_new( out1 ); + for ( ; value; value = list_next( value ) ) + { + LIST * rem; + size_t postfix_start; + + /* Handle end subscript (length actually). */ + + if ( sub2 >= 0 && --sub2 < 0 ) + break; + + string_truncate( buf, prefix_length ); + + /* Apply : mods, if present */ + + if ( colon && edits.filemods ) + var_edit_file( value->string, out1, &edits ); + else + string_append( out1, value->string ); + + if ( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) ) + var_edit_shift( out1, &edits ); + + /* Handle :J=joinval */ + /* If we have more values for this var, just keep appending them + * (using the join value) rather than creating separate LIST + * elements. + */ + if ( colon && edits.join.ptr && + ( list_next( value ) || list_next( vars ) ) ) + { + string_append( out1, edits.join.ptr ); + continue; + } + + string_append( buf, out1->value ); + string_free( out1 ); + string_new( out1 ); + + /* If no remainder, append result to output chain. */ + if ( in == end ) + { + l = list_new( l, newstr( buf->value ) ); + 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'. + */ + postfix_start = buf->size; + for ( rem = remainder; rem; rem = list_next( rem ) ) + { + string_truncate( buf, postfix_start ); + string_append( buf, rem->string ); + l = list_new( l, newstr( buf->value ) ); + } + } + string_free( out1 ); + + /* Toss used empty. */ + if ( evalue ) + list_free( evalue ); + + string_free( variable ); + } + + /* 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" ); + } + + string_free( buf ); + 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( 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; + case 'T': edits->to_slashes = 1; continue; + case 'W': edits->to_windows = 1; continue; + 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 != '=' ) + { + if ( !havezeroed++ ) + { + int i; + for ( i = 0; i < 6; ++i ) + { + edits->f.part[ i ].len = 0; + edits->f.part[ i ].ptr = ""; + } + } + + fp->ptr = 0; + continue; + } + + strval: + /* Handle :X=value, or :X */ + if ( *mods != '=' ) + { + fp->ptr = ""; + fp->len = 0; + } + else if ( ( p = strchr( mods, MAGIC_COLON ) ) ) + { + *p = 0; + fp->ptr = ++mods; + fp->len = p - mods; + mods = p + 1; + } + else + { + fp->ptr = ++mods; + fp->len = strlen( mods ); + mods += fp->len; + } + } +} + + +/* + * var_edit_file() - copy input target name to output, modifying filename. + */ + +static void var_edit_file( char * in, string * 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( string * out, VAR_EDITS * edits ) +{ + /* Handle upshifting, downshifting and slash translation now. */ + char * p; + for ( p = out->value; *p; ++p) + { + if ( edits->upshift ) + *p = toupper( *p ); + else if ( edits->downshift ) + *p = tolower( *p ); + if ( edits->to_slashes && ( *p == '\\' ) ) + *p = '/'; +# ifdef OS_CYGWIN + if ( edits->to_windows ) + { + char result[ MAX_PATH + 1 ]; + cygwin_conv_to_win32_path( out->value, result ); + assert( strlen( result ) <= MAX_PATH ); + string_free( out ); + string_copy( out, result ); + } +# endif + } + out->size = p - out->value; +} + + +#ifndef NDEBUG +void var_expand_unit_test() +{ + LOL lol[ 1 ]; + LIST * l; + LIST * l2; + LIST * expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) ); + LIST * e2; + char axyb[] = "a$(xy)b"; + char azb[] = "a$($(z))b"; + char path[] = "$(p:W)"; + +# ifdef OS_CYGWIN + char cygpath[ 256 ]; + cygwin_conv_to_posix_path( "c:\\foo\\bar", cygpath ); +# else + char cygpath[] = "/cygdrive/c/foo/bar"; +# endif + + lol_init(lol); + var_set( "xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET ); + var_set( "z", list_new( L0, newstr( "xy" ) ), VAR_SET ); + var_set( "p", list_new( L0, newstr( cygpath ) ), VAR_SET ); + + l = var_expand( 0, axyb, axyb + sizeof( axyb ) - 1, lol, 0 ); + for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) ) + assert( !strcmp( e2->string, l2->string ) ); + assert( l2 == 0 ); + assert( e2 == 0 ); + list_free( l ); + + l = var_expand( 0, azb, azb + sizeof( azb ) - 1, lol, 0 ); + for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next( l2 ), e2 = list_next( e2 ) ) + assert( !strcmp( e2->string, l2->string ) ); + assert( l2 == 0 ); + assert( e2 == 0 ); + list_free( l ); + + l = var_expand( 0, path, path + sizeof( path ) - 1, lol, 0 ); + assert( l != 0 ); + assert( list_next( l ) == 0 ); +# ifdef OS_CYGWIN + /* On some installations of cygwin the drive letter is expanded to other + * case. This has been reported to be the case if cygwin has been installed + * to C:\ as opposed to C:\cygwin. Since case of the drive letter will not + * matter, we allow for both. + */ + assert( !strcmp( l->string, "c:\\foo\\bar" ) || + !strcmp( l->string, "C:\\foo\\bar" ) ); +# else + assert( !strcmp( l->string, cygpath ) ); +# endif + list_free( l ); + list_free( expected ); + lol_free( lol ); +} +#endif diff --git a/jam-files/engine/expand.h b/jam-files/engine/expand.h new file mode 100644 index 00000000..cc25d190 --- /dev/null +++ b/jam-files/engine/expand.h @@ -0,0 +1,14 @@ +/* + * 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 + */ + +#include "lists.h" + +LIST *var_expand( LIST *l, char *in, char *end, LOL *lol, int cancopyin ); +void var_expand_unit_test(); diff --git a/jam-files/engine/filemac.c b/jam-files/engine/filemac.c new file mode 100644 index 00000000..e69aa648 --- /dev/null +++ b/jam-files/engine/filemac.c @@ -0,0 +1,175 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_MAC + +#include <Files.h> +#include <Folders.h> + +# include <:sys:stat.h> + +/* + * filemac.c - manipulate file names and scan directories on macintosh + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/19/94 (mikem) - solaris string table insanity support + * 02/14/95 (seiwald) - parse and build /xxx properly + * 05/03/96 (seiwald) - split into pathunix.c + * 11/21/96 (peterk) - BEOS does not have Unix-style archives + */ + + +void CopyC2PStr( char const * cstr, StringPtr pstr ) +{ + int len; + for ( len = 0; *cstr && ( len < 255 ); pstr[ ++len ] = *cstr++ ); + pstr[ 0 ] = len; +} + + +/* + * file_dirscan() - scan a directory for files. + */ + +void file_dirscan( char * dir, scanback func, void * closure ) +{ + PATHNAME f; + string filename[ 1 ]; + 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; + + string_new( filename ); + 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; + + string_truncate( filename, 0 ); + path_build( &f, filename, 0 ); + (*func)( closure, filename->value, 0 /* not stat()'ed */, (time_t)0 ); + } + string_free( filename ); +} + + +/* + * file_time() - get timestamp of file, if not done by file_dirscan(). + */ + +int file_time( char * filename, time_t * time ) +{ + struct stat statbuf; + + if ( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + + return 0; +} + + +int file_is_file( char * filename ) +{ + struct stat statbuf; + if ( stat( filename, &statbuf ) < 0 ) + return -1; + return S_ISREG( statbuf.st_mode ) ? 1 : 0; +} + +int file_mkdir(char *pathname) +{ + return mkdir(pathname, 0766); +} + + +/* + * file_archscan() - scan an archive for files. + */ + +void file_archscan( char * archive, scanback func, void * closure ) +{ +} + + +# endif /* macintosh */ diff --git a/jam-files/engine/filent.c b/jam-files/engine/filent.c new file mode 100644 index 00000000..ab189576 --- /dev/null +++ b/jam-files/engine/filent.c @@ -0,0 +1,387 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Copyright 2005 Rene Rivera. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" + +# include "filesys.h" +# include "pathsys.h" +# include "strings.h" +# include "newstr.h" + +# ifdef OS_NT + +# ifdef __BORLANDC__ +# if __BORLANDC__ < 0x550 +# include <dir.h> +# include <dos.h> +# endif +# undef FILENAME /* cpp namespace collision */ +# define _finddata_t ffblk +# endif + +# include <io.h> +# include <sys/stat.h> +# include <ctype.h> +# include <direct.h> + +/* + * filent.c - scan directories and archives on NT + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 07/10/95 (taylor) Findfirst() returns the first file on NT. + * 05/03/96 (seiwald) split apart into pathnt.c + */ + +/* + * file_dirscan() - scan a directory for files + */ + +void file_dirscan( char * dir, scanback func, void * closure ) +{ + PROFILE_ENTER( FILE_DIRSCAN ); + + file_info_t * d = 0; + + dir = short_path_to_long_path( dir ); + + /* First enter directory itself */ + + d = file_query( dir ); + + if ( !d || !d->is_dir ) + { + PROFILE_EXIT( FILE_DIRSCAN ); + return; + } + + if ( !d->files ) + { + PATHNAME f; + string filespec[ 1 ]; + string filename[ 1 ]; + long handle; + int ret; + struct _finddata_t finfo[ 1 ]; + LIST * files = L0; + int d_length = strlen( d->name ); + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_dir.ptr = d->name; + f.f_dir.len = d_length; + + /* Now enter contents of directory */ + + /* Prepare file search specification for the findfirst() API. */ + if ( d_length == 0 ) + string_copy( filespec, ".\\*" ); + else + { + /* + * We can not simply assume the given folder name will never include + * its trailing path separator or otherwise we would not support the + * Windows root folder specified without its drive letter, i.e. '\'. + */ + char trailingChar = d->name[ d_length - 1 ] ; + string_copy( filespec, d->name ); + if ( ( trailingChar != '\\' ) && ( trailingChar != '/' ) ) + string_append( filespec, "\\" ); + string_append( filespec, "*" ); + } + + if ( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + #if defined(__BORLANDC__) && __BORLANDC__ < 0x550 + if ( ret = findfirst( filespec->value, finfo, FA_NORMAL | FA_DIREC ) ) + { + string_free( filespec ); + PROFILE_EXIT( FILE_DIRSCAN ); + return; + } + + string_new ( filename ); + while ( !ret ) + { + file_info_t * ff = 0; + + f.f_base.ptr = finfo->ff_name; + f.f_base.len = strlen( finfo->ff_name ); + + string_truncate( filename, 0 ); + path_build( &f, filename ); + + files = list_new( files, newstr(filename->value) ); + ff = file_info( filename->value ); + ff->is_file = finfo->ff_attrib & FA_DIREC ? 0 : 1; + ff->is_dir = finfo->ff_attrib & FA_DIREC ? 1 : 0; + ff->size = finfo->ff_fsize; + ff->time = (finfo->ff_ftime << 16) | finfo->ff_ftime; + + ret = findnext( finfo ); + } + # else + handle = _findfirst( filespec->value, finfo ); + + if ( ret = ( handle < 0L ) ) + { + string_free( filespec ); + PROFILE_EXIT( FILE_DIRSCAN ); + return; + } + + string_new( filename ); + while ( !ret ) + { + file_info_t * ff = 0; + + f.f_base.ptr = finfo->name; + f.f_base.len = strlen( finfo->name ); + + string_truncate( filename, 0 ); + path_build( &f, filename, 0 ); + + files = list_new( files, newstr( filename->value ) ); + ff = file_info( filename->value ); + ff->is_file = finfo->attrib & _A_SUBDIR ? 0 : 1; + ff->is_dir = finfo->attrib & _A_SUBDIR ? 1 : 0; + ff->size = finfo->size; + ff->time = finfo->time_write; + + ret = _findnext( handle, finfo ); + } + + _findclose( handle ); + # endif + string_free( filename ); + string_free( filespec ); + + d->files = files; + } + + /* Special case \ or d:\ : enter it */ + { + unsigned long len = strlen(d->name); + if ( len == 1 && d->name[0] == '\\' ) + (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); + else if ( len == 3 && d->name[1] == ':' ) { + (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); + /* We've just entered 3-letter drive name spelling (with trailing + slash), into the hash table. Now enter two-letter variant, + without trailing slash, so that if we try to check whether + "c:" exists, we hit it. + + Jam core has workarounds for that. Given: + x = c:\whatever\foo ; + p = $(x:D) ; + p2 = $(p:D) ; + There will be no trailing slash in $(p), but there will be one + in $(p2). But, that seems rather fragile. + */ + d->name[2] = 0; + (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); + } + } + + /* Now enter contents of directory */ + if ( d->files ) + { + LIST * files = d->files; + while ( files ) + { + file_info_t * ff = file_info( files->string ); + (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time ); + files = list_next( files ); + } + } + + PROFILE_EXIT( FILE_DIRSCAN ); +} + +file_info_t * file_query( char * filename ) +{ + file_info_t * ff = file_info( filename ); + if ( ! ff->time ) + { + struct stat statbuf; + + if ( stat( *filename ? filename : ".", &statbuf ) < 0 ) + return 0; + + ff->is_file = statbuf.st_mode & S_IFREG ? 1 : 0; + ff->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0; + ff->size = statbuf.st_size; + ff->time = statbuf.st_mtime ? statbuf.st_mtime : 1; + } + return ff; +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + *time = ff->time; + return 0; +} + +int file_is_file(char* filename) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + return ff->is_file; +} + +int file_mkdir(char *pathname) +{ + return _mkdir(pathname); +} + +/* + * 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( + 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 = BJAM_MALLOC_ATOMIC(lar_size+1); + if (read(fd, string_table, lar_size) != lar_size) + printf("error reading string table\n"); + string_table[lar_size] = '\0'; + 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 ); + for ( endname = name; *endname && *endname != '\n'; ++endname) {} + } + else + { + /* normal name */ + name = ar_hdr.ar_name; + endname = name + sizeof( ar_hdr.ar_name ); + } + + /* strip trailing white-space, slashes, and backslashes */ + + while ( endname-- > name ) + if ( !isspace(*endname) && ( *endname != '\\' ) && ( *endname != '/' ) ) + break; + *++endname = 0; + + /* strip leading directory names, an NT specialty */ + + if ( c = strrchr( name, '/' ) ) + name = c + 1; + if ( c = strrchr( name, '\\' ) ) + name = c + 1; + + sprintf( buf, "%s(%.*s)", archive, endname - name, name ); + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + + offset += SARHDR + lar_size; + lseek( fd, offset, 0 ); + } + + close( fd ); +} + +# endif /* NT */ diff --git a/jam-files/engine/fileos2.c b/jam-files/engine/fileos2.c new file mode 100644 index 00000000..af2373ea --- /dev/null +++ b/jam-files/engine/fileos2.c @@ -0,0 +1,138 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +/* note that we use "fileunix.c" when compiling with EMX on OS/2 */ +# if defined(OS_OS2) && !defined(__EMX__) + +# include <io.h> +# include <dos.h> + +/* + * fileos2.c - scan directories and archives on NT + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 07/10/95 (taylor) Findfirst() returns the first file on NT. + * 05/03/96 (seiwald) split apart into pathnt.c + * 09/22/00 (seiwald) handle \ and c:\ specially: don't add extra / + */ + +/* + * file_dirscan() - scan a directory for files + */ + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + PATHNAME f; + string filespec[1]; + 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 */ + string_copy( 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 + string_push_back( filespec, '/' ); + + string_push_back( filespec, '*' ); + + /* Now enter contents of directory */ + + if ( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", filespec->value ); + + /* 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->value, _A_NORMAL|_A_RDONLY|_A_SUBDIR, finfo ) ) + { + string filename[1]; + string_new( filename ); + do + { + f.f_base.ptr = finfo->name; + f.f_base.len = strlen( finfo->name ); + + string_truncate( filename, 0 ); + path_build( &f, filename, 0 ); + (*func)( closure, filename->value, 0 /* not stat()'ed */, (time_t)0 ); + } + while ( !_dos_findnext( finfo ) ); + string_free( filename ); + } +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + /* This is called on OS2, not NT. */ + /* NT fills in the time in the dirscan. */ + + struct stat statbuf; + + if ( stat( filename, &statbuf ) < 0 ) + return -1; + + *time = statbuf.st_mtime; + + return 0; +} + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ +} + +# endif /* OS2 && !__EMX__ */ + diff --git a/jam-files/engine/filesys.c b/jam-files/engine/filesys.c new file mode 100644 index 00000000..eb62ed40 --- /dev/null +++ b/jam-files/engine/filesys.c @@ -0,0 +1,83 @@ +# include "jam.h" +# include "pathsys.h" +# include "strings.h" +# include "newstr.h" +# include "filesys.h" +# include "lists.h" + +void file_build1( PATHNAME * f, string * file ) +{ + 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 ); + printf( "\n" ); + } + + /* 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] != '<' ) + string_push_back( file, '<' ); + string_append_range( + file, f->f_grist.ptr, f->f_grist.ptr + f->f_grist.len ); + if ( file->value[file->size - 1] != '>' ) + string_push_back( file, '>' ); + } +} + +static struct hash * filecache_hash = 0; +static file_info_t filecache_finfo; + +file_info_t * file_info(char * filename) +{ + file_info_t *finfo = &filecache_finfo; + + if ( !filecache_hash ) + filecache_hash = hashinit( sizeof( file_info_t ), "file_info" ); + + finfo->name = filename; + finfo->is_file = 0; + finfo->is_dir = 0; + finfo->size = 0; + finfo->time = 0; + finfo->files = 0; + if ( hashenter( filecache_hash, (HASHDATA**)&finfo ) ) + { + /* printf( "file_info: %s\n", filename ); */ + finfo->name = newstr( finfo->name ); + } + + return finfo; +} + +static LIST * files_to_remove = L0; + +static void remove_files_atexit(void) +{ + /* we do pop front in case this exit function is called + more than once */ + while ( files_to_remove ) + { + remove( files_to_remove->string ); + files_to_remove = list_pop_front( files_to_remove ); + } +} + +void file_done() +{ + remove_files_atexit(); + hashdone( filecache_hash ); +} + +void file_remove_atexit( const char * path ) +{ + files_to_remove = list_new( files_to_remove, newstr((char*)path) ); +} diff --git a/jam-files/engine/filesys.h b/jam-files/engine/filesys.h new file mode 100644 index 00000000..efc081d1 --- /dev/null +++ b/jam-files/engine/filesys.h @@ -0,0 +1,60 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * filesys.h - OS specific file routines + */ + +#ifndef FILESYS_DWA20011025_H +# define FILESYS_DWA20011025_H + +# include "pathsys.h" +#include "hash.h" +#include "lists.h" + +typedef void (*scanback)( void *closure, char *file, int found, time_t t ); + +void file_dirscan( char *dir, scanback func, void *closure ); +void file_archscan( char *arch, scanback func, void *closure ); + +int file_time( char *filename, time_t *time ); + +void file_build1(PATHNAME *f, string* file) ; +int file_is_file(char* filename); +int file_mkdir(char *pathname); + +typedef struct file_info_t file_info_t ; +struct file_info_t +{ + char * name; + short is_file; + short is_dir; + unsigned long size; + time_t time; + LIST * files; +}; + + +/* Creates a pointer to information about file 'filename', creating it as + * necessary. If created, the structure will be default initialized. + */ +file_info_t * file_info( char * filename ); + +/* Returns information about a file, queries the OS if needed. */ +file_info_t * file_query( char * filename ); + +void file_done(); + +/* Marks a path/file to be removed when jam exits. */ +void file_remove_atexit( const char * path ); + +#endif diff --git a/jam-files/engine/fileunix.c b/jam-files/engine/fileunix.c new file mode 100644 index 00000000..680c3f53 --- /dev/null +++ b/jam-files/engine/fileunix.c @@ -0,0 +1,501 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Copyright 2005 Rene Rivera. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "filesys.h" +# include "strings.h" +# include "pathsys.h" +# include "newstr.h" +# include <stdio.h> +# include <sys/stat.h> + +#if defined(sun) || defined(__sun) || defined(linux) +# include <unistd.h> /* needed for read and close prototype */ +#endif + +# ifdef USE_FILEUNIX + +#if defined(sun) || defined(__sun) +# include <unistd.h> /* needed for read and close prototype */ +#endif + +# if defined( OS_SEQUENT ) || \ + defined( OS_DGUX ) || \ + defined( OS_SCO ) || \ + defined( OS_ISC ) +# define PORTAR 1 +# endif + +# ifdef __EMX__ +# include <sys/types.h> +# include <sys/stat.h> +# 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 OS_AIX +/* Define those for AIX to get the definitions for both the small and the + * big variant of the archive file format. */ +# define __AR_SMALL__ +# define __AR_BIG__ +# endif + +# include <ar.h> +# endif + +/* + * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/19/94 (mikem) - solaris string table insanity support + * 02/14/95 (seiwald) - parse and build /xxx properly + * 05/03/96 (seiwald) - split into pathunix.c + * 11/21/96 (peterk) - BEOS does not have Unix-style archives + */ + + +/* + * file_dirscan() - scan a directory for files. + */ + +void file_dirscan( char * dir, scanback func, void * closure ) +{ + PROFILE_ENTER( FILE_DIRSCAN ); + + file_info_t * d = 0; + + d = file_query( dir ); + + if ( !d || !d->is_dir ) + { + PROFILE_EXIT( FILE_DIRSCAN ); + return; + } + + if ( ! d->files ) + { + LIST* files = L0; + PATHNAME f; + DIR *dd; + STRUCT_DIRENT *dirent; + string filename[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 : "."; + + /* Now enter contents of directory. */ + + if ( !( dd = opendir( dir ) ) ) + { + PROFILE_EXIT( FILE_DIRSCAN ); + return; + } + + if ( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + string_new( filename ); + while ( ( dirent = readdir( dd ) ) ) + { + # 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 ); + + string_truncate( filename, 0 ); + path_build( &f, filename, 0 ); + + files = list_new( files, newstr(filename->value) ); + file_query( filename->value ); + } + string_free( filename ); + + closedir( dd ); + + d->files = files; + } + + /* Special case / : enter it */ + { + unsigned long len = strlen(d->name); + if ( ( len == 1 ) && ( d->name[0] == '/' ) ) + (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); + } + + /* Now enter contents of directory */ + if ( d->files ) + { + LIST * files = d->files; + while ( files ) + { + file_info_t * ff = file_info( files->string ); + (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time ); + files = list_next( files ); + } + } + + PROFILE_EXIT( FILE_DIRSCAN ); +} + + +file_info_t * file_query( char * filename ) +{ + file_info_t * ff = file_info( filename ); + if ( ! ff->time ) + { + struct stat statbuf; + + if ( stat( *filename ? filename : ".", &statbuf ) < 0 ) + return 0; + + ff->is_file = statbuf.st_mode & S_IFREG ? 1 : 0; + ff->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0; + ff->size = statbuf.st_size; + ff->time = statbuf.st_mtime ? statbuf.st_mtime : 1; + } + return ff; +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + *time = ff->time; + return 0; +} + +int file_is_file(char* filename) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + return ff->is_file; +} + +int file_mkdir(char* pathname) +{ + return mkdir(pathname, 0766); +} + +/* + * file_archscan() - scan an archive for files + */ + +# ifndef AIAMAG /* God-fearing UNIX */ + +# define SARFMAG 2 +# define SARHDR sizeof( struct ar_hdr ) + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ +# ifndef NO_AR + struct ar_hdr ar_hdr; + char buf[ MAXJPATH ]; + long offset; + char *string_table = 0; + int fd; + + if ( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 ) + return; + + if ( read( fd, buf, SARMAG ) != SARMAG || + strncmp( ARMAG, buf, SARMAG ) ) + { + close( fd ); + return; + } + + offset = SARMAG; + + if ( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) + && !( memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) +#ifdef ARFZMAG + /* OSF also has a compressed format */ + && memcmp( ar_hdr.ar_fmag, ARFZMAG, SARFMAG ) +#endif + ) ) + { + char lar_name_[257]; + char * lar_name = lar_name_ + 1; + long lar_date; + long lar_size; + long lar_offset; + char * c; + char * src; + char * dest; + + strncpy( lar_name, ar_hdr.ar_name, sizeof(ar_hdr.ar_name) ); + + sscanf( ar_hdr.ar_date, "%ld", &lar_date ); + sscanf( ar_hdr.ar_size, "%ld", &lar_size ); + + if (ar_hdr.ar_name[0] == '/') + { + if (ar_hdr.ar_name[1] == '/') + { + /* this is the "string table" entry of the symbol table, + ** which holds strings of filenames that are longer than + ** 15 characters (ie. don't fit into a ar_name + */ + + string_table = (char *)BJAM_MALLOC_ATOMIC(lar_size); + lseek(fd, offset + SARHDR, 0); + if (read(fd, string_table, lar_size) != lar_size) + printf("error reading string table\n"); + } + else if (string_table && ar_hdr.ar_name[1] != ' ') + { + /* Long filenames are recognized by "/nnnn" where nnnn is + ** the offset of the string in the string table represented + ** in ASCII decimals. + */ + dest = lar_name; + lar_offset = atoi(lar_name + 1); + src = &string_table[lar_offset]; + while (*src != '/') + *dest++ = *src++; + *dest = '/'; + } + } + + c = lar_name - 1; + while ( ( *++c != ' ' ) && ( *c != '/' ) ) ; + *c = '\0'; + + if ( DEBUG_BINDSCAN ) + printf( "archive name %s found\n", lar_name ); + + sprintf( buf, "%s(%s)", archive, lar_name ); + + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + + offset += SARHDR + ( ( lar_size + 1 ) & ~1 ); + lseek( fd, offset, 0 ); + } + + if (string_table) + BJAM_FREE(string_table); + + close( fd ); + +# endif /* NO_AR */ + +} + +# else /* AIAMAG - RS6000 AIX */ + +static void file_archscan_small( + int fd, char const *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; + + if ( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ) + return; + + sscanf( fl_hdr.fl_fstmoff, "%ld", &offset ); + + if ( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while ( ( offset > 0 ) + && ( lseek( fd, offset, 0 ) >= 0 ) + && ( read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) ) ) + { + long lar_date; + int lar_namlen; + + sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen ); + sscanf( ar_hdr.hdr.ar_date , "%ld", &lar_date ); + sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset ); + + if ( !lar_namlen ) + continue; + + ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0'; + + sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name ); + + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + } +} + +/* Check for OS version which supports the big variant. */ +#ifdef AR_HSZ_BIG + +static void file_archscan_big( + int fd, char const *archive, scanback func, void *closure) +{ + struct fl_hdr_big fl_hdr; + + struct { + struct ar_hdr_big hdr; + char pad[ 256 ]; + } ar_hdr ; + + char buf[ MAXJPATH ]; + long long offset; + + if ( read( fd, (char *)&fl_hdr, FL_HSZ_BIG) != FL_HSZ_BIG) + return; + + sscanf( fl_hdr.fl_fstmoff, "%lld", &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, "%lld", &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 ); + } + +} + +#endif /* AR_HSZ_BIG */ + +void file_archscan(char *archive, scanback func, void *closure) +{ + int fd; + char fl_magic[SAIAMAG]; + + if (( fd = open(archive, O_RDONLY, 0)) < 0) + return; + + if (read( fd, fl_magic, SAIAMAG) != SAIAMAG + || lseek(fd, 0, SEEK_SET) == -1) + { + close(fd); + return; + } + + if (strncmp(AIAMAG, fl_magic, SAIAMAG) == 0) + { + /* read small variant */ + file_archscan_small(fd, archive, func, closure); + } +#ifdef AR_HSZ_BIG + else if (strncmp(AIAMAGBIG, fl_magic, SAIAMAG) == 0) + { + /* read big variant */ + file_archscan_big(fd, archive, func, closure); + } +#endif + + close( fd ); +} + +# endif /* AIAMAG - RS6000 AIX */ + +# endif /* USE_FILEUNIX */ diff --git a/jam-files/engine/filevms.c b/jam-files/engine/filevms.c new file mode 100644 index 00000000..d2ab2047 --- /dev/null +++ b/jam-files/engine/filevms.c @@ -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. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "filesys.h" +# include "pathsys.h" + +# ifdef OS_VMS + +/* + * filevms.c - scan directories and libaries on VMS + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! + * 05/03/96 (seiwald) - split into pathvms.c + */ + +# include <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*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +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 ** ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void +file_cvttime( + unsigned int *curtime, + time_t *unixtime ) +{ + static const size_t divisor = 10000000; + static unsigned int bastim[2] = { 0x4BEB4000, 0x007C9567 }; /* 1/1/1970 */ + int delta[2], remainder; + + lib$subx( curtime, bastim, delta ); + lib$ediv( &divisor, delta, unixtime, &remainder ); +} + +# define DEFAULT_FILE_SPECIFICATION "[]*.*;0" + +# define min( a,b ) ((a)<(b)?(a):(b)) + +void +file_dirscan( + char *dir, + scanback func, + void *closure ) +{ + + struct FAB xfab; + struct NAM xnam; + struct XABDAT xab; + char esa[256]; + char filename[256]; + string filename2[1]; + char dirname[256]; + register int status; + PATHNAME f; + + memset( (char *)&f, '\0', sizeof( f ) ); + + f.f_root.ptr = dir; + f.f_root.len = strlen( dir ); + + /* get the input file specification + */ + xnam = cc$rms_nam; + xnam.nam$l_esa = esa; + xnam.nam$b_ess = sizeof( esa ) - 1; + xnam.nam$l_rsa = filename; + xnam.nam$b_rss = min( sizeof( filename ) - 1, NAM$C_MAXRSS ); + + xab = cc$rms_xabdat; /* initialize extended attributes */ + xab.xab$b_cod = XAB$C_DAT; /* ask for date */ + xab.xab$l_nxt = NULL; /* terminate XAB chain */ + + xfab = cc$rms_fab; + xfab.fab$l_dna = DEFAULT_FILE_SPECIFICATION; + xfab.fab$b_dns = sizeof( DEFAULT_FILE_SPECIFICATION ) - 1; + xfab.fab$l_fop = FAB$M_NAM; + xfab.fab$l_fna = dir; /* address of file name */ + xfab.fab$b_fns = strlen( dir ); /* length of file name */ + xfab.fab$l_nam = &xnam; /* address of NAB block */ + xfab.fab$l_xab = (char *)&xab; /* address of XAB block */ + + + status = sys$parse( &xfab ); + + if ( DEBUG_BINDSCAN ) + printf( "scan directory %s\n", dir ); + + if ( !( status & 1 ) ) + return; + + + + /* Add bogus directory for [000000] */ + + if ( !strcmp( dir, "[000000]" ) ) + { + (*func)( closure, "[000000]", 1 /* time valid */, 1 /* old but true */ ); + } + + /* Add bogus directory for [] */ + + if ( !strcmp( dir, "[]" ) ) + { + (*func)( closure, "[]", 1 /* time valid */, 1 /* old but true */ ); + (*func)( closure, "[-]", 1 /* time valid */, 1 /* old but true */ ); + } + + string_new( filename2 ); + 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; + } + + string_truncate( filename2, 0 ); + 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->value, 1 /* time valid */, time ); + } + string_free( filename2 ); +} + +int +file_time( + char *filename, + time_t *time ) +{ + /* This should never be called, as all files are */ + /* timestampped in file_dirscan() and file_archscan() */ + return -1; +} + +static char *VMS_archive = 0; +static scanback VMS_func; +static void *VMS_closure; +static void *context; + +static int +file_archmember( + struct dsc$descriptor_s *module, + unsigned long *rfa ) +{ + static struct dsc$descriptor_s bufdsc = + {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; + + struct mhddef *mhd; + char filename[128]; + char buf[ MAXJPATH ]; + + int status; + time_t library_date; + + register int i; + register char *p; + + bufdsc.dsc$a_pointer = filename; + bufdsc.dsc$w_length = sizeof( filename ); + status = lbr$set_module( &context, rfa, &bufdsc, + &bufdsc.dsc$w_length, NULL ); + + if ( !(status & 1) ) + return ( 1 ); + + mhd = (struct mhddef *)filename; + + file_cvttime( &mhd->mhd$l_datim, &library_date ); + + for ( i = 0, p = module->dsc$a_pointer; i < module->dsc$w_length; ++i, ++p ) + filename[ i ] = *p; + + filename[ i ] = '\0'; + + sprintf( buf, "%s(%s.obj)", VMS_archive, filename ); + + (*VMS_func)( VMS_closure, buf, 1 /* time valid */, (time_t)library_date ); + + return ( 1 ); +} + + +void file_archscan( char * archive, scanback func, void * closure ) +{ + static struct dsc$descriptor_s library = + {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}; + + unsigned long lfunc = LBR$C_READ; + unsigned long typ = LBR$C_TYP_UNK; + unsigned long index = 1; + + register int status; + + VMS_archive = archive; + VMS_func = func; + VMS_closure = closure; + + status = lbr$ini_control( &context, &lfunc, &typ, NULL ); + if ( !( status & 1 ) ) + return; + + library.dsc$a_pointer = archive; + library.dsc$w_length = strlen( archive ); + + status = lbr$open( &context, &library, NULL, NULL, NULL, NULL, NULL ); + if ( !( status & 1 ) ) + return; + + (void) lbr$get_index( &context, &index, file_archmember, NULL ); + + (void) lbr$close( &context ); +} + +# endif /* VMS */ diff --git a/jam-files/engine/frames.c b/jam-files/engine/frames.c new file mode 100644 index 00000000..84889f09 --- /dev/null +++ b/jam-files/engine/frames.c @@ -0,0 +1,22 @@ +/* + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "frames.h" +# include "lists.h" + +void frame_init( FRAME* frame ) +{ + frame->prev = 0; + lol_init(frame->args); + frame->module = root_module(); + frame->rulename = "module scope"; + frame->procedure = 0; +} + +void frame_free( FRAME* frame ) +{ + lol_free( frame->args ); +} diff --git a/jam-files/engine/frames.h b/jam-files/engine/frames.h new file mode 100644 index 00000000..693d77fa --- /dev/null +++ b/jam-files/engine/frames.h @@ -0,0 +1,37 @@ +/* + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef FRAMES_DWA20011021_H +#define FRAMES_DWA20011021_H + +#include "lists.h" +#include "modules.h" + +typedef struct _PARSE PARSE; +typedef struct frame FRAME; + +struct frame +{ + FRAME * prev; + /* The nearest enclosing frame for which module->user_module is true. */ + FRAME * prev_user; + LOL args[ 1 ]; + module_t * module; + PARSE * procedure; + char * rulename; +}; + + +/* When call into Python is in progress, this variable points to the bjam frame + * that was current at the moment of call. When the call completes, the variable + * is not defined. Further, if Jam calls Python which calls Jam and so on, this + * variable only keeps the most recent Jam frame. + */ +extern struct frame * frame_before_python_call; + +void frame_init( FRAME * ); /* implemented in compile.c */ +void frame_free( FRAME * ); /* implemented in compile.c */ + +#endif diff --git a/jam-files/engine/glob.c b/jam-files/engine/glob.c new file mode 100644 index 00000000..527d6c80 --- /dev/null +++ b/jam-files/engine/glob.c @@ -0,0 +1,152 @@ +/* + * Copyright 1994 Christopher Seiwald. All rights reserved. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * glob.c - match a string against a simple pattern + * + * Understands the following patterns: + * + * * any number of characters + * ? any single character + * [a-z] any single character in the range a-z + * [^a-z] any single character not in the range a-z + * \x match x + * + * External functions: + * + * glob() - match a string against a simple pattern + * + * Internal functions: + * + * globchars() - build a bitlist to check for character group match + */ + +# include "jam.h" + +# define CHECK_BIT( tab, bit ) ( tab[ (bit)/8 ] & (1<<( (bit)%8 )) ) +# define BITLISTSIZE 16 /* bytes used for [chars] in compiled expr */ + +static void globchars( char * s, char * e, char * b ); + + +/* + * glob() - match a string against a simple pattern. + */ + +int glob( char * c, char * s ) +{ + char bitlist[ BITLISTSIZE ]; + char * here; + + for ( ; ; ) + switch ( *c++ ) + { + case '\0': + return *s ? -1 : 0; + + case '?': + if ( !*s++ ) + return 1; + break; + + case '[': + /* Scan for matching ]. */ + + here = c; + do if ( !*c++ ) return 1; + while ( ( here == c ) || ( *c != ']' ) ); + ++c; + + /* Build character class bitlist. */ + + globchars( here, c, bitlist ); + + if ( !CHECK_BIT( bitlist, *(unsigned char *)s ) ) + return 1; + ++s; + break; + + case '*': + here = s; + + while ( *s ) + ++s; + + /* Try to match the rest of the pattern in a recursive */ + /* call. If the match fails we'll back up chars, retrying. */ + + while ( s != here ) + { + int r; + + /* A fast path for the last token in a pattern. */ + r = *c ? glob( c, s ) : *s ? -1 : 0; + + if ( !r ) + return 0; + if ( r < 0 ) + return 1; + --s; + } + break; + + case '\\': + /* Force literal match of next char. */ + if ( !*c || ( *s++ != *c++ ) ) + return 1; + break; + + default: + if ( *s++ != c[ -1 ] ) + return 1; + break; + } +} + + +/* + * globchars() - build a bitlist to check for character group match. + */ + +static void globchars( char * s, char * e, char * b ) +{ + int neg = 0; + + memset( b, '\0', BITLISTSIZE ); + + if ( *s == '^' ) + { + ++neg; + ++s; + } + + while ( s < e ) + { + int c; + + if ( ( s + 2 < e ) && ( s[1] == '-' ) ) + { + for ( c = s[0]; c <= s[2]; ++c ) + b[ c/8 ] |= ( 1 << ( c % 8 ) ); + s += 3; + } + else + { + c = *s++; + b[ c/8 ] |= ( 1 << ( c % 8 ) ); + } + } + + if ( neg ) + { + int i; + for ( i = 0; i < BITLISTSIZE; ++i ) + b[ i ] ^= 0377; + } + + /* Do not include \0 in either $[chars] or $[^chars]. */ + b[0] &= 0376; +} diff --git a/jam-files/engine/hash.c b/jam-files/engine/hash.c new file mode 100644 index 00000000..fbd1a899 --- /dev/null +++ b/jam-files/engine/hash.c @@ -0,0 +1,459 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "hash.h" +# include "compile.h" +# include <assert.h> + +/* + * hash.c - simple in-memory hashing routines + * + * External routines: + * + * hashinit() - initialize a hash table, returning a handle + * hashitem() - find a record in the table, and optionally enter a new one + * hashdone() - free a hash table, given its handle + * + * Internal routines: + * + * hashrehash() - resize and rebuild hp->tab, the hash table + * + * 4/29/93 - ensure ITEM's are aligned + */ + +/* */ +#define HASH_DEBUG_PROFILE 1 +/* */ + +char *hashsccssid="@(#)hash.c 1.14 () 6/20/88"; + +/* Header attached to all data items entered into a hash table. */ + +struct hashhdr +{ + struct item * next; + unsigned int keyval; /* for quick comparisons */ +}; + +/* This structure overlays the one handed to hashenter(). Its 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 ] */ + ITEM *free; /* free list of items */ + char *next; /* where to put more ITEMs in lists[ list ] */ + int datalen; /* length of records in this hash table */ + int size; /* sizeof( ITEM ) + aligned datalen */ + int nel; /* total ITEMs held by all lists[] */ + int list; /* index into lists[] */ + + struct { + int nel; /* total ITEMs held by this list */ + char *base; /* base of ITEMs array */ + } lists[ MAX_LISTS ]; + } items; + + char * name; /* just for hashstats() */ +}; + +static void hashrehash( struct hash *hp ); +static void hashstat( struct hash *hp ); +static void * hash_mem_alloc(size_t datalen, size_t size); +static void hash_mem_free(size_t datalen, void * data); +#ifdef OPT_BOEHM_GC +static void hash_mem_finalizer(char * key, struct hash * hp); +#endif + +static unsigned int jenkins_one_at_a_time_hash(const unsigned char *key) +{ + unsigned int hash = 0; + + while ( *key ) + { + hash += *key++; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +/* +static unsigned int knuth_hash(const unsigned char *key) +{ + unsigned int keyval = *key; + while ( *key ) + keyval = keyval * 2147059363 + *key++; + return keyval; +} +*/ + +static unsigned int hash_keyval( const char * key_ ) +{ + /* + return knuth_hash((const unsigned char *)key_); + */ + return jenkins_one_at_a_time_hash((const unsigned char *)key_); +} + +#define hash_bucket(hp,keyval) ((hp)->tab.base + ( (keyval) % (hp)->tab.nel )) + +/* Find the hash item for the given data. Returns pointer to the + item and if given a pointer to the item before the found item. + If it's the first item in a bucket, there is no previous item, + and zero is returned for the previous item instead. +*/ +static ITEM * hash_search( + struct hash *hp, + unsigned int keyval, + const char * keydata, + ITEM * * previous ) +{ + ITEM * i = *hash_bucket(hp,keyval); + ITEM * p = 0; + + for ( ; i; i = i->hdr.next ) + { + if ( ( keyval == i->hdr.keyval ) && + !strcmp( i->data.key, keydata ) ) + { + if (previous) + { + *previous = p; + } + return i; + } + p = i; + } + + return 0; +} + +/* + * hash_free() - remove the given item from the table if it's there. + * Returns 1 if found, 0 otherwise. + * + * NOTE: 2nd argument is HASHDATA*, not HASHDATA** as elsewhere. + */ +int +hash_free( + register struct hash *hp, + HASHDATA *data) +{ + ITEM * i = 0; + ITEM * prev = 0; + unsigned int keyval = hash_keyval(data->key); + + i = hash_search( hp, keyval, data->key, &prev ); + if (i) + { + /* mark it free so we skip it during enumeration */ + i->data.key = 0; + /* unlink the record from the hash chain */ + if (prev) prev->hdr.next = i->hdr.next; + else *hash_bucket(hp,keyval) = i->hdr.next; + /* link it into the freelist */ + i->hdr.next = hp->items.free; + hp->items.free = i; + /* we have another item */ + hp->items.more++; + + return 1; + } + return 0; +} + +/* + * hashitem() - find a record in the table, and optionally enter a new one + */ + +int +hashitem( + register struct hash *hp, + HASHDATA **data, + int enter ) +{ + register ITEM *i; + char *b = (*data)->key; + unsigned int keyval = hash_keyval(b); + + #ifdef HASH_DEBUG_PROFILE + profile_frame prof[1]; + if ( DEBUG_PROFILE ) + profile_enter( 0, prof ); + #endif + + if ( enter && !hp->items.more ) + hashrehash( hp ); + + if ( !enter && !hp->items.nel ) + { + #ifdef HASH_DEBUG_PROFILE + if ( DEBUG_PROFILE ) + profile_exit( prof ); + #endif + return 0; + } + + i = hash_search( hp, keyval, (*data)->key, 0 ); + if (i) + { + *data = &i->data; + #ifdef HASH_DEBUG_PROFILE + if ( DEBUG_PROFILE ) profile_exit( prof ); + #endif + return !0; + } + + if ( enter ) + { + ITEM * * base = hash_bucket(hp,keyval); + + /* try to grab one from the free list */ + if ( hp->items.free ) + { + i = hp->items.free; + hp->items.free = i->hdr.next; + assert( i->data.key == 0 ); + } + else + { + 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; + #ifdef OPT_BOEHM_GC + if (sizeof(HASHDATA) == hp->items.datalen) + { + GC_REGISTER_FINALIZER(i->data.key,&hash_mem_finalizer,hp,0,0); + } + #endif + } + + #ifdef HASH_DEBUG_PROFILE + if ( DEBUG_PROFILE ) + profile_exit( prof ); + #endif + 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 *)hash_mem_alloc( hp->items.datalen, hp->items.more * hp->items.size ); + hp->items.free = 0; + + 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 ) + hash_mem_free( hp->items.datalen, (char *)hp->tab.base ); + + hp->tab.nel = hp->items.nel * hp->bloat; + hp->tab.base = (ITEM **)hash_mem_alloc( hp->items.datalen, 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; + /* code currently assumes rehashing only when there are no free items */ + assert( i->data.key != 0 ); + + i->hdr.next = *ip; + *ip = i; + } + } +} + +void hashenumerate( struct hash * hp, void (* f)( void *, void * ), void * data ) +{ + int i; + for ( i = 0; i <= hp->items.list; ++i ) + { + char * next = hp->items.lists[i].base; + int nel = hp->items.lists[i].nel; + if ( i == hp->items.list ) + nel -= hp->items.more; + + for ( ; nel--; next += hp->items.size ) + { + ITEM * i = (ITEM *)next; + if ( i->data.key != 0 ) /* DO not enumerate freed items. */ + f( &i->data, data ); + } + } +} + +/* --- */ + +# define ALIGNED(x) ( ( x + sizeof( ITEM ) - 1 ) & ~( sizeof( ITEM ) - 1 ) ) + +/* + * hashinit() - initialize a hash table, returning a handle + */ + +struct hash * +hashinit( + int datalen, + char *name ) +{ + struct hash *hp = (struct hash *)hash_mem_alloc( datalen, sizeof( *hp ) ); + + hp->bloat = 3; + hp->tab.nel = 0; + hp->tab.base = (ITEM **)0; + hp->items.more = 0; + hp->items.free = 0; + hp->items.datalen = datalen; + hp->items.size = sizeof( struct hashhdr ) + ALIGNED( datalen ); + hp->items.list = -1; + hp->items.nel = 0; + hp->inel = 11 /* 47 */; + 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 || DEBUG_PROFILE ) + hashstat( hp ); + + if ( hp->tab.base ) + hash_mem_free( hp->items.datalen, (char *)hp->tab.base ); + for ( i = 0; i <= hp->items.list; ++i ) + hash_mem_free( hp->items.datalen, hp->items.lists[i].base ); + hash_mem_free( hp->items.datalen, (char *)hp ); +} + +static void * hash_mem_alloc(size_t datalen, size_t size) +{ + if (sizeof(HASHDATA) == datalen) + { + return BJAM_MALLOC_RAW(size); + } + else + { + return BJAM_MALLOC(size); + } +} + +static void hash_mem_free(size_t datalen, void * data) +{ + if (sizeof(HASHDATA) == datalen) + { + BJAM_FREE_RAW(data); + } + else + { + BJAM_FREE(data); + } +} + +#ifdef OPT_BOEHM_GC +static void hash_mem_finalizer(char * key, struct hash * hp) +{ + HASHDATA d; + d.key = key; + hash_free(hp,&d); +} +#endif + + +/* ---- */ + +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; + int 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+%luK) items+table+hash, %f density\n", + hp->name, + count, + hp->items.nel, + hp->tab.nel, + hp->items.nel * hp->items.size / 1024, + (long unsigned)hp->tab.nel * sizeof( ITEM ** ) / 1024, + (float)count / (float)sets ); +} diff --git a/jam-files/engine/hash.h b/jam-files/engine/hash.h new file mode 100644 index 00000000..7195b414 --- /dev/null +++ b/jam-files/engine/hash.h @@ -0,0 +1,25 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * hash.h - simple in-memory hashing routines + */ + +#ifndef BOOST_JAM_HASH_H +#define BOOST_JAM_HASH_H + +typedef struct hashdata HASHDATA; + +struct hash * hashinit ( int datalen, char * name ); +int hashitem ( struct hash * hp, HASHDATA * * data, int enter ); +void hashdone ( struct hash * hp ); +void hashenumerate( struct hash * hp, void (* f)( void *, void * ), void * data ); +int hash_free ( struct hash * hp, HASHDATA * data); + +#define hashenter( hp, data ) ( !hashitem( hp, data, !0 ) ) +#define hashcheck( hp, data ) hashitem( hp, data, 0 ) + +#endif diff --git a/jam-files/engine/hcache.c b/jam-files/engine/hcache.c new file mode 100644 index 00000000..70bb798c --- /dev/null +++ b/jam-files/engine/hcache.c @@ -0,0 +1,434 @@ +/* + * 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" + +#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 + */ + +typedef struct hcachedata HCACHEDATA ; + +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 */ + HCACHEDATA * next; +}; + + +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 not 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 ); + /* Do not expect the cache file to be generated, so pass 0 as the + * third argument to search. Expect the location to be specified via + * LOCATE, so pass 0 as the fourth arugment. + */ + t->boundname = search( t->name, &t->time, 0, 0 ); + popsettings( t->settings ); + + if ( hcachevar ) + name = copystr( t->boundname ); + } + } + return name; +} + + +/* + * Return the maximum age a cache entry can have before it is purged ftom 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 not 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 = (char *)BJAM_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, char const * s ) +{ + if ( !s ) + s = ""; + fprintf( f, "%lu\t%s\n", (long unsigned)strlen( s ), s ); +} + + +void hcache_init() +{ + HCACHEDATA cachedata; + HCACHEDATA * 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; + int 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 ); + } + 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 ); + } + 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", (long unsigned) list_length( c->includes ) ); + sprintf( hdrscan_count_str, "%lu", (long unsigned) list_length( c->hdrscan ) ); + sprintf( time_str, "%lu", (long unsigned) c->time ); + sprintf( age_str, "%lu", (long unsigned) 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, int rec, regexp * re[], LIST * hdrscan ) +{ + HCACHEDATA cachedata; + HCACHEDATA * c = &cachedata; + + LIST * l = 0; + + ++queries; + + 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 (0, t->boundname, rec, re); + + c->time = t->time; + c->age = 0; + c->includes = list_copy (0, l); + c->hdrscan = list_copy(0, hdrscan); + + return l; +} + +#endif diff --git a/jam-files/engine/hcache.h b/jam-files/engine/hcache.h new file mode 100644 index 00000000..c316e3bc --- /dev/null +++ b/jam-files/engine/hcache.h @@ -0,0 +1,18 @@ +/* + * This file is not part of Jam + */ + +/* + * hcache.h - handle #includes in source files + */ +#ifndef HCACHE_H +# define HCACHE_H + +# include "regexp.h" +# include "lists.h" + +void hcache_init(void); +void hcache_done(void); +LIST *hcache(TARGET *t, int rec, regexp *re[], LIST *hdrscan); + +#endif diff --git a/jam-files/engine/hdrmacro.c b/jam-files/engine/hdrmacro.c new file mode 100644 index 00000000..43031d48 --- /dev/null +++ b/jam-files/engine/hdrmacro.c @@ -0,0 +1,137 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "compile.h" +# include "rules.h" +# include "variable.h" +# include "regexp.h" +# include "hdrmacro.h" +# include "hash.h" +# include "newstr.h" +# include "strings.h" + +/* + * hdrmacro.c - handle header files that define macros used in + * #include statements. + * + * we look for lines like "#define MACRO <....>" or '#define MACRO " "' + * in the target file. When found, we + * + * we then phony up a rule invocation like: + * + * $(HDRRULE) <target> : <resolved included files> ; + * + * External routines: + * headers1() - scan a target for "#include MACRO" lines and try + * to resolve them when needed + * + * 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. + */ + +/* this type is used to store a dictionary of file header macros */ +typedef struct header_macro +{ + char * symbol; + char * filename; /* we could maybe use a LIST here ?? */ +} HEADER_MACRO; + +static struct hash * header_macros_hash = 0; + + +/* + * headers() - scan a target for include files and call HDRRULE + */ + +# define MAXINC 10 + +void +macro_headers( TARGET *t ) +{ + static regexp *re = 0; + FILE *f; + char buf[ 1024 ]; + + if ( DEBUG_HEADER ) + printf( "macro header scan for %s\n", t->name ); + + /* this regexp is used to detect lines of the form */ + /* "#define MACRO <....>" or "#define MACRO "....." */ + /* in the header macro files.. */ + if ( re == 0 ) + { + re = regex_compile( + "^[ ]*#[ ]*define[ ]*([A-Za-z][A-Za-z0-9_]*)[ ]*" + "[<\"]([^\">]*)[\">].*$" ); + } + + if ( !( f = fopen( t->boundname, "r" ) ) ) + return; + + while ( fgets( buf, sizeof( buf ), f ) ) + { + HEADER_MACRO var; + HEADER_MACRO *v = &var; + + if ( regexec( re, buf ) && re->startp[1] ) + { + /* we detected a line that looks like "#define MACRO filename */ + re->endp[1][0] = '\0'; + re->endp[2][0] = '\0'; + + if ( DEBUG_HEADER ) + printf( "macro '%s' used to define filename '%s' in '%s'\n", + re->startp[1], re->startp[2], t->boundname ); + + /* add macro definition to hash table */ + if ( !header_macros_hash ) + header_macros_hash = hashinit( sizeof( HEADER_MACRO ), "hdrmacros" ); + + v->symbol = re->startp[1]; + v->filename = 0; + if ( hashenter( header_macros_hash, (HASHDATA **)&v ) ) + { + v->symbol = newstr( re->startp[1] ); /* never freed */ + v->filename = newstr( re->startp[2] ); /* never freed */ + } + /* XXXX: FOR NOW, WE IGNORE MULTIPLE MACRO DEFINITIONS !! */ + /* WE MIGHT AS WELL USE A LIST TO STORE THEM.. */ + } + } + + fclose( f ); +} + + +char * macro_header_get( const char * macro_name ) +{ + HEADER_MACRO var; + HEADER_MACRO * v = &var; + + v->symbol = (char* )macro_name; + + if ( header_macros_hash && hashcheck( header_macros_hash, (HASHDATA **)&v ) ) + { + if ( DEBUG_HEADER ) + printf( "### macro '%s' evaluated to '%s'\n", macro_name, v->filename ); + return v->filename; + } + return 0; +} diff --git a/jam-files/engine/hdrmacro.h b/jam-files/engine/hdrmacro.h new file mode 100644 index 00000000..08cc1116 --- /dev/null +++ b/jam-files/engine/hdrmacro.h @@ -0,0 +1,14 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * hdrmacro.h - parses header files for #define MACRO <filename> or + * #define MACRO "filename" definitions + */ + +void macro_headers( TARGET *t ); + +char* macro_header_get( const char* macro_name ); diff --git a/jam-files/engine/headers.c b/jam-files/engine/headers.c new file mode 100644 index 00000000..b9d8f637 --- /dev/null +++ b/jam-files/engine/headers.c @@ -0,0 +1,203 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# 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 "hdrmacro.h" +# include "newstr.h" + +#ifdef OPT_HEADER_CACHE_EXT +# include "hcache.h" +#endif + +/* + * 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. + */ + +#ifndef OPT_HEADER_CACHE_EXT +static LIST *headers1( LIST *l, char *file, int rec, regexp *re[]); +#endif + +/* + * headers() - scan a target for include files and call HDRRULE + */ + +# define MAXINC 10 + +void +headers( TARGET *t ) +{ + LIST * hdrscan; + LIST * hdrrule; + #ifndef OPT_HEADER_CACHE_EXT + LIST * headlist = 0; + #endif + regexp * re[ MAXINC ]; + int rec = 0; + + if ( !( hdrscan = var_get( "HDRSCAN" ) ) || + !( hdrrule = var_get( "HDRRULE" ) ) ) + return; + + if ( DEBUG_HEADER ) + printf( "header scan %s\n", t->name ); + + /* Compile all regular expressions in HDRSCAN */ + while ( ( rec < MAXINC ) && hdrscan ) + { + re[ rec++ ] = regex_compile( hdrscan->string ); + hdrscan = list_next( hdrscan ); + } + + /* Doctor up call to HDRRULE rule */ + /* Call headers1() to get LIST of included files. */ + { + FRAME frame[1]; + frame_init( frame ); + lol_add( frame->args, list_new( L0, t->name ) ); +#ifdef OPT_HEADER_CACHE_EXT + lol_add( frame->args, hcache( t, rec, re, hdrscan ) ); +#else + lol_add( frame->args, headers1( headlist, t->boundname, rec, re ) ); +#endif + + if ( lol_get( frame->args, 1 ) ) + { + /* The third argument to HDRRULE is the bound name of + * $(<) */ + lol_add( frame->args, list_new( L0, t->boundname ) ); + + list_free( evaluate_rule( hdrrule->string, frame ) ); + } + + /* Clean up. */ + frame_free( frame ); + } +} + + +/* + * headers1() - using regexp, scan a file and build include LIST. + */ + +#ifdef OPT_HEADER_CACHE_EXT +LIST * +#else +static LIST * +#endif +headers1( + LIST * l, + char * file, + int rec, + regexp * re[] ) +{ + FILE * f; + char buf[ 1024 ]; + int i; + static regexp * re_macros = 0; + +#ifdef OPT_IMPROVED_PATIENCE_EXT + static int count = 0; + ++count; + if ( ((count == 100) || !( count % 1000 )) && DEBUG_MAKE ) + printf("...patience...\n"); +#endif + + /* the following regexp is used to detect cases where a */ + /* file is included through a line line "#include MACRO" */ + if ( re_macros == 0 ) + re_macros = regex_compile( + "^[ ]*#[ ]*include[ ]*([A-Za-z][A-Za-z0-9_]*).*$" ); + + if ( !( f = fopen( file, "r" ) ) ) + return l; + + while ( fgets( buf, sizeof( buf ), f ) ) + { + int size = strlen( buf ); + /* Remove trailing \r and \n, if any. */ + while ( ( size > 0 ) && + ( buf[ size - 1 ] == '\n' ) && + ( buf[ size - 1 ] == '\r' ) ) + { + buf[ size - 1 ] = '\0'; + --size; + } + + for ( i = 0; i < rec; ++i ) + if ( regexec( re[i], buf ) && re[i]->startp[1] ) + { + re[i]->endp[1][0] = '\0'; + + if ( DEBUG_HEADER ) + printf( "header found: %s\n", re[i]->startp[1] ); + + l = list_new( l, newstr( re[i]->startp[1] ) ); + } + + /* special treatment for #include MACRO */ + if ( regexec( re_macros, buf ) && re_macros->startp[1] ) + { + char* header_filename; + + re_macros->endp[1][0] = '\0'; + + if ( DEBUG_HEADER ) + printf( "macro header found: %s", re_macros->startp[1] ); + + header_filename = macro_header_get( re_macros->startp[1] ); + if ( header_filename ) + { + if ( DEBUG_HEADER ) + printf( " resolved to '%s'\n", header_filename ); + l = list_new( l, newstr( header_filename ) ); + } + else + { + if ( DEBUG_HEADER ) + printf( " ignored !!\n" ); + } + } + } + + fclose( f ); + + return l; +} + + +void regerror( char * s ) +{ + printf( "re error %s\n", s ); +} diff --git a/jam-files/engine/headers.h b/jam-files/engine/headers.h new file mode 100644 index 00000000..624475fe --- /dev/null +++ b/jam-files/engine/headers.h @@ -0,0 +1,16 @@ +/* + * 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 +struct regexp; +LIST *headers1( LIST *l, char *file, int rec, struct regexp *re[] ); +#endif diff --git a/jam-files/engine/jam.c b/jam-files/engine/jam.c new file mode 100644 index 00000000..e11d082b --- /dev/null +++ b/jam-files/engine/jam.c @@ -0,0 +1,632 @@ +/* + * /+\ + * +\ 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. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * 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 + * hdrmacro.c - handle header file parsing for filename macro definitions + * headers.c - handle #includes in source files + * jambase.c - compilable copy of Jambase + * jamgram.y - jam grammar + * lists.c - maintain lists of strings + * make.c - bring a target up to date, once rules are in place + * make1.c - execute command to bring targets up to date + * newstr.c - string manipulation routines + * option.c - command line option processing + * parse.c - make and destroy parse trees as driven by the parser + * path*.c - manipulate file names on * + * hash.c - simple in-memory hashing routines + * regexp.c - Henry Spencer's regexp + * rules.c - access to RULEs, TARGETs, and ACTIONs + * scan.c - the jam yacc scanner + * search.c - find a target along $(SEARCH) or $(LOCATE) + * timestamp.c - get the timestamp of a file or archive member + * variable.c - handle jam multi-element variables + * + * 05/04/94 (seiwald) - async multiprocess (-j) support + * 02/08/95 (seiwald) - -n implies -d2. + * 02/22/95 (seiwald) - -v for version info. + * 09/11/00 (seiwald) - PATCHLEVEL folded into VERSION. + * 01/10/01 (seiwald) - pathsys.h split from filesys.h + */ + + +#include "jam.h" +#include "option.h" +#include "patchlevel.h" + +/* These get various function declarations. */ +#include "lists.h" +#include "parse.h" +#include "variable.h" +#include "compile.h" +#include "builtins.h" +#include "rules.h" +#include "newstr.h" +#include "scan.h" +#include "timestamp.h" +#include "make.h" +#include "strings.h" +#include "expand.h" +#include "filesys.h" +#include "output.h" + +/* Macintosh is "special" */ +#ifdef OS_MAC + #include <QuickDraw.h> +#endif + +/* And UNIX for this. */ +#ifdef unix + #include <sys/utsname.h> + #include <signal.h> +#endif + +struct globs globs = +{ + 0, /* noexec */ + 1, /* jobs */ + 0, /* quitquick */ + 0, /* newestfirst */ + 0, /* pipes action stdout and stderr merged to action output */ +#ifdef OS_MAC + { 0, 0 }, /* debug - suppress tracing output */ +#else + { 0, 1 }, /* debug ... */ +#endif + 0, /* output commands, not run them */ + 0 /* action timeout */ +}; + +/* Symbols to be defined as true for use in Jambase. */ +static char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 }; + + +/* Known for sure: + * mac needs arg_enviro + * OS2 needs extern environ + */ + +#ifdef OS_MAC + #define use_environ arg_environ + #ifdef MPW + QDGlobals qd; + #endif +#endif + +/* on Win32-LCC */ +#if defined( OS_NT ) && defined( __LCC__ ) + #define use_environ _environ +#endif + +# if defined( __MWERKS__) + #define use_environ _environ + extern char * * _environ; +#endif + +#ifndef use_environ + #define use_environ environ + #if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT ) + extern char **environ; + #endif +#endif + +#if YYDEBUG != 0 + extern int yydebug; +#endif + +#ifndef NDEBUG +static void run_unit_tests() +{ +#if defined( USE_EXECNT ) + extern void execnt_unit_test(); + execnt_unit_test(); +#endif + string_unit_test(); + var_expand_unit_test(); +} +#endif + +int anyhow = 0; + +#ifdef HAVE_PYTHON + extern PyObject * bjam_call ( PyObject * self, PyObject * args ); + extern PyObject * bjam_import_rule ( PyObject * self, PyObject * args ); + extern PyObject * bjam_define_action( PyObject * self, PyObject * args ); + extern PyObject * bjam_variable ( PyObject * self, PyObject * args ); + extern PyObject * bjam_backtrace ( PyObject * self, PyObject * args ); + extern PyObject * bjam_caller ( PyObject * self, PyObject * args ); +#endif + +char *saved_argv0; + +int main( int argc, char * * argv, char * * arg_environ ) +{ + int n; + char * s; + struct bjam_option optv[N_OPTS]; + char const * all = "all"; + int status; + int arg_c = argc; + char * * arg_v = argv; + char const * progname = argv[0]; + + saved_argv0 = argv[0]; + + BJAM_MEM_INIT(); + +# ifdef OS_MAC + InitGraf(&qd.thePort); +# endif + + --argc; + ++argv; + + if ( getoptions( argc, argv, "-:l:d:j:p:f:gs:t:ano:qv", optv ) < 0 ) + { + printf( "\nusage: %s [ options ] targets...\n\n", progname ); + + printf( "-a Build all targets, even if they are current.\n" ); + printf( "-dx Set the debug level to x (0-9).\n" ); + printf( "-fx Read x instead of Jambase.\n" ); + /* printf( "-g Build from newest sources first.\n" ); */ + printf( "-jx Run up to x shell commands concurrently.\n" ); + printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" ); + printf( "-n Don't actually execute the updating actions.\n" ); + printf( "-ox Write the updating actions to file x.\n" ); + printf( "-px x=0, pipes action stdout and stderr merged into action output.\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" ); + printf( "--x Option is ignored.\n\n" ); + + exit( EXITBAD ); + } + + /* Version info. */ + if ( ( s = getoptval( optv, 'v', 0 ) ) ) + { + printf( "Boost.Jam " ); + printf( "Version %s. %s.\n", VERSION, OSMINOR ); + printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. \n" ); + printf( " Copyright 2001 David Turner.\n" ); + printf( " Copyright 2001-2004 David Abrahams.\n" ); + printf( " Copyright 2002-2008 Rene Rivera.\n" ); + printf( " Copyright 2003-2008 Vladimir Prus.\n" ); + + return EXITOK; + } + + /* Pick up interesting options. */ + if ( ( s = getoptval( optv, 'n', 0 ) ) ) + globs.noexec++, globs.debug[2] = 1; + + if ( ( s = getoptval( optv, 'p', 0 ) ) ) + { + /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action + * stdout and stderr. + */ + globs.pipe_action = atoi( s ); + if ( ( 3 < globs.pipe_action ) || ( globs.pipe_action < 0 ) ) + { + printf( + "Invalid pipe descriptor '%d', valid values are -p[0..3].\n", + globs.pipe_action ); + exit( EXITBAD ); + } + } + + 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 (globs.jobs == 0) + { + printf("Invalid value for the '-j' option.\n"); + exit(EXITBAD); + } + } + + if ( ( s = getoptval( optv, 'g', 0 ) ) ) + globs.newestfirst = 1; + + if ( ( s = getoptval( optv, 'l', 0 ) ) ) + globs.timeout = atoi( s ); + + /* Turn on/off debugging */ + for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n ) + { + int i; + + /* First -d, turn off defaults. */ + if ( !n ) + for ( i = 0; i < DEBUG_MAX; ++i ) + globs.debug[i] = 0; + + i = atoi( s ); + + if ( ( i < 0 ) || ( i >= DEBUG_MAX ) ) + { + printf( "Invalid debug level '%s'.\n", s ); + continue; + } + + /* n turns on levels 1-n. */ + /* +n turns on level n. */ + if ( *s == '+' ) + globs.debug[i] = 1; + else while ( i ) + globs.debug[i--] = 1; + } + + { + PROFILE_ENTER( MAIN ); + +#ifdef HAVE_PYTHON + { + PROFILE_ENTER( MAIN_PYTHON ); + Py_Initialize(); + { + static PyMethodDef BjamMethods[] = { + {"call", bjam_call, METH_VARARGS, + "Call the specified bjam rule."}, + {"import_rule", bjam_import_rule, METH_VARARGS, + "Imports Python callable to bjam."}, + {"define_action", bjam_define_action, METH_VARARGS, + "Defines a command line action."}, + {"variable", bjam_variable, METH_VARARGS, + "Obtains a variable from bjam's global module."}, + {"backtrace", bjam_backtrace, METH_VARARGS, + "Returns bjam backtrace from the last call into Python."}, + {"caller", bjam_caller, METH_VARARGS, + "Returns the module from which the last call into Python is made."}, + {NULL, NULL, 0, NULL} + }; + + Py_InitModule( "bjam", BjamMethods ); + } + PROFILE_EXIT( MAIN_PYTHON ); + } +#endif + +#ifndef NDEBUG + run_unit_tests(); +#endif +#if YYDEBUG != 0 + if ( DEBUG_PARSE ) + yydebug = 1; +#endif + + /* Set JAMDATE. */ + var_set( "JAMDATE", list_new( L0, outf_time(time(0)) ), VAR_SET ); + + /* Set JAM_VERSION. */ + var_set( "JAM_VERSION", + list_new( list_new( list_new( L0, + newstr( VERSION_MAJOR_SYM ) ), + newstr( VERSION_MINOR_SYM ) ), + newstr( VERSION_PATCH_SYM ) ), + VAR_SET ); + + /* Set JAMUNAME. */ +#ifdef unix + { + struct utsname u; + + if ( uname( &u ) >= 0 ) + { + var_set( "JAMUNAME", + list_new( + list_new( + list_new( + list_new( + list_new( L0, + newstr( u.sysname ) ), + newstr( u.nodename ) ), + newstr( u.release ) ), + newstr( u.version ) ), + newstr( u.machine ) ), VAR_SET ); + } + } +#endif /* unix */ + + /* Load up environment variables. */ + + /* First into the global module, with splitting, for backward + * compatibility. + */ + var_defines( use_environ, 1 ); + + /* Then into .ENVIRON, without splitting. */ + enter_module( bindmodule(".ENVIRON") ); + var_defines( use_environ, 0 ); + exit_module( bindmodule(".ENVIRON") ); + + /* + * Jam defined variables OS & OSPLAT. We load them after environment, so + * that setting OS in environment does not change Jam's notion of the + * current platform. + */ + var_defines( othersyms, 1 ); + + /* Load up variables set on command line. */ + for ( n = 0; ( s = getoptval( optv, 's', n ) ); ++n ) + { + char *symv[2]; + symv[ 0 ] = s; + symv[ 1 ] = 0; + var_defines( symv, 1 ); + enter_module( bindmodule(".ENVIRON") ); + var_defines( symv, 0 ); + exit_module( bindmodule(".ENVIRON") ); + } + + /* Set the ARGV to reflect the complete list of arguments of invocation. + */ + for ( n = 0; n < arg_c; ++n ) + var_set( "ARGV", list_new( L0, newstr( arg_v[n] ) ), VAR_APPEND ); + + /* Initialize built-in rules. */ + load_builtins(); + + /* Add the targets in the command line to the update list. */ + for ( n = 1; n < arg_c; ++n ) + { + if ( arg_v[ n ][ 0 ] == '-' ) + { + char * f = "-:l:d:j:f:gs:t:ano:qv"; + for ( ; *f; ++f ) if ( *f == arg_v[ n ][ 1 ] ) break; + if ( ( f[ 1 ] == ':' ) && ( arg_v[ n ][ 2 ] == '\0' ) ) ++n; + } + else + { + mark_target_for_updating( arg_v[ n ] ); + } + } + + if (!targets_to_update()) + mark_target_for_updating("all"); + + /* Parse ruleset. */ + { + FRAME frame[ 1 ]; + frame_init( frame ); + for ( n = 0; ( s = getoptval( optv, 'f', n ) ); ++n ) + parse_file( s, frame ); + + if ( !n ) + parse_file( "+", frame ); + } + + status = yyanyerrors(); + + /* Manually touch -t targets. */ + for ( n = 0; ( s = getoptval( optv, 't', n ) ); ++n ) + touch_target( 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; + } + + /* The build system may set the PARALLELISM variable to override -j + options. */ + { + LIST *p = L0; + p = var_get ("PARALLELISM"); + if (p) + { + int j = atoi (p->string); + if (j == -1) + { + printf( "Invalid value of PARALLELISM: %s\n", p->string); + } + else + { + globs.jobs = j; + } + } + } + + /* KEEP_GOING overrides -q option. */ + { + LIST *p = L0; + p = var_get ("KEEP_GOING"); + if (p) + { + int v = atoi (p->string); + if (v == 0) + globs.quitquick = 1; + else + globs.quitquick = 0; + } + } + + /* Now make target. */ + { + PROFILE_ENTER( MAIN_MAKE ); + + LIST * targets = targets_to_update(); + if (targets) + { + int targets_count = list_length( targets ); + const char * * targets2 = (const char * *) + BJAM_MALLOC( targets_count * sizeof( char * ) ); + int n = 0; + for ( ; targets; targets = list_next( targets ) ) + targets2[ n++ ] = targets->string; + status |= make( targets_count, targets2, anyhow ); + free( targets ); + } + else + { + status = last_update_now_status; + } + + PROFILE_EXIT( MAIN_MAKE ); + } + + PROFILE_EXIT( MAIN ); + } + + if ( DEBUG_PROFILE ) + profile_dump(); + + /* Widely scattered cleanup. */ + var_done(); + file_done(); + rules_done(); + stamps_done(); + str_done(); + + /* Close cmdout. */ + if ( globs.cmdout ) + fclose( globs.cmdout ); + +#ifdef HAVE_PYTHON + Py_Finalize(); +#endif + + BJAM_MEM_CLOSE(); + + return status ? EXITBAD : EXITOK; +} + +#if defined(_WIN32) +#include <windows.h> +char *executable_path(char *argv0) { + char buf[1024]; + DWORD ret = GetModuleFileName(NULL, buf, sizeof(buf)); + if (ret == 0 || ret == sizeof(buf)) return NULL; + return strdup (buf); +} +#elif defined(__APPLE__) /* Not tested */ +#include <mach-o/dyld.h> +char *executable_path(char *argv0) { + char buf[1024]; + uint32_t size = sizeof(buf); + int ret = _NSGetExecutablePath(buf, &size); + if (ret != 0) return NULL; + return strdup(buf); +} +#elif defined(sun) || defined(__sun) /* Not tested */ +#include <stdlib.h> + +char *executable_path(char *argv0) { + return strdup(getexecname()); +} +#elif defined(__FreeBSD__) +#include <sys/sysctl.h> +char *executable_path(char *argv0) { + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + char buf[1024]; + size_t size = sizeof(buf); + sysctl(mib, 4, buf, &size, NULL, 0); + if (size == 0 || size == sizeof(buf)) return NULL; + return strndup(buf, size); +} +#elif defined(__linux__) +#include <unistd.h> +char *executable_path(char *argv0) { + char buf[1024]; + ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)); + if (ret == 0 || ret == sizeof(buf)) return NULL; + return strndup(buf, ret); +} +#else +char *executable_path(char *argv0) { + /* If argv0 is absolute path, assume it's the right absolute path. */ + if (argv0[0] == "/") + return strdup(argv0); + return NULL; +} +#endif diff --git a/jam-files/engine/jam.h b/jam-files/engine/jam.h new file mode 100644 index 00000000..73a7a04c --- /dev/null +++ b/jam-files/engine/jam.h @@ -0,0 +1,579 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * jam.h - includes and globals for jam + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 04/21/94 (seiwald) - DGUX is __DGUX__, not just __DGUX. + * 05/04/94 (seiwald) - new globs.jobs (-j jobs) + * 11/01/94 (wingerd) - let us define path of Jambase at compile time. + * 12/30/94 (wingerd) - changed command buffer size for NT (MS-DOS shell). + * 02/22/95 (seiwald) - Jambase now in /usr/local/lib. + * 04/30/95 (seiwald) - FreeBSD added. Live Free or Die. + * 05/10/95 (seiwald) - SPLITPATH character set up here. + * 08/20/95 (seiwald) - added LINUX. + * 08/21/95 (seiwald) - added NCR. + * 10/23/95 (seiwald) - added SCO. + * 01/03/96 (seiwald) - SINIX (nixdorf) added. + * 03/13/96 (seiwald) - Jambase now compiled in; remove JAMBASE variable. + * 04/29/96 (seiwald) - AIX now has 31 and 42 OSVERs. + * 11/21/96 (peterk) - added BeOS with MW CW mwcc + * 12/21/96 (seiwald) - OSPLAT now defined for NT. + * 07/19/99 (sickel) - Mac OS X Server and Client support added + * 02/18/00 (belmonte)- Support for Cygwin. + * 09/12/00 (seiwald) - OSSYMS split to OSMAJOR/OSMINOR/OSPLAT + * 12/29/00 (seiwald) - OSVER dropped. + */ + +#ifndef JAM_H_VP_2003_08_01 +#define JAM_H_VP_2003_08_01 + +#ifdef HAVE_PYTHON +#include <Python.h> +#endif + +/* Assume popen support is available unless known otherwise. */ +#define HAVE_POPEN 1 + +/* + * VMS, OPENVMS + */ + +#ifdef VMS + +#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 <unistd.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 + +/* This may be inaccurate. */ +#ifndef __DECC +#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> +#ifndef __MWERKS__ + #include <memory.h> +#endif +#include <signal.h> +#include <string.h> +#include <time.h> + +#define OSMAJOR "NT=true" +#define OSMINOR "OS=NT" +#define OS_NT +#define SPLITPATH ';' +/* Windows NT 3.51 only allows 996 chars per line, but we deal with the problem + * in "execnt.c". + */ +#define MAXLINE (maxline()) /* longest 'together' actions */ +#define USE_EXECNT +#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 + +/* Metrowerks Standard Library on Windows. */ + +#ifdef __MSL__ + #undef HAVE_POPEN +#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 + +#ifdef __EMX__ + #define USE_FILEUNIX +#endif + +#endif + +/* + * Macintosh MPW + */ + +#ifdef macintosh + +#include <time.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define OSMAJOR "MAC=true" +#define OSMINOR "OS=MAC" +#define OS_MAC +#define SPLITPATH ',' + +#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 MAXLINE 23552 /* 24k - 1k, longest 'together' actions */ + #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 +#if defined(__cygwin__) || defined(__CYGWIN__) + #define OSMINOR "OS=CYGWIN" + #define OS_CYGWIN +#endif +#if defined(__FreeBSD__) && !defined(__DragonFly__) + #define OSMINOR "OS=FREEBSD" + #define OS_FREEBSD +#endif +#ifdef __DragonFly__ + #define OSMINOR "OS=DRAGONFLYBSD" + #define OS_DRAGONFLYBSD +#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__ + #define unix + #ifdef __QNXNTO__ + #define OSMINOR "OS=QNXNTO" + #define OS_QNXNTO + #else + #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__ + #ifndef unix + #define unix + #endif + #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 +#ifdef __OpenBSD__ + #define OSMINOR "OS=OPENBSD" + #define OS_OPENBSD + #define unix +#endif +#if defined (__FreeBSD_kernel__) && !defined(__FreeBSD__) + #define OSMINOR "OS=KFREEBSD" + #define OS_KFREEBSD +#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 <unistd.h> + +#ifndef OS_QNX + #include <memory.h> +#endif + +#ifndef OS_ULTRIX + #include <stdlib.h> +#endif + +#if !defined( OS_BSDI ) && \ + !defined( OS_FREEBSD ) && \ + !defined( OS_DRAGONFLYBSD ) && \ + !defined( OS_NEXT ) && \ + !defined( OS_MACHTEN ) && \ + !defined( OS_MACOSX ) && \ + !defined( OS_RHAPSODY ) && \ + !defined( OS_MVS ) && \ + !defined( OS_OPENBSD ) + #include <malloc.h> +#endif + +#endif + +/* + * OSPLAT definitions - suppressed when it is a one-of-a-kind. + */ + +#if defined( _M_PPC ) || \ + defined( PPC ) || \ + defined( ppc ) || \ + defined( __powerpc__ ) || \ + defined( __ppc__ ) + #define OSPLAT "OSPLAT=PPC" +#endif + +#if defined( _ALPHA_ ) || \ + defined( __alpha__ ) + #define OSPLAT "OSPLAT=AXP" +#endif + +#if defined( _i386_ ) || \ + defined( __i386__ ) || \ + defined( __i386 ) || \ + defined( _M_IX86 ) + #define OSPLAT "OSPLAT=X86" +#endif + +#if defined( __ia64__ ) || \ + defined( __IA64__ ) || \ + defined( __ia64 ) + #define OSPLAT "OSPLAT=IA64" +#endif + +#if defined( __x86_64__ ) || \ + defined( __amd64__ ) || \ + defined( _M_AMD64 ) + #define OSPLAT "OSPLAT=X86_64" +#endif + + +#if defined( __sparc__ ) || \ + defined( __sparc ) + #define OSPLAT "OSPLAT=SPARC" +#endif + +#ifdef __mips__ + #define OSPLAT "OSPLAT=MIPS" +#endif + +#ifdef __arm__ + #define OSPLAT "OSPLAT=ARM" +#endif + +#ifdef __s390__ + #define OSPLAT "OSPLAT=390" +#endif + +#ifdef __hppa + #define OSPLAT "OSPLAT=PARISC" +#endif + +#ifndef OSPLAT + #define OSPLAT "" +#endif + +/* + * Jam implementation misc. + */ + +#ifndef MAXLINE + #define MAXLINE 102400 /* longest 'together' actions' */ +#endif + +#ifndef EXITOK + #define EXITOK 0 + #define EXITBAD 1 +#endif + +#ifndef SPLITPATH + #define SPLITPATH ':' +#endif + +/* You probably do not need to muck with these. */ + +#define MAXSYM 1024 /* longest symbol in the environment */ +#define MAXJPATH 1024 /* longest filename */ + +#define MAXJOBS 64 /* silently enforced -j limit */ +#define MAXARGC 32 /* words in $(JAMSHELL) */ + +/* Jam private definitions below. */ + +#define DEBUG_MAX 14 + + +struct globs +{ + int noexec; + int jobs; + int quitquick; + int newestfirst; /* build newest sources first */ + int pipe_action; + char debug[ DEBUG_MAX ]; + FILE * cmdout; /* print cmds, not run them */ + long timeout; /* number of seconds to limit actions to, + * default 0 for no limit. + */ + int dart; /* output build and test results formatted for Dart */ +}; + +extern struct globs globs; + +#define DEBUG_MAKE ( globs.debug[ 1 ] ) /* show actions when executed */ +#define DEBUG_MAKEQ ( globs.debug[ 2 ] ) /* show even quiet actions */ +#define DEBUG_EXEC ( globs.debug[ 2 ] ) /* show text of actons */ +#define DEBUG_MAKEPROG ( globs.debug[ 3 ] ) /* show progress of make0 */ +#define DEBUG_BIND ( globs.debug[ 3 ] ) /* show when files bound */ + +#define DEBUG_EXECCMD ( globs.debug[ 4 ] ) /* show execcmds()'s work */ + +#define DEBUG_COMPILE ( globs.debug[ 5 ] ) /* show rule invocations */ + +#define DEBUG_HEADER ( globs.debug[ 6 ] ) /* show result of header scan */ +#define DEBUG_BINDSCAN ( globs.debug[ 6 ] ) /* show result of dir scan */ +#define DEBUG_SEARCH ( globs.debug[ 6 ] ) /* show attempts at binding */ + +#define DEBUG_VARSET ( globs.debug[ 7 ] ) /* show variable settings */ +#define DEBUG_VARGET ( globs.debug[ 8 ] ) /* show variable fetches */ +#define DEBUG_VAREXP ( globs.debug[ 8 ] ) /* show variable expansions */ +#define DEBUG_IF ( globs.debug[ 8 ] ) /* show 'if' calculations */ +#define DEBUG_LISTS ( globs.debug[ 9 ] ) /* show list manipulation */ +#define DEBUG_SCAN ( globs.debug[ 9 ] ) /* show scanner tokens */ +#define DEBUG_MEM ( globs.debug[ 9 ] ) /* show memory use */ + +#define DEBUG_PROFILE ( globs.debug[ 10 ] ) /* dump rule execution times */ +#define DEBUG_PARSE ( globs.debug[ 11 ] ) /* debug parsing */ +#define DEBUG_GRAPH ( globs.debug[ 12 ] ) /* debug dependencies */ +#define DEBUG_FATE ( globs.debug[ 13 ] ) /* show changes to fate in make0() */ + +/* Everyone gets the memory definitions. */ +#include "mem.h" + +/* They also get the profile functions. */ +#include "debug.h" + +#endif diff --git a/jam-files/engine/jambase.c b/jam-files/engine/jambase.c new file mode 100644 index 00000000..b15282bc --- /dev/null +++ b/jam-files/engine/jambase.c @@ -0,0 +1,1691 @@ +/* Generated by mkjambase from Jambase */ +char *jambase[] = { +/* Jambase */ +"if $(NT)\n", +"{\n", +"SLASH ?= \\\\ ;\n", +"}\n", +"SLASH ?= / ;\n", +"rule find-to-root ( dir : patterns + )\n", +"{\n", +"local globs = [ GLOB $(dir) : $(patterns) ] ;\n", +"while ! $(globs) && $(dir:P) != $(dir)\n", +"{\n", +"dir = $(dir:P) ;\n", +"globs = [ GLOB $(dir) : $(patterns) ] ;\n", +"}\n", +"return $(globs) ;\n", +"}\n", +".boost-build-file = ;\n", +".bootstrap-file = ;\n", +"BOOST_BUILD_PATH.user-value = $(BOOST_BUILD_PATH) ;\n", +"if ! $(BOOST_BUILD_PATH) && $(UNIX)\n", +"{\n", +"BOOST_BUILD_PATH = /usr/share/boost-build ;\n", +"}\n", +"rule _poke ( module-name ? : variables + : value * )\n", +"{\n", +"module $(<)\n", +"{\n", +"$(>) = $(3) ;\n", +"}\n", +"}\n", +"rule boost-build ( dir ? )\n", +"{\n", +"if $(.bootstrap-file)\n", +"{\n", +"ECHO \"Error: Illegal attempt to re-bootstrap the build system by invoking\" ;\n", +"ECHO ;\n", +"ECHO \" 'boost-build\" $(dir) \";'\" ;\n", +"ECHO ;\n", +"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n", +"}\n", +"BOOST_BUILD_PATH = $(dir:R=$(.boost-build-file:D)) $(BOOST_BUILD_PATH) ;\n", +"_poke .ENVIRON : BOOST_BUILD_PATH : $(BOOST_BUILD_PATH) ;\n", +"local bootstrap-file = [ GLOB $(BOOST_BUILD_PATH) : bootstrap.jam ] ;\n", +".bootstrap-file = $(bootstrap-file[1]) ;\n", +"if ! $(.bootstrap-file)\n", +"{\n", +"ECHO \"Unable to load Boost.Build: could not find build system.\" ;\n", +"ECHO --------------------------------------------------------- ;\n", +"ECHO \"$(.boost-build-file) attempted to load the build system by invoking\" ;\n", +"ECHO ;\n", +"ECHO \" 'boost-build\" $(dir) \";'\" ;\n", +"ECHO ;\n", +"ECHO \"but we were unable to find \\\"bootstrap.jam\\\" in the specified directory\" ;\n", +"ECHO \"or in BOOST_BUILD_PATH (searching \"$(BOOST_BUILD_PATH:J=\", \")\").\" ;\n", +"ECHO ;\n", +"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n", +"}\n", +"if [ MATCH .*(--debug-configuration).* : $(ARGV) ]\n", +"{\n", +"ECHO \"notice: loading Boost.Build from\"\n", +"[ NORMALIZE_PATH $(.bootstrap-file:D) ] ;\n", +"}\n", +"include $(.bootstrap-file) ;\n", +"}\n", +"if [ MATCH .*(b2).* : $(ARGV[1]:BL) ] \n", +"|| [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]\n", +"|| $(BOOST_ROOT) # A temporary measure so Jam works with Boost.Build v1.\n", +"{\n", +"local search-path = $(BOOST_BUILD_PATH) $(BOOST_ROOT) ;\n", +"local self = [ SELF_PATH ] ;\n", +"local boost-build-relative = ../../share/boost-build ;\n", +"local self-based-path = [ NORMALIZE_PATH $(boost-build-relative:R=$(self)) ] ;\n", +"local boost-build-files =\n", +"[ find-to-root [ PWD ] : boost-build.jam ]\n", +"[ GLOB $(self-based-path) : boost-build.jam ]\n", +"[ GLOB $(search-path) : boost-build.jam ] ;\n", +".boost-build-file = $(boost-build-files[1]) ;\n", +"if ! $(.boost-build-file)\n", +"{\n", +"ECHO \"Unable to load Boost.Build: could not find \\\"boost-build.jam\\\"\" ;\n", +"ECHO --------------------------------------------------------------- ;\n", +"if ! [ MATCH .*(bjam).* : $(ARGV[1]:BL) ]\n", +"{\n", +"ECHO \"BOOST_ROOT must be set, either in the environment, or \" ;\n", +"ECHO \"on the command-line with -sBOOST_ROOT=..., to the root\" ;\n", +"ECHO \"of the boost installation.\" ;\n", +"ECHO ;\n", +"}\n", +"ECHO \"Attempted search from\" [ PWD ] \"up to the root\" ;\n", +"ECHO \"at\" $(self-based-path) ;\n", +"ECHO \"and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: \"$(search-path:J=\", \")\".\" ;\n", +"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n", +"}\n", +"if [ MATCH .*(--debug-configuration).* : $(ARGV) ]\n", +"{\n", +"ECHO \"notice: found boost-build.jam at\"\n", +"[ NORMALIZE_PATH $(.boost-build-file) ] ;\n", +"}\n", +"include $(.boost-build-file) ;\n", +"if ! $(.bootstrap-file)\n", +"{\n", +"ECHO \"Unable to load Boost.Build\" ;\n", +"ECHO -------------------------- ;\n", +"ECHO \"\\\"$(.boost-build-file)\\\" was found by searching from\" [ PWD ] \"up to the root\" ;\n", +"ECHO \"and in these directories from BOOST_BUILD_PATH and BOOST_ROOT: \"$(search-path:J=\", \")\".\" ;\n", +"ECHO ;\n", +"ECHO \"However, it failed to call the \\\"boost-build\\\" rule to indicate\" ;\n", +"ECHO \"the location of the build system.\" ;\n", +"ECHO ;\n", +"EXIT \"Please consult the documentation at 'http://www.boost.org'.\" ;\n", +"}\n", +"}\n", +"else\n", +"{\n", +"if $(NT)\n", +"{\n", +"local SUPPORTED_TOOLSETS = \"BORLANDC\" \"VC7\" \"VISUALC\" \"VISUALC16\" \"INTELC\" \"WATCOM\"\n", +"\"MINGW\" \"LCC\" ;\n", +"TOOLSET = \"\" ;\n", +"if $(JAM_TOOLSET)\n", +"{\n", +"local t ;\n", +"for t in $(SUPPORTED_TOOLSETS)\n", +"{\n", +"$(t) = $($(t):J=\" \") ; # reconstitute paths with spaces in them\n", +"if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }\n", +"}\n", +"if ! $(TOOLSET)\n", +"{\n", +"ECHO \"The JAM_TOOLSET environment variable is defined but its value\" ;\n", +"ECHO \"is invalid, please use one of the following:\" ;\n", +"ECHO ;\n", +"for t in $(SUPPORTED_TOOLSETS) { ECHO \" \" $(t) ; }\n", +"EXIT ;\n", +"}\n", +"}\n", +"if ! $(TOOLSET)\n", +"{\n", +"if $(BCCROOT)\n", +"{\n", +"TOOLSET = BORLANDC ;\n", +"BORLANDC = $(BCCROOT:J=\" \") ;\n", +"}\n", +"else if $(MSVC)\n", +"{\n", +"TOOLSET = VISUALC16 ;\n", +"VISUALC16 = $(MSVC:J=\" \") ;\n", +"}\n", +"else if $(MSVCNT)\n", +"{\n", +"TOOLSET = VISUALC ;\n", +"VISUALC = $(MSVCNT:J=\" \") ;\n", +"}\n", +"else if $(MSVCDir)\n", +"{\n", +"TOOLSET = VISUALC ;\n", +"VISUALC = $(MSVCDir:J=\" \") ;\n", +"}\n", +"else if $(MINGW)\n", +"{\n", +"TOOLSET = MINGW ;\n", +"}\n", +"else\n", +"{\n", +"ECHO \"Jam cannot be run because, either:\" ;\n", +"ECHO \" a. You didn't set BOOST_ROOT to indicate the root of your\" ;\n", +"ECHO \" Boost installation.\" ;\n", +"ECHO \" b. You are trying to use stock Jam but didn't indicate which\" ;\n", +"ECHO \" compilation toolset to use. To do so, follow these simple\" ;\n", +"ECHO \" instructions:\" ;\n", +"ECHO ;\n", +"ECHO \" - define one of the following environment variable, with the\" ;\n", +"ECHO \" appropriate value according to this list:\" ;\n", +"ECHO ;\n", +"ECHO \" Variable Toolset Description\" ;\n", +"ECHO ;\n", +"ECHO \" BORLANDC Borland C++ BC++ install path\" ;\n", +"ECHO \" VISUALC Microsoft Visual C++ VC++ install path\" ;\n", +"ECHO \" VISUALC16 Microsoft Visual C++ 16 bit VC++ 16 bit install\" ;\n", +"ECHO \" INTELC Intel C/C++ IC++ install path\" ;\n", +"ECHO \" WATCOM Watcom C/C++ Watcom install path\" ;\n", +"ECHO \" MINGW MinGW (gcc) MinGW install path\" ;\n", +"ECHO \" LCC Win32-LCC LCC-Win32 install path\" ;\n", +"ECHO ;\n", +"ECHO \" - define the JAM_TOOLSET environment variable with the *name*\" ;\n", +"ECHO \" of the toolset variable you want to use.\" ;\n", +"ECHO ;\n", +"ECHO \" e.g.: set VISUALC=C:\\\\Visual6\" ;\n", +"ECHO \" set JAM_TOOLSET=VISUALC\" ;\n", +"EXIT ;\n", +"}\n", +"}\n", +"CP ?= copy ;\n", +"RM ?= del /f/q ;\n", +"SLASH ?= \\\\ ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .obj ;\n", +"SUFEXE ?= .exe ;\n", +"if $(TOOLSET) = BORLANDC\n", +"{\n", +"ECHO \"Compiler is Borland C++\" ;\n", +"AR ?= tlib /C /P64 ;\n", +"CC ?= bcc32 ;\n", +"CCFLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus ;\n", +"C++ ?= bcc32 ;\n", +"C++FLAGS ?= -q -y -d -v -w-par -w-ccc -w-rch -w-pro -w-aus -P ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"STDLIBPATH ?= $(BORLANDC)\\\\lib ;\n", +"STDHDRS ?= $(BORLANDC)\\\\include ;\n", +"NOARSCAN ?= true ;\n", +"}\n", +"else if $(TOOLSET) = VISUALC16\n", +"{\n", +"ECHO \"Compiler is Microsoft Visual C++ 16 bit\" ;\n", +"AR ?= lib /nologo ;\n", +"CC ?= cl /nologo ;\n", +"CCFLAGS ?= /D \\\"WIN\\\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"LINKLIBS ?=\n", +"\\\"$(VISUALC16)\\\\lib\\\\mlibce.lib\\\"\n", +"\\\"$(VISUALC16)\\\\lib\\\\oldnames.lib\\\"\n", +";\n", +"LINKLIBS ?= ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= \"\" ;\n", +"STDHDRS ?= $(VISUALC16)\\\\include ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = VISUALC\n", +"{\n", +"ECHO \"Compiler is Microsoft Visual C++\" ;\n", +"AR ?= lib ;\n", +"AS ?= masm386 ;\n", +"CC ?= cl /nologo ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= link /nologo ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= \\\"$(VISUALC)\\\\lib\\\\advapi32.lib\\\"\n", +"\\\"$(VISUALC)\\\\lib\\\\gdi32.lib\\\"\n", +"\\\"$(VISUALC)\\\\lib\\\\user32.lib\\\"\n", +"\\\"$(VISUALC)\\\\lib\\\\kernel32.lib\\\" ;\n", +"OPTIM ?= \"\" ;\n", +"STDHDRS ?= $(VISUALC)\\\\include ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = VC7\n", +"{\n", +"ECHO \"Compiler is Microsoft Visual C++ .NET\" ;\n", +"AR ?= lib ;\n", +"AS ?= masm386 ;\n", +"CC ?= cl /nologo ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= link /nologo ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= \\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\advapi32.lib\\\"\n", +"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\gdi32.lib\\\"\n", +"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\user32.lib\\\"\n", +"\\\"$(VISUALC)\\\\PlatformSDK\\\\lib\\\\kernel32.lib\\\" ;\n", +"OPTIM ?= \"\" ;\n", +"STDHDRS ?= \\\"$(VISUALC)\\\\include\\\"\n", +"\\\"$(VISUALC)\\\\PlatformSDK\\\\include\\\" ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = INTELC\n", +"{\n", +"ECHO \"Compiler is Intel C/C++\" ;\n", +"if ! $(VISUALC)\n", +"{\n", +"ECHO \"As a special exception, when using the Intel C++ compiler, you need\" ;\n", +"ECHO \"to define the VISUALC environment variable to indicate the location\" ;\n", +"ECHO \"of your Visual C++ installation. Aborting..\" ;\n", +"EXIT ;\n", +"}\n", +"AR ?= lib ;\n", +"AS ?= masm386 ;\n", +"CC ?= icl /nologo ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= link /nologo ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= $(VISUALC)\\\\lib\\\\advapi32.lib\n", +"$(VISUALC)\\\\lib\\\\kernel32.lib\n", +";\n", +"OPTIM ?= \"\" ;\n", +"STDHDRS ?= $(INTELC)\\include $(VISUALC)\\\\include ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = WATCOM\n", +"{\n", +"ECHO \"Compiler is Watcom C/C++\" ;\n", +"AR ?= wlib ;\n", +"CC ?= wcc386 ;\n", +"CCFLAGS ?= /zq /DWIN32 /I$(WATCOM)\\\\h ; # zq=quiet\n", +"C++ ?= wpp386 ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"CP ?= copy ;\n", +"DOT ?= . ;\n", +"DOTDOT ?= .. ;\n", +"LINK ?= wcl386 ;\n", +"LINKFLAGS ?= /zq ; # zq=quiet\n", +"LINKLIBS ?= ;\n", +"MV ?= move ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"RM ?= del /f ;\n", +"SLASH ?= \\\\ ;\n", +"STDHDRS ?= $(WATCOM)\\\\h $(WATCOM)\\\\h\\\\nt ;\n", +"SUFEXE ?= .exe ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .obj ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = MINGW\n", +"{\n", +"ECHO \"Compiler is GCC with Mingw\" ;\n", +"AR ?= ar -ru ;\n", +"CC ?= gcc ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= \"\" ;\n", +"OPTIM ?= ;\n", +"SUFOBJ = .o ;\n", +"SUFLIB = .a ;\n", +"SLASH = / ;\n", +"}\n", +"else if $(TOOLSET) = LCC\n", +"{\n", +"ECHO \"Compiler is Win32-LCC\" ;\n", +"AR ?= lcclib ;\n", +"CC ?= lcc ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= lcclnk ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= \"\" ;\n", +"OPTIM ?= ;\n", +"NOARSCAN = true ;\n", +"}\n", +"else\n", +"{\n", +"EXIT On NT, set BCCROOT, MSVCNT, MINGW or MSVC to the root of the\n", +"Borland or Microsoft directories. ;\n", +"}\n", +"}\n", +"else if $(OS2)\n", +"{\n", +"local SUPPORTED_TOOLSETS = \"EMX\" \"WATCOM\" ;\n", +"TOOLSET = \"\" ;\n", +"if $(JAM_TOOLSET)\n", +"{\n", +"local t ;\n", +"for t in $(SUPPORTED_TOOLSETS)\n", +"{\n", +"$(t) = $($(t):J=\" \") ; # reconstitute paths with spaces in them\n", +"if $(t) = $(JAM_TOOLSET) { TOOLSET = $(t) ; }\n", +"}\n", +"if ! $(TOOLSET)\n", +"{\n", +"ECHO \"The JAM_TOOLSET environment variable is defined but its value\" ;\n", +"ECHO \"is invalid, please use one of the following:\" ;\n", +"ECHO ;\n", +"for t in $(SUPPORTED_TOOLSETS) { ECHO \" \" $(t) ; }\n", +"EXIT ;\n", +"}\n", +"}\n", +"if ! $(TOOLSET)\n", +"{\n", +"if $(watcom)\n", +"{\n", +"WATCOM = $(watcom:J=\" \") ;\n", +"TOOLSET = WATCOM ;\n", +"}\n", +"else\n", +"{\n", +"ECHO \"Jam cannot be run because you didn't indicate which compilation toolset\" ;\n", +"ECHO \"to use. To do so, follow these simple instructions:\" ;\n", +"ECHO ;\n", +"ECHO \" - define one of the following environment variable, with the\" ;\n", +"ECHO \" appropriate value according to this list:\" ;\n", +"ECHO ;\n", +"ECHO \" Variable Toolset Description\" ;\n", +"ECHO ;\n", +"ECHO \" WATCOM Watcom C/C++ Watcom install path\" ;\n", +"ECHO \" EMX EMX (gcc) EMX install path\" ;\n", +"ECHO \" VISUALAGE IBM Visual Age C/C++ VisualAge install path\" ;\n", +"ECHO ;\n", +"ECHO \" - define the JAM_TOOLSET environment variable with the *name*\" ;\n", +"ECHO \" of the toolset variable you want to use.\" ;\n", +"ECHO ;\n", +"ECHO \" e.g.: set WATCOM=C:\\WATCOM\" ;\n", +"ECHO \" set JAM_TOOLSET=WATCOM\" ;\n", +"ECHO ;\n", +"EXIT ;\n", +"}\n", +"}\n", +"RM = del /f ;\n", +"CP = copy ;\n", +"MV ?= move ;\n", +"DOT ?= . ;\n", +"DOTDOT ?= .. ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .obj ;\n", +"SUFEXE ?= .exe ;\n", +"if $(TOOLSET) = WATCOM\n", +"{\n", +"AR ?= wlib ;\n", +"BINDIR ?= \\\\os2\\\\apps ;\n", +"CC ?= wcc386 ;\n", +"CCFLAGS ?= /zq /DOS2 /I$(WATCOM)\\\\h ; # zq=quiet\n", +"C++ ?= wpp386 ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= wcl386 ;\n", +"LINKFLAGS ?= /zq ; # zq=quiet\n", +"LINKLIBS ?= ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"SLASH ?= \\\\ ;\n", +"STDHDRS ?= $(WATCOM)\\\\h ;\n", +"UNDEFFLAG ?= \"/u _\" ;\n", +"}\n", +"else if $(TOOLSET) = EMX\n", +"{\n", +"ECHO \"Compiler is GCC-EMX\" ;\n", +"AR ?= ar -ru ;\n", +"CC ?= gcc ;\n", +"CCFLAGS ?= \"\" ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= \"\" ;\n", +"OPTIM ?= ;\n", +"SUFOBJ = .o ;\n", +"SUFLIB = .a ;\n", +"UNDEFFLAG ?= \"-U\" ;\n", +"SLASH = / ;\n", +"}\n", +"else\n", +"{\n", +"EXIT \"Sorry, but the $(JAM_TOOLSET) toolset isn't supported for now\" ;\n", +"}\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"C++ ?= cxx ;\n", +"C++FLAGS ?= ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= ;\n", +"CHMOD ?= set file/prot= ;\n", +"CP ?= copy/replace ;\n", +"CRELIB ?= true ;\n", +"DOT ?= [] ;\n", +"DOTDOT ?= [-] ;\n", +"EXEMODE ?= (w:e) ;\n", +"FILEMODE ?= (w:r) ;\n", +"HDRS ?= ;\n", +"LINK ?= link ;\n", +"LINKFLAGS ?= \"\" ;\n", +"LINKLIBS ?= ;\n", +"MKDIR ?= create/dir ;\n", +"MV ?= rename ;\n", +"OPTIM ?= \"\" ;\n", +"RM ?= delete ;\n", +"RUNVMS ?= mcr ;\n", +"SHELLMODE ?= (w:er) ;\n", +"SLASH ?= . ;\n", +"STDHDRS ?= decc$library_include ;\n", +"SUFEXE ?= .exe ;\n", +"SUFLIB ?= .olb ;\n", +"SUFOBJ ?= .obj ;\n", +"switch $(OS)\n", +"{\n", +"case OPENVMS : CCFLAGS ?= /stand=vaxc ;\n", +"case VMS : LINKLIBS ?= sys$library:vaxcrtl.olb/lib ;\n", +"}\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"local OPT ;\n", +"CW ?= \"{CW}\" ;\n", +"MACHDRS ?=\n", +"\"$(UMACHDRS):Universal:Interfaces:CIncludes\"\n", +"\"$(CW):MSL:MSL_C:MSL_Common:Include\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Include\" ;\n", +"MACLIBS ?=\n", +"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Interfacelib\"\n", +"\"$(CW):MacOS Support:Universal:Libraries:StubLibraries:Mathlib\" ;\n", +"MPWLIBS ?=\n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW.Lib\" ;\n", +"MPWNLLIBS ?=\n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL MPWCRuntime.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC MPW(NL).Lib\" ;\n", +"SIOUXHDRS ?= ;\n", +"SIOUXLIBS ?=\n", +"\"$(CW):MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL SIOUX.PPC.Lib\"\n", +"\"$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL C.PPC.Lib\" ;\n", +"C++ ?= mwcppc ;\n", +"C++FLAGS ?= -w off -nomapcr ;\n", +"CC ?= mwcppc ;\n", +"CCFLAGS ?= -w off -nomapcr ;\n", +"CP ?= duplicate -y ;\n", +"DOT ?= \":\" ;\n", +"DOTDOT ?= \"::\" ;\n", +"HDRS ?= $(MACHDRS) $(MPWHDRS) ;\n", +"LINK ?= mwlinkppc ;\n", +"LINKFLAGS ?= -mpwtool -warn ;\n", +"LINKLIBS ?= $(MACLIBS) $(MPWLIBS) ;\n", +"MKDIR ?= newfolder ;\n", +"MV ?= rename -y ;\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= ;\n", +"RM ?= delete -y ;\n", +"SLASH ?= \":\" ;\n", +"STDHDRS ?= ;\n", +"SUFLIB ?= .lib ;\n", +"SUFOBJ ?= .o ;\n", +"}\n", +"else if $(OS) = BEOS && $(METROWERKS)\n", +"{\n", +"AR ?= mwld -xml -o ;\n", +"BINDIR ?= /boot/apps ;\n", +"CC ?= mwcc ;\n", +"CCFLAGS ?= -nosyspath ;\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= -nosyspath ;\n", +"FORTRAN ?= \"\" ;\n", +"LIBDIR ?= /boot/develop/libraries ;\n", +"LINK ?= mwld ;\n", +"LINKFLAGS ?= \"\" ;\n", +"MANDIR ?= /boot/documentation/\"Shell Tools\"/HTML ;\n", +"NOARSCAN ?= true ;\n", +"STDHDRS ?= /boot/develop/headers/posix ;\n", +"}\n", +"else if $(OS) = BEOS\n", +"{\n", +"BINDIR ?= /boot/apps ;\n", +"CC ?= gcc ;\n", +"C++ ?= $(CC) ;\n", +"FORTRAN ?= \"\" ;\n", +"LIBDIR ?= /boot/develop/libraries ;\n", +"LINK ?= gcc ;\n", +"LINKLIBS ?= -lnet ;\n", +"NOARSCAN ?= true ;\n", +"STDHDRS ?= /boot/develop/headers/posix ;\n", +"}\n", +"else if $(UNIX)\n", +"{\n", +"switch $(OS)\n", +"{\n", +"case AIX :\n", +"LINKLIBS ?= -lbsd ;\n", +"case AMIGA :\n", +"CC ?= gcc ;\n", +"YACC ?= \"bison -y\" ;\n", +"case CYGWIN :\n", +"CC ?= gcc ;\n", +"CCFLAGS += -D__cygwin__ ;\n", +"LEX ?= flex ;\n", +"RANLIB ?= \"\" ;\n", +"SUFEXE ?= .exe ;\n", +"YACC ?= \"bison -y\" ;\n", +"case DGUX :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"case HPUX :\n", +"YACC = ;\n", +"CFLAGS += -Ae ;\n", +"CCFLAGS += -Ae ;\n", +"RANLIB ?= \"\" ;\n", +"case INTERIX :\n", +"CC ?= gcc ;\n", +"RANLIB ?= \"\" ;\n", +"case IRIX :\n", +"RANLIB ?= \"\" ;\n", +"case MPEIX :\n", +"CC ?= gcc ;\n", +"C++ ?= gcc ;\n", +"CCFLAGS += -D_POSIX_SOURCE ;\n", +"HDRS += /usr/include ;\n", +"RANLIB ?= \"\" ;\n", +"NOARSCAN ?= true ;\n", +"NOARUPDATE ?= true ;\n", +"case MVS :\n", +"RANLIB ?= \"\" ;\n", +"case NEXT :\n", +"AR ?= libtool -o ;\n", +"RANLIB ?= \"\" ;\n", +"case MACOSX :\n", +"AR ?= libtool -o ;\n", +"C++ ?= c++ ;\n", +"MANDIR ?= /usr/local/share/man ;\n", +"RANLIB ?= \"\" ;\n", +"case NCR :\n", +"RANLIB ?= \"\" ;\n", +"case PTX :\n", +"RANLIB ?= \"\" ;\n", +"case QNX :\n", +"AR ?= wlib ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= -Q ; # quiet\n", +"C++ ?= $(CC) ;\n", +"C++FLAGS ?= -Q ; # quiet\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= -Q ; # quiet\n", +"NOARSCAN ?= true ;\n", +"RANLIB ?= \"\" ;\n", +"case SCO :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"case SINIX :\n", +"RANLIB ?= \"\" ;\n", +"case SOLARIS :\n", +"RANLIB ?= \"\" ;\n", +"AR ?= \"/usr/ccs/bin/ar ru\" ;\n", +"case UNICOS :\n", +"NOARSCAN ?= true ;\n", +"OPTIM ?= -O0 ;\n", +"case UNIXWARE :\n", +"RANLIB ?= \"\" ;\n", +"RELOCATE ?= true ;\n", +"}\n", +"CCFLAGS ?= ;\n", +"C++FLAGS ?= $(CCFLAGS) ;\n", +"CHMOD ?= chmod ;\n", +"CHGRP ?= chgrp ;\n", +"CHOWN ?= chown ;\n", +"LEX ?= lex ;\n", +"LINKFLAGS ?= $(CCFLAGS) ;\n", +"LINKLIBS ?= ;\n", +"OPTIM ?= -O ;\n", +"RANLIB ?= ranlib ;\n", +"YACC ?= yacc ;\n", +"YACCFILES ?= y.tab ;\n", +"YACCFLAGS ?= -d ;\n", +"}\n", +"AR ?= ar ru ;\n", +"AS ?= as ;\n", +"ASFLAGS ?= ;\n", +"AWK ?= awk ;\n", +"BINDIR ?= /usr/local/bin ;\n", +"C++ ?= cc ;\n", +"C++FLAGS ?= ;\n", +"CC ?= cc ;\n", +"CCFLAGS ?= ;\n", +"CP ?= cp -f ;\n", +"CRELIB ?= ;\n", +"DOT ?= . ;\n", +"DOTDOT ?= .. ;\n", +"EXEMODE ?= 711 ;\n", +"FILEMODE ?= 644 ;\n", +"FORTRAN ?= f77 ;\n", +"FORTRANFLAGS ?= ;\n", +"HDRS ?= ;\n", +"INSTALLGRIST ?= installed ;\n", +"JAMFILE ?= Jamfile ;\n", +"JAMRULES ?= Jamrules ;\n", +"LEX ?= ;\n", +"LIBDIR ?= /usr/local/lib ;\n", +"LINK ?= $(CC) ;\n", +"LINKFLAGS ?= ;\n", +"LINKLIBS ?= ;\n", +"LN ?= ln ;\n", +"MANDIR ?= /usr/local/man ;\n", +"MKDIR ?= mkdir ;\n", +"MV ?= mv -f ;\n", +"OPTIM ?= ;\n", +"RCP ?= rcp ;\n", +"RM ?= rm -f ;\n", +"RSH ?= rsh ;\n", +"SED ?= sed ;\n", +"SHELLHEADER ?= \"#!/bin/sh\" ;\n", +"SHELLMODE ?= 755 ;\n", +"SLASH ?= / ;\n", +"STDHDRS ?= /usr/include ;\n", +"SUFEXE ?= \"\" ;\n", +"SUFLIB ?= .a ;\n", +"SUFOBJ ?= .o ;\n", +"UNDEFFLAG ?= \"-u _\" ;\n", +"YACC ?= ;\n", +"YACCFILES ?= ;\n", +"YACCFLAGS ?= ;\n", +"HDRPATTERN =\n", +"\"^[ ]*#[ ]*include[ ]*[<\\\"]([^\\\">]*)[\\\">].*$\" ;\n", +"OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ;\n", +"DEPENDS all : shell files lib exe obj ;\n", +"DEPENDS all shell files lib exe obj : first ;\n", +"NOTFILE all first shell files lib exe obj dirs clean uninstall ;\n", +"ALWAYS clean uninstall ;\n", +"rule As\n", +"{\n", +"DEPENDS $(<) : $(>) ;\n", +"ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;\n", +"}\n", +"rule Bulk\n", +"{\n", +"local i ;\n", +"for i in $(>)\n", +"{\n", +"File $(i:D=$(<)) : $(i) ;\n", +"}\n", +"}\n", +"rule Cc\n", +"{\n", +"local _h ;\n", +"DEPENDS $(<) : $(>) ;\n", +"CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) ;\n", +"if $(RELOCATE)\n", +"{\n", +"CcMv $(<) : $(>) ;\n", +"}\n", +"_h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n", +"if $(VMS) && $(_h)\n", +"{\n", +"SLASHINC on $(<) = \"/inc=(\" $(_h[1]) ,$(_h[2-]) \")\" ;\n", +"}\n", +"else if $(MAC) && $(_h)\n", +"{\n", +"local _i _j ;\n", +"_j = $(_h[1]) ;\n", +"for _i in $(_h[2-])\n", +"{\n", +"_j = $(_j),$(_i) ;\n", +"}\n", +"MACINC on $(<) = \\\"$(_j)\\\" ;\n", +"}\n", +"}\n", +"rule C++\n", +"{\n", +"local _h ;\n", +"DEPENDS $(<) : $(>) ;\n", +"C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) ;\n", +"if $(RELOCATE)\n", +"{\n", +"CcMv $(<) : $(>) ;\n", +"}\n", +"_h = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n", +"if $(VMS) && $(_h)\n", +"{\n", +"SLASHINC on $(<) = \"/inc=(\" $(_h[1]) ,$(_h[2-]) \")\" ;\n", +"}\n", +"else if $(MAC) && $(_h)\n", +"{\n", +"local _i _j ;\n", +"_j = $(_h[1]) ;\n", +"for _i in $(_h[2-])\n", +"{\n", +"_j = $(_j),$(_i) ;\n", +"}\n", +"MACINC on $(<) = \\\"$(_j)\\\" ;\n", +"}\n", +"}\n", +"rule Chmod\n", +"{\n", +"if $(CHMOD) { Chmod1 $(<) ; }\n", +"}\n", +"rule File\n", +"{\n", +"DEPENDS files : $(<) ;\n", +"DEPENDS $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MODE on $(<) = $(FILEMODE) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule Fortran\n", +"{\n", +"DEPENDS $(<) : $(>) ;\n", +"}\n", +"rule GenFile\n", +"{\n", +"local _t = [ FGristSourceFiles $(<) ] ;\n", +"local _s = [ FAppendSuffix $(>[1]) : $(SUFEXE) ] ;\n", +"Depends $(_t) : $(_s) $(>[2-]) ;\n", +"GenFile1 $(_t) : $(_s) $(>[2-]) ;\n", +"Clean clean : $(_t) ;\n", +"}\n", +"rule GenFile1\n", +"{\n", +"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"}\n", +"rule HardLink\n", +"{\n", +"DEPENDS files : $(<) ;\n", +"DEPENDS $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"}\n", +"rule HdrMacroFile\n", +"{\n", +"HDRMACRO $(<) ;\n", +"}\n", +"rule HdrRule\n", +"{\n", +"local s ;\n", +"if $(HDRGRIST)\n", +"{\n", +"s = $(>:G=$(HDRGRIST)) ;\n", +"} else {\n", +"s = $(>) ;\n", +"}\n", +"INCLUDES $(<) : $(s) ;\n", +"SEARCH on $(s) = $(HDRSEARCH) ;\n", +"NOCARE $(s) ;\n", +"HDRSEARCH on $(s) = $(HDRSEARCH) ;\n", +"HDRSCAN on $(s) = $(HDRSCAN) ;\n", +"HDRRULE on $(s) = $(HDRRULE) ;\n", +"HDRGRIST on $(s) = $(HDRGRIST) ;\n", +"}\n", +"rule InstallInto\n", +"{\n", +"local i t ;\n", +"t = $(>:G=$(INSTALLGRIST)) ;\n", +"Depends install : $(t) ;\n", +"Clean uninstall : $(t) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MakeLocate $(t) : $(<) ;\n", +"for i in $(>)\n", +"{\n", +"local tt = $(i:G=$(INSTALLGRIST)) ;\n", +"Depends $(tt) : $(i) ;\n", +"Install $(tt) : $(i) ;\n", +"Chmod $(tt) ;\n", +"if $(OWNER) && $(CHOWN)\n", +"{\n", +"Chown $(tt) ;\n", +"OWNER on $(tt) = $(OWNER) ;\n", +"}\n", +"if $(GROUP) && $(CHGRP)\n", +"{\n", +"Chgrp $(tt) ;\n", +"GROUP on $(tt) = $(GROUP) ;\n", +"}\n", +"}\n", +"}\n", +"rule InstallBin\n", +"{\n", +"local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;\n", +"InstallInto $(<) : $(_t) ;\n", +"MODE on $(_t:G=installed) = $(EXEMODE) ;\n", +"}\n", +"rule InstallFile\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=installed) = $(FILEMODE) ;\n", +"}\n", +"rule InstallLib\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=installed) = $(FILEMODE) ;\n", +"}\n", +"rule InstallMan\n", +"{\n", +"local i s d ;\n", +"for i in $(>)\n", +"{\n", +"switch $(i:S)\n", +"{\n", +"case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ;\n", +"case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ;\n", +"case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ;\n", +"case .n : s = n ; case .man : s = 1 ;\n", +"}\n", +"d = man$(s) ;\n", +"InstallInto $(d:R=$(<)) : $(i) ;\n", +"}\n", +"MODE on $(>:G=installed) = $(FILEMODE) ;\n", +"}\n", +"rule InstallShell\n", +"{\n", +"InstallInto $(<) : $(>) ;\n", +"MODE on $(>:G=installed) = $(SHELLMODE) ;\n", +"}\n", +"rule Lex\n", +"{\n", +"LexMv $(<) : $(>) ;\n", +"DEPENDS $(<) : $(>) ;\n", +"MakeLocate $(<) : $(LOCATE_SOURCE) ;\n", +"Clean clean : $(<) ;\n", +"}\n", +"rule Library\n", +"{\n", +"LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n", +"Objects $(>) ;\n", +"}\n", +"rule LibraryFromObjects\n", +"{\n", +"local _i _l _s ;\n", +"_s = [ FGristFiles $(>) ] ;\n", +"_l = $(<:S=$(SUFLIB)) ;\n", +"if $(KEEPOBJS)\n", +"{\n", +"DEPENDS obj : $(_s) ;\n", +"}\n", +"else\n", +"{\n", +"DEPENDS lib : $(_l) ;\n", +"}\n", +"if ! $(_l:D)\n", +"{\n", +"MakeLocate $(_l) $(_l)($(_s:BS)) : $(LOCATE_TARGET) ;\n", +"}\n", +"if $(NOARSCAN)\n", +"{\n", +"DEPENDS $(_l) : $(_s) ;\n", +"}\n", +"else\n", +"{\n", +"DEPENDS $(_l) : $(_l)($(_s:BS)) ;\n", +"for _i in $(_s)\n", +"{\n", +"DEPENDS $(_l)($(_i:BS)) : $(_i) ;\n", +"}\n", +"}\n", +"Clean clean : $(_l) ;\n", +"if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; }\n", +"Archive $(_l) : $(_s) ;\n", +"if $(RANLIB) { Ranlib $(_l) ; }\n", +"if ! ( $(NOARSCAN) || $(KEEPOBJS) ) { RmTemps $(_l) : $(_s) ; }\n", +"}\n", +"rule Link\n", +"{\n", +"MODE on $(<) = $(EXEMODE) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule LinkLibraries\n", +"{\n", +"local _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n", +"DEPENDS $(_t) : $(>:S=$(SUFLIB)) ;\n", +"NEEDLIBS on $(_t) += $(>:S=$(SUFLIB)) ;\n", +"}\n", +"rule Main\n", +"{\n", +"MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;\n", +"Objects $(>) ;\n", +"}\n", +"rule MainFromObjects\n", +"{\n", +"local _s _t ;\n", +"_s = [ FGristFiles $(>) ] ;\n", +"_t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;\n", +"if $(_t) != $(<)\n", +"{\n", +"DEPENDS $(<) : $(_t) ;\n", +"NOTFILE $(<) ;\n", +"}\n", +"DEPENDS exe : $(_t) ;\n", +"DEPENDS $(_t) : $(_s) ;\n", +"MakeLocate $(_t) : $(LOCATE_TARGET) ;\n", +"Clean clean : $(_t) ;\n", +"Link $(_t) : $(_s) ;\n", +"}\n", +"rule MakeLocate\n", +"{\n", +"if $(>)\n", +"{\n", +"LOCATE on $(<) = $(>) ;\n", +"Depends $(<) : $(>[1]) ;\n", +"MkDir $(>[1]) ;\n", +"}\n", +"}\n", +"rule MkDir\n", +"{\n", +"NOUPDATE $(<) ;\n", +"if $(<) != $(DOT) && ! $($(<)-mkdir)\n", +"{\n", +"local s ;\n", +"$(<)-mkdir = true ;\n", +"MkDir1 $(<) ;\n", +"Depends dirs : $(<) ;\n", +"s = $(<:P) ;\n", +"if $(NT)\n", +"{\n", +"switch $(s)\n", +"{\n", +"case *: : s = ;\n", +"case *:\\\\ : s = ;\n", +"}\n", +"}\n", +"if $(s) && $(s) != $(<)\n", +"{\n", +"Depends $(<) : $(s) ;\n", +"MkDir $(s) ;\n", +"}\n", +"else if $(s)\n", +"{\n", +"NOTFILE $(s) ;\n", +"}\n", +"}\n", +"}\n", +"rule Object\n", +"{\n", +"local h ;\n", +"Clean clean : $(<) ;\n", +"MakeLocate $(<) : $(LOCATE_TARGET) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"HDRS on $(<) = $(SEARCH_SOURCE) $(HDRS) $(SUBDIRHDRS) ;\n", +"if $(SEARCH_SOURCE)\n", +"{\n", +"h = $(SEARCH_SOURCE) ;\n", +"}\n", +"else\n", +"{\n", +"h = \"\" ;\n", +"}\n", +"HDRRULE on $(>) = HdrRule ;\n", +"HDRSCAN on $(>) = $(HDRPATTERN) ;\n", +"HDRSEARCH on $(>) = $(HDRS) $(SUBDIRHDRS) $(h) $(STDHDRS) ;\n", +"HDRGRIST on $(>) = $(HDRGRIST) ;\n", +"switch $(>:S)\n", +"{\n", +"case .asm : As $(<) : $(>) ;\n", +"case .c : Cc $(<) : $(>) ;\n", +"case .C : C++ $(<) : $(>) ;\n", +"case .cc : C++ $(<) : $(>) ;\n", +"case .cpp : C++ $(<) : $(>) ;\n", +"case .f : Fortran $(<) : $(>) ;\n", +"case .l : Cc $(<) : $(<:S=.c) ;\n", +"Lex $(<:S=.c) : $(>) ;\n", +"case .s : As $(<) : $(>) ;\n", +"case .y : Cc $(<) : $(<:S=.c) ;\n", +"Yacc $(<:S=.c) : $(>) ;\n", +"case * : UserObject $(<) : $(>) ;\n", +"}\n", +"}\n", +"rule ObjectCcFlags\n", +"{\n", +"CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n", +"}\n", +"rule ObjectC++Flags\n", +"{\n", +"C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n", +"}\n", +"rule ObjectHdrs\n", +"{\n", +"HDRS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;\n", +"}\n", +"rule Objects\n", +"{\n", +"local _i ;\n", +"for _i in [ FGristFiles $(<) ]\n", +"{\n", +"Object $(_i:S=$(SUFOBJ)) : $(_i) ;\n", +"DEPENDS obj : $(_i:S=$(SUFOBJ)) ;\n", +"}\n", +"}\n", +"rule RmTemps\n", +"{\n", +"TEMPORARY $(>) ;\n", +"}\n", +"rule Setuid\n", +"{\n", +"MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ;\n", +"}\n", +"rule Shell\n", +"{\n", +"DEPENDS shell : $(<) ;\n", +"DEPENDS $(<) : $(>) ;\n", +"SEARCH on $(>) = $(SEARCH_SOURCE) ;\n", +"MODE on $(<) = $(SHELLMODE) ;\n", +"Clean clean : $(<) ;\n", +"Chmod $(<) ;\n", +"}\n", +"rule SubDir\n", +"{\n", +"local _r _s ;\n", +"if ! $($(<[1]))\n", +"{\n", +"if ! $(<[1])\n", +"{\n", +"EXIT SubDir syntax error ;\n", +"}\n", +"$(<[1]) = [ FSubDir $(<[2-]) ] ;\n", +"}\n", +"if ! $($(<[1])-included)\n", +"{\n", +"$(<[1])-included = TRUE ;\n", +"_r = $($(<[1])RULES) ;\n", +"if ! $(_r)\n", +"{\n", +"_r = $(JAMRULES:R=$($(<[1]))) ;\n", +"}\n", +"include $(_r) ;\n", +"}\n", +"_s = [ FDirName $(<[2-]) ] ;\n", +"SUBDIR = $(_s:R=$($(<[1]))) ;\n", +"SUBDIR_TOKENS = $(<[2-]) ;\n", +"SEARCH_SOURCE = $(SUBDIR) ;\n", +"LOCATE_SOURCE = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n", +"LOCATE_TARGET = $(ALL_LOCATE_TARGET) $(SUBDIR) ;\n", +"SOURCE_GRIST = [ FGrist $(<[2-]) ] ;\n", +"SUBDIRCCFLAGS = ;\n", +"SUBDIRC++FLAGS = ;\n", +"SUBDIRHDRS = ;\n", +"}\n", +"rule SubDirCcFlags\n", +"{\n", +"SUBDIRCCFLAGS += $(<) ;\n", +"}\n", +"rule SubDirC++Flags\n", +"{\n", +"SUBDIRC++FLAGS += $(<) ;\n", +"}\n", +"rule SubDirHdrs\n", +"{\n", +"SUBDIRHDRS += $(<) ;\n", +"}\n", +"rule SubInclude\n", +"{\n", +"local _s ;\n", +"if ! $($(<[1]))\n", +"{\n", +"EXIT Top level of source tree has not been set with $(<[1]) ;\n", +"}\n", +"_s = [ FDirName $(<[2-]) ] ;\n", +"include $(JAMFILE:D=$(_s):R=$($(<[1]))) ;\n", +"}\n", +"rule Undefines\n", +"{\n", +"UNDEFS on [ FAppendSuffix $(<) : $(SUFEXE) ] += $(UNDEFFLAG)$(>) ;\n", +"}\n", +"rule UserObject\n", +"{\n", +"EXIT \"Unknown suffix on\" $(>) \"- see UserObject rule in Jamfile(5).\" ;\n", +"}\n", +"rule Yacc\n", +"{\n", +"local _h ;\n", +"_h = $(<:BS=.h) ;\n", +"MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;\n", +"if $(YACC)\n", +"{\n", +"DEPENDS $(<) $(_h) : $(>) ;\n", +"Yacc1 $(<) $(_h) : $(>) ;\n", +"YaccMv $(<) $(_h) : $(>) ;\n", +"Clean clean : $(<) $(_h) ;\n", +"}\n", +"INCLUDES $(<) : $(_h) ;\n", +"}\n", +"rule FGrist\n", +"{\n", +"local _g _i ;\n", +"_g = $(<[1]) ;\n", +"for _i in $(<[2-])\n", +"{\n", +"_g = $(_g)!$(_i) ;\n", +"}\n", +"return $(_g) ;\n", +"}\n", +"rule FGristFiles\n", +"{\n", +"if ! $(SOURCE_GRIST)\n", +"{\n", +"return $(<) ;\n", +"}\n", +"else\n", +"{\n", +"return $(<:G=$(SOURCE_GRIST)) ;\n", +"}\n", +"}\n", +"rule FGristSourceFiles\n", +"{\n", +"if ! $(SOURCE_GRIST)\n", +"{\n", +"return $(<) ;\n", +"}\n", +"else\n", +"{\n", +"local _i _o ;\n", +"for _i in $(<)\n", +"{\n", +"switch $(_i)\n", +"{\n", +"case *.h : _o += $(_i) ;\n", +"case * : _o += $(_i:G=$(SOURCE_GRIST)) ;\n", +"}\n", +"}\n", +"return $(_o) ;\n", +"}\n", +"}\n", +"rule FConcat\n", +"{\n", +"local _t _r ;\n", +"$(_r) = $(<[1]) ;\n", +"for _t in $(<[2-])\n", +"{\n", +"$(_r) = $(_r)$(_t) ;\n", +"}\n", +"return $(_r) ;\n", +"}\n", +"rule FSubDir\n", +"{\n", +"local _i _d ;\n", +"if ! $(<[1])\n", +"{\n", +"_d = $(DOT) ;\n", +"}\n", +"else\n", +"{\n", +"_d = $(DOTDOT) ;\n", +"for _i in $(<[2-])\n", +"{\n", +"_d = $(_d:R=$(DOTDOT)) ;\n", +"}\n", +"}\n", +"return $(_d) ;\n", +"}\n", +"rule FDirName\n", +"{\n", +"local _s _i ;\n", +"if ! $(<)\n", +"{\n", +"_s = $(DOT) ;\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"switch $(<[1])\n", +"{\n", +"case *:* : _s = $(<[1]) ;\n", +"case \\\\[*\\\\] : _s = $(<[1]) ;\n", +"case * : _s = [.$(<[1])] ;\n", +"}\n", +"for _i in [.$(<[2-])]\n", +"{\n", +"_s = $(_i:R=$(_s)) ;\n", +"}\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"_s = $(DOT) ;\n", +"for _i in $(<)\n", +"{\n", +"_s = $(_i:R=$(_s)) ;\n", +"}\n", +"}\n", +"else\n", +"{\n", +"_s = $(<[1]) ;\n", +"for _i in $(<[2-])\n", +"{\n", +"_s = $(_i:R=$(_s)) ;\n", +"}\n", +"}\n", +"return $(_s) ;\n", +"}\n", +"rule _makeCommon\n", +"{\n", +"if $($(<)[1]) && $($(<)[1]) = $($(>)[1])\n", +"{\n", +"$(<) = $($(<)[2-]) ;\n", +"$(>) = $($(>)[2-]) ;\n", +"_makeCommon $(<) : $(>) ;\n", +"}\n", +"}\n", +"rule FRelPath\n", +"{\n", +"local _l _r ;\n", +"_l = $(<) ;\n", +"_r = $(>) ;\n", +"_makeCommon _l : _r ;\n", +"_l = [ FSubDir $(_l) ] ;\n", +"_r = [ FDirName $(_r) ] ;\n", +"if $(_r) = $(DOT) {\n", +"return $(_l) ;\n", +"} else {\n", +"return $(_r:R=$(_l)) ;\n", +"}\n", +"}\n", +"rule FAppendSuffix\n", +"{\n", +"if $(>)\n", +"{\n", +"local _i _o ;\n", +"for _i in $(<)\n", +"{\n", +"if $(_i:S)\n", +"{\n", +"_o += $(_i) ;\n", +"}\n", +"else\n", +"{\n", +"_o += $(_i:S=$(>)) ;\n", +"}\n", +"}\n", +"return $(_o) ;\n", +"}\n", +"else\n", +"{\n", +"return $(<) ;\n", +"}\n", +"}\n", +"rule unmakeDir\n", +"{\n", +"if $(>[1]:D) && $(>[1]:D) != $(>[1]) && $(>[1]:D) != \\\\\\\\\n", +"{\n", +"unmakeDir $(<) : $(>[1]:D) $(>[1]:BS) $(>[2-]) ;\n", +"}\n", +"else\n", +"{\n", +"$(<) = $(>) ;\n", +"}\n", +"}\n", +"rule FConvertToSlashes\n", +"{\n", +"local _d, _s, _i ;\n", +"unmakeDir _d : $(<) ;\n", +"_s = $(_d[1]) ;\n", +"for _i in $(_d[2-])\n", +"{\n", +"_s = $(_s)/$(_i) ;\n", +"}\n", +"return $(_s) ;\n", +"}\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) $(>)\n", +"}\n", +"actions As\n", +"{\n", +"$(AS) $(ASFLAGS) -I$(HDRS) -o $(<) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o $(<) $(>)\n", +"}\n", +"actions Chgrp\n", +"{\n", +"$(CHGRP) $(GROUP) $(<)\n", +"}\n", +"actions Chmod1\n", +"{\n", +"$(CHMOD) $(MODE) $(<)\n", +"}\n", +"actions Chown\n", +"{\n", +"$(CHOWN) $(OWNER) $(<)\n", +"}\n", +"actions piecemeal together existing Clean\n", +"{\n", +"$(RM) $(>)\n", +"}\n", +"actions File\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"actions GenFile1\n", +"{\n", +"$(>[1]) $(<) $(>[2-])\n", +"}\n", +"actions Fortran\n", +"{\n", +"$(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>)\n", +"}\n", +"actions HardLink\n", +"{\n", +"$(RM) $(<) && $(LN) $(>) $(<)\n", +"}\n", +"actions Install\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"actions Lex\n", +"{\n", +"$(LEX) $(>)\n", +"}\n", +"actions LexMv\n", +"{\n", +"$(MV) lex.yy.c $(<)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"actions MkDir1\n", +"{\n", +"$(MKDIR) $(<)\n", +"}\n", +"actions together Ranlib\n", +"{\n", +"$(RANLIB) $(<)\n", +"}\n", +"actions quietly updated piecemeal together RmTemps\n", +"{\n", +"$(RM) $(>)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(AWK) '\n", +"NR == 1 { print \"$(SHELLHEADER)\" }\n", +"NR == 1 && /^[#:]/ { next }\n", +"/^##/ { next }\n", +"{ print }\n", +"' < $(>) > $(<)\n", +"}\n", +"actions Yacc1\n", +"{\n", +"$(YACC) $(YACCFLAGS) $(>)\n", +"}\n", +"actions YaccMv\n", +"{\n", +"$(MV) $(YACCFILES).c $(<[1])\n", +"$(MV) $(YACCFILES).h $(<[2])\n", +"}\n", +"if $(RELOCATE)\n", +"{\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) $(>)\n", +"}\n", +"actions ignore CcMv\n", +"{\n", +"[ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<)\n", +"}\n", +"}\n", +"if $(NOARUPDATE)\n", +"{\n", +"actions Archive\n", +"{\n", +"$(AR) $(<) $(>)\n", +"}\n", +"}\n", +"if $(NT)\n", +"{\n", +"if $(TOOLSET) = VISUALC || $(TOOLSET) = VC7 || $(TOOLSET) = INTELC\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"if exist $(<) set _$(<:B)_=$(<)\n", +"$(AR) /out:$(<) %_$(<:B)_% $(>)\n", +"}\n", +"actions As\n", +"{\n", +"$(AS) /Ml /p /v /w2 $(>) $(<) ,nul,nul;\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /I$(STDHDRS) /Tp$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = VISUALC16\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) -+$(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) /c $(CCFLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) /c $(C++FLAGS) $(OPTIM) /Fo$(<) /I$(HDRS) /Tp$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = BORLANDC\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"$(AR) $(<) -+$(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = MINGW\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) $(<) $(>:T)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = WATCOM\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) $(<) +-$(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = LCC\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) /out:$(<) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) $(CCFLAGS) $(OPTIM) -Fo$(<) -I$(HDRS) $(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"}\n", +"else if $(OS2)\n", +"{\n", +"if $(TOOLSET) = WATCOM\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) $(<) +-$(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) $(CCFLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) $(C++FLAGS) $(OPTIM) /Fo=$(<) /I$(HDRS) $(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"else if $(TOOLSET) = EMX\n", +"{\n", +"actions together piecemeal Archive\n", +"{\n", +"$(AR) $(<) $(>:T)\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC) -c $(CCFLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++) -c $(C++FLAGS) $(OPTIM) -I$(HDRS) -o$(<) $(>)\n", +"}\n", +"}\n", +"}\n", +"else if $(VMS)\n", +"{\n", +"actions updated together piecemeal Archive\n", +"{\n", +"lib/replace $(<) $(>[1]) ,$(>[2-])\n", +"}\n", +"actions Cc\n", +"{\n", +"$(CC)/obj=$(<) $(CCFLAGS) $(OPTIM) $(SLASHINC) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"$(C++)/obj=$(<) $(C++FLAGS) $(OPTIM) $(SLASHINC) $(>)\n", +"}\n", +"actions piecemeal together existing Clean\n", +"{\n", +"$(RM) $(>[1]);* ,$(>[2-]);*\n", +"}\n", +"actions together quietly CreLib\n", +"{\n", +"if f$search(\"$(<)\") .eqs. \"\" then lib/create $(<)\n", +"}\n", +"actions GenFile1\n", +"{\n", +"mcr $(>[1]) $(<) $(>[2-])\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK)/exe=$(<) $(LINKFLAGS) $(>[1]) ,$(>[2-]) ,$(NEEDLIBS)/lib ,$(LINKLIBS)\n", +"}\n", +"actions quietly updated piecemeal together RmTemps\n", +"{\n", +"$(RM) $(>[1]);* ,$(>[2-]);*\n", +"}\n", +"actions Shell\n", +"{\n", +"$(CP) $(>) $(<)\n", +"}\n", +"}\n", +"else if $(MAC)\n", +"{\n", +"actions together Archive\n", +"{\n", +"$(LINK) -library -o $(<) $(>)\n", +"}\n", +"actions Cc\n", +"{\n", +"set -e MWCincludes $(MACINC)\n", +"$(CC) -o $(<) $(CCFLAGS) $(OPTIM) $(>)\n", +"}\n", +"actions C++\n", +"{\n", +"set -e MWCincludes $(MACINC)\n", +"$(CC) -o $(<) $(C++FLAGS) $(OPTIM) $(>)\n", +"}\n", +"actions Link bind NEEDLIBS\n", +"{\n", +"$(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) \"$(LINKLIBS)\"\n", +"}\n", +"}\n", +"rule BULK { Bulk $(<) : $(>) ; }\n", +"rule FILE { File $(<) : $(>) ; }\n", +"rule HDRRULE { HdrRule $(<) : $(>) ; }\n", +"rule INSTALL { Install $(<) : $(>) ; }\n", +"rule LIBRARY { Library $(<) : $(>) ; }\n", +"rule LIBS { LinkLibraries $(<) : $(>) ; }\n", +"rule LINK { Link $(<) : $(>) ; }\n", +"rule MAIN { Main $(<) : $(>) ; }\n", +"rule SETUID { Setuid $(<) ; }\n", +"rule SHELL { Shell $(<) : $(>) ; }\n", +"rule UNDEFINES { Undefines $(<) : $(>) ; }\n", +"rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; }\n", +"rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; }\n", +"rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; }\n", +"rule addDirName { $(<) += [ FDirName $(>) ] ; }\n", +"rule makeDirName { $(<) = [ FDirName $(>) ] ; }\n", +"rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }\n", +"rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }\n", +"rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }\n", +"{\n", +"if $(JAMFILE) { include $(JAMFILE) ; }\n", +"}\n", +"}\n", +0 }; diff --git a/jam-files/engine/jambase.h b/jam-files/engine/jambase.h new file mode 100644 index 00000000..c05ec792 --- /dev/null +++ b/jam-files/engine/jambase.h @@ -0,0 +1,15 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * jambase.h - declaration for the internal jambase + * + * The file Jambase is turned into a C array of strings in jambase.c + * so that it can be built in to the executable. This is the + * declaration for that array. + */ + +extern char *jambase[]; diff --git a/jam-files/engine/jamgram.c b/jam-files/engine/jamgram.c new file mode 100644 index 00000000..b1fa0835 --- /dev/null +++ b/jam-files/engine/jamgram.c @@ -0,0 +1,1830 @@ +/* A Bison parser, made by GNU Bison 1.875. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 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. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + _BANG_t = 258, + _BANG_EQUALS_t = 259, + _AMPER_t = 260, + _AMPERAMPER_t = 261, + _LPAREN_t = 262, + _RPAREN_t = 263, + _PLUS_EQUALS_t = 264, + _COLON_t = 265, + _SEMIC_t = 266, + _LANGLE_t = 267, + _LANGLE_EQUALS_t = 268, + _EQUALS_t = 269, + _RANGLE_t = 270, + _RANGLE_EQUALS_t = 271, + _QUESTION_EQUALS_t = 272, + _LBRACKET_t = 273, + _RBRACKET_t = 274, + ACTIONS_t = 275, + BIND_t = 276, + CASE_t = 277, + CLASS_t = 278, + DEFAULT_t = 279, + ELSE_t = 280, + EXISTING_t = 281, + FOR_t = 282, + IF_t = 283, + IGNORE_t = 284, + IN_t = 285, + INCLUDE_t = 286, + LOCAL_t = 287, + MODULE_t = 288, + ON_t = 289, + PIECEMEAL_t = 290, + QUIETLY_t = 291, + RETURN_t = 292, + RULE_t = 293, + SWITCH_t = 294, + TOGETHER_t = 295, + UPDATED_t = 296, + WHILE_t = 297, + _LBRACE_t = 298, + _BAR_t = 299, + _BARBAR_t = 300, + _RBRACE_t = 301, + ARG = 302, + STRING = 303 + }; +#endif +#define _BANG_t 258 +#define _BANG_EQUALS_t 259 +#define _AMPER_t 260 +#define _AMPERAMPER_t 261 +#define _LPAREN_t 262 +#define _RPAREN_t 263 +#define _PLUS_EQUALS_t 264 +#define _COLON_t 265 +#define _SEMIC_t 266 +#define _LANGLE_t 267 +#define _LANGLE_EQUALS_t 268 +#define _EQUALS_t 269 +#define _RANGLE_t 270 +#define _RANGLE_EQUALS_t 271 +#define _QUESTION_EQUALS_t 272 +#define _LBRACKET_t 273 +#define _RBRACKET_t 274 +#define ACTIONS_t 275 +#define BIND_t 276 +#define CASE_t 277 +#define CLASS_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 LOCAL_t 287 +#define MODULE_t 288 +#define ON_t 289 +#define PIECEMEAL_t 290 +#define QUIETLY_t 291 +#define RETURN_t 292 +#define RULE_t 293 +#define SWITCH_t 294 +#define TOGETHER_t 295 +#define UPDATED_t 296 +#define WHILE_t 297 +#define _LBRACE_t 298 +#define _BAR_t 299 +#define _BARBAR_t 300 +#define _RBRACE_t 301 +#define ARG 302 +#define STRING 303 + + + + +/* Copy the first part of user declarations. */ +#line 96 "jamgram.y" + +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) +# 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 pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( compile_class,l,r,P0,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( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) +# 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 ) + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 223 "y.tab.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 43 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 261 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 49 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 24 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 75 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 159 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 303 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 4, 6, 8, 10, 12, 15, 21, + 22, 25, 27, 31, 32, 34, 35, 39, 43, 47, + 52, 59, 63, 72, 78, 84, 90, 96, 102, 110, + 116, 120, 121, 122, 132, 134, 136, 138, 141, 143, + 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, + 187, 190, 194, 195, 198, 203, 205, 209, 211, 212, + 215, 217, 218, 223, 226, 231, 236, 237, 240, 242, + 244, 246, 248, 250, 252, 253 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 50, 0, -1, -1, 52, -1, 53, -1, 52, -1, + 57, -1, 57, 52, -1, 32, 65, 54, 11, 51, + -1, -1, 14, 65, -1, 53, -1, 7, 64, 8, + -1, -1, 32, -1, -1, 43, 51, 46, -1, 31, + 65, 11, -1, 47, 64, 11, -1, 67, 60, 65, + 11, -1, 67, 34, 65, 60, 65, 11, -1, 37, + 65, 11, -1, 27, 56, 47, 30, 65, 43, 51, + 46, -1, 39, 65, 43, 62, 46, -1, 28, 61, + 43, 51, 46, -1, 33, 65, 43, 51, 46, -1, + 23, 64, 43, 51, 46, -1, 42, 61, 43, 51, + 46, -1, 28, 61, 43, 51, 46, 25, 57, -1, + 56, 38, 47, 55, 57, -1, 34, 67, 57, -1, + -1, -1, 20, 70, 47, 72, 43, 58, 48, 59, + 46, -1, 14, -1, 9, -1, 17, -1, 24, 14, + -1, 67, -1, 61, 14, 61, -1, 61, 4, 61, + -1, 61, 12, 61, -1, 61, 13, 61, -1, 61, + 15, 61, -1, 61, 16, 61, -1, 61, 5, 61, + -1, 61, 6, 61, -1, 61, 44, 61, -1, 61, + 45, 61, -1, 67, 30, 65, -1, 3, 61, -1, + 7, 61, 8, -1, -1, 63, 62, -1, 22, 47, + 10, 51, -1, 65, -1, 65, 10, 64, -1, 66, + -1, -1, 66, 67, -1, 47, -1, -1, 18, 68, + 69, 19, -1, 67, 64, -1, 34, 67, 67, 64, + -1, 34, 67, 37, 65, -1, -1, 70, 71, -1, + 41, -1, 40, -1, 29, -1, 36, -1, 35, -1, + 26, -1, -1, 21, 65, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 139, 139, 141, 152, 154, 158, 160, 162, 167, + 170, 172, 176, 179, 182, 185, 188, 190, 192, 194, + 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, + 216, 219, 221, 218, 230, 232, 234, 236, 243, 245, + 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, + 267, 269, 281, 282, 286, 295, 297, 307, 312, 313, + 317, 319, 319, 328, 330, 332, 343, 344, 348, 350, + 352, 354, 356, 358, 368, 369 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "_BANG_t", "_BANG_EQUALS_t", "_AMPER_t", + "_AMPERAMPER_t", "_LPAREN_t", "_RPAREN_t", "_PLUS_EQUALS_t", "_COLON_t", + "_SEMIC_t", "_LANGLE_t", "_LANGLE_EQUALS_t", "_EQUALS_t", "_RANGLE_t", + "_RANGLE_EQUALS_t", "_QUESTION_EQUALS_t", "_LBRACKET_t", "_RBRACKET_t", + "ACTIONS_t", "BIND_t", "CASE_t", "CLASS_t", "DEFAULT_t", "ELSE_t", + "EXISTING_t", "FOR_t", "IF_t", "IGNORE_t", "IN_t", "INCLUDE_t", + "LOCAL_t", "MODULE_t", "ON_t", "PIECEMEAL_t", "QUIETLY_t", "RETURN_t", + "RULE_t", "SWITCH_t", "TOGETHER_t", "UPDATED_t", "WHILE_t", "_LBRACE_t", + "_BAR_t", "_BARBAR_t", "_RBRACE_t", "ARG", "STRING", "$accept", "run", + "block", "rules", "null", "assign_list_opt", "arglist_opt", "local_opt", + "rule", "@1", "@2", "assign", "expr", "cases", "case", "lol", "list", + "listp", "arg", "@3", "func", "eflags", "eflag", "bindlist", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 49, 50, 50, 51, 51, 52, 52, 52, 53, + 54, 54, 55, 55, 56, 56, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 58, 59, 57, 60, 60, 60, 60, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 62, 62, 63, 64, 64, 65, 66, 66, + 67, 68, 67, 69, 69, 69, 70, 70, 71, 71, + 71, 71, 71, 71, 72, 72 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 0, 1, 1, 1, 1, 2, 5, 0, + 2, 1, 3, 0, 1, 0, 3, 3, 3, 4, + 6, 3, 8, 5, 5, 5, 5, 5, 7, 5, + 3, 0, 0, 9, 1, 1, 1, 2, 1, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 3, 0, 2, 4, 1, 3, 1, 0, 2, + 1, 0, 4, 2, 4, 4, 0, 2, 1, 1, + 1, 1, 1, 1, 0, 2 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 2, 61, 66, 58, 15, 0, 58, 58, 58, 0, + 58, 58, 0, 9, 60, 0, 3, 0, 6, 0, + 0, 0, 0, 55, 57, 14, 0, 0, 0, 60, + 0, 38, 0, 9, 0, 15, 0, 0, 0, 0, + 5, 4, 0, 1, 0, 7, 35, 34, 36, 0, + 58, 58, 0, 58, 0, 73, 70, 72, 71, 69, + 68, 74, 67, 9, 58, 59, 0, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, + 58, 17, 58, 11, 0, 9, 30, 21, 52, 9, + 16, 18, 13, 37, 0, 0, 0, 63, 62, 58, + 0, 0, 56, 58, 51, 40, 45, 46, 41, 42, + 39, 43, 44, 0, 47, 48, 49, 10, 9, 0, + 0, 0, 52, 0, 58, 15, 58, 19, 58, 58, + 75, 31, 26, 0, 24, 8, 25, 0, 23, 53, + 27, 0, 29, 0, 65, 64, 0, 9, 15, 9, + 12, 20, 32, 0, 28, 54, 0, 22, 33 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const short yydefgoto[] = +{ + -1, 15, 39, 40, 41, 84, 125, 17, 18, 146, + 156, 51, 30, 121, 122, 22, 23, 24, 31, 20, + 54, 21, 62, 100 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -48 +static const short yypact[] = +{ + 179, -48, -48, -48, -15, 7, -48, -16, -48, 3, + -48, -48, 7, 179, 1, 27, -48, -9, 179, 19, + -3, 33, -11, 24, 3, -48, -10, 7, 7, -48, + 138, 9, 30, 35, 13, 205, 53, 22, 151, 20, + -48, -48, 56, -48, 23, -48, -48, -48, -48, 61, + -48, -48, 3, -48, 62, -48, -48, -48, -48, -48, + -48, 58, -48, 179, -48, -48, 52, -48, 164, 7, + 7, 7, 7, 7, 7, 7, 7, 179, 7, 7, + -48, -48, -48, -48, 72, 179, -48, -48, 68, 179, + -48, -48, 85, -48, 77, 73, 8, -48, -48, -48, + 50, 57, -48, -48, -48, 45, 93, 93, -48, -48, + 45, -48, -48, 64, 245, 245, -48, -48, 179, 66, + 67, 69, 68, 71, -48, 205, -48, -48, -48, -48, + -48, -48, -48, 70, 79, -48, -48, 109, -48, -48, + -48, 112, -48, 115, -48, -48, 75, 179, 205, 179, + -48, -48, -48, 81, -48, -48, 82, -48, -48 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const short yypgoto[] = +{ + -48, -48, -47, 5, 104, -48, -48, 136, -27, -48, + -48, 47, 60, 36, -48, -13, -4, -48, 0, -48, + -48, -48, -48, -48 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -59 +static const short yytable[] = +{ + 19, 42, 32, 33, 34, 16, 36, 37, 86, 35, + 27, -58, -58, 19, 28, 1, 101, 25, 19, -58, + 53, 1, -14, 45, 65, 1, 1, 43, 46, 44, + 113, 52, 63, 47, 64, 19, 48, 66, 119, 80, + 97, 81, 123, 49, 29, 128, 94, 95, -58, 82, + 29, 102, 96, 50, 29, 29, 85, 72, 73, 55, + 75, 76, 56, 19, 87, 88, 90, 91, 57, 58, + 92, 135, 38, 59, 60, 93, 116, 19, 117, 99, + 61, 98, 103, 118, 127, 19, 46, 67, 68, 19, + 120, 47, 124, 131, 48, 130, 129, 69, 142, 133, + 153, 49, 155, 132, 148, 72, 73, 74, 75, 76, + 134, 141, 136, 147, 137, 138, 145, 140, 19, 149, + 150, 154, 143, 152, 144, 19, 151, 157, 158, 105, + 106, 107, 108, 109, 110, 111, 112, 83, 114, 115, + 26, 126, 69, 70, 71, 0, 0, 19, 19, 19, + 72, 73, 74, 75, 76, 69, 70, 71, 139, 0, + 0, 0, 0, 72, 73, 74, 75, 76, 69, 70, + 71, 0, 104, 0, 0, 0, 72, 73, 74, 75, + 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 89, 78, 79, 1, 0, 2, + 0, 0, 3, 0, 0, 0, 4, 5, 78, 79, + 6, 7, 8, 9, 0, 0, 10, -15, 11, 0, + 0, 12, 13, 1, 0, 2, 14, 0, 3, 0, + 0, 0, 4, 5, 0, 0, 6, 25, 8, 9, + 0, 0, 10, 0, 11, 0, 0, 12, 13, 69, + 70, 71, 14, 0, 0, 0, 0, 72, 73, 74, + 75, 76 +}; + +static const short yycheck[] = +{ + 0, 14, 6, 7, 8, 0, 10, 11, 35, 9, + 3, 10, 11, 13, 7, 18, 63, 32, 18, 18, + 20, 18, 38, 18, 24, 18, 18, 0, 9, 38, + 77, 34, 43, 14, 10, 35, 17, 47, 85, 30, + 53, 11, 89, 24, 47, 37, 50, 51, 47, 14, + 47, 64, 52, 34, 47, 47, 43, 12, 13, 26, + 15, 16, 29, 63, 11, 43, 46, 11, 35, 36, + 47, 118, 12, 40, 41, 14, 80, 77, 82, 21, + 47, 19, 30, 11, 11, 85, 9, 27, 28, 89, + 22, 14, 7, 43, 17, 99, 96, 4, 125, 103, + 147, 24, 149, 46, 25, 12, 13, 14, 15, 16, + 46, 124, 46, 43, 47, 46, 129, 46, 118, 10, + 8, 148, 126, 48, 128, 125, 11, 46, 46, 69, + 70, 71, 72, 73, 74, 75, 76, 33, 78, 79, + 4, 94, 4, 5, 6, -1, -1, 147, 148, 149, + 12, 13, 14, 15, 16, 4, 5, 6, 122, -1, + -1, -1, -1, 12, 13, 14, 15, 16, 4, 5, + 6, -1, 8, -1, -1, -1, 12, 13, 14, 15, + 16, 43, 44, 45, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 43, 44, 45, 18, -1, 20, + -1, -1, 23, -1, -1, -1, 27, 28, 44, 45, + 31, 32, 33, 34, -1, -1, 37, 38, 39, -1, + -1, 42, 43, 18, -1, 20, 47, -1, 23, -1, + -1, -1, 27, 28, -1, -1, 31, 32, 33, 34, + -1, -1, 37, -1, 39, -1, -1, 42, 43, 4, + 5, 6, 47, -1, -1, -1, -1, 12, 13, 14, + 15, 16 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 18, 20, 23, 27, 28, 31, 32, 33, 34, + 37, 39, 42, 43, 47, 50, 52, 56, 57, 67, + 68, 70, 64, 65, 66, 32, 56, 3, 7, 47, + 61, 67, 65, 65, 65, 67, 65, 65, 61, 51, + 52, 53, 64, 0, 38, 52, 9, 14, 17, 24, + 34, 60, 34, 67, 69, 26, 29, 35, 36, 40, + 41, 47, 71, 43, 10, 67, 47, 61, 61, 4, + 5, 6, 12, 13, 14, 15, 16, 43, 44, 45, + 30, 11, 14, 53, 54, 43, 57, 11, 43, 43, + 46, 11, 47, 14, 65, 65, 67, 64, 19, 21, + 72, 51, 64, 30, 8, 61, 61, 61, 61, 61, + 61, 61, 61, 51, 61, 61, 65, 65, 11, 51, + 22, 62, 63, 51, 7, 55, 60, 11, 37, 67, + 65, 43, 46, 65, 46, 51, 46, 47, 46, 62, + 46, 64, 57, 65, 65, 64, 58, 43, 25, 10, + 8, 11, 48, 51, 57, 51, 59, 46, 46 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.first_line = Rhs[1].first_line; \ + Current.first_column = Rhs[1].first_column; \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (cinluded). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylineno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylineno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: +#line 142 "jamgram.y" + { parse_save( yyvsp[0].parse ); } + break; + + case 4: +#line 153 "jamgram.y" + { yyval.parse = yyvsp[0].parse; } + break; + + case 5: +#line 155 "jamgram.y" + { yyval.parse = yyvsp[0].parse; } + break; + + case 6: +#line 159 "jamgram.y" + { yyval.parse = yyvsp[0].parse; } + break; + + case 7: +#line 161 "jamgram.y" + { yyval.parse = prules( yyvsp[-1].parse, yyvsp[0].parse ); } + break; + + case 8: +#line 163 "jamgram.y" + { yyval.parse = plocal( yyvsp[-3].parse, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 9: +#line 167 "jamgram.y" + { yyval.parse = pnull(); } + break; + + case 10: +#line 171 "jamgram.y" + { yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_SET; } + break; + + case 11: +#line 173 "jamgram.y" + { yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_APPEND; } + break; + + case 12: +#line 177 "jamgram.y" + { yyval.parse = yyvsp[-1].parse; } + break; + + case 13: +#line 179 "jamgram.y" + { yyval.parse = P0; } + break; + + case 14: +#line 183 "jamgram.y" + { yyval.number = 1; } + break; + + case 15: +#line 185 "jamgram.y" + { yyval.number = 0; } + break; + + case 16: +#line 189 "jamgram.y" + { yyval.parse = yyvsp[-1].parse; } + break; + + case 17: +#line 191 "jamgram.y" + { yyval.parse = pincl( yyvsp[-1].parse ); } + break; + + case 18: +#line 193 "jamgram.y" + { yyval.parse = prule( yyvsp[-2].string, yyvsp[-1].parse ); } + break; + + case 19: +#line 195 "jamgram.y" + { yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); } + break; + + case 20: +#line 197 "jamgram.y" + { yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); } + break; + + case 21: +#line 199 "jamgram.y" + { yyval.parse = yyvsp[-1].parse; } + break; + + case 22: +#line 201 "jamgram.y" + { yyval.parse = pfor( yyvsp[-5].string, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-6].number ); } + break; + + case 23: +#line 203 "jamgram.y" + { yyval.parse = pswitch( yyvsp[-3].parse, yyvsp[-1].parse ); } + break; + + case 24: +#line 205 "jamgram.y" + { yyval.parse = pif( yyvsp[-3].parse, yyvsp[-1].parse, pnull() ); } + break; + + case 25: +#line 207 "jamgram.y" + { yyval.parse = pmodule( yyvsp[-3].parse, yyvsp[-1].parse ); } + break; + + case 26: +#line 209 "jamgram.y" + { yyval.parse = pclass( yyvsp[-3].parse, yyvsp[-1].parse ); } + break; + + case 27: +#line 211 "jamgram.y" + { yyval.parse = pwhile( yyvsp[-3].parse, yyvsp[-1].parse ); } + break; + + case 28: +#line 213 "jamgram.y" + { yyval.parse = pif( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[0].parse ); } + break; + + case 29: +#line 215 "jamgram.y" + { yyval.parse = psetc( yyvsp[-2].string, yyvsp[0].parse, yyvsp[-1].parse, yyvsp[-4].number ); } + break; + + case 30: +#line 217 "jamgram.y" + { yyval.parse = pon( yyvsp[-1].parse, yyvsp[0].parse ); } + break; + + case 31: +#line 219 "jamgram.y" + { yymode( SCAN_STRING ); } + break; + + case 32: +#line 221 "jamgram.y" + { yymode( SCAN_NORMAL ); } + break; + + case 33: +#line 223 "jamgram.y" + { yyval.parse = psete( yyvsp[-6].string,yyvsp[-5].parse,yyvsp[-2].string,yyvsp[-7].number ); } + break; + + case 34: +#line 231 "jamgram.y" + { yyval.number = ASSIGN_SET; } + break; + + case 35: +#line 233 "jamgram.y" + { yyval.number = ASSIGN_APPEND; } + break; + + case 36: +#line 235 "jamgram.y" + { yyval.number = ASSIGN_DEFAULT; } + break; + + case 37: +#line 237 "jamgram.y" + { yyval.number = ASSIGN_DEFAULT; } + break; + + case 38: +#line 244 "jamgram.y" + { yyval.parse = peval( EXPR_EXISTS, yyvsp[0].parse, pnull() ); } + break; + + case 39: +#line 246 "jamgram.y" + { yyval.parse = peval( EXPR_EQUALS, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 40: +#line 248 "jamgram.y" + { yyval.parse = peval( EXPR_NOTEQ, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 41: +#line 250 "jamgram.y" + { yyval.parse = peval( EXPR_LESS, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 42: +#line 252 "jamgram.y" + { yyval.parse = peval( EXPR_LESSEQ, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 43: +#line 254 "jamgram.y" + { yyval.parse = peval( EXPR_MORE, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 44: +#line 256 "jamgram.y" + { yyval.parse = peval( EXPR_MOREEQ, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 45: +#line 258 "jamgram.y" + { yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 46: +#line 260 "jamgram.y" + { yyval.parse = peval( EXPR_AND, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 47: +#line 262 "jamgram.y" + { yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 48: +#line 264 "jamgram.y" + { yyval.parse = peval( EXPR_OR, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 49: +#line 266 "jamgram.y" + { yyval.parse = peval( EXPR_IN, yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 50: +#line 268 "jamgram.y" + { yyval.parse = peval( EXPR_NOT, yyvsp[0].parse, pnull() ); } + break; + + case 51: +#line 270 "jamgram.y" + { yyval.parse = yyvsp[-1].parse; } + break; + + case 52: +#line 281 "jamgram.y" + { yyval.parse = P0; } + break; + + case 53: +#line 283 "jamgram.y" + { yyval.parse = pnode( yyvsp[-1].parse, yyvsp[0].parse ); } + break; + + case 54: +#line 287 "jamgram.y" + { yyval.parse = psnode( yyvsp[-2].string, yyvsp[0].parse ); } + break; + + case 55: +#line 296 "jamgram.y" + { yyval.parse = pnode( P0, yyvsp[0].parse ); } + break; + + case 56: +#line 298 "jamgram.y" + { yyval.parse = pnode( yyvsp[0].parse, yyvsp[-2].parse ); } + break; + + case 57: +#line 308 "jamgram.y" + { yyval.parse = yyvsp[0].parse; yymode( SCAN_NORMAL ); } + break; + + case 58: +#line 312 "jamgram.y" + { yyval.parse = pnull(); yymode( SCAN_PUNCT ); } + break; + + case 59: +#line 314 "jamgram.y" + { yyval.parse = pappend( yyvsp[-1].parse, yyvsp[0].parse ); } + break; + + case 60: +#line 318 "jamgram.y" + { yyval.parse = plist( yyvsp[0].string ); } + break; + + case 61: +#line 319 "jamgram.y" + { yymode( SCAN_NORMAL ); } + break; + + case 62: +#line 320 "jamgram.y" + { yyval.parse = yyvsp[-1].parse; } + break; + + case 63: +#line 329 "jamgram.y" + { yyval.parse = prule( yyvsp[-1].string, yyvsp[0].parse ); } + break; + + case 64: +#line 331 "jamgram.y" + { yyval.parse = pon( yyvsp[-2].parse, prule( yyvsp[-1].string, yyvsp[0].parse ) ); } + break; + + case 65: +#line 333 "jamgram.y" + { yyval.parse = pon( yyvsp[-2].parse, yyvsp[0].parse ); } + break; + + case 66: +#line 343 "jamgram.y" + { yyval.number = 0; } + break; + + case 67: +#line 345 "jamgram.y" + { yyval.number = yyvsp[-1].number | yyvsp[0].number; } + break; + + case 68: +#line 349 "jamgram.y" + { yyval.number = EXEC_UPDATED; } + break; + + case 69: +#line 351 "jamgram.y" + { yyval.number = EXEC_TOGETHER; } + break; + + case 70: +#line 353 "jamgram.y" + { yyval.number = EXEC_IGNORE; } + break; + + case 71: +#line 355 "jamgram.y" + { yyval.number = EXEC_QUIETLY; } + break; + + case 72: +#line 357 "jamgram.y" + { yyval.number = EXEC_PIECEMEAL; } + break; + + case 73: +#line 359 "jamgram.y" + { yyval.number = EXEC_EXISTING; } + break; + + case 74: +#line 368 "jamgram.y" + { yyval.parse = pnull(); } + break; + + case 75: +#line 370 "jamgram.y" + { yyval.parse = yyvsp[0].parse; } + break; + + + } + +/* Line 991 of yacc.c. */ +#line 1621 "y.tab.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("syntax error, unexpected ") + 1; + yysize += yystrlen (yytname[yytype]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* Return failure if at end of input. */ + if (yychar == YYEOF) + { + /* Pop the error token. */ + YYPOPSTACK; + /* Pop the rest of the stack. */ + while (yyss < yyssp) + { + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + YYPOPSTACK; + } + YYABORT; + } + + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab2; + + +/*----------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action. | +`----------------------------------------------------*/ +yyerrlab1: + + /* Suppress GCC warning that yyerrlab1 is unused when no action + invokes YYERROR. */ +#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__) + __attribute__ ((__unused__)) +#endif + + + goto yyerrlab2; + + +/*---------------------------------------------------------------. +| yyerrlab2 -- pop states until the error token can be shifted. | +`---------------------------------------------------------------*/ +yyerrlab2: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + yyvsp--; + yystate = *--yyssp; + + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + + diff --git a/jam-files/engine/jamgram.h b/jam-files/engine/jamgram.h new file mode 100644 index 00000000..3cb76564 --- /dev/null +++ b/jam-files/engine/jamgram.h @@ -0,0 +1,140 @@ +/* A Bison parser, made by GNU Bison 1.875. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 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 { + _BANG_t = 258, + _BANG_EQUALS_t = 259, + _AMPER_t = 260, + _AMPERAMPER_t = 261, + _LPAREN_t = 262, + _RPAREN_t = 263, + _PLUS_EQUALS_t = 264, + _COLON_t = 265, + _SEMIC_t = 266, + _LANGLE_t = 267, + _LANGLE_EQUALS_t = 268, + _EQUALS_t = 269, + _RANGLE_t = 270, + _RANGLE_EQUALS_t = 271, + _QUESTION_EQUALS_t = 272, + _LBRACKET_t = 273, + _RBRACKET_t = 274, + ACTIONS_t = 275, + BIND_t = 276, + CASE_t = 277, + CLASS_t = 278, + DEFAULT_t = 279, + ELSE_t = 280, + EXISTING_t = 281, + FOR_t = 282, + IF_t = 283, + IGNORE_t = 284, + IN_t = 285, + INCLUDE_t = 286, + LOCAL_t = 287, + MODULE_t = 288, + ON_t = 289, + PIECEMEAL_t = 290, + QUIETLY_t = 291, + RETURN_t = 292, + RULE_t = 293, + SWITCH_t = 294, + TOGETHER_t = 295, + UPDATED_t = 296, + WHILE_t = 297, + _LBRACE_t = 298, + _BAR_t = 299, + _BARBAR_t = 300, + _RBRACE_t = 301, + ARG = 302, + STRING = 303 + }; +#endif +#define _BANG_t 258 +#define _BANG_EQUALS_t 259 +#define _AMPER_t 260 +#define _AMPERAMPER_t 261 +#define _LPAREN_t 262 +#define _RPAREN_t 263 +#define _PLUS_EQUALS_t 264 +#define _COLON_t 265 +#define _SEMIC_t 266 +#define _LANGLE_t 267 +#define _LANGLE_EQUALS_t 268 +#define _EQUALS_t 269 +#define _RANGLE_t 270 +#define _RANGLE_EQUALS_t 271 +#define _QUESTION_EQUALS_t 272 +#define _LBRACKET_t 273 +#define _RBRACKET_t 274 +#define ACTIONS_t 275 +#define BIND_t 276 +#define CASE_t 277 +#define CLASS_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 LOCAL_t 287 +#define MODULE_t 288 +#define ON_t 289 +#define PIECEMEAL_t 290 +#define QUIETLY_t 291 +#define RETURN_t 292 +#define RULE_t 293 +#define SWITCH_t 294 +#define TOGETHER_t 295 +#define UPDATED_t 296 +#define WHILE_t 297 +#define _LBRACE_t 298 +#define _BAR_t 299 +#define _BARBAR_t 300 +#define _RBRACE_t 301 +#define ARG 302 +#define STRING 303 + + + + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE yylval; + + + diff --git a/jam-files/engine/jamgram.y b/jam-files/engine/jamgram.y new file mode 100644 index 00000000..c26b1e1b --- /dev/null +++ b/jam-files/engine/jamgram.y @@ -0,0 +1,371 @@ +%token _BANG_t +%token _BANG_EQUALS_t +%token _AMPER_t +%token _AMPERAMPER_t +%token _LPAREN_t +%token _RPAREN_t +%token _PLUS_EQUALS_t +%token _COLON_t +%token _SEMIC_t +%token _LANGLE_t +%token _LANGLE_EQUALS_t +%token _EQUALS_t +%token _RANGLE_t +%token _RANGLE_EQUALS_t +%token _QUESTION_EQUALS_t +%token _LBRACKET_t +%token _RBRACKET_t +%token ACTIONS_t +%token BIND_t +%token CASE_t +%token CLASS_t +%token DEFAULT_t +%token ELSE_t +%token EXISTING_t +%token FOR_t +%token IF_t +%token IGNORE_t +%token IN_t +%token INCLUDE_t +%token LOCAL_t +%token MODULE_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, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * jamgram.yy - jam grammar + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 08/31/94 (seiwald) - Allow ?= as alias for "default =". + * 09/15/94 (seiwald) - if conditionals take only single arguments, so + * that 'if foo == bar' gives syntax error (use =). + * 02/11/95 (seiwald) - when scanning arguments to rules, only treat + * punctuation keywords as keywords. All arg lists + * are terminated with punctuation keywords. + * + * 09/11/00 (seiwald) - Support for function calls: + * + * Rules now return lists (LIST *), rather than void. + * + * New "[ rule ]" syntax evals rule into a LIST. + * + * Lists are now generated by compile_list() and + * compile_append(), and any other rule that indirectly + * makes a list, rather than being built directly here, + * so that lists values can contain rule evaluations. + * + * New 'return' rule sets the return value, though + * other statements also may have return values. + * + * 'run' production split from 'block' production so + * that empty blocks can be handled separately. + */ + +%token ARG STRING + +%left _BARBAR_t _BAR_t +%left _AMPERAMPER_t _AMPER_t +%left _EQUALS_t _BANG_EQUALS_t IN_t +%left _LANGLE_t _LANGLE_EQUALS_t _RANGLE_t _RANGLE_EQUALS_t +%left _BANG_t + +%{ +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) +# 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 pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( compile_class,l,r,P0,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( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) +# 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 : null + { $$.parse = $1.parse; } + | rules + { $$.parse = $1.parse; } + ; + +rules : rule + { $$.parse = $1.parse; } + | rule rules + { $$.parse = prules( $1.parse, $2.parse ); } + | LOCAL_t list assign_list_opt _SEMIC_t block + { $$.parse = plocal( $2.parse, $3.parse, $5.parse ); } + ; + +null : /* empty */ + { $$.parse = pnull(); } + ; + +assign_list_opt : _EQUALS_t list + { $$.parse = $2.parse; $$.number = ASSIGN_SET; } + | null + { $$.parse = $1.parse; $$.number = ASSIGN_APPEND; } + ; + +arglist_opt : _LPAREN_t lol _RPAREN_t + { $$.parse = $2.parse; } + | + { $$.parse = P0; } + ; + +local_opt : LOCAL_t + { $$.number = 1; } + | /* empty */ + { $$.number = 0; } + ; + +rule : _LBRACE_t block _RBRACE_t + { $$.parse = $2.parse; } + | INCLUDE_t list _SEMIC_t + { $$.parse = pincl( $2.parse ); } + | ARG lol _SEMIC_t + { $$.parse = prule( $1.string, $2.parse ); } + | arg assign list _SEMIC_t + { $$.parse = pset( $1.parse, $3.parse, $2.number ); } + | arg ON_t list assign list _SEMIC_t + { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); } + | RETURN_t list _SEMIC_t + { $$.parse = $2.parse; } + | FOR_t local_opt ARG IN_t list _LBRACE_t block _RBRACE_t + { $$.parse = pfor( $3.string, $5.parse, $7.parse, $2.number ); } + | 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() ); } + | MODULE_t list _LBRACE_t block _RBRACE_t + { $$.parse = pmodule( $2.parse, $4.parse ); } + | CLASS_t lol _LBRACE_t block _RBRACE_t + { $$.parse = pclass( $2.parse, $4.parse ); } + | WHILE_t expr _LBRACE_t block _RBRACE_t + { $$.parse = pwhile( $2.parse, $4.parse ); } + | IF_t expr _LBRACE_t block _RBRACE_t ELSE_t rule + { $$.parse = pif( $2.parse, $4.parse, $7.parse ); } + | local_opt RULE_t ARG arglist_opt rule + { $$.parse = psetc( $3.string, $5.parse, $4.parse, $1.number ); } + | ON_t arg rule + { $$.parse = pon( $2.parse, $3.parse ); } + | ACTIONS_t eflags ARG bindlist _LBRACE_t + { yymode( SCAN_STRING ); } + STRING + { yymode( SCAN_NORMAL ); } + _RBRACE_t + { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); } + ; + +/* + * assign - = or += + */ + +assign : _EQUALS_t + { $$.number = ASSIGN_SET; } + | _PLUS_EQUALS_t + { $$.number = ASSIGN_APPEND; } + | _QUESTION_EQUALS_t + { $$.number = ASSIGN_DEFAULT; } + | DEFAULT_t _EQUALS_t + { $$.number = ASSIGN_DEFAULT; } + ; + +/* + * expr - an expression for if + */ +expr : arg + { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); } + | expr _EQUALS_t expr + { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); } + | expr _BANG_EQUALS_t expr + { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); } + | expr _LANGLE_t expr + { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); } + | expr _LANGLE_EQUALS_t expr + { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); } + | expr _RANGLE_t expr + { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); } + | expr _RANGLE_EQUALS_t expr + { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); } + | expr _AMPER_t expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr _AMPERAMPER_t expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr _BAR_t expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | expr _BARBAR_t expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | arg IN_t list + { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); } + | _BANG_t expr + { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); } + | _LPAREN_t expr _RPAREN_t + { $$.parse = $2.parse; } + ; + + +/* + * cases - action elements inside a 'switch' + * case - a single action element inside a 'switch' + * right-recursive rule so cases can be examined in order. + */ + +cases : /* empty */ + { $$.parse = P0; } + | case cases + { $$.parse = pnode( $1.parse, $2.parse ); } + ; + +case : CASE_t ARG _COLON_t block + { $$.parse = psnode( $2.string, $4.parse ); } + ; + +/* + * lol - list of lists + * right-recursive rule so that lists can be added in order. + */ + +lol : list + { $$.parse = pnode( P0, $1.parse ); } + | list _COLON_t lol + { $$.parse = pnode( $3.parse, $1.parse ); } + ; + +/* + * list - zero or more args in a LIST + * listp - list (in puncutation only mode) + * arg - one ARG or function call + */ + +list : listp + { $$.parse = $1.parse; yymode( SCAN_NORMAL ); } + ; + +listp : /* empty */ + { $$.parse = pnull(); yymode( SCAN_PUNCT ); } + | listp arg + { $$.parse = pappend( $1.parse, $2.parse ); } + ; + +arg : ARG + { $$.parse = plist( $1.string ); } + | _LBRACKET_t { yymode( SCAN_NORMAL ); } func _RBRACKET_t + { $$.parse = $3.parse; } + ; + +/* + * func - a function call (inside []) + * This needs to be split cleanly out of 'rule' + */ + +func : arg lol + { $$.parse = prule( $1.string, $2.parse ); } + | ON_t arg arg lol + { $$.parse = pon( $2.parse, prule( $3.string, $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 = EXEC_UPDATED; } + | TOGETHER_t + { $$.number = EXEC_TOGETHER; } + | IGNORE_t + { $$.number = EXEC_IGNORE; } + | QUIETLY_t + { $$.number = EXEC_QUIETLY; } + | PIECEMEAL_t + { $$.number = EXEC_PIECEMEAL; } + | EXISTING_t + { $$.number = EXEC_EXISTING; } + ; + + +/* + * bindlist - list of variable to bind for an action + */ + +bindlist : /* empty */ + { $$.parse = pnull(); } + | BIND_t list + { $$.parse = $2.parse; } + ; diff --git a/jam-files/engine/jamgram.yy b/jam-files/engine/jamgram.yy new file mode 100644 index 00000000..15243487 --- /dev/null +++ b/jam-files/engine/jamgram.yy @@ -0,0 +1,329 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * jamgram.yy - jam grammar + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 08/31/94 (seiwald) - Allow ?= as alias for "default =". + * 09/15/94 (seiwald) - if conditionals take only single arguments, so + * that 'if foo == bar' gives syntax error (use =). + * 02/11/95 (seiwald) - when scanning arguments to rules, only treat + * punctuation keywords as keywords. All arg lists + * are terminated with punctuation keywords. + * + * 09/11/00 (seiwald) - Support for function calls: + * + * Rules now return lists (LIST *), rather than void. + * + * New "[ rule ]" syntax evals rule into a LIST. + * + * Lists are now generated by compile_list() and + * compile_append(), and any other rule that indirectly + * makes a list, rather than being built directly here, + * so that lists values can contain rule evaluations. + * + * New 'return' rule sets the return value, though + * other statements also may have return values. + * + * 'run' production split from 'block' production so + * that empty blocks can be handled separately. + */ + +%token ARG STRING + +%left `||` `|` +%left `&&` `&` +%left `=` `!=` `in` +%left `<` `<=` `>` `>=` +%left `!` + +%{ +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "compile.h" +#include "newstr.h" +#include "rules.h" + +# define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ + +# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define P0 (PARSE *)0 +# define S0 (char *)0 + +# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) +# 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 pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( compile_class,l,r,P0,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( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) +# 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 : null + { $$.parse = $1.parse; } + | rules + { $$.parse = $1.parse; } + ; + +rules : rule + { $$.parse = $1.parse; } + | rule rules + { $$.parse = prules( $1.parse, $2.parse ); } + | `local` list assign_list_opt `;` block + { $$.parse = plocal( $2.parse, $3.parse, $5.parse ); } + ; + +null : /* empty */ + { $$.parse = pnull(); } + ; + +assign_list_opt : `=` list + { $$.parse = $2.parse; $$.number = ASSIGN_SET; } + | null + { $$.parse = $1.parse; $$.number = ASSIGN_APPEND; } + ; + +arglist_opt : `(` lol `)` + { $$.parse = $2.parse; } + | + { $$.parse = P0; } + ; + +local_opt : `local` + { $$.number = 1; } + | /* empty */ + { $$.number = 0; } + ; + +rule : `{` block `}` + { $$.parse = $2.parse; } + | `include` list `;` + { $$.parse = pincl( $2.parse ); } + | ARG lol `;` + { $$.parse = prule( $1.string, $2.parse ); } + | arg assign list `;` + { $$.parse = pset( $1.parse, $3.parse, $2.number ); } + | arg `on` list assign list `;` + { $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); } + | `return` list `;` + { $$.parse = $2.parse; } + | `for` local_opt ARG `in` list `{` block `}` + { $$.parse = pfor( $3.string, $5.parse, $7.parse, $2.number ); } + | `switch` list `{` cases `}` + { $$.parse = pswitch( $2.parse, $4.parse ); } + | `if` expr `{` block `}` + { $$.parse = pif( $2.parse, $4.parse, pnull() ); } + | `module` list `{` block `}` + { $$.parse = pmodule( $2.parse, $4.parse ); } + | `class` lol `{` block `}` + { $$.parse = pclass( $2.parse, $4.parse ); } + | `while` expr `{` block `}` + { $$.parse = pwhile( $2.parse, $4.parse ); } + | `if` expr `{` block `}` `else` rule + { $$.parse = pif( $2.parse, $4.parse, $7.parse ); } + | local_opt `rule` ARG arglist_opt rule + { $$.parse = psetc( $3.string, $5.parse, $4.parse, $1.number ); } + | `on` arg rule + { $$.parse = pon( $2.parse, $3.parse ); } + | `actions` eflags ARG bindlist `{` + { yymode( SCAN_STRING ); } + STRING + { yymode( SCAN_NORMAL ); } + `}` + { $$.parse = psete( $3.string,$4.parse,$7.string,$2.number ); } + ; + +/* + * assign - = or += + */ + +assign : `=` + { $$.number = ASSIGN_SET; } + | `+=` + { $$.number = ASSIGN_APPEND; } + | `?=` + { $$.number = ASSIGN_DEFAULT; } + | `default` `=` + { $$.number = ASSIGN_DEFAULT; } + ; + +/* + * expr - an expression for if + */ +expr : arg + { $$.parse = peval( EXPR_EXISTS, $1.parse, pnull() ); } + | expr `=` expr + { $$.parse = peval( EXPR_EQUALS, $1.parse, $3.parse ); } + | expr `!=` expr + { $$.parse = peval( EXPR_NOTEQ, $1.parse, $3.parse ); } + | expr `<` expr + { $$.parse = peval( EXPR_LESS, $1.parse, $3.parse ); } + | expr `<=` expr + { $$.parse = peval( EXPR_LESSEQ, $1.parse, $3.parse ); } + | expr `>` expr + { $$.parse = peval( EXPR_MORE, $1.parse, $3.parse ); } + | expr `>=` expr + { $$.parse = peval( EXPR_MOREEQ, $1.parse, $3.parse ); } + | expr `&` expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr `&&` expr + { $$.parse = peval( EXPR_AND, $1.parse, $3.parse ); } + | expr `|` expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | expr `||` expr + { $$.parse = peval( EXPR_OR, $1.parse, $3.parse ); } + | arg `in` list + { $$.parse = peval( EXPR_IN, $1.parse, $3.parse ); } + | `!` expr + { $$.parse = peval( EXPR_NOT, $2.parse, pnull() ); } + | `(` expr `)` + { $$.parse = $2.parse; } + ; + + +/* + * cases - action elements inside a 'switch' + * case - a single action element inside a 'switch' + * right-recursive rule so cases can be examined in order. + */ + +cases : /* empty */ + { $$.parse = P0; } + | case cases + { $$.parse = pnode( $1.parse, $2.parse ); } + ; + +case : `case` ARG `:` block + { $$.parse = psnode( $2.string, $4.parse ); } + ; + +/* + * lol - list of lists + * right-recursive rule so that lists can be added in order. + */ + +lol : list + { $$.parse = pnode( P0, $1.parse ); } + | list `:` lol + { $$.parse = pnode( $3.parse, $1.parse ); } + ; + +/* + * list - zero or more args in a LIST + * listp - list (in puncutation only mode) + * arg - one ARG or function call + */ + +list : listp + { $$.parse = $1.parse; yymode( SCAN_NORMAL ); } + ; + +listp : /* empty */ + { $$.parse = pnull(); yymode( SCAN_PUNCT ); } + | listp arg + { $$.parse = pappend( $1.parse, $2.parse ); } + ; + +arg : ARG + { $$.parse = plist( $1.string ); } + | `[` { yymode( SCAN_NORMAL ); } func `]` + { $$.parse = $3.parse; } + ; + +/* + * func - a function call (inside []) + * This needs to be split cleanly out of 'rule' + */ + +func : arg lol + { $$.parse = prule( $1.string, $2.parse ); } + | `on` arg arg lol + { $$.parse = pon( $2.parse, prule( $3.string, $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 = EXEC_UPDATED; } + | `together` + { $$.number = EXEC_TOGETHER; } + | `ignore` + { $$.number = EXEC_IGNORE; } + | `quietly` + { $$.number = EXEC_QUIETLY; } + | `piecemeal` + { $$.number = EXEC_PIECEMEAL; } + | `existing` + { $$.number = EXEC_EXISTING; } + ; + + +/* + * bindlist - list of variable to bind for an action + */ + +bindlist : /* empty */ + { $$.parse = pnull(); } + | `bind` list + { $$.parse = $2.parse; } + ; + + diff --git a/jam-files/engine/jamgramtab.h b/jam-files/engine/jamgramtab.h new file mode 100644 index 00000000..a0fd43f6 --- /dev/null +++ b/jam-files/engine/jamgramtab.h @@ -0,0 +1,44 @@ + { "!", _BANG_t }, + { "!=", _BANG_EQUALS_t }, + { "&", _AMPER_t }, + { "&&", _AMPERAMPER_t }, + { "(", _LPAREN_t }, + { ")", _RPAREN_t }, + { "+=", _PLUS_EQUALS_t }, + { ":", _COLON_t }, + { ";", _SEMIC_t }, + { "<", _LANGLE_t }, + { "<=", _LANGLE_EQUALS_t }, + { "=", _EQUALS_t }, + { ">", _RANGLE_t }, + { ">=", _RANGLE_EQUALS_t }, + { "?=", _QUESTION_EQUALS_t }, + { "[", _LBRACKET_t }, + { "]", _RBRACKET_t }, + { "actions", ACTIONS_t }, + { "bind", BIND_t }, + { "case", CASE_t }, + { "class", CLASS_t }, + { "default", DEFAULT_t }, + { "else", ELSE_t }, + { "existing", EXISTING_t }, + { "for", FOR_t }, + { "if", IF_t }, + { "ignore", IGNORE_t }, + { "in", IN_t }, + { "include", INCLUDE_t }, + { "local", LOCAL_t }, + { "module", MODULE_t }, + { "on", ON_t }, + { "piecemeal", PIECEMEAL_t }, + { "quietly", QUIETLY_t }, + { "return", RETURN_t }, + { "rule", RULE_t }, + { "switch", SWITCH_t }, + { "together", TOGETHER_t }, + { "updated", UPDATED_t }, + { "while", WHILE_t }, + { "{", _LBRACE_t }, + { "|", _BAR_t }, + { "||", _BARBAR_t }, + { "}", _RBRACE_t }, diff --git a/jam-files/engine/lists.c b/jam-files/engine/lists.c new file mode 100644 index 00000000..ebabb63e --- /dev/null +++ b/jam-files/engine/lists.c @@ -0,0 +1,339 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "newstr.h" +# include "lists.h" + +/* + * lists.c - maintain lists of strings + * + * This implementation essentially uses a singly linked list, but + * guarantees that the head element of every list has a valid pointer + * to the tail of the list, so the new elements can efficiently and + * properly be appended to the end of a list. + * + * To avoid massive allocation, list_free() just tacks the whole freed + * chain onto freelist and list_new() looks on freelist first for an + * available list struct. list_free() does not free the strings in the + * chain: it lazily lets list_new() do so. + * + * 08/23/94 (seiwald) - new list_append() + * 09/07/00 (seiwald) - documented lol_*() functions + */ + +static LIST *freelist = 0; /* junkpile for list_free() */ + +/* + * list_append() - append a list onto another one, returning total + */ + +LIST * list_append( LIST * l, LIST * nl ) +{ + if ( !nl ) + { + /* Just return l */ + } + else if ( !l ) + { + l = nl; + } + else + { + /* Graft two non-empty lists. */ + l->tail->next = nl; + l->tail = nl->tail; + } + + return l; +} + +/* + * list_new() - tack a string onto the end of a list of strings + */ + +LIST * list_new( LIST * head, char * string ) +{ + LIST * l; + + if ( DEBUG_LISTS ) + printf( "list > %s <\n", string ); + + /* Get list struct from freelist, if one available. */ + /* Otherwise allocate. */ + /* If from freelist, must free string first */ + + if ( freelist ) + { + l = freelist; + freestr( l->string ); + freelist = freelist->next; + } + else + { + l = (LIST *)BJAM_MALLOC( sizeof( LIST ) ); + } + + /* If first on chain, head points here. */ + /* If adding to chain, tack us on. */ + /* Tail must point to this new, last element. */ + + if ( !head ) head = l; + else head->tail->next = l; + head->tail = l; + l->next = 0; + + l->string = string; + + return head; +} + + +/* + * list_copy() - copy a whole list of strings (nl) onto end of another (l). + */ + +LIST * list_copy( LIST * l, LIST * nl ) +{ + for ( ; nl; nl = list_next( nl ) ) + l = list_new( l, copystr( nl->string ) ); + return l; +} + + +/* + * list_sublist() - copy a subset of a list of strings. + */ + +LIST * list_sublist( LIST * l, int start, int count ) +{ + LIST * nl = 0; + for ( ; l && start--; l = list_next( l ) ); + for ( ; l && count--; l = list_next( l ) ) + nl = list_new( nl, copystr( l->string ) ); + return nl; +} + + +static int str_ptr_compare( void const * va, void const * vb ) +{ + char * a = *( (char * *)va ); + char * b = *( (char * *)vb ); + return strcmp(a, b); +} + + +LIST * list_sort( LIST * l ) +{ + int len; + int ii; + char * * strings; + LIST * listp; + LIST * result = 0; + + if ( !l ) + return L0; + + len = list_length( l ); + strings = (char * *)BJAM_MALLOC( len * sizeof(char*) ); + + listp = l; + for ( ii = 0; ii < len; ++ii ) + { + strings[ ii ] = listp->string; + listp = listp->next; + } + + qsort( strings, len, sizeof( char * ), str_ptr_compare ); + + for ( ii = 0; ii < len; ++ii ) + result = list_append( result, list_new( 0, strings[ ii ] ) ); + + BJAM_FREE( strings ); + + return result; +} + + +/* + * 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_pop_front() - remove the front element from a list of strings + */ + +LIST * list_pop_front( LIST * l ) +{ + LIST * result = l->next; + if ( result ) + { + result->tail = l->tail; + l->next = L0; + l->tail = l; + } + list_free( l ); + return result; +} + + +/* + * list_print() - print a list of strings to stdout + */ + +void list_print( LIST * l ) +{ + LIST * p = 0; + for ( ; l; p = l, l = list_next( l ) ) + if ( p ) + printf( "%s ", p->string ); + if ( p ) + printf( "%s", p->string ); +} + + +/* + * list_length() - return the number of items in the list + */ + +int list_length( LIST * l ) +{ + int n = 0; + for ( ; l; l = list_next( l ), ++n ); + return n; +} + + +int list_in( LIST * l, char * value ) +{ + for ( ; l; l = l->next ) + if ( strcmp( l->string, value ) == 0 ) + return 1; + return 0; +} + + +LIST * list_unique( LIST * sorted_list ) +{ + LIST * result = 0; + LIST * last_added = 0; + + for ( ; sorted_list; sorted_list = sorted_list->next ) + { + if ( !last_added || strcmp( sorted_list->string, last_added->string ) != 0 ) + { + result = list_new( result, sorted_list->string ); + last_added = sorted_list; + } + } + return result; +} + + +/* + * 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 ] ); + } +} + +#ifdef HAVE_PYTHON + +PyObject *list_to_python(LIST *l) +{ + PyObject *result = PyList_New(0); + + for (; l; l = l->next) + { + PyObject* s = PyString_FromString(l->string); + PyList_Append(result, s); + Py_DECREF(s); + } + + return result; +} + +LIST *list_from_python(PyObject *l) +{ + LIST * result = 0; + + Py_ssize_t i, n; + n = PySequence_Size(l); + for (i = 0; i < n; ++i) + { + PyObject *v = PySequence_GetItem(l, i); + result = list_new (result, newstr (PyString_AsString(v))); + Py_DECREF(v); + } + + return result; +} + +#endif diff --git a/jam-files/engine/lists.h b/jam-files/engine/lists.h new file mode 100644 index 00000000..1dc59827 --- /dev/null +++ b/jam-files/engine/lists.h @@ -0,0 +1,108 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * lists.h - the LIST structure and routines to manipulate them + * + * The whole of jam relies on lists of strings as a datatype. This + * module, in conjunction with newstr.c, handles these relatively + * efficiently. + * + * Structures defined: + * + * LIST - list of strings + * LOL - list of LISTs + * + * External routines: + * + * list_append() - append a list onto another one, returning total + * list_new() - tack a string onto the end of a list of strings + * list_copy() - copy a whole list of strings + * list_sublist() - copy a subset of a list of strings + * list_free() - free a list of strings + * list_print() - print a list of strings to stdout + * list_length() - return the number of items in the list + * + * lol_init() - initialize a LOL (list of lists) + * lol_add() - append a LIST onto an LOL + * lol_free() - free the LOL and its LISTs + * lol_get() - return one of the LISTs in the LOL + * lol_print() - debug print LISTS separated by ":" + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 08/23/94 (seiwald) - new list_append() + */ + +#ifndef LISTS_DWA20011022_H +# define LISTS_DWA20011022_H + +#ifdef HAVE_PYTHON +#include <Python.h> +#endif + +/* + * LIST - list of strings + */ + +typedef struct _list LIST; + +struct _list { + LIST *next; + LIST *tail; /* only valid in head node */ + char *string; /* private copy */ +}; + +/* + * LOL - list of LISTs + */ + +typedef struct _lol LOL; + +# define LOL_MAX 19 + +struct _lol { + int count; + LIST *list[ LOL_MAX ]; +}; + +LIST * list_append( LIST *l, LIST *nl ); +LIST * list_copy( LIST *l, LIST *nl ); +void list_free( LIST *head ); +LIST * list_new( LIST *head, char *string ); +void list_print( LIST *l ); +int list_length( LIST *l ); +LIST * list_sublist( LIST *l, int start, int count ); +LIST * list_pop_front( LIST *l ); +LIST * list_sort( LIST *l); +LIST * list_unique( LIST *sorted_list); +int list_in(LIST* l, char* value); + +# 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 ); +void lol_build( LOL* lol, char** elements ); + +#ifdef HAVE_PYTHON + +PyObject *list_to_python(LIST *l); +LIST *list_from_python(PyObject *l); + +#endif + +#endif + diff --git a/jam-files/engine/make.c b/jam-files/engine/make.c new file mode 100644 index 00000000..c871f0be --- /dev/null +++ b/jam-files/engine/make.c @@ -0,0 +1,814 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * 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 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 are not seen as being dependent on + * themselves. + * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets. + * 02/02/95 (seiwald) - propagate leaf source time for new LEAVES rule. + * 02/14/95 (seiwald) - NOUPDATE rule means don't update existing target. + * 08/22/95 (seiwald) - NOUPDATE targets immune to anyhow (-a) flag. + * 09/06/00 (seiwald) - NOCARE affects targets with sources/actions. + * 03/02/01 (seiwald) - reverse NOCARE change. + * 03/14/02 (seiwald) - TEMPORARY targets no longer take on parents age. + * 03/16/02 (seiwald) - support for -g (reorder builds by source time). + */ + +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "variable.h" +#include "rules.h" + +#ifdef OPT_HEADER_CACHE_EXT + #include "hcache.h" +#endif + +#include "search.h" +#include "newstr.h" +#include "make.h" +#include "headers.h" +#include "command.h" +#include <assert.h> + +#ifndef max + #define max( a,b ) ((a)>(b)?(a):(b)) +#endif + +static TARGETS * make0sort( TARGETS * c ); + +#ifdef OPT_GRAPH_DEBUG_EXT + static void dependGraphOutput( TARGET * t, int depth ); +#endif + +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 */ + "rebuild", /* T_FATE_REBUILD */ + "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) ( " " + ( x > 20 ? 0 : 20-x ) ) + + +/* + * make() - make a target, given its name. + */ + +int make( int n_targets, char const * * 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 ) ); + + /* First bind all targets with LOCATE_TARGET setting. This is needed to + * correctly handle dependencies to generated headers. + */ + bind_explicitly_located_targets(); + + { + PROFILE_ENTER( MAKE_MAKE0 ); + for ( i = 0; i < n_targets; ++i ) + make0( bindtarget( targets[ i ] ), 0, 0, counts, anyhow ); + PROFILE_EXIT( MAKE_MAKE0 ); + } + +#ifdef OPT_GRAPH_DEBUG_EXT + if ( DEBUG_GRAPH ) + for ( i = 0; i < n_targets; ++i ) + dependGraphOutput( bindtarget( targets[ i ] ), 0 ); +#endif + + if ( DEBUG_MAKE ) + { + if ( counts->targets ) + printf( "...found %d target%s...\n", counts->targets, + counts->targets > 1 ? "s" : "" ); + if ( counts->temp ) + printf( "...using %d temp target%s...\n", counts->temp, + counts->temp > 1 ? "s" : "" ); + if ( counts->updating ) + printf( "...updating %d target%s...\n", counts->updating, + counts->updating > 1 ? "s" : "" ); + if ( counts->cantfind ) + printf( "...can't find %d target%s...\n", counts->cantfind, + counts->cantfind > 1 ? "s" : "" ); + if ( counts->cantmake ) + printf( "...can't make %d target%s...\n", counts->cantmake, + counts->cantmake > 1 ? "s" : "" ); + } + +#ifdef OPT_HEADER_CACHE_EXT + hcache_done(); +#endif + + status = counts->cantfind || counts->cantmake; + + { + PROFILE_ENTER( MAKE_MAKE1 ); + for ( i = 0; i < n_targets; ++i ) + status |= make1( bindtarget( targets[ i ] ) ); + PROFILE_EXIT( MAKE_MAKE1 ); + } + + return status; +} + + +/* Force any dependants of t that have already at least begun being visited by + * make0() to be updated. + */ + +static void update_dependants( TARGET * t ) +{ + TARGETS * q; + + for ( q = t->dependants; q; q = q->next ) + { + TARGET * p = q->target; + char fate0 = p->fate; + + /* If we have already at least begun visiting it and we are not already + * rebuilding it for other reasons. + */ + if ( ( fate0 != T_FATE_INIT ) && ( fate0 < T_FATE_BUILD ) ) + { + p->fate = T_FATE_UPDATE; + + if ( DEBUG_FATE ) + { + printf( "fate change %s from %s to %s (as dependant of %s)\n", + p->name, target_fate[ (int) fate0 ], target_fate[ (int) p->fate ], t->name ); + } + + /* If we are done visiting it, go back and make sure its dependants + * get rebuilt. + */ + if ( fate0 > T_FATE_MAKING ) + update_dependants( p ); + } + } +} + + +/* + * Make sure that all of t's rebuilds get rebuilt. + */ + +static void force_rebuilds( TARGET * t ) +{ + TARGETS * d; + for ( d = t->rebuilds; d; d = d->next ) + { + TARGET * r = d->target; + + /* If it is not already being rebuilt for other reasons. */ + if ( r->fate < T_FATE_BUILD ) + { + if ( DEBUG_FATE ) + printf( "fate change %s from %s to %s (by rebuild)\n", + r->name, target_fate[ (int) r->fate ], target_fate[ T_FATE_REBUILD ] ); + + /* Force rebuild it. */ + r->fate = T_FATE_REBUILD; + + /* And make sure its dependants are updated too. */ + update_dependants( r ); + } + } +} + + +/* + * make0() - bind and scan everything to make a TARGET. + * + * Recursively binds a target, searches for #included headers, calls itself on + * those headers and any dependencies. + */ + +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; + TARGET * ptime = t; + time_t last; + time_t leaf; + time_t hlast; + int fate; + char const * flag = ""; + SETTINGS * s; + +#ifdef OPT_GRAPH_DEBUG_EXT + int savedFate, oldTimeStamp; +#endif + + if ( DEBUG_MAKEPROG ) + printf( "make\t--\t%s%s\n", spaces( depth ), t->name ); + + /* + * 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 is a file). */ + if ( ( t->binding == T_BIND_UNBOUND ) && !( t->flags & T_FLAG_NOTFILE ) ) + { + char * another_target; + t->boundname = search( t->name, &t->time, &another_target, + t->flags & T_FLAG_ISFILE ); + /* If it was detected that this target refers to an already existing and + * bound one, we add an include dependency, so that every target + * depending on us will depend on that other target as well. + */ + if ( another_target ) + target_include( t, bindtarget( another_target ) ); + + 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 does not 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; + } + +#ifdef OPT_SEMAPHORE + { + LIST * var = var_get( "JAM_SEMAPHORE" ); + if ( var ) + { + TARGET * semaphore = bindtarget( var->string ); + semaphore->progress = T_MAKE_SEMAPHORE; + t->semaphore = semaphore; + } + } +#endif + + /* 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_BIND ) + { + if ( strcmp( t->name, t->boundname ) ) + printf( "bind\t--\t%s%s: %s\n", + spaces( depth ), t->name, t->boundname ); + + switch ( t->binding ) + { + case T_BIND_UNBOUND: + case T_BIND_MISSING: + case T_BIND_PARENTS: + printf( "time\t--\t%s%s: %s\n", + spaces( depth ), t->name, target_bind[ (int) 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() dependencies & headers. + */ + + /* Step 3a: recursively make0() dependencies. */ + for ( c = t->depends; c; c = c->next ) + { + int internal = t->flags & T_FLAG_INTERNAL; + + /* 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 dependencies' includes to our direct dependencies. */ + { + TARGETS * 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 dependencies' 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 ); + +#ifdef OPT_GRAPH_DEBUG_EXT + if ( DEBUG_FATE ) + if ( fate < c->target->fate ) + printf( "fate change %s from %s to %s by dependency %s\n", + t->name, target_fate[(int) fate], target_fate[(int) c->target->fate], + c->target->name ); +#endif + } + + /* 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, mark it as having eternally old dependencies. + * Do not inherit our fate from our dependencies. Decide fate based only on + * other flags and our binding (done later). + */ + if ( t->flags & T_FLAG_NOUPDATE ) + { +#ifdef OPT_GRAPH_DEBUG_EXT + if ( DEBUG_FATE ) + if ( fate != T_FATE_STABLE ) + printf( "fate change %s back to stable, NOUPDATE.\n", t->name + ); +#endif + + last = 0; + t->time = 0; + + /* Do not inherit our fate from our dependencies. Decide fate based only + * upon other flags and our binding (done later). + */ + fate = T_FATE_STABLE; + } + + /* Step 4d: determine fate: rebuild target or what? */ + + /* + In English: + If can not find or make child, can not 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. + */ + +#ifdef OPT_GRAPH_DEBUG_EXT + savedFate = fate; + oldTimeStamp = 0; +#endif + + 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 ) ) + { +#ifdef OPT_GRAPH_DEBUG_EXT + oldTimeStamp = 1; +#endif + fate = T_FATE_OUTDATED; + } + else if ( ( t->binding == T_BIND_PARENTS ) && ( last > p->time ) ) + { +#ifdef OPT_GRAPH_DEBUG_EXT + oldTimeStamp = 1; +#endif + 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 ) ) + { +#ifdef OPT_GRAPH_DEBUG_EXT + oldTimeStamp = 1; +#endif + fate = T_FATE_NEWER; + } + else + { + fate = T_FATE_STABLE; + } +#ifdef OPT_GRAPH_DEBUG_EXT + if ( DEBUG_FATE && ( fate != savedFate ) ) + { + if ( savedFate == T_FATE_STABLE ) + printf( "fate change %s set to %s%s\n", t->name, + target_fate[ fate ], oldTimeStamp ? " (by timestamp)" : "" ); + else + printf( "fate change %s from %s to %s%s\n", t->name, + target_fate[ savedFate ], target_fate[ fate ], + oldTimeStamp ? " (by timestamp)" : "" ); + } +#endif + + /* Step 4e: handle missing files */ + /* If it is missing and there are no actions to create it, boom. */ + /* If we can not make a target we do not care about it, okay. */ + /* We could insist that there are updating actions for all missing */ + /* files, but if they have dependencies we just pretend it is a NOTFILE. */ + + if ( ( fate == T_FATE_MISSING ) && !t->actions && !t->depends ) + { + if ( t->flags & T_FLAG_NOCARE ) + { +#ifdef OPT_GRAPH_DEBUG_EXT + if ( DEBUG_FATE ) + printf( "fate change %s to STABLE from %s, " + "no actions, no dependencies and do not care\n", + t->name, target_fate[ fate ] ); +#endif + fate = T_FATE_STABLE; + } + else + { + printf( "don't know how to make %s\n", t->name ); + fate = T_FATE_CANTFIND; + } + } + + /* Step 4f: propagate dependencies' 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 ; + /* This target's fate may have been updated by virtue of following some + * target's rebuilds list, so only allow it to be increased to the fate we + * have calculated. Otherwise, grab its new fate. + */ + if ( fate > t->fate ) + t->fate = fate; + else + fate = t->fate; + + /* Step 4g: if this target needs to be built, force rebuild everything in + * this target's rebuilds list. + */ + if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) ) + force_rebuilds( t ); + + /* + * Step 5: sort dependencies by their update time. + */ + + if ( globs.newestfirst ) + t->depends = make0sort( t->depends ); + + /* + * Step 6: a little harmless tabulating for tracing purposes + */ + + /* Do not count or report interal includes nodes. */ + if ( t->flags & T_FLAG_INTERNAL ) + return; + + if ( counts ) + { +#ifdef OPT_IMPROVED_PATIENCE_EXT + ++counts->targets; +#else + if ( !( ++counts->targets % 1000 ) && DEBUG_MAKE ) + printf( "...patience...\n" ); +#endif + + 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[ (int) t->fate ], + spaces( depth ), t->name ); +} + + +#ifdef OPT_GRAPH_DEBUG_EXT + +static const char * target_name( TARGET * t ) +{ + static char buf[ 1000 ]; + if ( t->flags & T_FLAG_INTERNAL ) + { + sprintf( buf, "%s (internal node)", t->name ); + return buf; + } + return t->name; +} + + +/* + * dependGraphOutput() - output the DG after make0 has run. + */ + +static void dependGraphOutput( TARGET * t, int depth ) +{ + TARGETS * c; + + if ( ( t->flags & T_FLAG_VISITED ) || !t->name || !t->boundname ) + return; + + t->flags |= T_FLAG_VISITED; + + switch ( t->fate ) + { + case T_FATE_TOUCHED: + case T_FATE_MISSING: + case T_FATE_OUTDATED: + case T_FATE_UPDATE: + printf( "->%s%2d Name: %s\n", spaces( depth ), depth, target_name( t ) ); + break; + default: + printf( " %s%2d Name: %s\n", spaces( depth ), depth, target_name( t ) ); + break; + } + + if ( strcmp( t->name, t->boundname ) ) + printf( " %s Loc: %s\n", spaces( depth ), t->boundname ); + + switch ( t->fate ) + { + case T_FATE_STABLE: + printf( " %s : Stable\n", spaces( depth ) ); + break; + case T_FATE_NEWER: + printf( " %s : Newer\n", spaces( depth ) ); + break; + case T_FATE_ISTMP: + printf( " %s : Up to date temp file\n", spaces( depth ) ); + break; + case T_FATE_NEEDTMP: + printf( " %s : Temporary file, to be updated\n", spaces( depth ) ); + break; + case T_FATE_TOUCHED: + printf( " %s : Been touched, updating it\n", spaces( depth ) ); + break; + case T_FATE_MISSING: + printf( " %s : Missing, creating it\n", spaces( depth ) ); + break; + case T_FATE_OUTDATED: + printf( " %s : Outdated, updating it\n", spaces( depth ) ); + break; + case T_FATE_REBUILD: + printf( " %s : Rebuild, updating it\n", spaces( depth ) ); + break; + case T_FATE_UPDATE: + printf( " %s : Updating it\n", spaces( depth ) ); + break; + case T_FATE_CANTFIND: + printf( " %s : Can not find it\n", spaces( depth ) ); + break; + case T_FATE_CANTMAKE: + printf( " %s : Can make it\n", spaces( depth ) ); + break; + } + + if ( t->flags & ~T_FLAG_VISITED ) + { + printf( " %s : ", spaces( depth ) ); + if ( t->flags & T_FLAG_TEMP ) printf( "TEMPORARY " ); + if ( t->flags & T_FLAG_NOCARE ) printf( "NOCARE " ); + if ( t->flags & T_FLAG_NOTFILE ) printf( "NOTFILE " ); + if ( t->flags & T_FLAG_TOUCHED ) printf( "TOUCHED " ); + if ( t->flags & T_FLAG_LEAVES ) printf( "LEAVES " ); + if ( t->flags & T_FLAG_NOUPDATE ) printf( "NOUPDATE " ); + printf( "\n" ); + } + + for ( c = t->depends; c; c = c->next ) + { + printf( " %s : Depends on %s (%s)", spaces( depth ), + target_name( c->target ), target_fate[ (int) c->target->fate ] ); + if ( c->target->time == t->time ) + printf( " (max time)"); + printf( "\n" ); + } + + for ( c = t->depends; c; c = c->next ) + dependGraphOutput( c->target, depth + 1 ); +} +#endif + + +/* + * make0sort() - reorder TARGETS chain by their time (newest to oldest). + * + * We walk chain, taking each item and inserting it on the sorted result, with + * newest items at the front. This involves updating each of the 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. + */ + +static TARGETS * make0sort( TARGETS * chain ) +{ + PROFILE_ENTER( MAKE_MAKE0SORT ); + + TARGETS * result = 0; + + /* Walk the 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). Do not 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 */ + } + + PROFILE_EXIT( MAKE_MAKE0SORT ); + return result; +} + + +static LIST * targets_to_update_ = 0; + + +void mark_target_for_updating( char * target ) +{ + targets_to_update_ = list_new( targets_to_update_, target ); +} + + +LIST * targets_to_update() +{ + return targets_to_update_; +} + + +void clear_targets_to_update() +{ + list_free( targets_to_update_ ); + targets_to_update_ = 0; +} diff --git a/jam-files/engine/make.h b/jam-files/engine/make.h new file mode 100644 index 00000000..b372263e --- /dev/null +++ b/jam-files/engine/make.h @@ -0,0 +1,41 @@ +/* + * 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 + */ + +#include "lists.h" + +int make( int n_targets, const char **targets, int anyhow ); +int make1( TARGET *t ); + +typedef struct { + int temp; + int updating; + int cantfind; + int cantmake; + int targets; + int made; +} COUNTS ; + + +void make0( TARGET *t, TARGET *p, int depth, + COUNTS *counts, int anyhow ); + + +/* + * Specifies that the target should be updated. + */ +void mark_target_for_updating(char *target); +/* + * Returns the list of all the target previously passed to 'mark_target_for_updating'. + */ +LIST *targets_to_update(); +/* + * Cleasr/unmarks all targets that are currently marked for update. + */ +void clear_targets_to_update(); diff --git a/jam-files/engine/make1.c b/jam-files/engine/make1.c new file mode 100644 index 00000000..8001f333 --- /dev/null +++ b/jam-files/engine/make1.c @@ -0,0 +1,1145 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * 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 of its dependencies. + * + * Internal routines, the recursive/asynchronous command executors: + * + * make1a() - recursively traverse dependency target tree, calling make1b(). + * make1atail() - started processing all dependencies so go on to make1b(). + * make1b() - when dependencies are up to date, build target with make1c(). + * make1c() - launch target's next command, call parents' make1b() if none. + * make1d() - handle command execution completion and call back make1c(). + * + * Internal support routines: + * + * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc. + * make1list() - turn a list of targets into a LIST, for $(<) and $(>). + * make1settings() - for vars that get bound values, build up replacement lists. + * make1bind() - bind targets that weren't bound in dependency analysis. + * + * 04/16/94 (seiwald) - Split from make.c. + * 04/21/94 (seiwald) - Handle empty "updated" actions. + * 05/04/94 (seiwald) - async multiprocess (-j) support. + * 06/01/94 (seiwald) - new 'actions existing' does existing sources. + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets. + * 01/22/94 (seiwald) - pass per-target JAMSHELL down to exec_cmd(). + * 02/28/95 (seiwald) - Handle empty "existing" actions. + * 03/10/95 (seiwald) - Fancy counts. + */ + +#include "jam.h" + +#include "lists.h" +#include "parse.h" +#include "assert.h" +#include "variable.h" +#include "rules.h" +#include "headers.h" + +#include "search.h" +#include "newstr.h" +#include "make.h" +#include "command.h" +#include "execcmd.h" +#include "compile.h" +#include "output.h" + +#include <stdlib.h> + +#if ! defined(NT) || defined(__GNUC__) + #include <unistd.h> /* for unlink */ +#endif + +static CMD * make1cmds ( TARGET * ); +static LIST * make1list ( LIST *, TARGETS *, int flags ); +static SETTINGS * make1settings( LIST * vars ); +static void make1bind ( TARGET * ); + +/* Ugly static - it is too hard to carry it through the callbacks. */ + +static struct +{ + int failed; + int skipped; + int total; + int made; +} counts[ 1 ] ; + +/* Target state - remove recursive calls by just keeping track of state target + * is in. + */ +typedef struct _state +{ + struct _state * prev; /* previous state on stack */ + TARGET * t; /* current target */ + TARGET * parent; /* parent argument necessary for make1a() */ +#define T_STATE_MAKE1A 0 /* make1a() should be called */ +#define T_STATE_MAKE1ATAIL 1 /* make1atail() should be called */ +#define T_STATE_MAKE1B 2 /* make1b() should be called */ +#define T_STATE_MAKE1C 3 /* make1c() should be called */ +#define T_STATE_MAKE1D 4 /* make1d() should be called */ + int curstate; /* current state */ + int status; +} state; + +static void make1a ( state * ); +static void make1atail ( state * ); +static void make1b ( state * ); +static void make1c ( state * ); +static void make1d ( state * ); +static void make_closure( void * closure, int status, timing_info *, char *, char * ); + +typedef struct _stack +{ + state * stack; +} stack; + +static stack state_stack = { NULL }; + +static state * state_freelist = NULL; + + +static state * alloc_state() +{ + if ( state_freelist != NULL ) + { + state * pState = state_freelist; + state_freelist = pState->prev; + memset( pState, 0, sizeof( state ) ); + return pState; + } + + return (state *)BJAM_MALLOC( sizeof( state ) ); +} + + +static void free_state( state * pState ) +{ + pState->prev = state_freelist; + state_freelist = pState; +} + + +static void clear_state_freelist() +{ + while ( state_freelist != NULL ) + { + state * pState = state_freelist; + state_freelist = state_freelist->prev; + BJAM_FREE( pState ); + } +} + + +static state * current_state( stack * pStack ) +{ + return pStack->stack; +} + + +static void pop_state( stack * pStack ) +{ + if ( pStack->stack != NULL ) + { + state * pState = pStack->stack->prev; + free_state( pStack->stack ); + pStack->stack = pState; + } +} + + +static state * push_state( stack * pStack, TARGET * t, TARGET * parent, int curstate ) +{ + state * pState = alloc_state(); + + pState->t = t; + pState->parent = parent; + pState->prev = pStack->stack; + pState->curstate = curstate; + + pStack->stack = pState; + + return pStack->stack; +} + + +/* + * Pushes a stack onto another stack, effectively reversing the order. + */ + +static void push_stack_on_stack( stack * pDest, stack * pSrc ) +{ + while ( pSrc->stack != NULL ) + { + state * pState = pSrc->stack; + pSrc->stack = pSrc->stack->prev; + pState->prev = pDest->stack; + pDest->stack = pState; + } +} + + +/* + * make1() - execute commands to update a TARGET and all of its dependencies. + */ + +static int intr = 0; + +int make1( TARGET * t ) +{ + state * pState; + + memset( (char *)counts, 0, sizeof( *counts ) ); + + /* Recursively make the target and its dependencies. */ + push_state( &state_stack, t, NULL, T_STATE_MAKE1A ); + + do + { + while ( ( pState = current_state( &state_stack ) ) != NULL ) + { + if ( intr ) + pop_state( &state_stack ); + + switch ( pState->curstate ) + { + case T_STATE_MAKE1A : make1a ( pState ); break; + case T_STATE_MAKE1ATAIL: make1atail( pState ); break; + case T_STATE_MAKE1B : make1b ( pState ); break; + case T_STATE_MAKE1C : make1c ( pState ); break; + case T_STATE_MAKE1D : make1d ( pState ); break; + } + } + } + /* Wait for any outstanding commands to finish running. */ + while ( exec_wait() ); + + clear_state_freelist(); + + /* Talk about it. */ + if ( counts->failed ) + printf( "...failed updating %d target%s...\n", counts->failed, + counts->failed > 1 ? "s" : "" ); + if ( DEBUG_MAKE && counts->skipped ) + printf( "...skipped %d target%s...\n", counts->skipped, + counts->skipped > 1 ? "s" : "" ); + if ( DEBUG_MAKE && counts->made ) + printf( "...updated %d target%s...\n", counts->made, + counts->made > 1 ? "s" : "" ); + + return counts->total != counts->made; +} + + +/* + * make1a() - recursively traverse target tree, calling make1b(). + * + * Called to start processing a specified target. Does nothing if the target is + * already being processed or otherwise starts processing all of its + * dependencies. Once all of its dependencies have started being processed goes + * on and calls make1b() (actually does that indirectly via a helper + * make1atail() state). + */ + +static void make1a( state * pState ) +{ + TARGET * t = pState->t; + 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 ( pState->parent ) + switch ( pState->t->progress ) + { + case T_MAKE_INIT: + case T_MAKE_ACTIVE: + case T_MAKE_RUNNING: + pState->t->parents = targetentry( pState->t->parents, + pState->parent ); + ++pState->parent->asynccnt; + } + + /* If this target is already being processed then do nothing. There is no + * need to start processing the same target all over again. + */ + if ( pState->t->progress != T_MAKE_INIT ) + { + pop_state( &state_stack ); + return; + } + + /* Asynccnt counts the dependencies 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 notify all dependencies that + * they are needed. This 1 is accounted for when we call make1b() ourselves, + * below. Without this if a a dependency gets built before we finish + * processing all of our other dependencies our build might be triggerred + * prematurely. + */ + pState->t->asynccnt = 1; + + /* Add header nodes created during the building process. */ + { + TARGETS * inc = 0; + for ( c = t->depends; c; c = c->next ) + if ( c->target->rescanned && c->target->includes ) + inc = targetentry( inc, c->target->includes ); + t->depends = targetchain( t->depends, inc ); + } + + /* Guard against circular dependencies. */ + pState->t->progress = T_MAKE_ONSTACK; + + { + stack temp_stack = { NULL }; + for ( c = t->depends; c && !intr; c = c->next ) + push_state( &temp_stack, c->target, pState->t, T_STATE_MAKE1A ); + + /* Using stacks reverses the order of execution. Reverse it back. */ + push_stack_on_stack( &state_stack, &temp_stack ); + } + + pState->curstate = T_STATE_MAKE1ATAIL; +} + + +/* + * make1atail() - started processing all dependencies so go on to make1b(). + */ + +static void make1atail( state * pState ) +{ + pState->t->progress = T_MAKE_ACTIVE; + /* Now that all of our dependencies have bumped up our asynccnt we can + * remove our own internal bump added to prevent this target from being + * built before all of its dependencies start getting processed. + */ + pState->curstate = T_STATE_MAKE1B; +} + + +/* + * make1b() - when dependencies are up to date, build target with make1c(). + * + * Called after all dependencies have started being processed and after each of + * them finishes its processing. The target actually goes on to getting built in + * make1c() only after all of its dependencies have finished their processing. + */ + +static void make1b( state * pState ) +{ + TARGET * t = pState->t; + TARGETS * c; + TARGET * failed = 0; + char * failed_name = "dependencies"; + + /* If any dependencies are still outstanding, wait until they call make1b() + * to signal their completion. + */ + if ( --pState->t->asynccnt ) + { + pop_state( &state_stack ); + return; + } + + /* Try to aquire a semaphore. If it is locked, wait until the target that + * locked it is built and signal completition. + */ +#ifdef OPT_SEMAPHORE + if ( t->semaphore && t->semaphore->asynccnt ) + { + /* Append 't' to the list of targets waiting on semaphore. */ + t->semaphore->parents = targetentry( t->semaphore->parents, t ); + t->asynccnt++; + + if ( DEBUG_EXECCMD ) + printf( "SEM: %s is busy, delaying launch of %s\n", + t->semaphore->name, t->name ); + pop_state( &state_stack ); + return; + } +#endif + + /* Now ready to build target 't', if dependencies built OK. */ + + /* Collect status from dependencies. */ + for ( c = t->depends; c; c = c->next ) + if ( c->target->status > t->status && !( c->target->flags & T_FLAG_NOCARE ) ) + { + failed = c->target; + pState->t->status = c->target->status; + } + /* If an internal header node failed to build, we want to output the target + * that it failed on. + */ + if ( failed ) + { + failed_name = failed->flags & T_FLAG_INTERNAL + ? failed->failed + : failed->name; + } + t->failed = failed_name; + + /* If actions for building any of the dependencies have failed, bail. + * Otherwise, execute all actions to make the current target. + */ + if ( ( pState->t->status == EXEC_CMD_FAIL ) && pState->t->actions ) + { + ++counts->skipped; + if ( ( pState->t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD ) + { + if ( !unlink( pState->t->boundname ) ) + printf( "...removing outdated %s\n", pState->t->boundname ); + } + else + printf( "...skipped %s for lack of %s...\n", pState->t->name, failed_name ); + } + + if ( pState->t->status == EXEC_CMD_OK ) + switch ( pState->t->fate ) + { + /* These are handled by the default case below now + case T_FATE_INIT: + case T_FATE_MAKING: + */ + + case T_FATE_STABLE: + case T_FATE_NEWER: + break; + + case T_FATE_CANTFIND: + case T_FATE_CANTMAKE: + pState->t->status = EXEC_CMD_FAIL; + break; + + case T_FATE_ISTMP: + if ( DEBUG_MAKE ) + printf( "...using %s...\n", pState->t->name ); + break; + + case T_FATE_TOUCHED: + case T_FATE_MISSING: + case T_FATE_NEEDTMP: + case T_FATE_OUTDATED: + case T_FATE_UPDATE: + case T_FATE_REBUILD: + /* Prepare commands for executing actions scheduled for this target + * and then schedule transfer to make1c() state to proceed with + * executing the prepared commands. Commands have their embedded + * variables automatically expanded, including making use of any "on + * target" variables. + */ + if ( pState->t->actions ) + { + ++counts->total; + if ( DEBUG_MAKE && !( counts->total % 100 ) ) + printf( "...on %dth target...\n", counts->total ); + + pState->t->cmds = (char *)make1cmds( pState->t ); + /* Set the target's "progress" so that make1c() counts it among + * its successes/failures. + */ + pState->t->progress = T_MAKE_RUNNING; + } + break; + + /* All possible fates should have been accounted for by now. */ + default: + printf( "ERROR: %s has bad fate %d", pState->t->name, + pState->t->fate ); + abort(); + } + + /* Call make1c() to begin the execution of the chain of commands needed to + * build the target. If we are not going to build the target (due of + * dependency failures or no commands needing to be run) the chain will be + * empty and make1c() will directly signal the target's completion. + */ + +#ifdef OPT_SEMAPHORE + /* If there is a semaphore, indicate that it is in use. */ + if ( pState->t->semaphore ) + { + ++pState->t->semaphore->asynccnt; + if ( DEBUG_EXECCMD ) + printf( "SEM: %s now used by %s\n", pState->t->semaphore->name, + pState->t->name ); + } +#endif + + pState->curstate = T_STATE_MAKE1C; +} + + +/* + * make1c() - launch target's next command, call parents' make1b() if none. + * + * If there are (more) commands to run to build this target (and we have not hit + * an error running earlier comands) we launch the command using exec_cmd(). If + * there are no more commands to run, we collect the status from all the actions + * and report our completion to all the parents. + */ + +static void make1c( state * pState ) +{ + CMD * cmd = (CMD *)pState->t->cmds; + + if ( cmd && ( pState->t->status == EXEC_CMD_OK ) ) + { + char * rule_name = 0; + char * target = 0; + + if ( DEBUG_MAKEQ || + ( !( cmd->rule->actions->flags & RULE_QUIETLY ) && DEBUG_MAKE ) ) + { + rule_name = cmd->rule->name; + target = lol_get( &cmd->args, 0 )->string; + if ( globs.noexec ) + out_action( rule_name, target, cmd->buf, "", "", EXIT_OK ); + } + + if ( globs.noexec ) + { + pState->curstate = T_STATE_MAKE1D; + pState->status = EXEC_CMD_OK; + } + else + { + /* Pop state first because exec_cmd() could push state. */ + pop_state( &state_stack ); + exec_cmd( cmd->buf, make_closure, pState->t, cmd->shell, rule_name, + target ); + } + } + else + { + TARGETS * c; + ACTIONS * actions; + + /* Collect status from actions, and distribute it as well. */ + for ( actions = pState->t->actions; actions; actions = actions->next ) + if ( actions->action->status > pState->t->status ) + pState->t->status = actions->action->status; + for ( actions = pState->t->actions; actions; actions = actions->next ) + if ( pState->t->status > actions->action->status ) + actions->action->status = pState->t->status; + + /* Tally success/failure for those we tried to update. */ + if ( pState->t->progress == T_MAKE_RUNNING ) + switch ( pState->t->status ) + { + case EXEC_CMD_OK : ++counts->made ; break; + case EXEC_CMD_FAIL: ++counts->failed; break; + } + + /* Tell parents their dependency has been built. */ + { + stack temp_stack = { NULL }; + TARGET * t = pState->t; + TARGET * additional_includes = NULL; + + t->progress = T_MAKE_DONE; + + /* Target has been updated so rescan it for dependencies. */ + if ( ( t->fate >= T_FATE_MISSING ) && + ( t->status == EXEC_CMD_OK ) && + !t->rescanned ) + { + TARGET * target_to_rescan = t; + SETTINGS * s; + + target_to_rescan->rescanned = 1; + + if ( target_to_rescan->flags & T_FLAG_INTERNAL ) + target_to_rescan = t->original_target; + + /* Clean current includes. */ + target_to_rescan->includes = 0; + + s = copysettings( target_to_rescan->settings ); + pushsettings( s ); + headers( target_to_rescan ); + popsettings( s ); + freesettings( s ); + + if ( target_to_rescan->includes ) + { + target_to_rescan->includes->rescanned = 1; + /* Tricky. The parents have already been processed, but they + * have not seen the internal node, because it was just + * created. We need to make the calls to make1a() that would + * have been made by the parents here, and also make sure + * all unprocessed parents will pick up the includes. We + * must make sure processing of the additional make1a() + * invocations is done before make1b() which means this + * target is built, otherwise the parent would be considered + * built before this make1a() processing has even started. + */ + make0( target_to_rescan->includes, target_to_rescan->parents->target, 0, 0, 0 ); + for ( c = target_to_rescan->parents; c; c = c->next ) + c->target->depends = targetentry( c->target->depends, + target_to_rescan->includes ); + /* Will be processed below. */ + additional_includes = target_to_rescan->includes; + } + } + + if ( additional_includes ) + for ( c = t->parents; c; c = c->next ) + push_state( &temp_stack, additional_includes, c->target, T_STATE_MAKE1A ); + + for ( c = t->parents; c; c = c->next ) + push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B ); + +#ifdef OPT_SEMAPHORE + /* If there is a semaphore, it is now free. */ + if ( t->semaphore ) + { + assert( t->semaphore->asynccnt == 1 ); + --t->semaphore->asynccnt; + + if ( DEBUG_EXECCMD ) + printf( "SEM: %s is now free\n", t->semaphore->name ); + + /* If anything is waiting, notify the next target. There is no + * point in notifying waiting targets, since they will be + * notified again. + */ + if ( t->semaphore->parents ) + { + TARGETS * first = t->semaphore->parents; + if ( first->next ) + first->next->tail = first->tail; + t->semaphore->parents = first->next; + + if ( DEBUG_EXECCMD ) + printf( "SEM: placing %s on stack\n", first->target->name ); + push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B ); + BJAM_FREE( first ); + } + } +#endif + + /* Must pop state before pushing any more. */ + pop_state( &state_stack ); + + /* Using stacks reverses the order of execution. Reverse it back. */ + push_stack_on_stack( &state_stack, &temp_stack ); + } + } +} + + +/* + * call_timing_rule() - Look up the __TIMING_RULE__ variable on the given + * target, and if non-empty, invoke the rule it names, passing the given + * timing_info. + */ + +static void call_timing_rule( TARGET * target, timing_info * time ) +{ + LIST * timing_rule; + + pushsettings( target->settings ); + timing_rule = var_get( "__TIMING_RULE__" ); + popsettings( target->settings ); + + if ( timing_rule ) + { + /* rule timing-rule ( args * : target : start end user system ) */ + + /* Prepare the argument list. */ + FRAME frame[ 1 ]; + frame_init( frame ); + + /* args * :: $(__TIMING_RULE__[2-]) */ + lol_add( frame->args, list_copy( L0, timing_rule->next ) ); + + /* target :: the name of the target */ + lol_add( frame->args, list_new( L0, target->name ) ); + + /* start end user system :: info about the action command */ + lol_add( frame->args, list_new( list_new( list_new( list_new( L0, + outf_time ( time->start ) ), + outf_time ( time->end ) ), + outf_double( time->user ) ), + outf_double( time->system ) ) ); + + /* Call the rule. */ + evaluate_rule( timing_rule->string, frame ); + + /* Clean up. */ + frame_free( frame ); + } +} + + +/* + * call_action_rule() - Look up the __ACTION_RULE__ variable on the given + * target, and if non-empty, invoke the rule it names, passing the given info, + * timing_info, executed command and command output. + */ + +static void call_action_rule +( + TARGET * target, + int status, + timing_info * time, + char * executed_command, + char * command_output +) +{ + LIST * action_rule; + + pushsettings( target->settings ); + action_rule = var_get( "__ACTION_RULE__" ); + popsettings( target->settings ); + + if ( action_rule ) + { + /* rule action-rule ( + args * : + target : + command status start end user system : + output ? ) */ + + /* Prepare the argument list. */ + FRAME frame[ 1 ]; + frame_init( frame ); + + /* args * :: $(__ACTION_RULE__[2-]) */ + lol_add( frame->args, list_copy( L0, action_rule->next ) ); + + /* target :: the name of the target */ + lol_add( frame->args, list_new( L0, target->name ) ); + + /* command status start end user system :: info about the action command */ + lol_add( frame->args, + list_new( list_new( list_new( list_new( list_new( list_new( L0, + newstr( executed_command ) ), + outf_int( status ) ), + outf_time( time->start ) ), + outf_time( time->end ) ), + outf_double( time->user ) ), + outf_double( time->system ) ) ); + + /* output ? :: the output of the action command */ + if ( command_output ) + lol_add( frame->args, list_new( L0, newstr( command_output ) ) ); + else + lol_add( frame->args, L0 ); + + /* Call the rule. */ + evaluate_rule( action_rule->string, frame ); + + /* Clean up. */ + frame_free( frame ); + } +} + + +/* + * make_closure() - internal function passed as a notification callback for when + * commands finish getting executed by the OS. + */ + +static void make_closure +( + void * closure, + int status, + timing_info * time, + char * executed_command, + char * command_output +) +{ + TARGET * built = (TARGET *)closure; + + call_timing_rule( built, time ); + if ( DEBUG_EXECCMD ) + printf( "%f sec system; %f sec user\n", time->system, time->user ); + + call_action_rule( built, status, time, executed_command, command_output ); + + push_state( &state_stack, built, NULL, T_STATE_MAKE1D )->status = status; +} + + +/* + * make1d() - handle command execution completion and call back make1c(). + * + * exec_cmd() has completed and now all we need to do is fiddle with the status + * and call back to make1c() so it can run the next command scheduled for + * building this target or close up the target's build process in case there are + * no more commands scheduled for it. On interrupts, we bail heavily. + */ + +static void make1d( state * pState ) +{ + TARGET * t = pState->t; + CMD * cmd = (CMD *)t->cmds; + int status = pState->status; + + if ( t->flags & T_FLAG_FAIL_EXPECTED ) + { + /* Invert execution result when FAIL_EXPECTED has been applied. */ + switch ( status ) + { + case EXEC_CMD_FAIL: status = EXEC_CMD_OK ; break; + case EXEC_CMD_OK: status = EXEC_CMD_FAIL; break; + } + } + + if ( ( status == EXEC_CMD_FAIL ) && + ( cmd->rule->actions->flags & RULE_IGNORE ) ) + status = EXEC_CMD_OK; + + /* On interrupt, set intr so _everything_ fails. */ + if ( status == EXEC_CMD_INTR ) + ++intr; + + /* Print command text on failure. */ + if ( ( status == EXEC_CMD_FAIL ) && DEBUG_MAKE ) + { + if ( !DEBUG_EXEC ) + printf( "%s\n", cmd->buf ); + + printf( "...failed %s ", cmd->rule->name ); + list_print( lol_get( &cmd->args, 0 ) ); + printf( "...\n" ); + } + + /* Treat failed commands as interrupts in case we were asked to stop the + * build in case of any errors. + */ + if ( ( status == EXEC_CMD_FAIL ) && globs.quitquick ) + ++intr; + + /* If the command was interrupted or failed and the target is not + * "precious", remove the targets. + */ + if (status != EXEC_CMD_OK) + { + LIST * targets = lol_get( &cmd->args, 0 ); + for ( ; targets; targets = list_next( targets ) ) + { + int need_unlink = 1; + TARGET* t = bindtarget ( targets->string ); + if (t->flags & T_FLAG_PRECIOUS) + { + need_unlink = 0; + } + if (need_unlink && !unlink( targets->string ) ) + printf( "...removing %s\n", targets->string ); + } + } + + /* Free this command and call make1c() to move onto the next one scheduled + * for building this same target. + */ + t->status = status; + t->cmds = (char *)cmd_next( cmd ); + cmd_free( cmd ); + pState->curstate = T_STATE_MAKE1C; +} + + +/* + * swap_settings() - replace the settings from the current module and target + * with those from the new module and target + */ + +static void swap_settings +( + module_t * * current_module, + TARGET * * current_target, + module_t * new_module, + TARGET * new_target +) +{ + if ( new_module == root_module() ) + new_module = 0; + + if ( ( new_target == *current_target ) && ( new_module == *current_module ) ) + return; + + if ( *current_target ) + popsettings( (*current_target)->settings ); + + if ( new_module != *current_module ) + { + if ( *current_module ) + exit_module( *current_module ); + + *current_module = new_module; + + if ( new_module ) + enter_module( new_module ); + } + + *current_target = new_target; + if ( new_target ) + pushsettings( new_target->settings ); +} + + +/* + * 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_NEWSRCS actions. The result is a chain of CMDs which can be expanded by + * var_string() and executed using exec_cmd(). + */ + +static CMD * make1cmds( TARGET * t ) +{ + CMD * cmds = 0; + LIST * shell = 0; + module_t * settings_module = 0; + TARGET * settings_target = 0; + ACTIONS * a0; + + /* Step through actions. Actions may be shared with other targets or grouped + * using RULE_TOGETHER, so actions already seen are skipped. + */ + for ( a0 = t->actions ; a0; a0 = a0->next ) + { + RULE * rule = a0->action->rule; + rule_actions * actions = rule->actions; + SETTINGS * boundvars; + LIST * nt; + LIST * ns; + ACTIONS * a1; + int start; + int chunk; + int length; + + /* Only do rules with commands to execute. If this action has already + * been executed, use saved status. + */ + if ( !actions || a0->action->running ) + continue; + + a0->action->running = 1; + + /* Make LISTS of targets and sources. If `execute together` has been + * specified for this rule, tack on sources from each instance of this + * rule for this target. + */ + nt = make1list( L0, a0->action->targets, 0 ); + ns = make1list( L0, a0->action->sources, actions->flags ); + if ( actions->flags & RULE_TOGETHER ) + for ( a1 = a0->next; a1; a1 = a1->next ) + if ( a1->action->rule == rule && !a1->action->running ) + { + ns = make1list( ns, a1->action->sources, actions->flags ); + a1->action->running = 1; + } + + /* If doing only updated (or existing) sources, but none have been + * updated (or exist), skip this action. + */ + if ( !ns && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) + { + list_free( nt ); + continue; + } + + swap_settings( &settings_module, &settings_target, rule->module, t ); + if ( !shell ) + shell = var_get( "JAMSHELL" ); /* shell is per-target */ + + /* If we had 'actions xxx bind vars' we bind the vars now. */ + boundvars = make1settings( actions->bindlist ); + pushsettings( boundvars ); + + /* + * Build command, starting with all source args. + * + * If cmd_new returns 0, it is because the resulting command length is + * > MAXLINE. In this case, we will 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 is worth it to get as close to MAXLINE as possible, because + * launching the commands we are executing is likely to be much more + * compute intensive. + * + * Note we loop through at least once, for sourceless actions. + */ + + start = 0; + chunk = length = list_length( ns ); + + do + { + /* Build cmd: cmd_new consumes its lists. */ + CMD * cmd = cmd_new( rule, + list_copy( L0, nt ), + list_sublist( ns, start, chunk ), + list_copy( L0, shell ) ); + + if ( cmd ) + { + /* It fit: chain it up. */ + if ( !cmds ) cmds = cmd; + else cmds->tail->next = cmd; + cmds->tail = cmd; + start += chunk; + } + else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) + { + /* Reduce chunk size slowly. */ + chunk = chunk * 9 / 10; + } + else + { + /* Too long and not splittable. */ + printf( "%s actions too long (max %d):\n", rule->name, MAXLINE + ); + + /* Tell the user what didn't fit. */ + cmd = cmd_new( rule, list_copy( L0, nt ), + list_sublist( ns, start, chunk ), + list_new( L0, newstr( "%" ) ) ); + fputs( cmd->buf, stdout ); + 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 ); + } + + swap_settings( &settings_module, &settings_target, 0, 0 ); + return cmds; +} + + +/* + * make1list() - turn a list of targets into a LIST, for $(<) and $(>). + */ + +static LIST * make1list( LIST * l, TARGETS * targets, int flags ) +{ + for ( ; targets; targets = targets->next ) + { + TARGET * t = targets->target; + + if ( t->binding == T_BIND_UNBOUND ) + make1bind( t ); + + if ( ( flags & RULE_EXISTING ) && ( flags & RULE_NEWSRCS ) ) + { + if ( ( t->binding != T_BIND_EXISTS ) && ( t->fate <= T_FATE_STABLE ) ) + continue; + } + else + { + if ( ( flags & RULE_EXISTING ) && ( t->binding != T_BIND_EXISTS ) ) + continue; + + if ( ( flags & RULE_NEWSRCS ) && ( t->fate <= T_FATE_STABLE ) ) + continue; + } + + /* Prohibit duplicates for RULE_TOGETHER. */ + if ( flags & RULE_TOGETHER ) + { + LIST * m; + for ( m = l; m; m = m->next ) + if ( !strcmp( m->string, t->boundname ) ) + break; + if ( m ) + continue; + } + + /* Build new list. */ + l = list_new( l, copystr( t->boundname ) ); + } + + return l; +} + + +/* + * make1settings() - for vars that get bound values, build up replacement lists. + */ + +static SETTINGS * make1settings( LIST * vars ) +{ + SETTINGS * settings = 0; + + for ( ; vars; vars = list_next( vars ) ) + { + LIST * l = var_get( vars->string ); + LIST * nl = 0; + + for ( ; l; l = list_next( l ) ) + { + TARGET * t = bindtarget( l->string ); + + /* Make sure the target is bound. */ + if ( t->binding == T_BIND_UNBOUND ) + make1bind( t ); + + /* Build a new list. */ + nl = list_new( nl, copystr( t->boundname ) ); + } + + /* Add to settings chain. */ + settings = addsettings( settings, VAR_SET, vars->string, nl ); + } + + return settings; +} + + +/* + * make1bind() - bind targets that were not bound during dependency analysis + * + * Spot the kludge! If a target is not in the dependency tree, it did not get + * bound by make0(), so we have to do it here. Ugly. + */ + +static void make1bind( TARGET * t ) +{ + if ( t->flags & T_FLAG_NOTFILE ) + return; + + pushsettings( t->settings ); + t->boundname = search( t->name, &t->time, 0, ( t->flags & T_FLAG_ISFILE ) ); + t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING; + popsettings( t->settings ); +} diff --git a/jam-files/engine/md5.c b/jam-files/engine/md5.c new file mode 100644 index 00000000..c35d96c5 --- /dev/null +++ b/jam-files/engine/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/jam-files/engine/md5.h b/jam-files/engine/md5.h new file mode 100644 index 00000000..698c995d --- /dev/null +++ b/jam-files/engine/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/jam-files/engine/mem.c b/jam-files/engine/mem.c new file mode 100644 index 00000000..6a11fb38 --- /dev/null +++ b/jam-files/engine/mem.c @@ -0,0 +1,75 @@ +/* +Copyright Rene Rivera 2006. +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include "jam.h" + +#ifdef OPT_BOEHM_GC + + /* Compile the Boehm GC as one big chunk of code. It's much easier + this way, than trying to make radical changes to the bjam build + scripts. */ + + #define ATOMIC_UNCOLLECTABLE + #define NO_EXECUTE_PERMISSION + #define ALL_INTERIOR_POINTERS + + #define LARGE_CONFIG + /* + #define NO_SIGNALS + #define SILENT + */ + #ifndef GC_DEBUG + #define NO_DEBUGGING + #endif + + #ifdef __GLIBC__ + #define __USE_GNU + #endif + + #include "boehm_gc/reclaim.c" + #include "boehm_gc/allchblk.c" + #include "boehm_gc/misc.c" + #include "boehm_gc/alloc.c" + #include "boehm_gc/mach_dep.c" + #include "boehm_gc/os_dep.c" + #include "boehm_gc/mark_rts.c" + #include "boehm_gc/headers.c" + #include "boehm_gc/mark.c" + #include "boehm_gc/obj_map.c" + #include "boehm_gc/pcr_interface.c" + #include "boehm_gc/blacklst.c" + #include "boehm_gc/new_hblk.c" + #include "boehm_gc/real_malloc.c" + #include "boehm_gc/dyn_load.c" + #include "boehm_gc/dbg_mlc.c" + #include "boehm_gc/malloc.c" + #include "boehm_gc/stubborn.c" + #include "boehm_gc/checksums.c" + #include "boehm_gc/pthread_support.c" + #include "boehm_gc/pthread_stop_world.c" + #include "boehm_gc/darwin_stop_world.c" + #include "boehm_gc/typd_mlc.c" + #include "boehm_gc/ptr_chck.c" + #include "boehm_gc/mallocx.c" + #include "boehm_gc/gcj_mlc.c" + #include "boehm_gc/specific.c" + #include "boehm_gc/gc_dlopen.c" + #include "boehm_gc/backgraph.c" + #include "boehm_gc/win32_threads.c" + + /* Needs to be last. */ + #include "boehm_gc/finalize.c" + +#elif defined(OPT_DUMA) + + #ifdef OS_NT + #define WIN32 + #endif + #include "duma/duma.c" + #include "duma/print.c" + +#endif diff --git a/jam-files/engine/mem.h b/jam-files/engine/mem.h new file mode 100644 index 00000000..71b2fb4b --- /dev/null +++ b/jam-files/engine/mem.h @@ -0,0 +1,134 @@ +/* +Copyright Rene Rivera 2006. +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE_1_0.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef BJAM_MEM_H +#define BJAM_MEM_H + + +#ifdef OPT_BOEHM_GC + + /* Use Boehm GC memory allocator. */ + #include <gc.h> + #define bjam_malloc_x(s) memset(GC_malloc(s),0,s) + #define bjam_malloc_atomic_x(s) memset(GC_malloc_atomic(s),0,s) + #define bjam_calloc_x(n,s) memset(GC_malloc((n)*(s)),0,(n)*(s)) + #define bjam_calloc_atomic_x(n,s) memset(GC_malloc_atomic((n)*(s)),0,(n)*(s)) + #define bjam_realloc_x(p,s) GC_realloc(p,s) + #define bjam_free_x(p) GC_free(p) + #define bjam_mem_init_x() GC_init(); GC_enable_incremental() + + #define bjam_malloc_raw_x(s) malloc(s) + #define bjam_calloc_raw_x(n,s) calloc(n,s) + #define bjam_realloc_raw_x(p,s) realloc(p,s) + #define bjam_free_raw_x(p) free(p) + + #ifndef BJAM_NEWSTR_NO_ALLOCATE + #define BJAM_NEWSTR_NO_ALLOCATE + #endif + +#elif defined(OPT_DUMA) + + /* Use Duma memory debugging library. */ + #include <stdlib.h> + #define _DUMA_CONFIG_H_ + #define DUMA_NO_GLOBAL_MALLOC_FREE + #define DUMA_EXPLICIT_INIT + #define DUMA_NO_THREAD_SAFETY + #define DUMA_NO_CPP_SUPPORT + /* #define DUMA_NO_LEAKDETECTION */ + /* #define DUMA_USE_FRAMENO */ + /* #define DUMA_PREFER_ATEXIT */ + /* #define DUMA_OLD_DEL_MACRO */ + /* #define DUMA_NO_HANG_MSG */ + #define DUMA_PAGE_SIZE 4096 + #define DUMA_MIN_ALIGNMENT 1 + /* #define DUMA_GNU_INIT_ATTR 0 */ + typedef unsigned int DUMA_ADDR; + typedef unsigned int DUMA_SIZE; + #include <duma.h> + #define bjam_malloc_x(s) malloc(s) + #define bjam_calloc_x(n,s) calloc(n,s) + #define bjam_realloc_x(p,s) realloc(p,s) + #define bjam_free_x(p) free(p) + + #ifndef BJAM_NEWSTR_NO_ALLOCATE + #define BJAM_NEWSTR_NO_ALLOCATE + #endif + +#else + + /* Standard C memory allocation. */ + #define bjam_malloc_x(s) malloc(s) + #define bjam_calloc_x(n,s) calloc(n,s) + #define bjam_realloc_x(p,s) realloc(p,s) + #define bjam_free_x(p) free(p) + +#endif + +#ifndef bjam_malloc_atomic_x + #define bjam_malloc_atomic_x(s) bjam_malloc_x(s) +#endif +#ifndef bjam_calloc_atomic_x + #define bjam_calloc_atomic_x(n,s) bjam_calloc_x(n,s) +#endif +#ifndef bjam_mem_init_x + #define bjam_mem_init_x() +#endif +#ifndef bjam_mem_close_x + #define bjam_mem_close_x() +#endif +#ifndef bjam_malloc_raw_x + #define bjam_malloc_raw_x(s) bjam_malloc_x(s) +#endif +#ifndef bjam_calloc_raw_x + #define bjam_calloc_raw_x(n,s) bjam_calloc_x(n,s) +#endif +#ifndef bjam_realloc_raw_x + #define bjam_realloc_raw_x(p,s) bjam_realloc_x(p,s) +#endif +#ifndef bjam_free_raw_x + #define bjam_free_raw_x(p) bjam_free_x(p) +#endif + +#ifdef OPT_DEBUG_PROFILE + + /* Profile tracing of memory allocations. */ + #define BJAM_MALLOC(s) (profile_memory(s), bjam_malloc_x(s)) + #define BJAM_MALLOC_ATOMIC(s) (profile_memory(s), bjam_malloc_atomic_x(s)) + #define BJAM_CALLOC(n,s) (profile_memory(n*s), bjam_calloc_x(n,s)) + #define BJAM_CALLOC_ATOMIC(n,s) (profile_memory(n*s), bjam_calloc_atomic_x(n,s)) + #define BJAM_REALLOC(p,s) (profile_memory(s), bjam_realloc_x(p,s)) + #define BJAM_FREE(p) bjam_free_x(p) + #define BJAM_MEM_INIT() bjam_mem_init_x() + #define BJAM_MEM_CLOSE() bjam_mem_close_x() + + #define BJAM_MALLOC_RAW(s) (profile_memory(s), bjam_malloc_raw_x(s)) + #define BJAM_CALLOC_RAW(n,s) (profile_memory(n*s), bjam_calloc_raw_x(n,s)) + #define BJAM_REALLOC_RAW(p,s) (profile_memory(s), bjam_realloc_raw_x(p,s)) + #define BJAM_FREE_RAW(p) bjam_free_raw_x(p) + +#else + + /* No mem tracing. */ + #define BJAM_MALLOC(s) bjam_malloc_x(s) + #define BJAM_MALLOC_ATOMIC(s) bjam_malloc_atomic_x(s) + #define BJAM_CALLOC(n,s) bjam_calloc_x(n,s) + #define BJAM_CALLOC_ATOMIC(n,s) bjam_calloc_atomic_x(n,s) + #define BJAM_REALLOC(p,s) bjam_realloc_x(p,s) + #define BJAM_FREE(p) bjam_free_x(p) + #define BJAM_MEM_INIT() bjam_mem_init_x() + #define BJAM_MEM_CLOSE() bjam_mem_close_x() + + #define BJAM_MALLOC_RAW(s) bjam_malloc_raw_x(s) + #define BJAM_CALLOC_RAW(n,s) bjam_calloc_raw_x(n,s) + #define BJAM_REALLOC_RAW(p,s) bjam_realloc_raw_x(p,s) + #define BJAM_FREE_RAW(p) bjam_free_raw_x(p) + +#endif + + +#endif diff --git a/jam-files/engine/mkjambase.c b/jam-files/engine/mkjambase.c new file mode 100644 index 00000000..cdf59982 --- /dev/null +++ b/jam-files/engine/mkjambase.c @@ -0,0 +1,123 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * mkjambase.c - turn Jambase into a big C structure + * + * Usage: mkjambase jambase.c Jambase ... + * + * Results look like this: + * + * char *jambase[] = { + * "...\n", + * ... + * 0 }; + * + * Handles \'s and "'s specially; knows to delete blank and comment lines. + * + */ + +#include <stdio.h> +#include <string.h> + + +int main( int argc, char * * argv, char * * envp ) +{ + char buf[ 1024 ]; + FILE * fin; + FILE * fout; + char * p; + int doDotC = 0; + + if ( argc < 3 ) + { + fprintf( stderr, "usage: %s jambase.c Jambase ...\n", argv[ 0 ] ); + return -1; + } + + if ( !( fout = fopen( argv[1], "w" ) ) ) + { + perror( argv[ 1 ] ); + return -1; + } + + /* If the file ends in .c generate a C source file. */ + if ( ( p = strrchr( argv[1], '.' ) ) && !strcmp( p, ".c" ) ) + doDotC++; + + /* Now process the files. */ + + argc -= 2; + argv += 2; + + if ( doDotC ) + { + fprintf( fout, "/* Generated by mkjambase from Jambase */\n" ); + fprintf( fout, "char *jambase[] = {\n" ); + } + + for ( ; argc--; ++argv ) + { + if ( !( fin = fopen( *argv, "r" ) ) ) + { + perror( *argv ); + return -1; + } + + if ( doDotC ) + fprintf( fout, "/* %s */\n", *argv ); + else + fprintf( fout, "### %s ###\n", *argv ); + + while ( fgets( buf, sizeof( buf ), fin ) ) + { + if ( doDotC ) + { + char * p = buf; + + /* Strip leading whitespace. */ + while ( ( *p == ' ' ) || ( *p == '\t' ) || ( *p == '\n' ) ) + ++p; + + /* Drop comments and empty lines. */ + if ( ( *p == '#' ) || !*p ) + continue; + + /* Copy. */ + putc( '"', fout ); + for ( ; *p && ( *p != '\n' ); ++p ) + switch ( *p ) + { + case '\\': putc( '\\', fout ); putc( '\\', fout ); break; + case '"' : putc( '\\', fout ); putc( '"' , fout ); break; + case '\r': break; + default: putc( *p, fout ); break; + } + + fprintf( fout, "\\n\",\n" ); + } + else + { + fprintf( fout, "%s", buf ); + } + } + + fclose( fin ); + } + + if ( doDotC ) + fprintf( fout, "0 };\n" ); + + fclose( fout ); + + return 0; +} diff --git a/jam-files/engine/modules.c b/jam-files/engine/modules.c new file mode 100644 index 00000000..72952594 --- /dev/null +++ b/jam-files/engine/modules.c @@ -0,0 +1,168 @@ +/* + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ +#include "jam.h" + +#include "modules.h" +#include "string.h" +#include "hash.h" +#include "newstr.h" +#include "lists.h" +#include "parse.h" +#include "rules.h" +#include "variable.h" +#include "strings.h" +#include <assert.h> + +static struct hash * module_hash = 0; + + +static char * new_module_str( module_t * m, char * suffix ) +{ + char * result; + string s; + string_copy( &s, m->name ); + string_append( &s, suffix ); + result = newstr( s.value ); + string_free( &s ); + return result; +} + + +module_t * bindmodule( char * name ) +{ + PROFILE_ENTER( BINDMODULE ); + + string s; + module_t m_; + module_t * m = &m_; + + if ( !module_hash ) + module_hash = hashinit( sizeof( module_t ), "modules" ); + + string_new( &s ); + if ( name ) + { + string_append( &s, name ); + string_push_back( &s, '.' ); + } + + m->name = s.value; + + if ( hashenter( module_hash, (HASHDATA * *)&m ) ) + { + m->name = newstr( m->name ); + m->variables = 0; + m->rules = 0; + m->imported_modules = 0; + m->class_module = 0; + m->native_rules = 0; + m->user_module = 0; + } + string_free( &s ); + + PROFILE_EXIT( BINDMODULE ); + + return m; +} + +/* + * demand_rules() - Get the module's "rules" hash on demand. + */ +struct hash * demand_rules( module_t * m ) +{ + if ( !m->rules ) + m->rules = hashinit( sizeof( RULE ), new_module_str( m, "rules" ) ); + return m->rules; +} + + +/* + * delete_module() - wipe out the module's rules and variables. + */ + +static void delete_rule_( void * xrule, void * data ) +{ + rule_free( (RULE *)xrule ); +} + + +void delete_module( module_t * m ) +{ + /* Clear out all the rules. */ + if ( m->rules ) + { + hashenumerate( m->rules, delete_rule_, (void *)0 ); + hashdone( m->rules ); + m->rules = 0; + } + + if ( m->variables ) + { + var_hash_swap( &m->variables ); + var_done(); + var_hash_swap( &m->variables ); + m->variables = 0; + } +} + + +module_t * root_module() +{ + static module_t * root = 0; + if ( !root ) + root = bindmodule( 0 ); + return root; +} + +void enter_module( module_t * m ) +{ + var_hash_swap( &m->variables ); +} + + +void exit_module( module_t * m ) +{ + var_hash_swap( &m->variables ); +} + + +void import_module( LIST * module_names, module_t * target_module ) +{ + PROFILE_ENTER( IMPORT_MODULE ); + + struct hash * h; + + if ( !target_module->imported_modules ) + target_module->imported_modules = hashinit( sizeof( char * ), "imported" ); + h = target_module->imported_modules; + + for ( ; module_names; module_names = module_names->next ) + { + char * s = module_names->string; + char * * ss = &s; + hashenter( h, (HASHDATA * *)&ss ); + } + + PROFILE_EXIT( IMPORT_MODULE ); +} + + +static void add_module_name( void * r_, void * result_ ) +{ + char * * r = (char * *)r_; + LIST * * result = (LIST * *)result_; + + *result = list_new( *result, copystr( *r ) ); +} + + +LIST * imported_modules( module_t * module ) +{ + LIST * result = L0; + if ( module->imported_modules ) + hashenumerate( module->imported_modules, add_module_name, &result ); + return result; +} diff --git a/jam-files/engine/modules.h b/jam-files/engine/modules.h new file mode 100644 index 00000000..60053a23 --- /dev/null +++ b/jam-files/engine/modules.h @@ -0,0 +1,37 @@ +/* + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef MODULES_DWA10182001_H +# define MODULES_DWA10182001_H + +#include "lists.h" + +struct module_t +{ + char* name; + struct hash* rules; + struct hash* variables; + struct hash* imported_modules; + struct module_t* class_module; + struct hash* native_rules; + int user_module; +}; + +typedef struct module_t module_t ; /* MSVC debugger gets confused unless this is provided */ + +module_t* bindmodule( char* name ); +module_t* root_module(); +void enter_module( module_t* ); +void exit_module( module_t* ); +void delete_module( module_t* ); + +void import_module(LIST* module_names, module_t* target_module); +LIST* imported_modules(module_t* module); + +struct hash* demand_rules( module_t* ); + + +#endif + diff --git a/jam-files/engine/modules/order.c b/jam-files/engine/modules/order.c new file mode 100644 index 00000000..d77943a7 --- /dev/null +++ b/jam-files/engine/modules/order.c @@ -0,0 +1,144 @@ +/* Copyright Vladimir Prus 2004. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" +#include "../lists.h" +#include "../strings.h" +#include "../newstr.h" +#include "../variable.h" + + +/* Use quite klugy approach: when we add order dependency from 'a' to 'b', + just append 'b' to of value of variable 'a'. +*/ +LIST *add_pair( PARSE *parse, FRAME *frame ) +{ + LIST* arg = lol_get( frame->args, 0 ); + + var_set(arg->string, list_copy(0, arg->next), VAR_APPEND); + + return L0; +} + +/** Given a list and a value, returns position of that value in + the list, or -1 if not found. +*/ +int list_index(LIST* list, const char* value) +{ + int result = 0; + for(; list; list = list->next, ++result) { + if (strcmp(list->string, value) == 0) + return result; + } + return -1; +} + +enum colors { white, gray, black }; + +/* Main routite of topological sort. Calls itself recursively on all + adjacent vertices which were not yet visited. After that, 'current_vertex' + is added to '*result_ptr'. +*/ +void do_ts(int** graph, int current_vertex, int* colors, int** result_ptr) +{ + int i; + + colors[current_vertex] = gray; + for(i = 0; graph[current_vertex][i] != -1; ++i) { + int adjacent_vertex = graph[current_vertex][i]; + + if (colors[adjacent_vertex] == white) + do_ts(graph, adjacent_vertex, colors, result_ptr); + /* The vertex is either black, in which case we don't have to do + anything, a gray, in which case we have a loop. If we have a loop, + it's not clear what useful diagnostic we can emit, so we emit + nothing. */ + } + colors[current_vertex] = black; + **result_ptr = current_vertex; + (*result_ptr)++; +} + +void topological_sort(int** graph, int num_vertices, int* result) +{ + int i; + int* colors = (int*)BJAM_CALLOC(num_vertices, sizeof(int)); + for (i = 0; i < num_vertices; ++i) + colors[i] = white; + + for(i = 0; i < num_vertices; ++i) + if (colors[i] == white) + do_ts(graph, i, colors, &result); + + BJAM_FREE(colors); +} + +LIST *order( PARSE *parse, FRAME *frame ) +{ + LIST* arg = lol_get( frame->args, 0 ); + LIST* tmp; + LIST* result = 0; + int src; + + /* We need to create a graph of order dependencies between + the passed objects. We assume that there are no duplicates + passed to 'add_pair'. + */ + int length = list_length(arg); + int** graph = (int**)BJAM_CALLOC(length, sizeof(int*)); + int* order = (int*)BJAM_MALLOC((length+1)*sizeof(int)); + + for(tmp = arg, src = 0; tmp; tmp = tmp->next, ++src) { + /* For all object this one depend upon, add elements + to 'graph' */ + LIST* dependencies = var_get(tmp->string); + int index = 0; + + graph[src] = (int*)BJAM_CALLOC(list_length(dependencies)+1, sizeof(int)); + for(; dependencies; dependencies = dependencies->next) { + int dst = list_index(arg, dependencies->string); + if (dst != -1) + graph[src][index++] = dst; + } + graph[src][index] = -1; + } + + topological_sort(graph, length, order); + + { + int index = length-1; + for(; index >= 0; --index) { + int i; + tmp = arg; + for (i = 0; i < order[index]; ++i, tmp = tmp->next); + result = list_new(result, tmp->string); + } + } + + /* Clean up */ + { + int i; + for(i = 0; i < length; ++i) + BJAM_FREE(graph[i]); + BJAM_FREE(graph); + BJAM_FREE(order); + } + + return result; +} + +void init_order() +{ + { + char* args[] = { "first", "second", 0 }; + declare_native_rule("class@order", "add-pair", args, add_pair, 1); + } + + { + char* args[] = { "objects", "*", 0 }; + declare_native_rule("class@order", "order", args, order, 1); + } + + +} diff --git a/jam-files/engine/modules/path.c b/jam-files/engine/modules/path.c new file mode 100644 index 00000000..f5d09622 --- /dev/null +++ b/jam-files/engine/modules/path.c @@ -0,0 +1,32 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" +#include "../timestamp.h" +#include "../newstr.h" + +LIST *path_exists( PARSE *parse, FRAME *frame ) +{ + LIST* l = lol_get( frame->args, 0 ); + + time_t time; + timestamp(l->string, &time); + if (time != 0) + { + return list_new(0, newstr("true")); + } + else + { + return L0; + } +} + +void init_path() +{ + { + char* args[] = { "location", 0 }; + declare_native_rule("path", "exists", args, path_exists, 1); + } + +} diff --git a/jam-files/engine/modules/property-set.c b/jam-files/engine/modules/property-set.c new file mode 100644 index 00000000..2b0fb5d9 --- /dev/null +++ b/jam-files/engine/modules/property-set.c @@ -0,0 +1,110 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" +#include "../timestamp.h" +#include "../newstr.h" +#include "../strings.h" +#include "../lists.h" +#include "../variable.h" +#include "../compile.h" + +LIST* get_grist(char* f) +{ + char* end = strchr(f, '>'); + string s[1]; + LIST* result; + + string_new(s); + + string_append_range(s, f, end+1); + result = list_new(0, newstr(s->value)); + + string_free(s); + return result; +} + +/* +rule create ( raw-properties * ) +{ + raw-properties = [ sequence.unique + [ sequence.insertion-sort $(raw-properties) ] ] ; + + local key = $(raw-properties:J=-:E=) ; + + if ! $(.ps.$(key)) + { + .ps.$(key) = [ new property-set $(raw-properties) ] ; + } + return $(.ps.$(key)) ; +} +*/ + +LIST *property_set_create( PARSE *parse, FRAME *frame ) +{ + LIST* properties = lol_get( frame->args, 0 ); + LIST* sorted = 0; +#if 0 + LIST* order_sensitive = 0; +#endif + LIST* unique; + LIST* tmp; + LIST* val; + string var[1]; + +#if 0 + /* Sort all properties which are not order sensitive */ + for(tmp = properties; tmp; tmp = tmp->next) { + LIST* g = get_grist(tmp->string); + LIST* att = call_rule("feature.attributes", frame, g, 0); + if (list_in(att, "order-sensitive")) { + order_sensitive = list_new( order_sensitive, tmp->string); + } else { + sorted = list_new( sorted, tmp->string); + } + list_free(att); + } + + sorted = list_sort(sorted); + sorted = list_append(sorted, order_sensitive); + unique = list_unique(sorted); +#endif + sorted = list_sort(properties); + unique = list_unique(sorted); + + string_new(var); + string_append(var, ".ps."); + + for(tmp = unique; tmp; tmp = tmp->next) { + string_append(var, tmp->string); + string_push_back(var, '-'); + } + val = var_get(var->value); + if (val == 0) + { + val = call_rule("new", frame, + list_append(list_new(0, "property-set"), unique), 0); + + var_set(newstr(var->value), list_copy(0, val), VAR_SET); + } + else + { + val = list_copy(0, val); + } + + string_free(var); + /* The 'unique' variable is freed in 'call_rule'. */ + list_free(sorted); + + return val; + +} + +void init_property_set() +{ + { + char* args[] = { "raw-properties", "*", 0 }; + declare_native_rule("property-set", "create", args, property_set_create, 1); + } +} diff --git a/jam-files/engine/modules/readme.txt b/jam-files/engine/modules/readme.txt new file mode 100644 index 00000000..2edf6e17 --- /dev/null +++ b/jam-files/engine/modules/readme.txt @@ -0,0 +1,3 @@ + +This directory constains sources which declare native +rules for Boost.Build modules.
\ No newline at end of file diff --git a/jam-files/engine/modules/regex.c b/jam-files/engine/modules/regex.c new file mode 100644 index 00000000..d048ba1d --- /dev/null +++ b/jam-files/engine/modules/regex.c @@ -0,0 +1,96 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" +#include "../timestamp.h" +#include "../newstr.h" +#include "../strings.h" +#include "../regexp.h" +#include "../compile.h" + +/* +rule transform ( list * : pattern : indices * ) +{ + indices ?= 1 ; + local result ; + for local e in $(list) + { + local m = [ MATCH $(pattern) : $(e) ] ; + if $(m) + { + result += $(m[$(indices)]) ; + } + } + return $(result) ; +} +*/ +LIST *regex_transform( PARSE *parse, FRAME *frame ) +{ + LIST* l = lol_get( frame->args, 0 ); + LIST* pattern = lol_get( frame->args, 1 ); + LIST* indices_list = lol_get(frame->args, 2); + int* indices = 0; + int size; + int* p; + LIST* result = 0; + + string buf[1]; + string_new(buf); + + if (indices_list) + { + size = list_length(indices_list); + indices = (int*)BJAM_MALLOC(size*sizeof(int)); + for(p = indices; indices_list; indices_list = indices_list->next) + { + *p++ = atoi(indices_list->string); + } + } + else + { + size = 1; + indices = (int*)BJAM_MALLOC(sizeof(int)); + *indices = 1; + } + + { + /* Result is cached and intentionally never freed */ + regexp *re = regex_compile( pattern->string ); + + for(; l; l = l->next) + { + if( regexec( re, l->string ) ) + { + int i = 0; + for(; i < size; ++i) + { + int index = indices[i]; + /* Skip empty submatches. Not sure it's right in all cases, + but surely is right for the case for which this routine + is optimized -- header scanning. + */ + if (re->startp[index] != re->endp[index]) + { + string_append_range( buf, re->startp[index], re->endp[index] ); + result = list_new( result, newstr( buf->value ) ); + string_truncate( buf, 0 ); + } + } + } + } + string_free( buf ); + } + + BJAM_FREE(indices); + + return result; +} + +void init_regex() +{ + { + char* args[] = { "list", "*", ":", "pattern", ":", "indices", "*", 0 }; + declare_native_rule("regex", "transform", args, regex_transform, 2); + } +} diff --git a/jam-files/engine/modules/sequence.c b/jam-files/engine/modules/sequence.c new file mode 100644 index 00000000..bda80d94 --- /dev/null +++ b/jam-files/engine/modules/sequence.c @@ -0,0 +1,42 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" + +# ifndef max +# define max( a,b ) ((a)>(b)?(a):(b)) +# endif + + +LIST *sequence_select_highest_ranked( PARSE *parse, FRAME *frame ) +{ + /* Returns all of 'elements' for which corresponding element in parallel */ + /* list 'rank' is equal to the maximum value in 'rank'. */ + + LIST* elements = lol_get( frame->args, 0 ); + LIST* rank = lol_get( frame->args, 1 ); + + LIST* result = 0; + LIST* tmp; + int highest_rank = -1; + + for (tmp = rank; tmp; tmp = tmp->next) + highest_rank = max(highest_rank, atoi(tmp->string)); + + for (; rank; rank = rank->next, elements = elements->next) + if (atoi(rank->string) == highest_rank) + result = list_new(result, elements->string); + + return result; +} + +void init_sequence() +{ + { + char* args[] = { "elements", "*", ":", "rank", "*", 0 }; + declare_native_rule("sequence", "select-highest-ranked", args, + sequence_select_highest_ranked, 1); + } + +} diff --git a/jam-files/engine/modules/set.c b/jam-files/engine/modules/set.c new file mode 100644 index 00000000..f8219403 --- /dev/null +++ b/jam-files/engine/modules/set.c @@ -0,0 +1,41 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "../native.h" + +/* + local result = ; + local element ; + for element in $(B) + { + if ! ( $(element) in $(A) ) + { + result += $(element) ; + } + } + return $(result) ; +*/ +LIST *set_difference( PARSE *parse, FRAME *frame ) +{ + + LIST* b = lol_get( frame->args, 0 ); + LIST* a = lol_get( frame->args, 1 ); + + LIST* result = 0; + for(; b; b = b->next) + { + if (!list_in(a, b->string)) + result = list_new(result, b->string); + } + return result; +} + +void init_set() +{ + { + char* args[] = { "B", "*", ":", "A", "*", 0 }; + declare_native_rule("set", "difference", args, set_difference, 1); + } + +} diff --git a/jam-files/engine/native.c b/jam-files/engine/native.c new file mode 100644 index 00000000..4c289959 --- /dev/null +++ b/jam-files/engine/native.c @@ -0,0 +1,36 @@ +/* Copyright Vladimir Prus 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "native.h" +#include "hash.h" + +# define P0 (PARSE *)0 +# define C0 (char *)0 + + +void declare_native_rule(char* module, char* rule, char** args, + LIST*(*f)(PARSE*, FRAME*), int version) +{ + module_t* m = bindmodule(module); + if (m->native_rules == 0) { + m->native_rules = hashinit( sizeof( native_rule_t ), "native rules"); + } + + { + native_rule_t n, *np = &n; + n.name = rule; + if (args) + { + n.arguments = args_new(); + lol_build( n.arguments->data, args ); + } + else + { + n.arguments = 0; + } + n.procedure = parse_make( f, P0, P0, P0, C0, C0, 0 ); + n.version = version; + hashenter(m->native_rules, (HASHDATA**)&np); + } +} diff --git a/jam-files/engine/native.h b/jam-files/engine/native.h new file mode 100644 index 00000000..3fc710b9 --- /dev/null +++ b/jam-files/engine/native.h @@ -0,0 +1,34 @@ +/* Copyright David Abrahams 2003. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#ifndef NATIVE_H_VP_2003_12_09 +#define NATIVE_H_VP_2003_12_09 + +#include "rules.h" + +struct native_rule_t +{ + char* name; + argument_list* arguments; + PARSE* procedure; + /* Version of the interface that the native rule provides. + It's possible that we want to change the set parameter + for existing native rule. In that case, version number + should be incremented so that Boost.Build can check for + version it relies on. + + Versions are numbered from 1. + */ + int version; +}; + +/* MSVC debugger gets confused unless this is provided */ +typedef struct native_rule_t native_rule_t ; + +void declare_native_rule(char* module, char* rule, char** args, + LIST*(*f)(PARSE*, FRAME*), int version); + + + +#endif diff --git a/jam-files/engine/newstr.c b/jam-files/engine/newstr.c new file mode 100644 index 00000000..6a229eb2 --- /dev/null +++ b/jam-files/engine/newstr.c @@ -0,0 +1,174 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "newstr.h" +# include "hash.h" +# include "compile.h" +# include <stddef.h> +# include <stdlib.h> + +/* + * newstr.c - string manipulation routines + * + * To minimize string copying, string creation, copying, and freeing + * is done through newstr. + * + * External functions: + * + * newstr() - return a dynamically allocated copy of a string + * copystr() - return a copy of a string previously returned by newstr() + * freestr() - free a string returned by newstr() or copystr() + * str_done() - free string tables + * + * Once a string is passed to newstr(), the returned string is readonly. + * + * This implementation builds a hash table of all strings, so that multiple + * calls of newstr() on the same string allocate memory for the string once. + * Strings are never actually freed. + */ + +typedef char * STRING; + +static struct hash * strhash = 0; +static int strtotal = 0; +static int strcount_in = 0; +static int strcount_out = 0; + + +/* + * Immortal string allocator implementation speeds string allocation and cuts + * down on internal fragmentation. + */ + +# define STRING_BLOCK 4096 +typedef struct strblock +{ + struct strblock * next; + char data[STRING_BLOCK]; +} strblock; + +static strblock * strblock_chain = 0; + +/* Storage remaining in the current strblock */ +static char * storage_start = 0; +static char * storage_finish = 0; + + +/* + * allocate() - Allocate n bytes of immortal string storage. + */ + +static char * allocate( size_t const n ) +{ +#ifdef BJAM_NEWSTR_NO_ALLOCATE + return (char*)BJAM_MALLOC_ATOMIC(n); +#else + /* See if we can grab storage from an existing block. */ + size_t remaining = storage_finish - storage_start; + if ( remaining >= n ) + { + char * result = storage_start; + storage_start += n; + return result; + } + else /* Must allocate a new block. */ + { + strblock * new_block; + size_t nalloc = n; + if ( nalloc < STRING_BLOCK ) + nalloc = STRING_BLOCK; + + /* Allocate a new block and link into the chain. */ + new_block = (strblock *)BJAM_MALLOC( offsetof( strblock, data[0] ) + nalloc * sizeof( new_block->data[0] ) ); + if ( new_block == 0 ) + return 0; + new_block->next = strblock_chain; + strblock_chain = new_block; + + /* Take future allocations out of the larger remaining space. */ + if ( remaining < nalloc - n ) + { + storage_start = new_block->data + n; + storage_finish = new_block->data + nalloc; + } + return new_block->data; + } +#endif +} + + +/* + * newstr() - return a dynamically allocated copy of a string. + */ + +char * newstr( char * string ) +{ + STRING str; + STRING * s = &str; + + if ( !strhash ) + strhash = hashinit( sizeof( STRING ), "strings" ); + + *s = string; + + if ( hashenter( strhash, (HASHDATA **)&s ) ) + { + int l = strlen( string ); + char * m = (char *)allocate( l + 1 ); + + strtotal += l + 1; + memcpy( m, string, l + 1 ); + *s = m; + } + + strcount_in += 1; + return *s; +} + + +/* + * copystr() - return a copy of a string previously returned by newstr() + */ + +char * copystr( char * s ) +{ + strcount_in += 1; + return s; +} + + +/* + * freestr() - free a string returned by newstr() or copystr() + */ + +void freestr( char * s ) +{ + strcount_out += 1; +} + + +/* + * str_done() - free string tables. + */ + +void str_done() +{ + /* Reclaim string blocks. */ + while ( strblock_chain != 0 ) + { + strblock * n = strblock_chain->next; + BJAM_FREE(strblock_chain); + strblock_chain = n; + } + + hashdone( strhash ); + + if ( DEBUG_MEM ) + printf( "%dK in strings\n", strtotal / 1024 ); + + /* printf( "--- %d strings of %d dangling\n", strcount_in-strcount_out, strcount_in ); */ +} diff --git a/jam-files/engine/newstr.h b/jam-files/engine/newstr.h new file mode 100644 index 00000000..84a4d7b6 --- /dev/null +++ b/jam-files/engine/newstr.h @@ -0,0 +1,14 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * newstr.h - string manipulation routines + */ + +char * copystr ( char * ); +void freestr ( char * ); +char * newstr ( char * ); +void str_done(); diff --git a/jam-files/engine/option.c b/jam-files/engine/option.c new file mode 100644 index 00000000..d25e5e8a --- /dev/null +++ b/jam-files/engine/option.c @@ -0,0 +1,94 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "option.h" + +/* + * option.c - command line option processing + * + * {o >o + * \<>) "Process command line options as defined in <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." + */ + +int getoptions( int argc, char * * argv, char * opts, bjam_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 ] != '-' ) || + ( ( argv[ i ][ 1 ] != '-' ) && !isalpha( argv[ i ][ 1 ] ) ) ) + continue; + + if ( !optc-- ) + { + printf( "too many options (%d max)\n", N_OPTS ); + return -1; + } + + for ( arg = &argv[ i ][ 1 ]; *arg; ++arg ) + { + char * f; + + for ( f = opts; *f; ++f ) + if ( *f == *arg ) + break; + + if ( !*f ) + { + printf( "Invalid option: -%c\n", *arg ); + return -1; + } + + optv->flag = *f; + + if ( f[ 1 ] != ':' ) + { + optv++->val = "true"; + } + else if ( arg[ 1 ] ) + { + optv++->val = &arg[1]; + break; + } + else if ( ++i < argc ) + { + optv++->val = argv[ i ]; + break; + } + else + { + printf( "option: -%c needs argument\n", *f ); + return -1; + } + } + } + + return i; +} + + +/* + * Name: getoptval() - find an option given its character. + */ + +char * getoptval( bjam_option * optv, char opt, int subopt ) +{ + int i; + for ( i = 0; i < N_OPTS; ++i, ++optv ) + if ( ( optv->flag == opt ) && !subopt-- ) + return optv->val; + return 0; +} diff --git a/jam-files/engine/option.h b/jam-files/engine/option.h new file mode 100644 index 00000000..99ef620d --- /dev/null +++ b/jam-files/engine/option.h @@ -0,0 +1,23 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * option.h - command line option processing + * + * {o >o + * \ -) "Command line option." + */ + +typedef struct bjam_option +{ + char flag; /* filled in by getoption() */ + char *val; /* set to random address if true */ +} bjam_option; + +# define N_OPTS 256 + +int getoptions( int argc, char **argv, char *opts, bjam_option *optv ); +char * getoptval( bjam_option *optv, char opt, int subopt ); diff --git a/jam-files/engine/output.c b/jam-files/engine/output.c new file mode 100644 index 00000000..483c6ca9 --- /dev/null +++ b/jam-files/engine/output.c @@ -0,0 +1,125 @@ +/* + Copyright 2007 Rene Rivera + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include "jam.h" +#include "output.h" +#include "newstr.h" +#include <stdio.h> + +#define bjam_out (stdout) +#define bjam_err (stderr) + +static void out_ +( + char const * data, + FILE * io +) +{ + while ( *data ) + { + size_t len = strcspn(data,"\r"); + data += fwrite(data,1,len,io); + if ( *data == '\r' ) ++data; + } +} + + +void out_action +( + char const * action, + char const * target, + char const * command, + char const * out_data, + char const * err_data, + int exit_reason +) +{ + /* Print out the action+target line, if the action is quite the action + * should be null. + */ + if ( action ) + { + fprintf( bjam_out, "%s %s\n", action, target ); + } + + /* Print out the command executed if given -d+2. */ + if ( DEBUG_EXEC ) + { + fputs( command, bjam_out ); + fputc( '\n', bjam_out ); + } + + /* Print out the command executed to the command stream. */ + if ( globs.cmdout ) + { + fputs( command, globs.cmdout ); + } + + switch ( exit_reason ) + { + case EXIT_OK: + break; + case EXIT_FAIL: + break; + case EXIT_TIMEOUT: + { + /* Process expired, make user aware with explicit message. */ + if ( action ) + { + /* But only output for non-quietly actions. */ + fprintf( bjam_out, "%ld second time limit exceeded\n", globs.timeout ); + } + break; + } + default: + break; + } + + /* Print out the command output, if requested, or if the program failed. */ + if ( action || exit_reason != EXIT_OK) + { + /* But only output for non-quietly actions. */ + if ( ( 0 != out_data ) && + ( ( globs.pipe_action & 1 /* STDOUT_FILENO */ ) || + ( globs.pipe_action == 0 ) ) ) + { + out_( out_data, bjam_out ); + } + if ( ( 0 != err_data ) && + ( globs.pipe_action & 2 /* STDERR_FILENO */ ) ) + { + out_( err_data, bjam_err ); + } + } + + fflush( bjam_out ); + fflush( bjam_err ); + fflush( globs.cmdout ); +} + + +char * outf_int( int value ) +{ + char buffer[50]; + sprintf( buffer, "%i", value ); + return newstr( buffer ); +} + + +char * outf_double( double value ) +{ + char buffer[50]; + sprintf( buffer, "%f", value ); + return newstr( buffer ); +} + + +char * outf_time( time_t value ) +{ + char buffer[50]; + strftime( buffer, 49, "%Y-%m-%d %H:%M:%SZ", gmtime( &value ) ); + return newstr( buffer ); +} diff --git a/jam-files/engine/output.h b/jam-files/engine/output.h new file mode 100644 index 00000000..9e9876cf --- /dev/null +++ b/jam-files/engine/output.h @@ -0,0 +1,29 @@ +/* + Copyright 2007 Rene Rivera + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef BJAM_OUTPUT_H +#define BJAM_OUTPUT_H + +#include <time.h> + +#define EXIT_OK 0 +#define EXIT_FAIL 1 +#define EXIT_TIMEOUT 2 + +void out_action( + const char * action, + const char * target, + const char * command, + const char * out_data, + const char * err_data, + int exit_reason + ); + +char * outf_int( int value ); +char * outf_double( double value ); +char * outf_time( time_t value ); + +#endif diff --git a/jam-files/engine/parse.c b/jam-files/engine/parse.c new file mode 100644 index 00000000..9114fa05 --- /dev/null +++ b/jam-files/engine/parse.c @@ -0,0 +1,132 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "jam.h" +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "newstr.h" +#include "modules.h" +#include "frames.h" + +/* + * parse.c - make and destroy parse trees as driven by the parser + * + * 09/07/00 (seiwald) - ref count on PARSE to avoid freeing when used, + * as per Matt Armstrong. + * 09/11/00 (seiwald) - structure reworked to reflect that (*func)() + * returns a LIST *. + */ + +static PARSE * yypsave; + +void parse_file( char * f, FRAME * frame ) +{ + /* 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 ( ; ; ) + { + PARSE * p; + + /* 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. */ + parse_evaluate( p, frame ); + parse_free( p ); + } +} + + +void parse_save( PARSE * p ) +{ + yypsave = p; +} + + +PARSE * parse_make( + LIST * (* func)( PARSE *, FRAME * ), + PARSE * left, + PARSE * right, + PARSE * third, + char * string, + char * string1, + int num ) +{ + PARSE * p = (PARSE *)BJAM_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; + p->rulename = 0; + + if ( left ) + { + p->file = left->file; + p->line = left->line; + } + else + { + yyinput_stream( &p->file, &p->line ); + } + + 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 ); + if ( p->rulename ) + freestr( p->rulename ); + + BJAM_FREE( (char *)p ); +} + + +LIST * parse_evaluate( PARSE * p, FRAME * frame ) +{ + frame->procedure = p; + return (*p->func)( p, frame ); +} diff --git a/jam-files/engine/parse.h b/jam-files/engine/parse.h new file mode 100644 index 00000000..e324972f --- /dev/null +++ b/jam-files/engine/parse.h @@ -0,0 +1,59 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef PARSE_DWA20011020_H +#define PARSE_DWA20011020_H + +#include "frames.h" +#include "modules.h" +#include "lists.h" + +/* + * parse.h - make and destroy parse trees as driven by the parser. + */ + +/* + * Parse tree node. + */ + +struct _PARSE { + LIST * (* func)( PARSE *, FRAME * ); + PARSE * left; + PARSE * right; + PARSE * third; + char * string; + char * string1; + int num; + int refs; +/* module * module; */ + char * rulename; + char * file; + int line; +}; + +void parse_file( char *, FRAME * ); +void parse_save( PARSE * ); + +PARSE * parse_make( + LIST * (* func)( PARSE *, FRAME * ), + PARSE * left, + PARSE * right, + PARSE * third, + char * string, + char * string1, + int num ); + +void parse_refer ( PARSE * ); +void parse_free ( PARSE * ); +LIST * parse_evaluate( PARSE *, FRAME * ); + +#endif diff --git a/jam-files/engine/patchlevel.h b/jam-files/engine/patchlevel.h new file mode 100644 index 00000000..699efd84 --- /dev/null +++ b/jam-files/engine/patchlevel.h @@ -0,0 +1,17 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* Keep JAMVERSYM in sync with VERSION. */ +/* It can be accessed as $(JAMVERSION) in the Jamfile. */ + +#define VERSION_MAJOR 2011 +#define VERSION_MINOR 04 +#define VERSION_PATCH 0 +#define VERSION_MAJOR_SYM "2011" +#define VERSION_MINOR_SYM "04" +#define VERSION_PATCH_SYM "00" +#define VERSION "2011.4" +#define JAMVERSYM "JAMVERSION=2011.4" diff --git a/jam-files/engine/pathmac.c b/jam-files/engine/pathmac.c new file mode 100644 index 00000000..e2c250e3 --- /dev/null +++ b/jam-files/engine/pathmac.c @@ -0,0 +1,252 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "pathsys.h" + +# ifdef OS_MAC + +# define DELIM ':' + +/* + * pathunix.c - manipulate file names on UNIX, NT, OS2 + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build() + * 12/19/94 (mikem) - solaris string table insanity support + * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way. + * 02/14/95 (seiwald) - parse and build /xxx properly + * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we + * should expect hdr searches to come up with strings + * like "thing/thing.h". So we need to test for "/" as + * well as "\" when parsing pathnames. + * 03/16/95 (seiwald) - fixed accursed typo on line 69. + * 05/03/96 (seiwald) - split from filent.c, fileunix.c + * 12/20/96 (seiwald) - when looking for the rightmost . in a file name, + * don't include the archive member name. + * 01/10/01 (seiwald) - path_parse now strips the trailing : from the + * directory name, unless the directory name is all + * :'s, so that $(d:P) works. + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member + */ + +void +path_parse( + char *file, + PATHNAME *f ) +{ + char *p, *q; + char *end; + + memset( (char *)f, 0, sizeof( *f ) ); + + /* Look for <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( 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, string * file, int binding ) +{ + int dflag; + int rflag; + int act; + + file_build1( f, 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 */ + string_push_back( file, DELIM ); + } + /* fall through */ + + case G_DIR: + /* take dir */ + string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len ); + break; + + case G_ROOT: + /* take root */ + string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); + break; + + case G_CAT: + /* prepend root to dir */ + string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); + if ( file->value[ file->size - 1 ] == DELIM ) + string_pop_back( file ); + string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len ); + break; + + case G_DDDD: + /* make it ::: (../..) */ + string_append( file, ":::" ); + break; + } + + /* Put : between dir and file (if none already). */ + + if ( ( act != G_MT ) && + ( file->value[ file->size - 1 ] != DELIM ) && + ( f->f_base.len || f->f_suffix.len ) ) + { + string_push_back( file, DELIM ); + } + + if ( f->f_base.len ) + string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len ); + + if ( f->f_suffix.len ) + string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len ); + + if ( f->f_member.len ) + { + string_push_back( file, '(' ); + string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len ); + string_push_back( file, ')' ); + } + + if ( DEBUG_SEARCH ) + printf( " -> '%s'\n", file->value ); +} + + +/* + * path_parent() - make a PATHNAME point to its parent dir + */ + +void path_parent( PATHNAME * f ) +{ + /* Just set everything else to nothing. */ + + f->f_base.ptr = + f->f_suffix.ptr = + f->f_member.ptr = ""; + + f->f_base.len = + f->f_suffix.len = + f->f_member.len = 0; +} + +# endif /* OS_MAC */ diff --git a/jam-files/engine/pathsys.h b/jam-files/engine/pathsys.h new file mode 100644 index 00000000..73775810 --- /dev/null +++ b/jam-files/engine/pathsys.h @@ -0,0 +1,91 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * pathsys.h - PATHNAME struct + */ + +/* + * PATHNAME - a name of a file, broken into <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. + */ + +#ifndef PATHSYS_VP_20020211_H +# define PATHSYS_VP_20020211_H + +#include "strings.h" + +typedef struct _pathname PATHNAME; +typedef struct _pathpart PATHPART; + +struct _pathpart +{ + char * ptr; + int len; +}; + +struct _pathname +{ + PATHPART part[6]; +#ifdef OS_VMS + int parent; +#endif + +#define f_grist part[0] +#define f_root part[1] +#define f_dir part[2] +#define f_base part[3] +#define f_suffix part[4] +#define f_member part[5] +}; + +void path_build( PATHNAME * f, string * file, int binding ); +void path_build1( PATHNAME * f, string * file ); + +void path_parse( char * file, PATHNAME * f ); +void path_parent( PATHNAME * f ); + +#ifdef NT + +/** Returns newstr-allocated string with long equivivalent of 'short_name'. + If none exists -- i.e. 'short_path' is already long path, it's returned + unaltered. */ +char * short_path_to_long_path( char * short_path ); + +#endif + +#ifdef USE_PATHUNIX +/** Returns a static pointer to the system dependent path to the temporary + directory. NOTE: *without* a trailing path separator. +*/ +const char * path_tmpdir( void ); + +/** Returns a new temporary name. +*/ +const char * path_tmpnam( void ); + +/** Returns a new temporary path. +*/ +const char * path_tmpfile( void ); +#endif + +/** Give the first argument to 'main', return a full path to + our executable. Returns null in the unlikely case it + cannot be determined. Caller is responsible for freeing + the string. + + Implemented in jam.c +*/ +char * executable_path (char *argv0); + +#endif diff --git a/jam-files/engine/pathunix.c b/jam-files/engine/pathunix.c new file mode 100644 index 00000000..2daad14b --- /dev/null +++ b/jam-files/engine/pathunix.c @@ -0,0 +1,457 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Copyright 2005 Rene Rivera. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "pathsys.h" +# include "strings.h" +# include "newstr.h" +# include "filesys.h" +# include <time.h> +# include <stdlib.h> +# ifndef OS_NT +# include <unistd.h> +# endif + +# ifdef USE_PATHUNIX + +/* + * pathunix.c - manipulate file names on UNIX, NT, OS2, AmigaOS + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * 04/08/94 (seiwald) - Coherent/386 support added. + * 12/26/93 (seiwald) - handle dir/.suffix properly in path_build() + * 12/19/94 (mikem) - solaris string table insanity support + * 12/21/94 (wingerd) Use backslashes for pathnames - the NT way. + * 02/14/95 (seiwald) - parse and build /xxx properly + * 02/23/95 (wingerd) Compilers on NT can handle "/" in pathnames, so we + * should expect hdr searches to come up with strings + * like "thing/thing.h". So we need to test for "/" as + * well as "\" when parsing pathnames. + * 03/16/95 (seiwald) - fixed accursed typo on line 69. + * 05/03/96 (seiwald) - split from filent.c, fileunix.c + * 12/20/96 (seiwald) - when looking for the rightmost . in a file name, + * don't include the archive member name. + * 01/13/01 (seiwald) - turn on \ handling on UNIX, on by accident + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member + */ + +void path_parse( char * file, PATHNAME * f ) +{ + char * p; + char * q; + 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_delims - the string of legal path delimiters + */ +static char path_delims[] = { + PATH_DELIM, +# if PATH_DELIM == '\\' + '/', +# endif + 0 +}; + +/* + * is_path_delim() - true iff c is a path delimiter + */ +static int is_path_delim( char c ) +{ + char* p = strchr( path_delims, c ); + return p && *p; +} + +/* + * as_path_delim() - convert c to a path delimiter if it isn't one + * already + */ +static char as_path_delim( char c ) +{ + return is_path_delim( c ) ? c : PATH_DELIM; +} + +/* + * path_build() - build a filename given dir/base/suffix/member + * + * To avoid changing slash direction on NT when reconstituting paths, + * instead of unconditionally appending PATH_DELIM we check the + * past-the-end character of the previous path element. If it is in + * path_delims, we append that, and only append PATH_DELIM as a last + * resort. This heuristic is based on the fact that PATHNAME objects + * are usually the result of calling path_parse, which leaves the + * original slashes in the past-the-end position. Correctness depends + * on the assumption that all strings are zero terminated, so a + * past-the-end character will always be available. + * + * As an attendant patch, we had to ensure that backslashes are used + * explicitly in timestamp.c + */ + +void +path_build( + PATHNAME *f, + string *file, + int binding ) +{ + file_build1( f, 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 */ + + { + string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); + /* If 'root' already ends with path delimeter, + don't add yet another one. */ + if ( ! is_path_delim( f->f_root.ptr[f->f_root.len-1] ) ) + string_push_back( file, as_path_delim( f->f_root.ptr[f->f_root.len] ) ); + } + + if ( f->f_dir.len ) + string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + 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 && is_path_delim( f->f_dir.ptr[0] ) ) ) + string_push_back( file, as_path_delim( f->f_dir.ptr[f->f_dir.len] ) ); + } + + if ( f->f_base.len ) + { + string_append_range( file, f->f_base.ptr, f->f_base.ptr + f->f_base.len ); + } + + if ( f->f_suffix.len ) + { + string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len ); + } + + if ( f->f_member.len ) + { + string_push_back( file, '(' ); + string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len ); + string_push_back( file, ')' ); + } +} + +/* + * 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; +} + +#ifdef NT +#include <windows.h> +#include <tchar.h> + +/* The definition of this in winnt.h is not ANSI-C compatible. */ +#undef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) + + +DWORD ShortPathToLongPath(LPCTSTR lpszShortPath,LPTSTR lpszLongPath,DWORD + cchBuffer) +{ + LONG i=0; + TCHAR path[_MAX_PATH]={0}; + TCHAR ret[_MAX_PATH]={0}; + LONG pos=0, prev_pos=0; + LONG len=_tcslen(lpszShortPath); + + /* Is the string valid? */ + if (!lpszShortPath) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + /* Is the path valid? */ + if (GetFileAttributes(lpszShortPath)==INVALID_FILE_ATTRIBUTES) + return 0; + + /* Convert "/" to "\" */ + for (i=0;i<len;++i) { + if (lpszShortPath[i]==_T('/')) + path[i]=_T('\\'); + else + path[i]=lpszShortPath[i]; + } + + /* UNC path? */ + if (path[0]==_T('\\') && path[1]==_T('\\')) { + pos=2; + for (i=0;i<2;++i) { + while (path[pos]!=_T('\\') && path[pos]!=_T('\0')) + ++pos; + ++pos; + } + _tcsncpy(ret,path,pos-1); + } /* Drive letter? */ + else if (path[1]==_T(':')) { + if (path[2]==_T('\\')) + pos=3; + if (len==3) { + if (cchBuffer>3) + _tcscpy(lpszLongPath,lpszShortPath); + return len; + } + _tcsncpy(ret,path,2); + } + + /* Expand the path for each subpath, and strip trailing backslashes */ + for (prev_pos = pos-1;pos<=len;++pos) { + if (path[pos]==_T('\\') || (path[pos]==_T('\0') && + path[pos-1]!=_T('\\'))) { + WIN32_FIND_DATA fd; + HANDLE hf=0; + TCHAR c=path[pos]; + char* new_element; + path[pos]=_T('\0'); + + /* the path[prev_pos+1]... path[pos] range is the part of + path we're handling right now. We need to find long + name for that element and add it. */ + new_element = path + prev_pos + 1; + + /* First add separator, but only if there's something in result already. */ + if (ret[0] != _T('\0')) + { + _tcscat(ret,_T("\\")); + } + + /* If it's ".." element, we need to append it, not + the name in parent that FindFirstFile will return. + Same goes for "." */ + + if (new_element[0] == _T('.') && new_element[1] == _T('\0') || + new_element[0] == _T('.') && new_element[1] == _T('.') + && new_element[2] == _T('\0')) + { + _tcscat(ret, new_element); + } + else + { + hf=FindFirstFile(path, &fd); + if (hf==INVALID_HANDLE_VALUE) + return 0; + + _tcscat(ret,fd.cFileName); + FindClose(hf); + } + + path[pos]=c; + + prev_pos = pos; + } + } + + len=_tcslen(ret)+1; + if (cchBuffer>=len) + _tcscpy(lpszLongPath,ret); + + return len; +} + +char* short_path_to_long_path(char* short_path) +{ + char buffer2[_MAX_PATH]; + int ret = ShortPathToLongPath(short_path, buffer2, _MAX_PATH); + + if (ret) + return newstr(buffer2); + else + return newstr(short_path); +} + +#endif + +static string path_tmpdir_buffer[1]; +static const char * path_tmpdir_result = 0; + +const char * path_tmpdir() +{ + if (!path_tmpdir_result) + { + # ifdef OS_NT + DWORD pathLength = 0; + pathLength = GetTempPath(pathLength,NULL); + string_new(path_tmpdir_buffer); + string_reserve(path_tmpdir_buffer,pathLength); + pathLength = GetTempPathA(pathLength,path_tmpdir_buffer[0].value); + path_tmpdir_buffer[0].value[pathLength-1] = '\0'; + path_tmpdir_buffer[0].size = pathLength-1; + # else + const char * t = getenv("TMPDIR"); + if (!t) + { + t = "/tmp"; + } + string_new(path_tmpdir_buffer); + string_append(path_tmpdir_buffer,t); + # endif + path_tmpdir_result = path_tmpdir_buffer[0].value; + } + return path_tmpdir_result; +} + +const char * path_tmpnam(void) +{ + char name_buffer[64]; + # ifdef OS_NT + unsigned long c0 = GetCurrentProcessId(); + # else + unsigned long c0 = getpid(); + # endif + static unsigned long c1 = 0; + if (0 == c1) c1 = time(0)&0xffff; + c1 += 1; + sprintf(name_buffer,"jam%lx%lx.000",c0,c1); + return newstr(name_buffer); +} + +const char * path_tmpfile(void) +{ + const char * result = 0; + + string file_path; + string_copy(&file_path,path_tmpdir()); + string_push_back(&file_path,PATH_DELIM); + string_append(&file_path,path_tmpnam()); + result = newstr(file_path.value); + string_free(&file_path); + + return result; +} + + +# endif /* unix, NT, OS/2, AmigaOS */ diff --git a/jam-files/engine/pathvms.c b/jam-files/engine/pathvms.c new file mode 100644 index 00000000..975fe5a5 --- /dev/null +++ b/jam-files/engine/pathvms.c @@ -0,0 +1,406 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" +# include "pathsys.h" + +# ifdef OS_VMS + +# define DEBUG + +/* + * pathvms.c - manipulate file names on VMS + * + * External routines: + * + * path_parse() - split a file name into dir/base/suffix/member + * path_build() - build a filename given dir/base/suffix/member + * path_parent() - make a PATHNAME point to its parent dir + * + * File_parse() and path_build() just manipuate a string and a structure; + * they do not make system calls. + * + * WARNING! This file contains voodoo logic, as black magic is + * necessary for wrangling with VMS file name. Woe be to people + * who mess with this code. + * + * 02/09/95 (seiwald) - bungled R=[xxx] - was using directory length! + * 05/03/96 (seiwald) - split from filevms.c + */ + +/* + * path_parse() - split a file name into dir/base/suffix/member. + */ + +void path_parse( char * file, PATHNAME * f ) +{ + char * p; + char * q; + 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( char * buf, int len, struct dirinf * i ) +{ + char * p; + + if ( !buf || !len ) + { + i->flags = DIR_EMPTY; + i->dev.ptr = + i->dir.ptr = 0; + i->dev.len = + i->dir.len = 0; + } + else if ( p = strnchr( buf, ':', len ) ) + { + i->dev.ptr = buf; + i->dev.len = p + 1 - buf; + i->dir.ptr = buf + i->dev.len; + i->dir.len = len - i->dev.len; + i->flags = i->dir.len && *i->dir.ptr == '[' ? DIR_DEVDIR : DIR_DEV; + } + else + { + i->dev.ptr = buf; + i->dev.len = 0; + i->dir.ptr = buf; + i->dir.len = len; + + if ( ( *buf == '[' ) && ( buf[1] == ']' ) ) + i->flags = DIR_EMPTY; + else if ( ( *buf == '[' ) && ( buf[1] == '.' ) ) + i->flags = DIR_DOTDIR; + else if ( ( *buf == '[' ) && ( buf[1] == '-' ) ) + i->flags = DIR_DASHDIR; + else + i->flags = DIR_ABSDIR; + } + + /* But if its rooted in any way. */ + + if ( ( i->dir.len == 8 ) && !strncmp( i->dir.ptr, "[000000]", 8 ) ) + i->flags = DIR_ROOT; +} + + +/* + * path_build() - build a filename given dir/base/suffix/member + */ + +void path_build( PATHNAME * f, string * file, int binding ) +{ + struct dirinf root; + struct dirinf dir; + int g; + + file_build1( f, 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 */ + string_append_range( file, f->f_dir.ptr, f->f_dir.ptr + f->f_dir.len ); + break; + + case G_ROOT: + /* take root */ + string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); + break; + + case G_VAD: + /* root's dev + abs directory */ + string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len ); + string_append_range( file, dir.dir.ptr, dir.dir.ptr + dir.dir.len ); + break; + + case G_DRD: + case G_DDD: + /* root's dev:[dir] + rel directory */ + string_append_range( file, f->f_root.ptr, f->f_root.ptr + f->f_root.len ); + + /* sanity checks: root ends with ] */ + + if ( file->value[file->size - 1] == ']' ) + string_pop_back( file ); + + /* Add . if separating two -'s */ + + if ( g == G_DDD ) + string_push_back( file, '.' ); + + /* skip [ of dir */ + string_append_range( file, dir.dir.ptr + 1, dir.dir.ptr + 1 + dir.dir.len - 1 ); + break; + + case G_VRD: + /* root's dev + rel directory made abs */ + string_append_range( file, root.dev.ptr, root.dev.ptr + root.dev.len ); + string_push_back( file, '[' ); + /* skip [. of rel dir */ + string_append_range( file, dir.dir.ptr + 2, dir.dir.ptr + 2 + dir.dir.len - 2 ); + break; + } + +# ifdef DEBUG + if ( DEBUG_SEARCH && ( root.flags || dir.flags ) ) + printf( "%d x %d = %d (%s)\n", root.flags, dir.flags, + grid[ root.flags ][ dir.flags ], file->value ); +# endif + + /* + * Now do the special :P modifier when no file was present. + * (none) (none) + * [dir1.dir2] [dir1] + * [dir] [000000] + * [.dir] (none) + * [] [] + */ + + if ( ( file->value[ file->size - 1 ] == ']' ) && f->parent ) + { + char * p = file->value + file->size; + while ( p-- > file->value ) + { + if ( *p == '.' ) + { + /* If we've truncated everything and left with '[', + return empty string. */ + if ( p == file->value + 1 ) + string_truncate( file, 0 ); + else + { + string_truncate( file, p - file->value ); + string_push_back( file, ']' ); + } + break; + } + + if ( *p == '-' ) + { + /* handle .- or - */ + if ( ( p > file->value ) && ( p[ -1 ] == '.' ) ) + --p; + + *p++ = ']'; + break; + } + + if ( *p == '[' ) + { + if ( p[ 1 ] == ']' ) + { + /* CONSIDER: I don't see any use of this code. We immediately + break, and 'p' is a local variable. */ + p += 2; + } + else + { + string_truncate( file, p - file->value ); + string_append( file, "[000000]" ); + } + break; + } + } + } + + /* Now copy the file pieces. */ + if ( f->f_base.len ) + { + string_append_range( file, f->f_base.ptr, f->f_base.ptr + 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 ) + string_append_range( file, f->f_suffix.ptr, f->f_suffix.ptr + f->f_suffix.len ); + else if ( binding && f->f_base.len ) + string_push_back( file, '.' ); + + if ( f->f_member.len ) + { + string_push_back( file, '(' ); + string_append_range( file, f->f_member.ptr, f->f_member.ptr + f->f_member.len ); + string_push_back( file, ')' ); + } + +# 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, + file->value ); +# endif +} + + +/* + * path_parent() - make a PATHNAME point to its parent dir + */ + +void path_parent( PATHNAME * f ) +{ + if ( f->f_base.len ) + { + f->f_base.ptr = + f->f_suffix.ptr = + f->f_member.ptr = ""; + + f->f_base.len = + f->f_suffix.len = + f->f_member.len = 0; + } + else + { + f->parent = 1; + } +} + +# endif /* VMS */ diff --git a/jam-files/engine/pwd.c b/jam-files/engine/pwd.c new file mode 100644 index 00000000..90c8eb17 --- /dev/null +++ b/jam-files/engine/pwd.c @@ -0,0 +1,66 @@ +/* Copyright Vladimir Prus 2002, Rene Rivera 2005. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "jam.h" +#include "lists.h" +#include "newstr.h" +#include "pathsys.h" +#include "mem.h" + +#include <limits.h> +#include <errno.h> + +/* MinGW on windows declares PATH_MAX in limits.h */ +#if defined(NT) && ! defined(__GNUC__) +#include <direct.h> +#define PATH_MAX _MAX_PATH +#else +#include <unistd.h> +#if defined(__COMO__) + #include <linux/limits.h> +#endif +#endif + +#ifndef PATH_MAX + #define PATH_MAX 1024 +#endif + +/* The current directory can't change in bjam, so optimize this to cache +** the result. +*/ +static char * pwd_result = NULL; + + +LIST* +pwd(void) +{ + if (!pwd_result) + { + int buffer_size = PATH_MAX; + char * result_buffer = 0; + do + { + char * buffer = BJAM_MALLOC_RAW(buffer_size); + result_buffer = getcwd(buffer,buffer_size); + if (result_buffer) + { + #ifdef NT + pwd_result = short_path_to_long_path(result_buffer); + #else + pwd_result = newstr(result_buffer); + #endif + } + buffer_size *= 2; + BJAM_FREE_RAW(buffer); + } + while (!pwd_result && errno == ERANGE); + + if (!pwd_result) + { + perror("can not get current directory"); + return L0; + } + } + return list_new(L0, pwd_result); +} diff --git a/jam-files/engine/pwd.h b/jam-files/engine/pwd.h new file mode 100644 index 00000000..37cb531e --- /dev/null +++ b/jam-files/engine/pwd.h @@ -0,0 +1,10 @@ +/* Copyright Vladimir Prus 2002. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#ifndef PWD_H +#define PWD_H + +LIST* pwd(void); + +#endif diff --git a/jam-files/engine/regexp.c b/jam-files/engine/regexp.c new file mode 100644 index 00000000..30197a2f --- /dev/null +++ b/jam-files/engine/regexp.c @@ -0,0 +1,1328 @@ +/* + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to | + *** to assist in implementing egrep. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching + *** as in BSD grep and ex. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \. + *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods, + *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy. + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@vix.com, on 28 August 1993, for use in jam. Regmagic.h + *** was moved into regexp.h, and the include of regexp.h now uses "'s + *** to avoid conflicting with the system regexp.h. Const, bless its + *** soul, was removed so it can compile everywhere. The declaration + *** of strchr() was in conflict on AIX, so it was removed (as it is + *** happily defined in string.h). + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@perforce.com, on 20 January 2000, to use function prototypes. + * + * Beware that some of this code is subtly aware of the way operator precedence + * is structured in regular expressions. Serious changes in regular-expression + * syntax might require a total rethink. + */ + + +#include "jam.h" +#include "regexp.h" +#include <stdio.h> +#include <ctype.h> +#ifndef ultrix + #include <stdlib.h> +#endif +#include <string.h> + + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + : + * regstart char that must begin a match; '\0' if none obvious. + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL. + * regmlen length of regmust string. + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection of + * lines that cannot possibly match. The regmust tests are costly enough that + * regcomp() supplies a regmust only if the r.e. contains something potentially + * expensive (at present, the only such thing detected is * or + at the start of + * the r.e., which can involve a lot of backup). Regmlen is supplied because the + * test in regexec() needs it and regcomp() is computing it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding of a + * nondeterministic finite-state machine (aka syntax charts or "railroad normal + * form" in parsing technology). Each node is an opcode plus a "next" pointer, + * possibly plus an operand. "Next" pointers of all nodes except BRANCH + * implement concatenation; a "next" pointer with a BRANCH on both ends of it is + * connecting two alternatives. [Here we have one of the subtle syntax + * dependencies: an individual BRANCH, as opposed to a collection of them, is + * never concatenated with anything because of operator precedence.] The operand + * of some types of node is a literal string; for others, it is a node leading + * into a sub-FSM. In particular, the operand of a BRANCH node is the first node + * of the branch. [NB this is *not* a tree structure: the tail of the branch + * connects to the thing following the set of BRANCHes.] The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ +#define WORDA 12 /* no Match "" at wordchar, where prev is nonword */ +#define WORDZ 13 /* no Match "" at nonwordchar, where prev is word */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * See regmagic.h for one further detail of program structure. + */ + + +/* + * Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +/* + * Global work variables for regcomp(). + */ +static char *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +#ifndef STATIC +#define STATIC static +#endif +STATIC char *reg( int paren, int *flagp ); +STATIC char *regbranch( int *flagp ); +STATIC char *regpiece( int *flagp ); +STATIC char *regatom( int *flagp ); +STATIC char *regnode( int op ); +STATIC char *regnext( register char *p ); +STATIC void regc( int b ); +STATIC void reginsert( char op, char *opnd ); +STATIC void regtail( char *p, char *val ); +STATIC void regoptail( char *p, char *val ); +#ifdef STRCSPN +STATIC int strcspn(); +#endif + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp * +regcomp( char *exp ) +{ + register regexp *r; + register char *scan; + register char *longest; + register unsigned len; + int flags; + + if (exp == NULL) + FAIL("NULL argument"); + + /* First pass: determine size, legality. */ +#ifdef notdef + if (exp[0] == '.' && exp[1] == '*') exp += 2; /* aid grep */ +#endif + regparse = (char *)exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *)BJAM_MALLOC(sizeof(regexp) + (unsigned)regsize); + if (r == NULL) + FAIL("out of space"); + + /* Second pass: emit code. */ + regparse = (char *)exp; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program+1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char * +reg( + int paren, /* Parenthesized? */ + int *flagp ) +{ + register char *ret; + register char *br; + register char *ender; + register int parno = 0; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN+parno); + } else + ret = NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == NULL) + return(NULL); + if (ret != NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*regparse == '|' || *regparse == '\n') { + regparse++; + br = regbranch(&flags); + if (br == NULL) + return(NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE+parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char * +regbranch( int *flagp ) +{ + register char *ret; + register char *chain; + register char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = NULL; + while (*regparse != '\0' && *regparse != ')' && + *regparse != '\n' && *regparse != '|') { + latest = regpiece(&flags); + if (latest == NULL) + return(NULL); + *flagp |= flags&HASWIDTH; + if (chain == NULL) /* First piece. */ + *flagp |= flags&SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == NULL) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char * +regpiece( int *flagp ) +{ + register char *ret; + register char op; + register char *next; + int flags; + + ret = regatom(&flags); + if (ret == NULL) + return(NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); + + if (op == '*' && (flags&SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags&SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING); /* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static char * +regatom( int *flagp ) +{ + register char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + /* FIXME: these chars only have meaning at beg/end of pat? */ + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + register int classr; + register int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + classr = UCHARAT(regparse-2)+1; + classend = UCHARAT(regparse); + if (classr > classend+1) + FAIL("invalid [] range"); + for (; classr <= classend; classr++) + regc(classr); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == NULL) + return(NULL); + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case '\n': + case ')': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + break; + case '\\': + switch (*regparse++) { + case '\0': + FAIL("trailing \\"); + break; + case '<': + ret = regnode(WORDA); + break; + case '>': + ret = regnode(WORDZ); + break; + /* FIXME: Someday handle \1, \2, ... */ + default: + /* Handle general quoted chars in exact-match routine */ + goto de_fault; + } + break; + de_fault: + default: + /* + * Encode a string of characters to be matched exactly. + * + * This is a bit tricky due to quoted chars and due to + * '*', '+', and '?' taking the SINGLE char previous + * as their operand. + * + * On entry, the char at regparse[-1] is going to go + * into the string, no matter what it is. (It could be + * following a \ if we are entered from the '\' case.) + * + * Basic idea is to pick up a good char in ch and + * examine the next char. If it's *+? then we twiddle. + * If it's \ then we frozzle. If it's other magic char + * we push ch and terminate the string. If none of the + * above, we push ch on the string and go around again. + * + * regprev is used to remember where "the current char" + * starts in the string, if due to a *+? we need to back + * up and put the current char in a separate, 1-char, string. + * When regprev is NULL, ch is the only char in the + * string; this is used in *+? handling, and in setting + * flags |= SIMPLE at the end. + */ + { + char *regprev; + register char ch; + + regparse--; /* Look at cur char */ + ret = regnode(EXACTLY); + for ( regprev = 0 ; ; ) { + ch = *regparse++; /* Get current char */ + switch (*regparse) { /* look at next one */ + + default: + regc(ch); /* Add cur to string */ + break; + + case '.': case '[': case '(': + case ')': case '|': case '\n': + case '$': case '^': + case '\0': + /* FIXME, $ and ^ should not always be magic */ + magic: + regc(ch); /* dump cur char */ + goto done; /* and we are done */ + + case '?': case '+': case '*': + if (!regprev) /* If just ch in str, */ + goto magic; /* use it */ + /* End mult-char string one early */ + regparse = regprev; /* Back up parse */ + goto done; + + case '\\': + regc(ch); /* Cur char OK */ + switch (regparse[1]){ /* Look after \ */ + case '\0': + case '<': + case '>': + /* FIXME: Someday handle \1, \2, ... */ + goto done; /* Not quoted */ + default: + /* Backup point is \, scan * point is after it. */ + regprev = regparse; + regparse++; + continue; /* NOT break; */ + } + } + regprev = regparse; /* Set backup point */ + } + done: + regc('\0'); + *flagp |= HASWIDTH; + if (!regprev) /* One char? */ + *flagp |= SIMPLE; + } + break; + } + + return(ret); +} + +/* + - regnode - emit a node + */ +static char * /* Location. */ +regnode( int op ) +{ + register char *ret; + register char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void +regc( int b ) +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void +reginsert( + char op, + char *opnd ) +{ + register char *src; + register char *dst; + register char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void +regtail( + char *p, + char *val ) +{ + register char *scan; + register char *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan+1) = (offset>>8)&0377; + *(scan+2) = offset&0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ + +static void +regoptail( + char *p, + char *val ) +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* + * Forwards. + */ +STATIC int regtry( regexp *prog, char *string ); +STATIC int regmatch( char *prog ); +STATIC int regrepeat( char *p ); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int +regexec( + register regexp *prog, + register char *string ) +{ + register char *s; + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { + regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if ( prog->regmust != NULL ) + { + s = (char *)string; + while ( ( s = strchr( s, prog->regmust[ 0 ] ) ) != NULL ) + { + if ( !strncmp( s, prog->regmust, prog->regmlen ) ) + break; /* Found it. */ + ++s; + } + if ( s == NULL ) /* Not present. */ + return 0; + } + + /* Mark beginning of line for ^ . */ + regbol = (char *)string; + + /* Simplest case: anchored match need be tried only once. */ + if ( prog->reganch ) + return regtry( prog, string ); + + /* Messy cases: unanchored match. */ + s = (char *)string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = strchr(s, prog->regstart)) != NULL) { + if (regtry(prog, s)) + return(1); + s++; + } + else + /* We do not -- general case. */ + do { + if ( regtry( prog, s ) ) + return( 1 ); + } while ( *s++ != '\0' ); + + /* Failure. */ + return 0; +} + + +/* + * regtry() - try match at specific point. + */ + +static int /* 0 failure, 1 success */ +regtry( + regexp *prog, + char *string ) +{ + register int i; + register char * * sp; + register char * * ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for ( i = NSUBEXP; i > 0; --i ) + { + *sp++ = NULL; + *ep++ = NULL; + } + if ( regmatch( prog->program + 1 ) ) + { + prog->startp[ 0 ] = string; + prog->endp[ 0 ] = reginput; + return 1; + } + else + return 0; +} + + +/* + * regmatch() - main matching routine. + * + * Conceptually the strategy is simple: check to see whether the current node + * matches, call self recursively to see whether the rest matches, and then act + * accordingly. In practice we make some effort to avoid recursion, in + * particular by going through "ordinary" nodes (that do not need to know + * whether the rest of the match failed) by a loop instead of by recursion. + */ + +static int /* 0 failure, 1 success */ +regmatch( char * prog ) +{ + char * scan; /* Current node. */ + char * next; /* Next node. */ + + scan = prog; +#ifdef DEBUG + if (scan != NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return(0); + break; + case EOL: + if (*reginput != '\0') + return(0); + break; + case WORDA: + /* Must be looking at a letter, digit, or _ */ + if ((!isalnum(*reginput)) && *reginput != '_') + return(0); + /* Prev must be BOL or nonword */ + if (reginput > regbol && + (isalnum(reginput[-1]) || reginput[-1] == '_')) + return(0); + break; + case WORDZ: + /* Must be looking at non letter, digit, or _ */ + if (isalnum(*reginput) || *reginput == '_') + return(0); + /* We don't care what the previous char was */ + break; + case ANY: + if (*reginput == '\0') + return(0); + reginput++; + break; + case EXACTLY: { + register int len; + register char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return(0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: { + register int no; + register char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (regstartp[no] == NULL) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: { + register int no; + register char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later + * invocation of the same parentheses + * already has. + */ + if (regendp[no] == NULL) + regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH: { + register char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != NULL && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS: { + register char nextch; + register int no; + register char *save; + register int min; + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + break; + default: + regerror("memory corruption"); + return(0); + break; + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int +regrepeat( char *p ) +{ + register int count = 0; + register char *scan; + register char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static char * +regnext( register char *p ) +{ + register int offset; + + if (p == ®dummy) + return(NULL); + + offset = NEXT(p); + if (offset == 0) + return(NULL); + + if (OP(p) == BACK) + return(p-offset); + else + return(p+offset); +} + +#ifdef DEBUG + +STATIC char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void +regdump( regexp *r ) +{ + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *next; + + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (s-r->program)+(next-s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') + printf("start `%c' ", r->regstart); + if (r->reganch) + printf("anchored "); + if (r->regmust != NULL) + printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +static char * +regprop( char *op ) +{ + register char *p; + static char buf[50]; + + (void) strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: + sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); + p = NULL; + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: + sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + case WORDA: + p = "WORDA"; + break; + case WORDZ: + p = "WORDZ"; + break; + default: + regerror("corrupted opcode"); + break; + } + if (p != NULL) + (void) strcat(buf, p); + return(buf); +} +#endif + +/* + * The following is provided for those people who do not have strcspn() in + * their C libraries. They should get off their butts and do something + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +#ifdef STRCSPN +/* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + +static int +strcspn( + char *s1, + char *s2 ) +{ + register char *scan1; + register char *scan2; + register int count; + + count = 0; + for (scan1 = s1; *scan1 != '\0'; scan1++) { + for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ + if (*scan1 == *scan2++) + return(count); + count++; + } + return(count); +} +#endif diff --git a/jam-files/engine/regexp.h b/jam-files/engine/regexp.h new file mode 100644 index 00000000..9d4604f6 --- /dev/null +++ b/jam-files/engine/regexp.h @@ -0,0 +1,32 @@ +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#ifndef REGEXP_DWA20011023_H +# define REGEXP_DWA20011023_H + +#define NSUBEXP 10 +typedef struct regexp { + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +regexp *regcomp( char *exp ); +int regexec( regexp *prog, char *string ); +void regerror( char *s ); + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 + +#endif + diff --git a/jam-files/engine/rules.c b/jam-files/engine/rules.c new file mode 100644 index 00000000..a0be1d34 --- /dev/null +++ b/jam-files/engine/rules.c @@ -0,0 +1,810 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +# include "jam.h" +# include "lists.h" +# include "parse.h" +# include "variable.h" +# include "rules.h" +# include "newstr.h" +# include "hash.h" +# include "modules.h" +# include "search.h" +# include "lists.h" +# include "pathsys.h" +# include "timestamp.h" + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * 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. + * touch_target() - mark a target to simulate being new. + * targetlist() - turn list of target names into a TARGET chain. + * targetentry() - add a TARGET to a chain of TARGETS. + * actionlist() - append to an ACTION chain. + * addsettings() - add a deferred "set" command to a target. + * pushsettings() - set all target specific variables. + * popsettings() - reset target specific variables to their pre-push values. + * freesettings() - delete a settings list. + * rules_done() - free RULE and TARGET tables. + * + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + */ + +static void set_rule_actions( RULE *, rule_actions * ); +static void set_rule_body ( RULE *, argument_list *, PARSE * procedure ); + +static struct hash * targethash = 0; + +struct _located_target +{ + char * file_name; + TARGET * target; +}; +typedef struct _located_target LOCATED_TARGET ; + +static struct hash * located_targets = 0; + + +/* + * target_include() - adds the 'included' TARGET to the list of targets included + * by the 'including' TARGET. Such targets are modeled as dependencies of the + * internal include node belonging to the 'including' TARGET. + */ + +void target_include( TARGET * including, TARGET * included ) +{ + TARGET * internal; + if ( !including->includes ) + { + including->includes = copytarget( including ); + including->includes->original_target = including; + } + internal = including->includes; + internal->depends = targetentry( internal->depends, included ); +} + + +/* + * enter_rule() - return pointer to RULE, creating it if necessary in + * target_module. + */ + +static RULE * enter_rule( char * rulename, module_t * target_module ) +{ + RULE rule; + RULE * r = &rule; + + r->name = rulename; + + if ( hashenter( demand_rules( target_module ), (HASHDATA * *)&r ) ) + { + r->name = newstr( rulename ); /* never freed */ + r->procedure = (PARSE *)0; + r->module = 0; + r->actions = 0; + r->arguments = 0; + r->exported = 0; + r->module = target_module; +#ifdef HAVE_PYTHON + r->python_function = 0; +#endif + } + return r; +} + + +/* + * define_rule() - return pointer to RULE, creating it if necessary in + * target_module. Prepare it to accept a body or action originating in + * src_module. + */ + +static RULE * define_rule +( + module_t * src_module, + char * rulename, + module_t * target_module +) +{ + RULE * r = enter_rule( rulename, target_module ); + if ( r->module != src_module ) /* if the rule was imported from elsewhere, clear it now */ + { + set_rule_body( r, 0, 0 ); + set_rule_actions( r, 0 ); + r->module = src_module; /* r will be executed in the source module */ + } + return r; +} + + +void rule_free( RULE * r ) +{ + freestr( r->name ); + r->name = ""; + parse_free( r->procedure ); + r->procedure = 0; + if ( r->arguments ) + args_free( r->arguments ); + r->arguments = 0; + if ( r->actions ) + actions_free( r->actions ); + r->actions = 0; +} + + +/* + * bindtarget() - return pointer to TARGET, creating it if necessary. + */ + +TARGET * bindtarget( char const * target_name ) +{ + TARGET target; + TARGET * t = ⌖ + + if ( !targethash ) + targethash = hashinit( sizeof( TARGET ), "targets" ); + + /* Perforce added const everywhere. No time to merge that change. */ +#ifdef NT + target_name = short_path_to_long_path( (char *)target_name ); +#endif + t->name = (char *)target_name; + + if ( hashenter( targethash, (HASHDATA * *)&t ) ) + { + memset( (char *)t, '\0', sizeof( *t ) ); + t->name = newstr( (char *)target_name ); /* never freed */ + t->boundname = t->name; /* default for T_FLAG_NOTFILE */ + } + + return t; +} + + +static void bind_explicitly_located_target( void * xtarget, void * data ) +{ + TARGET * t = (TARGET *)xtarget; + if ( !( t->flags & T_FLAG_NOTFILE ) ) + { + /* Check if there's a setting for LOCATE */ + SETTINGS * s = t->settings; + for ( ; s ; s = s->next ) + { + if ( strcmp( s->symbol, "LOCATE" ) == 0 ) + { + pushsettings( t->settings ); + /* We are binding a target with explicit LOCATE. So third + * argument is of no use: nothing will be returned through it. + */ + t->boundname = search( t->name, &t->time, 0, 0 ); + popsettings( t->settings ); + break; + } + } + } +} + + +void bind_explicitly_located_targets() +{ + if ( targethash ) + hashenumerate( targethash, bind_explicitly_located_target, (void *)0 ); +} + + +/* TODO: It is probably not a good idea to use functions in other modules like + this. */ +void call_bind_rule( char * target, char * boundname ); + + +TARGET * search_for_target ( char * name, LIST * search_path ) +{ + PATHNAME f[1]; + string buf[1]; + LOCATED_TARGET lt; + LOCATED_TARGET * lta = < + time_t time; + int found = 0; + TARGET * result; + + string_new( buf ); + + path_parse( name, f ); + + f->f_grist.ptr = 0; + f->f_grist.len = 0; + + while ( search_path ) + { + f->f_root.ptr = search_path->string; + f->f_root.len = strlen( search_path->string ); + + string_truncate( buf, 0 ); + path_build( f, buf, 1 ); + + lt.file_name = buf->value ; + + if ( !located_targets ) + located_targets = hashinit( sizeof(LOCATED_TARGET), + "located targets" ); + + if ( hashcheck( located_targets, (HASHDATA * *)<a ) ) + { + return lta->target; + } + + timestamp( buf->value, &time ); + if ( time ) + { + found = 1; + break; + } + + search_path = list_next( search_path ); + } + + if ( !found ) + { + f->f_root.ptr = 0; + f->f_root.len = 0; + + string_truncate( buf, 0 ); + path_build( f, buf, 1 ); + + timestamp( buf->value, &time ); + } + + result = bindtarget( name ); + result->boundname = newstr( buf->value ); + result->time = time; + result->binding = time ? T_BIND_EXISTS : T_BIND_MISSING; + + call_bind_rule( result->name, result->boundname ); + + string_free( buf ); + + return result; +} + + +/* + * 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 = (TARGET *)BJAM_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; +} + + +/* + * touch_target() - mark a target to simulate being new. + */ + +void touch_target( 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 * target_names ) +{ + for ( ; target_names; target_names = list_next( target_names ) ) + chain = targetentry( chain, bindtarget( target_names->string ) ); + return chain; +} + + +/* + * targetentry() - add a TARGET to a chain of TARGETS. + * + * Inputs: + * chain existing TARGETS to append to + * target new target to append + */ + +TARGETS * targetentry( TARGETS * chain, TARGET * target ) +{ + TARGETS * c = (TARGETS *)BJAM_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 ) +{ + if ( !targets ) return chain; + 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 *)BJAM_MALLOC( sizeof( ACTIONS ) ); + + actions->action = action; + + if ( !chain ) chain = actions; + else chain->tail->next = actions; + chain->tail = actions; + actions->next = 0; + + return chain; +} + +static SETTINGS * settings_freelist; + + +/* + * addsettings() - add a deferred "set" command to a target. + * + * Adds a variable setting (varname=list) onto a chain of settings for a + * particular target. 'flag' controls the relationship between new and old + * values in the same way as in var_set() function (see variable.c). Returns + * the head of the settings chain. + */ + +SETTINGS * addsettings( SETTINGS * head, int flag, char * symbol, LIST * value ) +{ + SETTINGS * v; + + /* Look for previous settings. */ + 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_freelist; + + if ( v ) + settings_freelist = v->next; + else + v = (SETTINGS *)BJAM_MALLOC( sizeof( *v ) ); + + v->symbol = newstr( symbol ); + v->value = value; + v->next = head; + v->multiple = 0; + head = v; + } + else if ( flag == VAR_APPEND ) + { + v->value = list_append( v->value, value ); + } + else if ( flag != VAR_DEFAULT ) + { + list_free( v->value ); + v->value = value; + } + else + list_free( value ); + + /* Return (new) head of list. */ + return head; +} + + +/* + * pushsettings() - set all target specific variables. + */ + +void pushsettings( SETTINGS * v ) +{ + for ( ; v; v = v->next ) + v->value = var_swap( v->symbol, v->value ); +} + + +/* + * popsettings() - reset target specific variables to their pre-push values. + */ + +void popsettings( SETTINGS * v ) +{ + pushsettings( v ); /* just swap again */ +} + + +/* + * copysettings() - duplicate a settings list, returning the new copy. + */ + +SETTINGS * copysettings( SETTINGS * head ) +{ + SETTINGS * copy = 0; + SETTINGS * v; + for ( v = head; v; v = v->next ) + copy = addsettings( copy, VAR_SET, v->symbol, list_copy( 0, v->value ) ); + return copy; +} + + +/* + * freetargets() - delete a targets list. + */ + +void freetargets( TARGETS * chain ) +{ + while ( chain ) + { + TARGETS * n = chain->next; + BJAM_FREE( chain ); + chain = n; + } +} + + +/* + * freeactions() - delete an action list. + */ + +void freeactions( ACTIONS * chain ) +{ + while ( chain ) + { + ACTIONS * n = chain->next; + BJAM_FREE( chain ); + chain = n; + } +} + + +/* + * freesettings() - delete a settings list. + */ + +void freesettings( SETTINGS * v ) +{ + while ( v ) + { + SETTINGS * n = v->next; + freestr( v->symbol ); + list_free( v->value ); + v->next = settings_freelist; + settings_freelist = v; + v = n; + } +} + + +static void freetarget( void * xt, void * data ) +{ + TARGET * t = (TARGET *)xt; + if ( t->settings ) freesettings( t->settings ); + if ( t->depends ) freetargets ( t->depends ); + if ( t->includes ) freetarget ( t->includes, (void *)0 ); + if ( t->actions ) freeactions ( t->actions ); +} + + +/* + * rules_done() - free RULE and TARGET tables. + */ + +void rules_done() +{ + hashenumerate( targethash, freetarget, 0 ); + hashdone( targethash ); + while ( settings_freelist ) + { + SETTINGS * n = settings_freelist->next; + BJAM_FREE( settings_freelist ); + settings_freelist = n; + } +} + + +/* + * args_new() - make a new reference-counted argument list. + */ + +argument_list * args_new() +{ + argument_list * r = (argument_list *)BJAM_MALLOC( sizeof(argument_list) ); + r->reference_count = 0; + lol_init( r->data ); + return r; +} + + +/* + * args_refer() - add a new reference to the given argument list. + */ + +void args_refer( argument_list * a ) +{ + ++a->reference_count; +} + + +/* + * args_free() - release a reference to the given argument list. + */ + +void args_free( argument_list * a ) +{ + if ( --a->reference_count <= 0 ) + { + lol_free( a->data ); + BJAM_FREE( a ); + } +} + + +/* + * actions_refer() - add a new reference to the given actions. + */ + +void actions_refer( rule_actions * a ) +{ + ++a->reference_count; +} + + +/* + * actions_free() - release a reference to the given actions. + */ + +void actions_free( rule_actions * a ) +{ + if ( --a->reference_count <= 0 ) + { + freestr( a->command ); + list_free( a->bindlist ); + BJAM_FREE( a ); + } +} + + +/* + * set_rule_body() - set the argument list and procedure of the given rule. + */ + +static void set_rule_body( RULE * rule, argument_list * args, PARSE * procedure ) +{ + if ( args ) + args_refer( args ); + if ( rule->arguments ) + args_free( rule->arguments ); + rule->arguments = args; + + if ( procedure ) + parse_refer( procedure ); + if ( rule->procedure ) + parse_free( rule->procedure ); + rule->procedure = procedure; +} + + +/* + * global_name() - given a rule, return the name for a corresponding rule in the + * global module. + */ + +static char * global_rule_name( RULE * r ) +{ + if ( r->module == root_module() ) + return r->name; + + { + char name[4096] = ""; + strncat( name, r->module->name, sizeof( name ) - 1 ); + strncat( name, r->name, sizeof( name ) - 1 ); + return newstr( name); + } +} + + +/* + * global_rule() - given a rule, produce the corresponding entry in the global + * module. + */ + +static RULE * global_rule( RULE * r ) +{ + if ( r->module == root_module() ) + return r; + + { + char * name = global_rule_name( r ); + RULE * result = define_rule( r->module, name, root_module() ); + freestr( name ); + return result; + } +} + + +/* + * new_rule_body() - make a new rule named rulename in the given module, with + * the given argument list and procedure. If exported is true, the rule is + * exported to the global module as modulename.rulename. + */ + +RULE * new_rule_body( module_t * m, char * rulename, argument_list * args, PARSE * procedure, int exported ) +{ + RULE * local = define_rule( m, rulename, m ); + local->exported = exported; + set_rule_body( local, args, procedure ); + + /* Mark the procedure with the global rule name, regardless of whether the + * rule is exported. That gives us something reasonably identifiable that we + * can use, e.g. in profiling output. Only do this once, since this could be + * called multiple times with the same procedure. + */ + if ( procedure->rulename == 0 ) + procedure->rulename = global_rule_name( local ); + + return local; +} + + +static void set_rule_actions( RULE * rule, rule_actions * actions ) +{ + if ( actions ) + actions_refer( actions ); + if ( rule->actions ) + actions_free( rule->actions ); + rule->actions = actions; +} + + +static rule_actions * actions_new( char * command, LIST * bindlist, int flags ) +{ + rule_actions * result = (rule_actions *)BJAM_MALLOC( sizeof( rule_actions ) ); + result->command = copystr( command ); + result->bindlist = bindlist; + result->flags = flags; + result->reference_count = 0; + return result; +} + + +RULE * new_rule_actions( module_t * m, char * rulename, char * command, LIST * bindlist, int flags ) +{ + RULE * local = define_rule( m, rulename, m ); + RULE * global = global_rule( local ); + set_rule_actions( local, actions_new( command, bindlist, flags ) ); + set_rule_actions( global, local->actions ); + return local; +} + + +/* + * Looks for a rule in the specified module, and returns it, if found. First + * checks if the rule is present in the module's rule table. Second, if name of + * the rule is in the form name1.name2 and name1 is in the list of imported + * modules, look in module 'name1' for rule 'name2'. + */ + +RULE * lookup_rule( char * rulename, module_t * m, int local_only ) +{ + RULE rule; + RULE * r = &rule; + RULE * result = 0; + module_t * original_module = m; + + r->name = rulename; + + if ( m->class_module ) + m = m->class_module; + + if ( m->rules && hashcheck( m->rules, (HASHDATA * *)&r ) ) + result = r; + else if ( !local_only && m->imported_modules ) + { + /* Try splitting the name into module and rule. */ + char *p = strchr( r->name, '.' ) ; + if ( p ) + { + *p = '\0'; + /* Now, r->name keeps the module name, and p+1 keeps the rule name. + */ + if ( hashcheck( m->imported_modules, (HASHDATA * *)&r ) ) + result = lookup_rule( p + 1, bindmodule( rulename ), 1 ); + *p = '.'; + } + } + + if ( result ) + { + if ( local_only && !result->exported ) + result = 0; + else + { + /* Lookup started in class module. We have found a rule in class + * module, which is marked for execution in that module, or in some + * instances. Mark it for execution in the instance where we started + * the lookup. + */ + int execute_in_class = ( result->module == m ); + int execute_in_some_instance = ( result->module->class_module && + ( result->module->class_module == m ) ); + if ( ( original_module != m ) && + ( execute_in_class || execute_in_some_instance ) ) + result->module = original_module; + } + } + + return result; +} + + +RULE * bindrule( char * rulename, module_t * m ) +{ + RULE * result = lookup_rule( rulename, m, 0 ); + if ( !result ) + result = lookup_rule( rulename, root_module(), 0 ); + /* We have only one caller, 'evaluate_rule', which will complain about + * calling an undefined rule. We could issue the error here, but we do not + * have the necessary information, such as frame. + */ + if ( !result ) + result = enter_rule( rulename, m ); + return result; +} + + +RULE * import_rule( RULE * source, module_t * m, char * name ) +{ + RULE * dest = define_rule( source->module, name, m ); + set_rule_body( dest, source->arguments, source->procedure ); + set_rule_actions( dest, source->actions ); + return dest; +} diff --git a/jam-files/engine/rules.h b/jam-files/engine/rules.h new file mode 100644 index 00000000..806a1469 --- /dev/null +++ b/jam-files/engine/rules.h @@ -0,0 +1,280 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef RULES_DWA_20011020_H +#define RULES_DWA_20011020_H + +#include "modules.h" +#include "jam.h" +#include "parse.h" + + +/* + * 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 - an entity (e.g. a file) that can be built. + * + * 04/11/94 (seiwald) - Combined deps & headers into deps[2] in TARGET. + * 04/12/94 (seiwald) - actionlist() now just appends a single action. + * 06/01/94 (seiwald) - new 'actions existing' does existing sources + * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. + * 01/19/95 (seiwald) - split DONTKNOW into CANTFIND/CANTMAKE. + * 02/02/95 (seiwald) - new LEAVES modifier on targets. + * 02/14/95 (seiwald) - new NOUPDATE modifier on targets. + */ + +typedef struct _rule RULE; +typedef struct _target TARGET; +typedef struct _targets TARGETS; +typedef struct _action ACTION; +typedef struct _actions ACTIONS; +typedef struct _settings SETTINGS ; + +/* RULE - a generic jam rule, the product of RULE and ACTIONS. */ + +/* A rule's argument list. */ +struct argument_list +{ + int reference_count; + LOL data[1]; +}; + +/* Build actions corresponding to a rule. */ +struct rule_actions +{ + int reference_count; + char * command; /* command string from ACTIONS */ + LIST * bindlist; + int flags; /* modifiers on ACTIONS */ + +#define RULE_NEWSRCS 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 /* do not mention it unless verbose */ +#define RULE_PIECEMEAL 0x10 /* split exec so each $(>) is small */ +#define RULE_EXISTING 0x20 /* $(>) is pre-exisitng sources only */ +}; + +typedef struct rule_actions rule_actions; +typedef struct argument_list argument_list; + +struct _rule +{ + char * name; + PARSE * procedure; /* parse tree from RULE */ + argument_list * arguments; /* argument checking info, or NULL for unchecked + */ + rule_actions * actions; /* build actions, or NULL for no actions */ + module_t * module; /* module in which this rule is executed */ + int exported; /* nonzero if this rule is supposed to appear in + * the global module and be automatically + * imported into other modules + */ +#ifdef HAVE_PYTHON + PyObject * python_function; +#endif +}; + +/* ACTIONS - a chain of ACTIONs. */ +struct _actions +{ + ACTIONS * next; + ACTIONS * tail; /* valid only for head */ + ACTION * action; +}; + +/* ACTION - a RULE instance with targets and sources. */ +struct _action +{ + RULE * rule; + TARGETS * targets; + TARGETS * sources; /* aka $(>) */ + char running; /* has been started */ + char status; /* see TARGET status */ +}; + +/* SETTINGS - variables to set when executing a TARGET's ACTIONS. */ +struct _settings +{ + SETTINGS * next; + char * symbol; /* symbol name for var_set() */ + LIST * value; /* symbol value for var_set() */ + int multiple; +}; + +/* TARGETS - a chain of TARGETs. */ +struct _targets +{ + TARGETS * next; + TARGETS * tail; /* valid only for head */ + TARGET * target; +}; + +/* TARGET - an entity (e.g. a file) that can be built. */ +struct _target +{ + char * name; + char * boundname; /* if search() relocates target */ + ACTIONS * actions; /* rules to execute, if any */ + SETTINGS * settings; /* variables to define */ + + short flags; /* status info */ + +#define T_FLAG_TEMP 0x0001 /* TEMPORARY applied */ +#define T_FLAG_NOCARE 0x0002 /* NOCARE applied */ +#define T_FLAG_NOTFILE 0x0004 /* NOTFILE applied */ +#define T_FLAG_TOUCHED 0x0008 /* ALWAYS applied or -t target */ +#define T_FLAG_LEAVES 0x0010 /* LEAVES applied */ +#define T_FLAG_NOUPDATE 0x0020 /* NOUPDATE applied */ +#define T_FLAG_VISITED 0x0040 /* CWM: Used in debugging */ + +/* This flag has been added to support a new built-in rule named "RMBAD". It is + * used to force removal of outdated targets whose dependencies fail to build. + */ +#define T_FLAG_RMOLD 0x0080 /* RMBAD applied */ + +/* This flag was added to support a new built-in rule named "FAIL_EXPECTED" used + * to indicate that the result of running a given action should be inverted, + * i.e. ok <=> fail. This is useful for launching certain test runs from a + * Jamfile. + */ +#define T_FLAG_FAIL_EXPECTED 0x0100 /* FAIL_EXPECTED applied */ + +#define T_FLAG_INTERNAL 0x0200 /* internal INCLUDES node */ + +/* Indicates that the target must be a file. This prevents matching non-files, + * like directories, when a target is searched. + */ +#define T_FLAG_ISFILE 0x0400 + +#define T_FLAG_PRECIOUS 0x0800 + + char binding; /* how target relates to a real file or + * folder + */ + +#define T_BIND_UNBOUND 0 /* a disembodied name */ +#define T_BIND_MISSING 1 /* could not find real file */ +#define T_BIND_PARENTS 2 /* using parent's timestamp */ +#define T_BIND_EXISTS 3 /* real file, timestamp valid */ + + TARGETS * depends; /* dependencies */ + TARGETS * dependants; /* the inverse of dependencies */ + TARGETS * rebuilds; /* targets that should be force-rebuilt + * whenever this one is + */ + TARGET * includes; /* internal includes node */ + TARGET * original_target; /* original_target->includes = this */ + char rescanned; + + 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 did not 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_REBUILD 6 +#define T_FATE_MISSING 7 /* is missing, needs updating */ +#define T_FATE_NEEDTMP 8 /* missing temp that must be rebuild */ +#define T_FATE_OUTDATED 9 /* is out of date, needs updating */ +#define T_FATE_UPDATE 10 /* deps updated, needs updating */ + +#define T_FATE_BROKEN 11 /* >= BROKEN ruins parents */ +#define T_FATE_CANTFIND 11 /* no rules to make missing target */ +#define T_FATE_CANTMAKE 12 /* can not find dependencies */ + + 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 */ + +#ifdef OPT_SEMAPHORE + #define T_MAKE_SEMAPHORE 5 /* Special target type for semaphores */ +#endif + +#ifdef OPT_SEMAPHORE + TARGET * semaphore; /* used in serialization */ +#endif + + char status; /* exec_cmd() result */ + + int asynccnt; /* child deps outstanding */ + TARGETS * parents; /* used by make1() for completion */ + char * cmds; /* type-punned command list */ + + char * failed; +}; + + +/* Action related functions. */ +ACTIONS * actionlist ( ACTIONS *, ACTION * ); +void freeactions ( ACTIONS * ); +SETTINGS * addsettings ( SETTINGS *, int flag, char * symbol, LIST * value ); +void pushsettings ( SETTINGS * ); +void popsettings ( SETTINGS * ); +SETTINGS * copysettings ( SETTINGS * ); +void freesettings ( SETTINGS * ); +void actions_refer( rule_actions * ); +void actions_free ( rule_actions * ); + +/* Argument list related functions. */ +void args_free ( argument_list * ); +argument_list * args_new (); +void args_refer( argument_list * ); + +/* Rule related functions. */ +RULE * bindrule ( char * rulename, module_t * ); +RULE * import_rule ( RULE * source, module_t *, char * name ); +RULE * new_rule_body ( module_t *, char * rulename, argument_list *, PARSE * procedure, int exprt ); +RULE * new_rule_actions( module_t *, char * rulename, char * command, LIST * bindlist, int flags ); +void rule_free ( RULE * ); + +/* Target related functions. */ +void bind_explicitly_located_targets(); +TARGET * bindtarget ( char const * target_name ); +TARGET * copytarget ( TARGET const * t ); +void freetargets ( TARGETS * ); +TARGET * search_for_target ( char * name, LIST * search_path ); +TARGETS * targetchain ( TARGETS * chain, TARGETS * ); +TARGETS * targetentry ( TARGETS * chain, TARGET * ); +void target_include ( TARGET * including, TARGET * included ); +TARGETS * targetlist ( TARGETS * chain, LIST * target_names ); +void touch_target ( char * t ); + +/* Final module cleanup. */ +void rules_done(); + +#endif diff --git a/jam-files/engine/scan.c b/jam-files/engine/scan.c new file mode 100644 index 00000000..11c44c0e --- /dev/null +++ b/jam-files/engine/scan.c @@ -0,0 +1,418 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +#include "jam.h" +#include "lists.h" +#include "parse.h" +#include "scan.h" +#include "jamgram.h" +#include "jambase.h" +#include "newstr.h" + +/* + * scan.c - the jam yacc scanner + * + * 12/26/93 (seiwald) - bump buf in yylex to 10240 - yuk. + * 09/16/94 (seiwald) - check for overflows, unmatched {}'s, etc. + * Also handle tokens abutting EOF by remembering + * to return EOF now matter how many times yylex() + * reinvokes yyline(). + * 02/11/95 (seiwald) - honor only punctuation keywords if SCAN_PUNCT. + * 07/27/95 (seiwald) - Include jamgram.h after scan.h, so that YYSTYPE is + * defined before Linux's yacc tries to redefine it. + */ + +struct keyword +{ + char * word; + int type; +} keywords[] = +{ +#include "jamgramtab.h" + { 0, 0 } +}; + +struct include +{ + struct include * next; /* next serial include file */ + char * string; /* pointer into current line */ + char * * strings; /* for yyfparse() -- text to parse */ + FILE * file; /* for yyfparse() -- file being read */ + char * fname; /* for yyfparse() -- file name */ + int line; /* line counter for error messages */ + char buf[ 512 ]; /* for yyfparse() -- line buffer */ +}; + +static struct include * incp = 0; /* current file; head of chain */ + +static int scanmode = SCAN_NORMAL; +static int anyerrors = 0; + + +static char * symdump( YYSTYPE * ); + +#define BIGGEST_TOKEN 10240 /* no single token can be larger */ + + +/* + * Set parser mode: normal, string, or keyword. + */ + +void yymode( int n ) +{ + scanmode = n; +} + + +void yyerror( char * s ) +{ + /* We use yylval instead of incp to access the error location information as + * the incp pointer will already be reset to 0 in case the error occurred at + * EOF. + * + * The two may differ only if we get an error while reading a lexical token + * spanning muliple lines, e.g. a multi-line string literal or action body, + * in which case yylval location information will hold the information about + * where this token started while incp will hold the information about where + * reading it broke. + * + * TODO: Test the theory about when yylval and incp location information are + * the same and when they differ. + */ + printf( "%s:%d: %s at %s\n", yylval.file, yylval.line, s, symdump( &yylval ) ); + ++anyerrors; +} + + +int yyanyerrors() +{ + return anyerrors != 0; +} + + +void yyfparse( char * s ) +{ + struct include * i = (struct include *)BJAM_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 are reading from an internal string list, go to the next string. */ + if ( i->strings ) + { + if ( *i->strings ) + { + ++i->line; + i->string = *(i->strings++); + return *i->string++; + } + } + else + { + /* If necessary, open the file. */ + if ( !i->file ) + { + FILE * f = stdin; + if ( strcmp( i->fname, "-" ) && !( f = fopen( i->fname, "r" ) ) ) + perror( i->fname ); + i->file = f; + } + + /* If there is 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++; + } + } + + /* 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 ); + BJAM_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(); + + /* Turn trailing "\r\n" sequences into plain "\n" for Cygwin. */ + if ( ( c == '\n' ) && ( b[ -1 ] == '\r' ) ) + --b; + } + + /* We ate the ending brace -- regurgitate it. */ + if ( c != EOF ) + yyprev(); + + /* Check for 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 ); + yylval.file = incp->fname; + yylval.line = incp->line; + } + 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? */ + if ( c != '#' ) + break; + + /* Swallow up comment line. */ + while ( ( ( c = yychar() ) != EOF ) && ( c != '\n' ) ) ; + } + + /* c now points to the first character of a token. */ + if ( c == EOF ) + goto eof; + + yylval.file = incp->fname; + yylval.line = incp->line; + + /* 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 */ + if (c == 'n') + c = '\n'; + else if (c == 'r') + c = '\r'; + else if (c == 't') + c = '\t'; + *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. Do not scan if it is obviously not a keyword or if + * it is 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: + /* We do not reset yylval.file & yylval.line here so unexpected EOF error + * messages would include correct error location information. + */ + 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; +} + + +/* + * Get information about the current file and line, for those epsilon + * transitions that produce a parse. + */ + +void yyinput_stream( char * * name, int * line ) +{ + if ( incp ) + { + *name = incp->fname; + *line = incp->line; + } + else + { + *name = "(builtin)"; + *line = -1; + } +} diff --git a/jam-files/engine/scan.h b/jam-files/engine/scan.h new file mode 100644 index 00000000..3fad1c24 --- /dev/null +++ b/jam-files/engine/scan.h @@ -0,0 +1,56 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * scan.h - the jam yacc scanner + * + * External functions: + * + * yyerror( char *s ) - print a parsing error message. + * yyfparse( char *s ) - scan include file s. + * yylex() - parse the next token, returning its type. + * yymode() - adjust lexicon of scanner. + * yyparse() - declaration for yacc parser. + * yyanyerrors() - indicate if any parsing errors occured. + * + * The yymode() function is for the parser to adjust the lexicon of the scanner. + * Aside from normal keyword scanning, there is a mode to handle action strings + * (look only for the closing }) and a mode to ignore most keywords when looking + * for a punctuation keyword. This allows non-punctuation keywords to be used in + * lists without quoting. + */ + +/* + * YYSTYPE - value of a lexical token + */ + +#define YYSTYPE YYSYMBOL + +typedef struct _YYSTYPE +{ + int type; + char * string; + PARSE * parse; + LIST * list; + int number; + char * file; + int line; +} YYSTYPE; + +extern YYSTYPE yylval; + +void yymode( int n ); +void yyerror( char * s ); +int yyanyerrors(); +void yyfparse( char * s ); +int yyline(); +int yylex(); +int yyparse(); +void yyinput_stream( char * * name, int * line ); + +# define SCAN_NORMAL 0 /* normal parsing */ +# define SCAN_STRING 1 /* look only for matching } */ +# define SCAN_PUNCT 2 /* only punctuation keywords */ diff --git a/jam-files/engine/search.c b/jam-files/engine/search.c new file mode 100644 index 00000000..6c23d97a --- /dev/null +++ b/jam-files/engine/search.c @@ -0,0 +1,223 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "jam.h" +#include "lists.h" +#include "search.h" +#include "timestamp.h" +#include "pathsys.h" +#include "variable.h" +#include "newstr.h" +#include "compile.h" +#include "strings.h" +#include "hash.h" +#include "filesys.h" +#include <string.h> + + +typedef struct _binding +{ + char * binding; + char * target; +} BINDING; + +static struct hash *explicit_bindings = 0; + + +void call_bind_rule +( + char * target_, + char * boundname_ +) +{ + LIST * bind_rule = var_get( "BINDRULE" ); + if ( bind_rule ) + { + /* No guarantee that the target is an allocated string, so be on the + * safe side. + */ + char * target = copystr( target_ ); + + /* Likewise, do not rely on implementation details of newstr.c: allocate + * a copy of boundname. + */ + char * boundname = copystr( boundname_ ); + if ( boundname && target ) + { + /* Prepare the argument list. */ + FRAME frame[1]; + frame_init( frame ); + + /* First argument is the target name. */ + lol_add( frame->args, list_new( L0, target ) ); + + lol_add( frame->args, list_new( L0, boundname ) ); + if ( lol_get( frame->args, 1 ) ) + evaluate_rule( bind_rule->string, frame ); + + /* Clean up */ + frame_free( frame ); + } + else + { + if ( boundname ) + freestr( boundname ); + if ( target ) + freestr( target ); + } + } +} + +/* + * search.c - find a target along $(SEARCH) or $(LOCATE) + * First, check if LOCATE is set. If so, use it to determine + * the location of target and return, regardless of whether anything + * exists on that location. + * + * Second, examine all directories in SEARCH. If there's file already + * or there's another target with the same name which was placed + * to this location via LOCATE setting, stop and return the location. + * In case of previous target, return it's name via the third argument. + * + * This bevahiour allow to handle dependency on generated files. If + * caller does not expect that target is generated, 0 can be passed as + * the third argument. + */ + +char * +search( + char *target, + time_t *time, + char **another_target, + int file +) +{ + PATHNAME f[1]; + LIST *varlist; + string buf[1]; + int found = 0; + /* Will be set to 1 if target location is specified via LOCATE. */ + int explicitly_located = 0; + char *boundname = 0; + + if ( another_target ) + *another_target = 0; + + if (! explicit_bindings ) + explicit_bindings = hashinit( sizeof(BINDING), + "explicitly specified locations"); + + string_new( buf ); + /* 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->value ); + + explicitly_located = 1; + + timestamp( buf->value, time ); + found = 1; + } + else if ( ( varlist = var_get( "SEARCH" ) ) ) + { + while ( varlist ) + { + BINDING b, *ba = &b; + file_info_t *ff; + + f->f_root.ptr = varlist->string; + f->f_root.len = strlen( varlist->string ); + + string_truncate( buf, 0 ); + path_build( f, buf, 1 ); + + if ( DEBUG_SEARCH ) + printf( "search %s: %s\n", target, buf->value ); + + ff = file_query(buf->value); + timestamp( buf->value, time ); + + b.binding = buf->value; + + if ( hashcheck( explicit_bindings, (HASHDATA**)&ba ) ) + { + if ( DEBUG_SEARCH ) + printf(" search %s: found explicitly located target %s\n", + target, ba->target); + if ( another_target ) + *another_target = ba->target; + found = 1; + break; + } + else if ( ff && ff->time ) + { + if ( !file || ff->is_file ) + { + found = 1; + break; + } + } + + varlist = list_next( varlist ); + } + } + + if ( !found ) + { + /* 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; + + string_truncate( buf, 0 ); + path_build( f, buf, 1 ); + + if ( DEBUG_SEARCH ) + printf( "search %s: %s\n", target, buf->value ); + + timestamp( buf->value, time ); + } + + boundname = newstr( buf->value ); + string_free( buf ); + + if ( explicitly_located ) + { + BINDING b; + BINDING * ba = &b; + b.binding = boundname; + b.target = target; + /* CONSIDER: we probably should issue a warning is another file + is explicitly bound to the same location. This might break + compatibility, though. */ + hashenter( explicit_bindings, (HASHDATA * *)&ba ); + } + + /* prepare a call to BINDRULE if the variable is set */ + call_bind_rule( target, boundname ); + + return boundname; +} diff --git a/jam-files/engine/search.h b/jam-files/engine/search.h new file mode 100644 index 00000000..c364cac0 --- /dev/null +++ b/jam-files/engine/search.h @@ -0,0 +1,11 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * search.h - find a target along $(SEARCH) or $(LOCATE) + */ + +char *search( char *target, time_t *time, char **another_target, int file ); diff --git a/jam-files/engine/strings.c b/jam-files/engine/strings.c new file mode 100644 index 00000000..89561237 --- /dev/null +++ b/jam-files/engine/strings.c @@ -0,0 +1,201 @@ +/* Copyright David Abrahams 2004. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +#include "jam.h" +#include "strings.h" +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + + +#ifndef NDEBUG +# define JAM_STRING_MAGIC ((char)0xcf) +# define JAM_STRING_MAGIC_SIZE 4 +static void assert_invariants( string* self ) +{ + int i; + + if ( self->value == 0 ) + { + assert( self->size == 0 ); + assert( self->capacity == 0 ); + assert( self->opt[0] == 0 ); + return; + } + + assert( self->size < self->capacity ); + assert( ( self->capacity <= sizeof(self->opt) ) == ( self->value == self->opt ) ); + assert( strlen( self->value ) == self->size ); + + for (i = 0; i < 4; ++i) + { + assert( self->magic[i] == JAM_STRING_MAGIC ); + assert( self->value[self->capacity + i] == JAM_STRING_MAGIC ); + } +} +#else +# define JAM_STRING_MAGIC_SIZE 0 +# define assert_invariants(x) do {} while (0) +#endif + +void string_new( string* s ) +{ + s->value = s->opt; + s->size = 0; + s->capacity = sizeof(s->opt); + s->opt[0] = 0; +#ifndef NDEBUG + memset(s->magic, JAM_STRING_MAGIC, sizeof(s->magic)); +#endif + assert_invariants( s ); +} + +void string_free( string* s ) +{ + assert_invariants( s ); + if ( s->value != s->opt ) + BJAM_FREE( s->value ); + string_new( s ); +} + +static void string_reserve_internal( string* self, size_t capacity ) +{ + if ( self->value == self->opt ) + { + self->value = (char*)BJAM_MALLOC_ATOMIC( capacity + JAM_STRING_MAGIC_SIZE ); + self->value[0] = 0; + strncat( self->value, self->opt, sizeof(self->opt) ); + assert( strlen( self->value ) <= self->capacity ); /* This is a regression test */ + } + else + { + self->value = (char*)BJAM_REALLOC( self->value, capacity + JAM_STRING_MAGIC_SIZE ); + } +#ifndef NDEBUG + memcpy( self->value + capacity, self->magic, JAM_STRING_MAGIC_SIZE ); +#endif + self->capacity = capacity; +} + +void string_reserve( string* self, size_t capacity ) +{ + assert_invariants( self ); + if ( capacity <= self->capacity ) + return; + string_reserve_internal( self, capacity ); + assert_invariants( self ); +} + +static void extend_full( string* self, char const* start, char const* finish ) +{ + size_t new_size = self->capacity + ( finish - start ); + size_t new_capacity = self->capacity; + size_t old_size = self->capacity; + while ( new_capacity < new_size + 1) + new_capacity <<= 1; + string_reserve_internal( self, new_capacity ); + memcpy( self->value + old_size, start, new_size - old_size ); + self->value[new_size] = 0; + self->size = new_size; +} + +void string_append( string* self, char const* rhs ) +{ + char* p = self->value + self->size; + char* end = self->value + self->capacity; + assert_invariants( self ); + + while ( *rhs && p != end) + *p++ = *rhs++; + + if ( p != end ) + { + *p = 0; + self->size = p - self->value; + } + else + { + extend_full( self, rhs, rhs + strlen(rhs) ); + } + assert_invariants( self ); +} + +void string_append_range( string* self, char const* start, char const* finish ) +{ + char* p = self->value + self->size; + char* end = self->value + self->capacity; + assert_invariants( self ); + + while ( p != end && start != finish ) + *p++ = *start++; + + if ( p != end ) + { + *p = 0; + self->size = p - self->value; + } + else + { + extend_full( self, start, finish ); + } + assert_invariants( self ); +} + +void string_copy( string* s, char const* rhs ) +{ + string_new( s ); + string_append( s, rhs ); +} + +void string_truncate( string* self, size_t n ) +{ + assert_invariants( self ); + assert( n <= self->capacity ); + self->value[self->size = n] = 0; + assert_invariants( self ); +} + +void string_pop_back( string* self ) +{ + string_truncate( self, self->size - 1 ); +} + +void string_push_back( string* self, char x ) +{ + string_append_range( self, &x, &x + 1 ); +} + +char string_back( string* self ) +{ + assert_invariants( self ); + return self->value[self->size - 1]; +} + +#ifndef NDEBUG +void string_unit_test() +{ + string s[1]; + int i; + char buffer[sizeof(s->opt) * 2 + 2]; + int limit = sizeof(buffer) > 254 ? 254 : sizeof(buffer); + + string_new(s); + + for (i = 0; i < limit; ++i) + { + string_push_back( s, (char)(i + 1) ); + }; + + for (i = 0; i < limit; ++i) + { + assert( i < s->size ); + assert( s->value[i] == (char)(i + 1)); + } + + string_free(s); + +} +#endif + diff --git a/jam-files/engine/strings.h b/jam-files/engine/strings.h new file mode 100644 index 00000000..33c77bd7 --- /dev/null +++ b/jam-files/engine/strings.h @@ -0,0 +1,34 @@ +#ifndef STRINGS_DWA20011024_H +# define STRINGS_DWA20011024_H + +/* Copyright David Abrahams 2004. Distributed under the Boost */ +/* Software License, Version 1.0. (See accompanying */ +/* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ + +# include <stddef.h> + +typedef struct string +{ + char* value; + unsigned long size; + unsigned long capacity; + char opt[32]; +#ifndef NDEBUG + char magic[4]; +#endif +} string; + +void string_new( string* ); +void string_copy( string*, char const* ); +void string_free( string* ); +void string_append( string*, char const* ); +void string_append_range( string*, char const*, char const* ); +void string_push_back( string* s, char x ); +void string_reserve( string*, size_t ); +void string_truncate( string*, size_t ); +void string_pop_back( string* ); +char string_back( string* ); +void string_unit_test(); + +#endif + diff --git a/jam-files/engine/subst.c b/jam-files/engine/subst.c new file mode 100644 index 00000000..75524ecc --- /dev/null +++ b/jam-files/engine/subst.c @@ -0,0 +1,94 @@ +#include <stddef.h> +#include "jam.h" +#include "regexp.h" +#include "hash.h" + +#include "newstr.h" +#include "lists.h" +#include "parse.h" +#include "compile.h" +#include "frames.h" + +struct regex_entry +{ + const char* pattern; + regexp* regex; +}; +typedef struct regex_entry regex_entry; + +static struct hash* regex_hash; + +regexp* regex_compile( const char* pattern ) +{ + regex_entry entry, *e = &entry; + entry.pattern = pattern; + + if ( !regex_hash ) + regex_hash = hashinit(sizeof(regex_entry), "regex"); + + if ( hashenter( regex_hash, (HASHDATA **)&e ) ) + e->regex = regcomp( (char*)pattern ); + + return e->regex; +} + +LIST* +builtin_subst( + PARSE *parse, + FRAME *frame ) +{ + LIST* result = L0; + LIST* arg1 = lol_get( frame->args, 0 ); + + if ( arg1 && list_next(arg1) && list_next(list_next(arg1)) ) + { + + const char* source = arg1->string; + const char* pattern = list_next(arg1)->string; + regexp* repat = regex_compile( pattern ); + + if ( regexec( repat, (char*)source) ) + { + LIST* subst = list_next(arg1); + + while ((subst = list_next(subst)) != L0) + { +# define BUFLEN 4096 + char buf[BUFLEN + 1]; + const char* in = subst->string; + char* out = buf; + + for ( in = subst->string; *in && out < buf + BUFLEN; ++in ) + { + if ( *in == '\\' || *in == '$' ) + { + ++in; + if ( *in == 0 ) + { + break; + } + else if ( *in >= '0' && *in <= '9' ) + { + unsigned n = *in - '0'; + const size_t srclen = repat->endp[n] - repat->startp[n]; + const size_t remaining = buf + BUFLEN - out; + const size_t len = srclen < remaining ? srclen : remaining; + memcpy( out, repat->startp[n], len ); + out += len; + continue; + } + /* fall through and copy the next character */ + } + *out++ = *in; + } + *out = 0; + + result = list_new( result, newstr( buf ) ); +#undef BUFLEN + } + } + } + + return result; +} + diff --git a/jam-files/engine/timestamp.c b/jam-files/engine/timestamp.c new file mode 100644 index 00000000..8a59c8c0 --- /dev/null +++ b/jam-files/engine/timestamp.c @@ -0,0 +1,226 @@ +/* + * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +# include "jam.h" + +# include "hash.h" +# include "filesys.h" +# include "pathsys.h" +# include "timestamp.h" +# include "newstr.h" +# include "strings.h" + +/* + * timestamp.c - get the timestamp of a file or archive member + * + * 09/22/00 (seiwald) - downshift names on OS2, too + */ + +/* + * BINDING - all known files + */ + +typedef struct _binding BINDING; + +struct _binding { + char *name; + short flags; + +# define BIND_SCANNED 0x01 /* if directory or arch, has been scanned */ + + short progress; + +# define BIND_INIT 0 /* never seen */ +# define BIND_NOENTRY 1 /* timestamp requested but file never found */ +# define BIND_SPOTTED 2 /* file found but not timed yet */ +# define BIND_MISSING 3 /* file found but can't get timestamp */ +# define BIND_FOUND 4 /* file found and time stamped */ + + time_t time; /* update time - 0 if not exist */ +}; + +static struct hash * bindhash = 0; +static void time_enter( void *, char *, int, time_t ); + +static char * time_progress[] = +{ + "INIT", + "NOENTRY", + "SPOTTED", + "MISSING", + "FOUND" +}; + + +/* + * timestamp() - return timestamp on a file, if present. + */ + +void timestamp( char * target, time_t * time ) +{ + PROFILE_ENTER( timestamp ); + + PATHNAME f1; + PATHNAME f2; + BINDING binding; + BINDING * b = &binding; + string buf[ 1 ]; +#ifdef DOWNSHIFT_PATHS + string path; + char * p; +#endif + +#ifdef DOWNSHIFT_PATHS + string_copy( &path, target ); + p = path.value; + + do + { + *p = tolower( *p ); +#ifdef NT + /* On NT, we must use backslashes or the file will not be found. */ + if ( *p == '/' ) + *p = PATH_DELIM; +#endif + } + while ( *p++ ); + + target = path.value; +#endif /* #ifdef DOWNSHIFT_PATHS */ + string_new( buf ); + + 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; + BINDING * b = &binding; + + f2 = f1; + f2.f_grist.len = 0; + path_parent( &f2 ); + path_build( &f2, buf, 0 ); + + b->name = buf->value; + b->time = b->flags = 0; + b->progress = BIND_INIT; + + if ( hashenter( bindhash, (HASHDATA * *)&b ) ) + b->name = newstr( buf->value ); /* never freed */ + + if ( !( b->flags & BIND_SCANNED ) ) + { + file_dirscan( buf->value, time_enter, bindhash ); + b->flags |= BIND_SCANNED; + } + } + + /* Scan archive if not already done so. */ + if ( f1.f_member.len ) + { + BINDING binding; + BINDING * b = &binding; + + f2 = f1; + f2.f_grist.len = 0; + f2.f_member.len = 0; + string_truncate( buf, 0 ); + path_build( &f2, buf, 0 ); + + b->name = buf->value; + b->time = b->flags = 0; + b->progress = BIND_INIT; + + if ( hashenter( bindhash, (HASHDATA * *)&b ) ) + b->name = newstr( buf->value ); /* never freed */ + + if ( !( b->flags & BIND_SCANNED ) ) + { + file_archscan( buf->value, time_enter, bindhash ); + b->flags |= BIND_SCANNED; + } + } + + afterscanning: + + if ( b->progress == BIND_SPOTTED ) + { + b->progress = file_time( b->name, &b->time ) < 0 + ? BIND_MISSING + : BIND_FOUND; + } + + *time = b->progress == BIND_FOUND ? b->time : 0; + string_free( buf ); +#ifdef DOWNSHIFT_PATHS + string_free( &path ); +#endif + + PROFILE_EXIT( timestamp ); +} + + +static void time_enter( void * closure, char * target, int found, time_t time ) +{ + BINDING 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 ] ); +} + + +/* + * stamps_done() - free timestamp tables. + */ + +void stamps_done() +{ + hashdone( bindhash ); +} diff --git a/jam-files/engine/timestamp.h b/jam-files/engine/timestamp.h new file mode 100644 index 00000000..f5752763 --- /dev/null +++ b/jam-files/engine/timestamp.h @@ -0,0 +1,12 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * timestamp.h - get the timestamp of a file or archive member + */ + +void timestamp( char * target, time_t * time ); +void stamps_done(); diff --git a/jam-files/engine/variable.c b/jam-files/engine/variable.c new file mode 100644 index 00000000..795f3458 --- /dev/null +++ b/jam-files/engine/variable.c @@ -0,0 +1,631 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * Copyright 2001-2004 David Abrahams. + * Copyright 2005 Reece H. Dunn. + * Copyright 2005 Rene Rivera. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "jam.h" +#include "lists.h" +#include "parse.h" +#include "variable.h" +#include "expand.h" +#include "hash.h" +#include "filesys.h" +#include "newstr.h" +#include "strings.h" +#include "pathsys.h" +#include <stdlib.h> +#include <stdio.h> + +/* + * variable.c - handle Jam multi-element variables. + * + * External routines: + * + * var_defines() - load a bunch of variable=value settings. + * var_string() - expand a string with variables in it. + * var_get() - get value of a user defined symbol. + * var_set() - set a variable in jam's user defined symbol table. + * var_swap() - swap a variable's value with the given one. + * var_done() - free variable tables. + * + * Internal routines: + * + * var_enter() - make new var symbol table entry, returning var ptr. + * var_dump() - dump a variable to stdout. + * + * 04/13/94 (seiwald) - added shorthand L0 for null list pointer + * 08/23/94 (seiwald) - Support for '+=' (append to variable) + * 01/22/95 (seiwald) - split environment variables at blanks or :'s + * 05/10/95 (seiwald) - split path variables at SPLITPATH (not :) + * 09/11/00 (seiwald) - defunct var_list() removed + */ + +static struct hash *varhash = 0; + +/* + * VARIABLE - a user defined multi-value variable + */ + +typedef struct _variable VARIABLE ; + +struct _variable +{ + char * symbol; + LIST * value; +}; + +static VARIABLE * var_enter( char * symbol ); +static void var_dump( char * symbol, LIST * value, char * what ); + + +/* + * var_hash_swap() - swap all variable settings with those passed + * + * Used to implement separate settings spaces for modules + */ + +void var_hash_swap( struct hash * * new_vars ) +{ + struct hash * old = varhash; + varhash = *new_vars; + *new_vars = old; +} + + +/* + * var_defines() - load a bunch of variable=value settings + * + * If preprocess is false, take the value verbatim. + * + * Otherwise, if the variable value is enclosed in quotes, strip the + * quotes. + * + * Otherwise, if variable name ends in PATH, split value at :'s. + * + * Otherwise, split the value at blanks. + */ + +void var_defines( char * const * e, int preprocess ) +{ + string buf[1]; + + string_new( buf ); + + for ( ; *e; ++e ) + { + char * val; + +# ifdef OS_MAC + /* On the mac (MPW), the var=val is actually var\0val */ + /* Think different. */ + + if ( ( val = strchr( *e, '=' ) ) || ( val = *e + strlen( *e ) ) ) +# else + if ( ( val = strchr( *e, '=' ) ) ) +# endif + { + LIST * l = L0; + char * pp; + char * p; +# ifdef OPT_NO_EXTERNAL_VARIABLE_SPLIT + char split = '\0'; +# else + # ifdef OS_MAC + char split = ','; + # else + char split = ' '; + # endif +# endif + size_t len = strlen( val + 1 ); + + int quoted = ( val[1] == '"' ) && ( val[len] == '"' ) && + ( len > 1 ); + + if ( quoted && preprocess ) + { + string_append_range( buf, val + 2, val + len ); + l = list_new( l, newstr( buf->value ) ); + string_truncate( buf, 0 ); + } + else + { + /* 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; + preprocess && ( ( p = strchr( pp, split ) ) != 0 ); + pp = p + 1 + ) + { + string_append_range( buf, pp, p ); + l = list_new( l, newstr( buf->value ) ); + string_truncate( buf, 0 ); + } + + l = list_new( l, newstr( pp ) ); + } + + /* Get name. */ + string_append_range( buf, *e, val ); + var_set( buf->value, l, VAR_SET ); + string_truncate( buf, 0 ); + } + } + string_free( buf ); +} + + +/* + * var_string() - expand a string with variables in it + * + * Copies in to out; doesn't modify targets & sources. + */ + +int var_string( char * in, char * out, int outsize, LOL * lol ) +{ + char * out0 = out; + char * oute = out + outsize - 1; + + while ( *in ) + { + char * lastword; + int dollar = 0; + + /* Copy white space. */ + while ( isspace( *in ) ) + { + if ( out >= oute ) + return -1; + *out++ = *in++; + } + + lastword = out; + + /* Copy non-white space, watching for variables. */ + while ( *in && !isspace( *in ) ) + { + if ( out >= oute ) + return -1; + + if ( ( in[ 0 ] == '$' ) && ( in[ 1 ] == '(' ) ) + { + ++dollar; + *out++ = *in++; + } + #ifdef OPT_AT_FILES + else if ( ( in[ 0 ] == '@' ) && ( in[ 1 ] == '(' ) ) + { + int depth = 1; + char * ine = in + 2; + char * split = 0; + + /* Scan the content of the response file @() section. */ + while ( *ine && ( depth > 0 ) ) + { + switch ( *ine ) + { + case '(': ++depth; break; + case ')': --depth; break; + case ':': + if ( ( depth == 1 ) && ( ine[ 1 ] == 'E' ) && ( ine[ 2 ] == '=' ) ) + split = ine; + break; + } + ++ine; + } + + if ( !split ) + { + /* the @() reference doesn't match the @(foo:E=bar) format. + hence we leave it alone by copying directly to output. */ + int l = 0; + if ( out + 2 >= oute ) return -1; + *( out++ ) = '@'; + *( out++ ) = '('; + l = var_string( in + 2, out, oute - out, lol ); + if ( l < 0 ) return -1; + out += l; + if ( out + 1 >= oute ) return -1; + *( out++ ) = ')'; + } + else if ( depth == 0 ) + { + string file_name_v; + int file_name_l = 0; + const char * file_name_s = 0; + + /* Expand the temporary file name var inline. */ + #if 0 + string_copy( &file_name_v, "$(" ); + string_append_range( &file_name_v, in + 2, split ); + string_push_back( &file_name_v, ')' ); + #else + string_new( &file_name_v ); + string_append_range( &file_name_v, in + 2, split ); + #endif + file_name_l = var_string( file_name_v.value, out, oute - out + 1, lol ); + string_free( &file_name_v ); + if ( file_name_l < 0 ) return -1; + file_name_s = out; + + /* For stdout/stderr we will create a temp file and generate + * a command that outputs the content as needed. + */ + if ( ( strcmp( "STDOUT", out ) == 0 ) || + ( strcmp( "STDERR", out ) == 0 ) ) + { + int err_redir = strcmp( "STDERR", out ) == 0; + out[ 0 ] = '\0'; + file_name_s = path_tmpfile(); + file_name_l = strlen(file_name_s); + #ifdef OS_NT + if ( ( out + 7 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute ) + return -1; + sprintf( out,"type \"%s\"%s", file_name_s, + err_redir ? " 1>&2" : "" ); + #else + if ( ( out + 6 + file_name_l + ( err_redir ? 5 : 0 ) ) >= oute ) + return -1; + sprintf( out,"cat \"%s\"%s", file_name_s, + err_redir ? " 1>&2" : "" ); + #endif + /* We also make sure that the temp files created by this + * get nuked eventually. + */ + file_remove_atexit( file_name_s ); + } + + /* Expand the file value into the file reference. */ + var_string_to_file( split + 3, ine - split - 4, file_name_s, + lol ); + + /* Continue on with the expansion. */ + out += strlen( out ); + } + + /* And continue with the parsing just past the @() reference. */ + in = ine; + } + #endif + else + { + *out++ = *in++; + } + } + + /* Add zero to 'out' so that 'lastword' is correctly zero-terminated. */ + if ( out >= oute ) + return -1; + /* Do not increment, intentionally. */ + *out = '\0'; + + /* 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; + l = list_next( l ); + if ( l ) *out++ = ' '; + } + + list_free( l ); + } + } + + if ( out >= oute ) + return -1; + + *out++ = '\0'; + + return out - out0; +} + + +void var_string_to_file( const char * in, int insize, const char * out, LOL * lol ) +{ + char const * ine = in + insize; + FILE * out_file = 0; + int out_debug = DEBUG_EXEC ? 1 : 0; + if ( globs.noexec ) + { + /* out_debug = 1; */ + } + else if ( strcmp( out, "STDOUT" ) == 0 ) + { + out_file = stdout; + } + else if ( strcmp( out, "STDERR" ) == 0 ) + { + out_file = stderr; + } + else + { + /* Handle "path to file" filenames. */ + string out_name; + if ( ( out[ 0 ] == '"' ) && ( out[ strlen( out ) - 1 ] == '"' ) ) + { + string_copy( &out_name, out + 1 ); + string_truncate( &out_name, out_name.size - 1 ); + } + else + { + string_copy( &out_name,out ); + } + out_file = fopen( out_name.value, "w" ); + if ( !out_file ) + { + printf( "failed to write output file '%s'!\n", out_name.value ); + exit( EXITBAD ); + } + string_free( &out_name ); + } + + if ( out_debug ) printf( "\nfile %s\n", out ); + + while ( *in && ( in < ine ) ) + { + int dollar = 0; + const char * output_0 = in; + const char * output_1 = in; + + /* Copy white space. */ + while ( ( output_1 < ine ) && isspace( *output_1 ) ) + ++output_1; + + if ( output_0 < output_1 ) + { + if ( out_file ) fwrite( output_0, output_1 - output_0, 1, out_file ); + if ( out_debug ) fwrite( output_0, output_1 - output_0, 1, stdout ); + } + output_0 = output_1; + + /* Copy non-white space, watching for variables. */ + while ( ( output_1 < ine ) && *output_1 && !isspace( *output_1 ) ) + { + if ( ( output_1[ 0 ] == '$' ) && ( output_1[ 1 ] == '(' ) ) + ++dollar; + ++output_1; + } + + /* If a variable encountered, expand it and embed the space-separated + * members of the list in the output. + */ + if ( dollar ) + { + LIST * l = var_expand( L0, (char *)output_0, (char *)output_1, lol, 0 ); + + while ( l ) + { + if ( out_file ) fputs( l->string, out_file ); + if ( out_debug ) puts( l->string ); + l = list_next( l ); + if ( l ) + { + if ( out_file ) fputc( ' ', out_file ); + if ( out_debug ) fputc( ' ', stdout ); + } + } + + list_free( l ); + } + else if ( output_0 < output_1 ) + { + if ( out_file ) + { + const char * output_n = output_0; + while ( output_n < output_1 ) + { + output_n += fwrite( output_n, 1, output_1-output_n, out_file ); + } + } + if ( out_debug ) + { + const char * output_n = output_0; + while ( output_n < output_1 ) + { + output_n += fwrite( output_n, 1, output_1-output_n, stdout ); + } + } + } + + in = output_1; + } + + if ( out_file && ( out_file != stdout ) && ( out_file != stderr ) ) + { + fflush( out_file ); + fclose( out_file ); + } + + if ( out_debug ) fputc( '\n', stdout ); +} + + +/* + * var_get() - get value of a user defined symbol. + * + * Returns NULL if symbol unset. + */ + +LIST * var_get( char * symbol ) +{ + LIST * result = 0; +#ifdef OPT_AT_FILES + /* Some "fixed" variables... */ + if ( strcmp( "TMPDIR", symbol ) == 0 ) + { + result = list_new( L0, newstr( (char *)path_tmpdir() ) ); + } + else if ( strcmp( "TMPNAME", symbol ) == 0 ) + { + result = list_new( L0, newstr( (char *)path_tmpnam() ) ); + } + else if ( strcmp( "TMPFILE", symbol ) == 0 ) + { + result = list_new( L0, newstr( (char *)path_tmpfile() ) ); + } + else if ( strcmp( "STDOUT", symbol ) == 0 ) + { + result = list_new( L0, newstr( "STDOUT" ) ); + } + else if ( strcmp( "STDERR", symbol ) == 0 ) + { + result = list_new( L0, newstr( "STDERR" ) ); + } + else +#endif + { + VARIABLE var; + VARIABLE * v = &var; + + v->symbol = symbol; + + if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) ) + { + if ( DEBUG_VARGET ) + var_dump( v->symbol, v->value, "get" ); + result = v->value; + } + } + return result; +} + + +/* + * var_set() - set a variable in Jam's user defined symbol table. + * + * 'flag' controls the relationship between new and old values of the variable: + * SET replaces the old with the new; APPEND appends the new to the old; DEFAULT + * only uses the new if the variable was previously unset. + * + * Copies symbol. Takes ownership of value. + */ + +void var_set( char * symbol, LIST * value, int flag ) +{ + VARIABLE * v = var_enter( symbol ); + + if ( DEBUG_VARSET ) + var_dump( symbol, value, "set" ); + + switch ( flag ) + { + case VAR_SET: + /* Replace value */ + list_free( v->value ); + v->value = value; + break; + + case VAR_APPEND: + /* Append value */ + v->value = list_append( v->value, value ); + break; + + case VAR_DEFAULT: + /* Set only if unset */ + if ( !v->value ) + v->value = value; + else + list_free( value ); + break; + } +} + + +/* + * var_swap() - swap a variable's value with the given one. + */ + +LIST * var_swap( char * symbol, LIST * value ) +{ + VARIABLE * v = var_enter( symbol ); + LIST * oldvalue = v->value; + if ( DEBUG_VARSET ) + var_dump( symbol, value, "set" ); + v->value = value; + return oldvalue; +} + + +/* + * var_enter() - make new var symbol table entry, returning var ptr. + */ + +static VARIABLE * var_enter( char * symbol ) +{ + VARIABLE var; + VARIABLE * v = &var; + + if ( !varhash ) + varhash = hashinit( sizeof( VARIABLE ), "variables" ); + + v->symbol = symbol; + v->value = 0; + + if ( hashenter( varhash, (HASHDATA * *)&v ) ) + v->symbol = newstr( symbol ); /* never freed */ + + return v; +} + + +/* + * var_dump() - dump a variable to stdout. + */ + +static void var_dump( char * symbol, LIST * value, char * what ) +{ + printf( "%s %s = ", what, symbol ); + list_print( value ); + printf( "\n" ); +} + + +/* + * var_done() - free variable tables. + */ + +static void delete_var_( void * xvar, void * data ) +{ + VARIABLE * v = (VARIABLE *)xvar; + freestr( v->symbol ); + list_free( v-> value ); +} + + +void var_done() +{ + hashenumerate( varhash, delete_var_, (void *)0 ); + hashdone( varhash ); +} diff --git a/jam-files/engine/variable.h b/jam-files/engine/variable.h new file mode 100644 index 00000000..5c49e3ca --- /dev/null +++ b/jam-files/engine/variable.h @@ -0,0 +1,35 @@ +/* + * Copyright 1993, 2000 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* + * variable.h - handle jam multi-element variables + */ + +struct hash; + +void var_defines( char* const *e, int preprocess ); +int var_string( char *in, char *out, int outsize, LOL *lol ); +LIST * var_get( char *symbol ); +void var_set( char *symbol, LIST *value, int flag ); +LIST * var_swap( char *symbol, LIST *value ); +void var_done(); +void var_hash_swap( struct hash** ); + +/** Expands the "in" expression directly into the "out" file. + The file can be one of: a path, STDOUT, or STDERR to send + the output to a file overwriting previous content, to + the console, or to the error output respectively. +*/ +void var_string_to_file( const char * in, int insize, const char * out, LOL * lol ); + +/* + * Defines for var_set(). + */ + +# define VAR_SET 0 /* override previous value */ +# define VAR_APPEND 1 /* append to previous value */ +# define VAR_DEFAULT 2 /* set only if no previous value */ + diff --git a/jam-files/engine/w32_getreg.c b/jam-files/engine/w32_getreg.c new file mode 100644 index 00000000..5a06f43e --- /dev/null +++ b/jam-files/engine/w32_getreg.c @@ -0,0 +1,207 @@ +/* +Copyright Paul Lin 2003. Copyright 2006 Bojan Resnik. +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +*/ + +# include "jam.h" + +# if defined( OS_NT ) || defined( OS_CYGWIN ) + +# include "lists.h" +# include "newstr.h" +# include "parse.h" +# include "frames.h" +# include "strings.h" + +# define WIN32_LEAN_AND_MEAN +# include <windows.h> + +# define MAX_REGISTRY_DATA_LENGTH 4096 +# define MAX_REGISTRY_KEYNAME_LENGTH 256 +# define MAX_REGISTRY_VALUENAME_LENGTH 16384 + +typedef struct +{ + LPCSTR name; + HKEY value; +} KeyMap; + +static const KeyMap dlRootKeys[] = { + { "HKLM", HKEY_LOCAL_MACHINE }, + { "HKCU", HKEY_CURRENT_USER }, + { "HKCR", HKEY_CLASSES_ROOT }, + { "HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE }, + { "HKEY_CURRENT_USER", HKEY_CURRENT_USER }, + { "HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT }, + { 0, 0 } +}; + +static HKEY get_key(char const** path) +{ + const KeyMap *p; + + for (p = dlRootKeys; p->name; ++p) + { + int n = strlen(p->name); + if (!strncmp(*path,p->name,n)) + { + if ((*path)[n] == '\\' || (*path)[n] == 0) + { + *path += n + 1; + break; + } + } + } + + return p->value; +} + +LIST* +builtin_system_registry( + PARSE *parse, + FRAME *frame ) +{ + char const* path = lol_get(frame->args, 0)->string; + LIST* result = L0; + HKEY key = get_key(&path); + + if ( + key != 0 + && ERROR_SUCCESS == RegOpenKeyEx(key, path, 0, KEY_QUERY_VALUE, &key) + ) + { + DWORD type; + BYTE data[MAX_REGISTRY_DATA_LENGTH]; + DWORD len = sizeof(data); + LIST const* const field = lol_get(frame->args, 1); + + if ( ERROR_SUCCESS == + RegQueryValueEx(key, field ? field->string : 0, 0, &type, data, &len) ) + { + switch (type) + { + + case REG_EXPAND_SZ: + { + long len; + string expanded[1]; + string_new(expanded); + + while ( + (len = ExpandEnvironmentStrings( + (LPCSTR)data, expanded->value, expanded->capacity)) + > expanded->capacity + ) + string_reserve(expanded, len); + + expanded->size = len - 1; + + result = list_new( result, newstr(expanded->value) ); + string_free( expanded ); + } + break; + + case REG_MULTI_SZ: + { + char* s; + + for (s = (char*)data; *s; s += strlen(s) + 1) + result = list_new( result, newstr(s) ); + + } + break; + + case REG_DWORD: + { + char buf[100]; + sprintf( buf, "%u", *(PDWORD)data ); + result = list_new( result, newstr(buf) ); + } + break; + + case REG_SZ: + result = list_new( result, newstr((char*)data) ); + break; + } + } + RegCloseKey(key); + } + return result; +} + +static LIST* get_subkey_names(HKEY key, char const* path) +{ + LIST* result = 0; + + if ( ERROR_SUCCESS == + RegOpenKeyEx(key, path, 0, KEY_ENUMERATE_SUB_KEYS, &key) + ) + { + char name[MAX_REGISTRY_KEYNAME_LENGTH]; + DWORD name_size = sizeof(name); + DWORD index; + FILETIME last_write_time; + + for ( index = 0; + ERROR_SUCCESS == RegEnumKeyEx( + key, index, name, &name_size, 0, 0, 0, &last_write_time); + ++index, + name_size = sizeof(name) + ) + { + name[name_size] = 0; + result = list_append(result, list_new(0, newstr(name))); + } + + RegCloseKey(key); + } + + return result; +} + +static LIST* get_value_names(HKEY key, char const* path) +{ + LIST* result = 0; + + if ( ERROR_SUCCESS == RegOpenKeyEx(key, path, 0, KEY_QUERY_VALUE, &key) ) + { + char name[MAX_REGISTRY_VALUENAME_LENGTH]; + DWORD name_size = sizeof(name); + DWORD index; + + for ( index = 0; + ERROR_SUCCESS == RegEnumValue( + key, index, name, &name_size, 0, 0, 0, 0); + ++index, + name_size = sizeof(name) + ) + { + name[name_size] = 0; + result = list_append(result, list_new(0, newstr(name))); + } + + RegCloseKey(key); + } + + return result; +} + +LIST* +builtin_system_registry_names( + PARSE *parse, + FRAME *frame ) +{ + char const* path = lol_get(frame->args, 0)->string; + char const* result_type = lol_get(frame->args, 1)->string; + + HKEY key = get_key(&path); + + if ( !strcmp(result_type, "subkeys") ) + return get_subkey_names(key, path); + if ( !strcmp(result_type, "values") ) + return get_value_names(key, path); + return 0; +} + +# endif diff --git a/jam-files/engine/yyacc.c b/jam-files/engine/yyacc.c new file mode 100644 index 00000000..b5efc96b --- /dev/null +++ b/jam-files/engine/yyacc.c @@ -0,0 +1,268 @@ +/* Copyright 2002 Rene Rivera. +** Distributed under the Boost Software License, Version 1.0. +** (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +/* +# yyacc - yacc wrapper +# +# Allows tokens to be written as `literal` and then automatically +# substituted with #defined tokens. +# +# Usage: +# yyacc file.y filetab.h file.yy +# +# inputs: +# file.yy yacc grammar with ` literals +# +# outputs: +# file.y yacc grammar +# filetab.h array of string <-> token mappings +# +# 3-13-93 +# Documented and p moved in sed command (for some reason, +# s/x/y/p doesn't work). +# 10-12-93 +# Take basename as second argument. +# 12-31-96 +# reversed order of args to be compatible with GenFile rule +# 11-20-2002 +# Reimplemented as a C program for portability. (Rene Rivera) +*/ + +void print_usage(); +char * copy_string(char * s, int l); +char * tokenize_string(char * s); +int cmp_literal(const void * a, const void * b); + +typedef struct +{ + char * string; + char * token; +} literal; + +int main(int argc, char ** argv) +{ + int result = 0; + if (argc != 4) + { + print_usage(); + result = 1; + } + else + { + FILE * token_output_f = 0; + FILE * grammar_output_f = 0; + FILE * grammar_source_f = 0; + + grammar_source_f = fopen(argv[3],"r"); + if (grammar_source_f == 0) { result = 1; } + if (result == 0) + { + literal literals[1024]; + int t = 0; + char l[2048]; + while (1) + { + if (fgets(l,2048,grammar_source_f) != 0) + { + char * c = l; + while (1) + { + char * c1 = strchr(c,'`'); + if (c1 != 0) + { + char * c2 = strchr(c1+1,'`'); + if (c2 != 0) + { + literals[t].string = copy_string(c1+1,c2-c1-1); + literals[t].token = tokenize_string(literals[t].string); + t += 1; + c = c2+1; + } + else + break; + } + else + break; + } + } + else + { + break; + } + } + literals[t].string = 0; + literals[t].token = 0; + qsort(literals,t,sizeof(literal),cmp_literal); + { + int p = 1; + int i = 1; + while (literals[i].string != 0) + { + if (strcmp(literals[p-1].string,literals[i].string) != 0) + { + literals[p] = literals[i]; + p += 1; + } + i += 1; + } + literals[p].string = 0; + literals[p].token = 0; + t = p; + } + token_output_f = fopen(argv[2],"w"); + if (token_output_f != 0) + { + int i = 0; + while (literals[i].string != 0) + { + fprintf(token_output_f," { \"%s\", %s },\n",literals[i].string,literals[i].token); + i += 1; + } + fclose(token_output_f); + } + else + result = 1; + if (result == 0) + { + grammar_output_f = fopen(argv[1],"w"); + if (grammar_output_f != 0) + { + int i = 0; + while (literals[i].string != 0) + { + fprintf(grammar_output_f,"%%token %s\n",literals[i].token); + i += 1; + } + rewind(grammar_source_f); + while (1) + { + if (fgets(l,2048,grammar_source_f) != 0) + { + char * c = l; + while (1) + { + char * c1 = strchr(c,'`'); + if (c1 != 0) + { + char * c2 = strchr(c1+1,'`'); + if (c2 != 0) + { + literal key; + literal * replacement = 0; + key.string = copy_string(c1+1,c2-c1-1); + key.token = 0; + replacement = (literal*)bsearch( + &key,literals,t,sizeof(literal),cmp_literal); + *c1 = 0; + fprintf(grammar_output_f,"%s%s",c,replacement->token); + c = c2+1; + } + else + { + fprintf(grammar_output_f,"%s",c); + break; + } + } + else + { + fprintf(grammar_output_f,"%s",c); + break; + } + } + } + else + { + break; + } + } + fclose(grammar_output_f); + } + else + result = 1; + } + } + if (result != 0) + { + perror("yyacc"); + } + } + return result; +} + +static char * usage[] = { + "yyacc <grammar output.y> <token table output.h> <grammar source.yy>", + 0 }; + +void print_usage() +{ + char ** u; + for (u = usage; *u != 0; ++u) + { + fputs(*u,stderr); putc('\n',stderr); + } +} + +char * copy_string(char * s, int l) +{ + char * result = (char*)malloc(l+1); + strncpy(result,s,l); + result[l] = 0; + return result; +} + +char * tokenize_string(char * s) +{ + char * result; + char * literal = s; + int l; + int c; + + if (strcmp(s,":") == 0) literal = "_colon"; + else if (strcmp(s,"!") == 0) literal = "_bang"; + else if (strcmp(s,"!=") == 0) literal = "_bang_equals"; + else if (strcmp(s,"&&") == 0) literal = "_amperamper"; + else if (strcmp(s,"&") == 0) literal = "_amper"; + else if (strcmp(s,"+") == 0) literal = "_plus"; + else if (strcmp(s,"+=") == 0) literal = "_plus_equals"; + else if (strcmp(s,"||") == 0) literal = "_barbar"; + else if (strcmp(s,"|") == 0) literal = "_bar"; + else if (strcmp(s,";") == 0) literal = "_semic"; + else if (strcmp(s,"-") == 0) literal = "_minus"; + else if (strcmp(s,"<") == 0) literal = "_langle"; + else if (strcmp(s,"<=") == 0) literal = "_langle_equals"; + else if (strcmp(s,">") == 0) literal = "_rangle"; + else if (strcmp(s,">=") == 0) literal = "_rangle_equals"; + else if (strcmp(s,".") == 0) literal = "_period"; + else if (strcmp(s,"?") == 0) literal = "_question"; + else if (strcmp(s,"?=") == 0) literal = "_question_equals"; + else if (strcmp(s,"=") == 0) literal = "_equals"; + else if (strcmp(s,",") == 0) literal = "_comma"; + else if (strcmp(s,"[") == 0) literal = "_lbracket"; + else if (strcmp(s,"]") == 0) literal = "_rbracket"; + else if (strcmp(s,"{") == 0) literal = "_lbrace"; + else if (strcmp(s,"}") == 0) literal = "_rbrace"; + else if (strcmp(s,"(") == 0) literal = "_lparen"; + else if (strcmp(s,")") == 0) literal = "_rparen"; + l = strlen(literal)+2; + result = (char*)malloc(l+1); + for (c = 0; literal[c] != 0; ++c) + { + result[c] = toupper(literal[c]); + } + result[l-2] = '_'; + result[l-1] = 't'; + result[l] = 0; + return result; +} + +int cmp_literal(const void * a, const void * b) +{ + return strcmp(((const literal *)a)->string,((const literal *)b)->string); +} |