#
# /+\
#  +\    Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
# \+/
#
# This file is part of Jam - see jam.c for Copyright information.
#

#
# JAMBASE - jam 2.5 ruleset providing make(1)-like functionality
#
# Supports UNIX, NT, and VMS.
#
# 12/27/93 (seiwald) - purturb library sources with SOURCE_GRIST
# 04/18/94 (seiwald) - use '?=' when setting OS specific vars
# 04/21/94 (seiwald) - do RmTemps together
# 05/05/94 (seiwald) - all supported C compilers support -o: relegate
#              RELOCATE as an option; set Ranlib to "" to disable it
# 06/01/94 (seiwald) - new 'actions existing' to do existing sources
# 08/25/94 (seiwald) - new ObjectCcFlags rule to append to per-target CCFLAGS
# 08/29/94 (seiwald) - new ObjectHdrs rule to append to per-target HDRS
# 09/19/94 (seiwald) - LinkLibraries and Undefs now append
#            - Rule names downshifted.
# 10/06/94 (seiwald) - Dumb yyacc stuff moved into Jamfile.
# 10/14/94 (seiwald) - (Crude) support for .s, .C, .cc, .cpp, and .f files.
# 01/08/95 (seiwald) - Shell now handled with awk, not sed
# 01/09/95 (seiwald) - Install* now take dest directory as target
# 01/10/95 (seiwald) - All entries sorted.
# 01/10/95 (seiwald) - NT support moved in, with LauraW's help.
# 01/10/95 (seiwald) - VMS support moved in.
# 02/06/95 (seiwald) - ObjectC++Flags and SubDirC++Flags added.
# 02/07/95 (seiwald) - Iron out when HDRSEARCH uses "" or SEARCH_SOURCE.
# 02/08/95 (seiwald) - SubDir works on VMS.
# 02/14/95 (seiwald) - MkDir and entourage.
# 04/30/95 (seiwald) - Use install -c flag so that it copies, not moves.
# 07/10/95 (taylor) - Support for Microsoft C++.
# 11/21/96 (peterk) - Support for BeOS
# 07/19/99 (sickel) - Support for Mac OS X Server (and maybe client)
# 02/18/00 (belmonte)- Support for Cygwin.

# Special targets defined in this file:
#
# all       - parent of first, shell, files, lib, exe
# first     - first dependent of 'all', for potential initialization
# shell     - parent of all Shell targets
# files     - parent of all File targets
# lib       - parent of all Library targets
# exe       - parent of all Main targets
# dirs      - parent of all MkDir targets
# clean     - removes all Shell, File, Library, and Main targets
# uninstall - removes all Install targets
#

# Rules defined by this file:
#
# as obj.o : source.s ;         .s -> .o
# Bulk dir : files ;            populate directory with many files
# Cc obj.o : source.c ;         .c -> .o
# C++ obj.o : source.cc ;       .cc -> .o
# Clean clean : sources ;       remove sources with 'jam clean'
# File dest : source ;          copy file
# Fortran obj.o : source.f ;        .f -> .o
# GenFile source.c : program args ; make custom file
# HardLink target : source ;        make link from source to target
# HdrRule source : headers ;        handle #includes
# InstallInto dir : sources ;       install any files
# InstallBin dir : sources ;        install binaries
# InstallLib dir : sources ;        install files
# InstallFile dir : sources ;       install files
# InstallMan dir : sources ;        install man pages
# InstallShell dir : sources ;      install shell scripts
# Lex source.c : source.l ;     .l -> .c
# Library lib : source ;        archive library from compiled sources
# LibraryFromObjects lib : objects ;    archive library from objects
# LinkLibraries images : libraries ;    bag libraries onto Mains
# Main image : source ;         link executable from compiled sources
# MainFromObjects image : objects ; link executable from objects
# MkDir dir ;               make a directory, if not there
# Object object : source ;      compile object from source
# ObjectCcFlags source : flags ;    add compiler flags for object
# ObjectC++Flags source : flags ;   add compiler flags for object
# ObjectHdrs source : dirs ;        add include directories for object
# Objects sources ;         compile sources
# RmTemps target : sources ;        remove temp sources after target made
# Setuid images ;           mark executables Setuid
# SoftLink target : source ;        make symlink from source to target
# SubDir TOP d1 d2 ... ;        start a subdirectory Jamfile
# SubDirCcFlags flags ;         add compiler flags until next SubDir
# SubDirC++Flags flags ;        add compiler flags until next SubDir
# SubDirHdrs d1 d2 ... ;        add include dir until next SubDir
# SubInclude TOP d1 d2 ... ;        include a subdirectory Jamfile
# Shell exe : source ;          make a shell executable
# Undefines images : symbols ;      save undef's for linking
# UserObject object : source ;      handle unknown suffixes for Object
# Yacc source.c : source.y ;        .y -> .c
#
# Utility rules that have no side effects (not supported):
#
# FAppendSuffix f1 f2 ... : $(SUF) ;    return $(<) with suffixes
# FDirName d1 d2 ... ;          return path from root to dir
# FGrist d1 d2 ... ;            return d1!d2!...
# FGristFiles value ;           return $(value:G=$(SOURCE_GRIST))
# FGristSourceFiles value ;     return $(value:G=$(SOURCE_GRIST))
# FStripCommon v1 : v2 ;        strip common initial parts of v1 v2
# FReverse a1 a2 ... ;          return ... a2 a1
# 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.5)
#
# 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
#

# for perforce use -- jambase version

JAMBASEDATE = 2004.10.07 ;

# Initialize variables
#

#
# OS specific variable settings
#

if $(NT)
{
    # the list of supported toolsets on Windows NT and Windows 95/98
    #
    local SUPPORTED_TOOLSETS = BORLANDC
                               VISUALC
                               VISUALC16
                               INTELC
                               WATCOM
                               MINGW
                               LCC
                               DIGITALMARS
                               PELLESC
                               ;

    # if the JAM_TOOLSET environment variable is defined, check that it is
    # one of our supported values
    #
    if $(JAM_TOOLSET)
    {
        if ! $(JAM_TOOLSET) in $(SUPPORTED_TOOLSETS)
        {
            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 JAM_TOOLSET is empty, we'll try to detect the toolset from other
    # environment variables to remain backwards compatible with Jam 2.5
    #
    if ! $(JAM_TOOLSET)
    {
        if $(BCCROOT)
        {
            JAM_TOOLSET  = BORLANDC ;
            BORLANDC     = $(BCCROOT) ;
        }
        else if $(MSVC)
        {
            JAM_TOOLSET = VISUALC16 ;
            VISUALC16   = $(MSVC) ;
        }
        else if $(MSVCNT)
        {
            JAM_TOOLSET = VISUALC ;
            VISUALC     = $(MSVCNT) ;
        }
        else if $(MINGW)
        {
            # MINGW is defined when trying to compile FT-Jam with
            # classic Jam
            #
            JAM_TOOLSET = MINGW ;
        }
        else
        {
            Echo  "Jam cannot be run because you didn't indicate which compilation toolset" ;
            Echo  "to use. To do so, define the JAM_TOOLSET environment variable with" ;
            Echo  "one of the following values:" ;
            Echo  ;
            Echo  "   Value       Toolset Description" ;
            Echo  ;
            Echo  "   BORLANDC     Borland C++" ;
            Echo  "   VISUALC      Microsoft Visual C++" ;
            Echo  "   VISUALC16    Microsoft Visual C++ 16 bit" ;
            Echo  "   INTELC       Intel C/C++" ;
            Echo  "   WATCOM       Watcom C/C++" ;
            Echo  "   MINGW        MinGW (gcc)" ;
            Echo  "   LCC          Win32-LCC" ;
            Echo  "   DIGITALMARS  Digital Mars C/C++" ;
            Echo  "   PELLESC      Pelles C" ;
            Echo  ;
            Echo  "The corresponding compiler must be in your path" ;
            Echo  ;
            Echo  "  e.g.:  set JAM_TOOLSET=VISUALC" ;
            Exit  ;
        }
    }

    MV      ?= move /y ;
    CP      ?= copy ;
    RM      ?= del /f/q ;
    RMDIR       ?= rmdir /s/q ;
    SLASH   ?= \\ ;
    SUFLIB  ?= .lib ;
    SUFOBJ  ?= .obj ;
    SUFEXE  ?= .exe ;

    SUFLIBSHR ?= .dll ;

    if $(JAM_TOOLSET) = BORLANDC
    {
        Echo "Compiler is Borland C++" ;

        AR          ?= tlib /C /P64 ;
        CC          ?= bcc32 ;
        CCFLAGS     ?= -w- -q -DWIN -tWR -tWM -tWC ;
        C++         ?= $(CC) ;
        C++FLAGS    ?= $(CCFLAGS) -P ;
        LINK        ?= $(CC) ;
        ILINK       ?= ilink32 -q ;
        IMPLIB      ?= implib ;
        LINKFLAGS   ?= $(CCFLAGS) ;
        STDLIBPATH  ?= $(BORLANDC)\\lib ;
        STDHDRS     ?= $(BORLANDC)\\include ;
        NOARSCAN    ?= true ;
        ILINKLIBS   ?= C0D32.OBJ CW32.LIB IMPORT32.LIB ;
        PICFLAGS    ?= -tWD ;
    }
    else if $(JAM_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    ?=
                $(MSVC)\\lib\\mlibce.lib
                $(MSVC)\\lib\\oldnames.lib
            ;
        LINKLIBS    ?= ;
        NOARSCAN    ?= true ;
        OPTIM       ?= "" ;
        STDHDRS     ?= $(VISUALC16)\\include ;
        UNDEFFLAG   ?= "/u _" ;
    }
        else if $(JAM_TOOLSET) = VISUALC
    {
        # Visual C++ 6.0 uses MSVCDIR

        MSVCNT      ?= $(MSVCDIR) ;

        # bury IA64 in the path for the SDK

        local I ; if $(OSPLAT) = IA64 { I = ia64\\ ; } else { I = "" ; }

        AR          ?= lib ;
        AS          ?= masm386 ;
        CC          ?= cl /nologo ;
        CCFLAGS     ?= "" ;
        C++         ?= $(CC) ;
        C++FLAGS    ?= $(CCFLAGS) ;
        LINK        ?= link /nologo ;
        LINKFLAGS   ?= "" ;
        LINKLIBS    ?=
                $(MSVCNT)\\lib\\$(I)libc.lib
                $(MSVCNT)\\lib\\$(I)oldnames.lib
                $(MSVCNT)\\lib\\$(I)kernel32.lib ;
        OPTIM       ?= "" ;
        STDHDRS     ?= $(VISUALC)\\include ;
        UNDEFFLAG   ?= "/u _" ;
    }
    else if $(JAM_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 $(JAM_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 _" ;
        PICFLAGS     = -s ;  # disable stack checks
    }
    else if $(JAM_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 $(JAM_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 if $(JAM_TOOLSET) = DIGITALMARS
    {
        Echo "Compiler is Digital Mars C/C++" ;

        AR              ?= lib -c ;
        CC              ?= dmc ;
        CCFLAGS         ?= "" ;
        C++             ?= $(CC) ;
        C++FLAGS        ?= $(CCFLAGS) ;
        LINK            ?= link /nologo ;
        LINKFLAGS       ?= "/EXETYPE:NT /NOMAP" ;
        LINKLIBS        ?= USER32.LIB
                           KERNEL32.LIB
                           GDI32.LIB ;
        OPTIM           ?= ;
        NOARSCAN         = true ;
        PICFLAGS         = -mn -WD ;
    }
    else if $(JAM_TOOLSET) = PELLESC
    {
        Echo "Compiler is PellesC" ;

        AR        ?= polib ;
        CC        ?= pocc ;
        CCFLAGS   ?= "" ;
        LINK      ?= polink ;
        LINKFLAGS ?= ;
        LINKLIBS  ?= ;
        OPTIM     ?= ;
        NOARSCAN   = true ;
        LINKLIBS  ?=
                crt.lib oldnames.lib Win\\kernel32.lib ;
    }
    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." ;
    }

    STDHRS ?= "" ;
}
else if $(OS2)
{
    # the list of supported toolsets on OS/2
    #
    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)
    {
        if ! $(JAM_TOOLSET) in $(SUPPORTED_TOOLSETS)
        {
            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 ! $(JAM_TOOLSET)
    {
        if $(watcom)
        {
            WATCOM   = $(watcom) ;
            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 ;

    SUFLIBSHR ?= .dll ;

    if $(JAM_TOOLSET) = WATCOM
    {
        AR          ?= wlib ;
        BINDIR      ?= \\os2\\apps ;
        CC          ?= wcc386 ;
        CCFLAGS     ?= /zq /DOS2 /I$(WATCOM)\\h ; # zq=quiet
        C++         ?= wpp386 ;
        C++FLAGS    ?= $(CCFLAGS) ;
        CP          ?= copy ;
        DOT         ?= . ;
        DOTDOT      ?= .. ;
        LINK        ?= wcl386 ;
        LINKFLAGS   ?= /zq ; # zq=quiet
        LINKLIBS    ?= ;
        MV          ?= move ;
        NOARSCAN    ?= true ;
        OPTIM       ?= ;
        RM          ?= del /f ;
        SLASH       ?= \\ ;
        STDHDRS     ?= $(WATCOM)\\h ;
        SUFEXE      ?= .exe ;
        SUFLIB      ?= .lib ;
        SUFOBJ      ?= .obj ;
        UNDEFFLAG   ?= "/u _" ;
    }
    else if $(JAM_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          = / ;
    }
    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:Libs:MSL_MPWCRuntime_PPC.lib"
        "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL_C_PPC_MPW.Lib" ;

    MPWNLLIBS ?=
        "$(CW):MacOS Support:Libraries:Runtime:Libs:MSL_MPWCRuntime_PPC.lib"
        "$(CW):MSL:MSL_C:MSL_MacOS:Lib:PPC:MSL_C_PPC_MPW(NL).Lib" ;

    SIOUXHDRS ?= ;

    SIOUXLIBS ?=
        "$(CW):MacOS Support:Libraries:Runtime:Libs:MSL_Runtime_PPC.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 ;
    CC      ?= mwcppc ;
    CCFLAGS     ?= -w off ;
    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 && $(OSPLAT) = PPC
{
    AR      ?= mwld -xml -o ;
    BINDIR      ?= /boot/home/config/bin ;
    CC      ?= mwcc ;
    CCFLAGS     ?= -nosyspath ;
    C++     ?= $(CC) ;
    C++FLAGS    ?= -nosyspath ;
    CHMOD       ?= chmod ;
    CHGRP       ?= chgrp ;
    CHOWN       ?= chown ;
    FORTRAN     ?= "" ;
    LEX     ?= flex ;
    LIBDIR      ?= /boot/home/config/lib ;
    LINK        ?= mwld ;
    LINKFLAGS   ?= "" ;
    MANDIR      ?= /boot/home/config/man ;
    NOARSCAN    ?= true ;
    RANLIB      ?= ranlib ;
    STDHDRS     ?= /boot/develop/headers/posix ;
    YACC        ?= bison -y ;
    YACCGEN     ?= .c ;
    YACCFILES   ?= y.tab ;
    YACCFLAGS   ?= -d ;
}
else if $(OS) = BEOS
{
    BINDIR      ?= /boot/home/config/bin ;
    CC      ?= gcc ;
    C++     ?= $(CC) ;
    CHMOD       ?= chmod ;
    CHGRP       ?= chgrp ;
    CHOWN       ?= chown ;
    FORTRAN     ?= "" ;
    LEX     ?= flex ;
    LIBDIR      ?= /boot/home/config/lib ;
    LINK        ?= gcc ;
    MANDIR      ?= /boot/home/config/man ;
    NOARSCAN    ?= true ;
    RANLIB      ?= ranlib ;
    STDHDRS     ?= /boot/develop/headers/posix ;
    YACC        ?= bison -y ;
    YACCGEN     ?= .c ;
    YACCFILES   ?= y.tab ;
    YACCFLAGS   ?= -d ;
}
else if $(UNIX)
{
    switch $(OS)
    {
    case AIX :
    LINKLIBS    ?= -lbsd ;

    case AMIGA :
    CC      ?= gcc ;
    YACC        ?= bison -y ;

    case CYGWIN :
    CC      ?= gcc ;
    CCFLAGS     += -D__cygwin__ ;
    LEX     ?= flex ;
    JAMSHELL    ?= sh -c ;
    RANLIB      ?= "" ;
    SUFEXE      ?= .exe ;
    YACC        ?= bison -y ;

    case DGUX :
    RANLIB      ?= "" ;
    RELOCATE    ?= true ;

    case HPUX :
    RANLIB      ?= "" ;

    case INTERIX :
    CC      ?= gcc ;
    JAMSHELL    ?= sh -c ;
    RANLIB      ?= "" ;

    case IRIX :
    RANLIB      ?= "" ;

    case MACOSX :
    PICFLAGS ?= -fPIC ;

    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 :
    C++     ?= c++ ;
    MANDIR      ?= /usr/local/share/man ;

    case NCR :
    RANLIB      ?= "" ;

    case PTX :
    RANLIB      ?= "" ;

    case QNX :
    AR      ?= wlib ;
    CC      ?= cc ;
    CCFLAGS     ?= -Q ; # quiet
    C++     ?= $(CC) ;
    C++FLAGS    ?= -Q ; # quiet
    LINK        ?= $(CC) ;
    LINKFLAGS   ?= -Q ; # quiet
    NOARSCAN    ?= true ;
    RANLIB      ?= "" ;

    case SCO :
    RANLIB      ?= "" ;
    RELOCATE    ?= true ;

    case SINIX :
    RANLIB      ?= "" ;

    case SOLARIS :
    RANLIB      ?= "" ;
    AR      ?= "/usr/ccs/bin/ar ru" ;

    case UNICOS :
    NOARSCAN    ?= true ;
    OPTIM       ?= -O0 ;

    case UNIXWARE :
    RANLIB      ?= "" ;
    RELOCATE    ?= true ;
    }

    # UNIX defaults

    CCFLAGS     ?= ;
    C++FLAGS    ?= $(CCFLAGS) ;
    CHMOD       ?= chmod ;
    CHGRP       ?= chgrp ;
    CHOWN       ?= chown ;
    LEX         ?= lex ;
    LINKFLAGS   ?= $(CCFLAGS) ;
    LINKLIBS    ?= ;
    OPTIM       ?= -O ;
    RANLIB      ?= ranlib ;
    YACC        ?= yacc ;
    YACCGEN     ?= .c ;
    YACCFILES   ?= y.tab ;
    YACCFLAGS   ?= -d ;

    SUFOBJSHR   ?= .lo ;
    SUFLIBSHR   ?= .la ;
    PICFLAGS    ?= -fpic ;
    STDHDRS     ?= /usr/include ;
}

# shared library object file suffix. We assume that it is identical
# than the normal one
SUFOBJSHR ?= $(SUFOBJ) ;
SUFLIBSHR ?= $(SUFLIB) ;


# the D compiler
DC ?= dmd ;

#
# 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 ;
    RMDIR       ?= $(RM) ;
    RSH     ?= rsh ;
    SED     ?= sed ;
    SHELLHEADER ?= "#!/bin/sh" ;
    SHELLMODE   ?= 755 ;
    SLASH       ?= / ;
    SUBDIRRULES     ?= ;
    SUBDIRRESET     ?= ASFLAGS HDRS C++FLAGS CCFLAGS ;
    SUFEXE      ?= "" ;
    SUFLIB      ?= .a ;
    SUFOBJ      ?= .o ;
    UNDEFFLAG   ?= "-u _" ;
    YACC        ?= ;
    YACCGEN     ?= ;
    YACCFILES   ?= ;
    YACCFLAGS   ?= ;

    HDRPATTERN =
            "^[ 	]*#[ 	]*include[ 	]*[<\"]([^\">]*)[\">].*$" ;

    OSFULL = $(OS)$(OSVER)$(OSPLAT) $(OS)$(OSPLAT) $(OS)$(OSVER) $(OS) ;


#
# Base dependencies - first for "bootstrap" kinds of rules
#

Depends all : shell files lib exe obj ;
Depends all shell files lib exe obj : first ;
NotFile all first shell files lib exe obj dirs clean uninstall ;
Always  clean uninstall ;

#
# Rules
#

# /As object : source ;
#
# Assemble the file _source_, called by the @Object rule.
#
# Do not call this rule directly, since _object_ and _source_ may have
# have platform-specific file extensions
#
rule As
{
    Depends $(<) : $(>) ;
    ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;
    ASHDRS on $(<) = [ FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ;
}

# /Bulk  directory : sources ;
#
# Copies _sources_ into _directory_
#
rule Bulk
{
    local i ;

    for i in $(>)
    {
        File $(i:D=$(<)) : $(i) ;
    }
}


# /Dc object : source ;
#
# Compile the file source into object, usin the D compiler $(DC), its
# flags $(DCFLAGS) and $(DOPTIM)
# Called by the @Object rule
#
# Do not call this rule directly, since _object_ and _source_ may have
# have platform-specific file extensions
#
rule Dc
{
    Depends $(<) : $(>) ;

    # Just to clarify here: this sets the per-target DCFLAGS to
    # be the current value of (global) DCFLAGS and SUBDIRDCFLAGS.

    DCFLAGS on $(<) += $(DCFLAGS) $(SUBDIRDCFLAGS) ;
}


# /Cc object : source ;
#
# Compile the file source into object, using the C compiler $(CC), its
# flags $(CCFLAGS) and $(OPTIM), and the header file directories $(HDRS).
# Called by the @Object rule
#
# Do not call this rule directly, since _object_ and _source_ may have
# have platform-specific file extensions
#
rule Cc
{
    Depends $(<) : $(>) ;

    # If the compiler's -o flag doesn't work, relocate the .o

    if $(RELOCATE)
    {
        CcMv $(<) : $(>) ;
    }

    # Just to clarify here: this sets the per-target CCFLAGS to
    # be the current value of (global) CCFLAGS and SUBDIRCCFLAGS.
    # CCHDRS and CCDEFS must be reformatted each time for some
    # compiles (VMS, NT) that malign multiple -D or -I flags.

    CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) ;

    CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;
    CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
}

# /C++ object : source ;
#
# Compile the C++ source file _source_. Similar to @CC, called by @Object
#
# Do not call this rule directly, since _object_ and _source_ may have
# have platform-specific file extensions
#
rule C++
{
    Depends $(<) : $(>) ;

    if $(RELOCATE)
    {
        CcMv $(<) : $(>) ;
    }

    C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) ;

    CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;
    CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
}

# /Chmod target ;
#
# (Unix and VMS only). Change file permissions on _target_ to target-specific
# $(MODE) value set by @Link, @File, @Install* and @Shell rules
#
rule Chmod
{
    if $(CHMOD) { Chmod1 $(<) ; }
}

# /Clean  clean : targets ;
#
# Removes existing _targets_ when _clean_ is built. clean is not a dependency
# of all, and must be built explicitely for targets to be removed
#

# /File target : source ;
#
# Copies _source_ into _target_
#
rule File
{
    Depends files : $(<) ;
    Depends $(<) : $(>) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;
    MODE on $(<) = $(FILEMODE) ;
    Chmod $(<) ;
}

# /Fortran object : source ;
#
# Compile the Fortran source file _source_. Called by the @Object rule
#
# Do not call this rule directly, since _obj_ and _source_ may have
# have platform-specific file extensions
#
rule Fortran
{
    Depends $(<) : $(>) ;
}

# /GenFile target : image sources ;
#
# Runs the command "_image_ _target_ _sources_" to create _target_ from
# _sources_ and _image_ (where _image_ is an executable built by the
# @Main rule)
#
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) ;
}

# /HardLink target : source ;
#
# Makes _target_ a hard link to _source_, if it isn't one already
# (Unix only)
#
rule HardLink
{
    Depends files : $(<) ;
    Depends $(<) : $(>) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;
}

# /HdrMacroFile
#
# this rule is specific to FT-Jam. It 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
#
# these files must be parsed before any make is tried.
#
rule HdrMacroFile
{
  HDRMACRO $(<) ;
}

# /HdrRule source : headers ;
#
# Arranges the proper dependencies when the file _source_ includes the files
# _headers_ through the #include C preprocessor directive
#
# this rule is not intendend to be called explicitely. It is called
# automatically during header scanning on sources handled by the @Object
# rule (e.g. sources in @Main or @Library rules)
#
rule HdrRule
{
    # HdrRule source : headers ;

    # N.B.  This rule is called during binding, potentially after
    # the fate of many targets has been determined, and must be
    # used with caution: don't add dependencies to unrelated
    # targets, and don't set variables on $(<).

    # Tell Jam that anything depending on $(<) also depends on $(>),
    # set SEARCH so Jam can find the headers, but then say we don't
    # care if we can't actually find the headers (they may have been
    # within ifdefs),

    local s = $(>:G=$(HDRGRIST:E)) ;

    Includes $(<) : $(s) ;
    SEARCH on $(s) = $(HDRSEARCH) ;
    NoCare $(s) ;

    # Propagate on $(<) to $(>)

    HDRSEARCH on $(s) = $(HDRSEARCH) ;
    HDRSCAN on $(s) = $(HDRSCAN) ;
    HDRRULE on $(s) = $(HDRRULE) ;
    HDRGRIST on $(s) = $(HDRGRIST) ;
}


rule InstallInto
{
    # InstallInto dir : sources ;

    local i t ;

    t = $(>:G=$(INSTALLGRIST)) ;

    # Arrange for jam install
    # Arrange for jam uninstall
    # sources are in SEARCH_SOURCE
    # targets are in dir

    Depends install : $(t) ;
    Clean uninstall : $(t) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;
    MakeLocate $(t) : $(<) ;

    # For each source, make gristed target name
    # and Install, Chmod, Chown, and Chgrp

    for i in $(>)
    {
        local tt = $(i:G=$(INSTALLGRIST)) ;

        Depends $(tt) : $(i) ;
        Install $(tt) : $(i) ;
        Chmod $(tt) ;

        if $(OWNER) && $(CHOWN)
        {
        Chown $(tt) ;
        OWNER on $(tt) = $(OWNER) ;
        }

        if $(GROUP) && $(CHGRP)
        {
        Chgrp $(tt) ;
        GROUP on $(tt) = $(GROUP) ;
        }
    }
}

# /InstallBin dir : sources ;
#
# Copy _sources_ into _dir_ with mode $(EXEMODE)
#
rule InstallBin
{
    local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;

    InstallInto $(<) : $(_t) ;
    MODE on $(_t:G=$(INSTALLGRIST)) = $(EXEMODE) ;
}

# /InstallFile dir : sources ;
#
# Copy _sources_ into _dir_ with mode $(FILEMODE)
#
rule InstallFile
{
    InstallInto $(<) : $(>) ;
    MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;
}

# /InstallLib dir : sources ;
#
# Copy _sources_ into _dir_ with mode $(FILEMODE)
#
rule InstallLib
{
    InstallInto $(<) : $(>) ;
    MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;
}

# /InstallLib dir : sources ;
#
#  Copy _sources_ into the appropriate subdirectory of _dir_ with mode
#  $(FILEMODE). The subdirectory is manS, where S is the suffix of each of
#  sources.
#
rule InstallMan
{
    # Really this just strips the . from the suffix

    local i s d ;

    for i in $(>)
    {
        switch $(i:S)
        {
        case .1 : s = 1 ; case .2 : s = 2 ; case .3 : s = 3 ;
        case .4 : s = 4 ; case .5 : s = 5 ; case .6 : s = 6 ;
        case .7 : s = 7 ; case .8 : s = 8 ; case .l : s = l ;
        case .n : s = n ; case .man : s = 1 ;
        }

        d = man$(s) ;

        InstallInto $(d:R=$(<)) : $(i) ;
    }

    MODE on $(>:G=$(INSTALLGRIST)) = $(FILEMODE) ;
}

# /InstallShell dir : sources ;
#
# Copy _sources_ into _dir_ with mode $(SHELLMODE)
#
rule InstallShell
{
    InstallInto $(<) : $(>) ;
    MODE on $(>:G=$(INSTALLGRIST)) = $(SHELLMODE) ;
}

# /Lex source.c : source.l ;
#
# Process the lex source file _source.l_ and rename the lex.yy.c
# to _source.c_ . Called by the @Object rule
#
rule Lex
{
    LexMv $(<) : $(>) ;
    Depends $(<) : $(>) ;
    MakeLocate $(<) : $(LOCATE_SOURCE) ;
    Clean clean : $(<) ;
}

# /Library  library : sources ;
#
#  Compiles _sources_ and archives them into _library_. The intermediate
#  objects are deleted. Calles @Object and @LibraryFromObjects
#
#  If @Library is invoked with no suffix on _library_, the $(SUFLIB)
#  suffix is used
#
rule Library
{
    LibraryFromObjects $(<) : $(>:S=$(SUFOBJ)) ;
    Objects $(>) ;
}

# /SharedLibrary  library : sources : def : import ;
#
# Compiles _sources_ and generates a shared _library_ (i.e. DLL on Windows,
# or shared object on Unix). Calls @SharedObjects and @SharedLibraryFromObjects
#
# If @SharedLibrary is invoked with no suffix on _library_, then
# $(SUFLIBSHR) suffix is used
#
# _def_ is the name of the corresponding definition file used to generate
# the library on Windows and OS/2 (ignored otherwise). If undefined, it
# will default to _library_ with the .def suffix
#
# _import_ is the name of the corresponding import library for Windows
# and OS/2 platforms (ignored otherwise). If undefined, it will default
# to _library_ with the .dll.lib suffix.
#
rule SharedLibrary
{
  SharedLibraryFromObjects $(<) : $(>:S=$(SUFOBJSHR)) : $(3) : $(4) ;
  SharedObjects            $(>) ;
}

if $(UNIX)
{
  # this rule is used to find the 'libtool' script in the current
  # path, this is required when compiling shared objects on Unix
  #
  rule LibToolFind
  {
    if $(LIBTOOL) { return $(LIBTOOL) ; }

    local  matches = [ Glob $(PATH) : libtool ] ;

    if ! $(matches)
    {
      Exit "could not find 'libtool' program in current path. Aborting !" ;
    }
    LIBTOOL = $(matches[1]) ;

    return $(LIBTOOL) ;
  }
}

# /LibraryFromObjects library : objects ;
#
# Archives _objects_ into _library_. The _objects_ are then deleted
#
# If _library_ has no suffix, the $(SUFLIB) suffix is used
#
# Called by @Library rule. Most people should never call this rule
# directly.
#
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) || $(NOARUPDATE) ) { RmTemps $(_l) : $(_s) ; }
}


# /SharedLibraryFromObjects  library : objects : def : import ;
#
# Equivalent of @LibraryFromObjects for shared libraries.
#
# Called by @SharedLibrary. Most people shouldn't call this rule
# directly
#
rule SharedLibraryFromObjects
{
    local _i _l _s ;

    # Add grist to file names

    _s = [ FGristFiles $(>) ] ;
    _l = $(<:S=$(SUFLIBSHR)) ;

    #Echo "Library is $(_l)"    ;
    # 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) : $(LOCATE_TARGET) ;
    }

    # we never scan shared libraries for member objects
    Depends $(_l) : $(_s) ;

    Clean clean : $(_l) ;

    # I don't know if VMS supports shared libraries, so I prefer
    # to disable the following right now
    #
    #if $(CRELIB) { CreLib $(_l) : $(_s[1]) ; }

    # creating the library is so much fun on Unix :-)
    if $(UNIX)
    {
      local  libtool = [ LibToolFind ] ;  # find the right libtool

      AR on $(_l) = "$(libtool) --mode=link $(AR)" ;
    }
    else if $(NT)
    {
      local  _implib = $(4) ;
      local  _def    = $(3) ;

      _implib ?= $(_l:S=$(SUFLIBSHR)$(SUFLIB)) ;
      _def    ?= $(_l:S=.def) ;

      Clean    clean : $(_implib) ;
      Depends  lib   : $(_implib) $(_def) ;

      Depends $(_implib) : $(_def) $(_l) ;
      Depends $(_l)      : $(_def) ;

      DEFFILENAME on $(_l) = $(_def) ;
      IMPLIBNAME  on $(_l) = $(_implib) ;

      MakeLocate $(_implib)        : $(LOCATE_TARGET) ;
      MakeLocate $(_implib:S=.exp) : $(LOCATE_TARGET) ;

      if $(JAM_TOOLSET) in VISUALC BORLANDC LCC WATCOM DIGITALMARS
      {
        SharedLink-$(JAM_TOOLSET) $(_l) : $(_s) : $(_implib) : $(_def) ;
      }

      DllLink $(_l) : $(_s) ;
    }
    else
    {
      Echo "Sorry, I don't know how to make a shared library on your system" ;
      Exit "Please contact the FTJam maintainer for help" ;
    }
}


# Since building shared libraries is so different depending on the
# compiler being used, I've broken this task into compiler-specific
# ones
#
#

# This contains the Visual C++ specific rule used to build a DLL
# and its import library
#
rule SharedLink-VISUALC
{
  # get rid of the '.exp' file when cleaning
  #
  Clean  clean : $(3:S=.exp) ;
}


rule SharedLink-BORLANDC
{
  local  _deffile = $(4) ;
  local  _implib  = $(3) ;

  LINKFLAGS on $(<) += /x /Gn /Tpd ;
  LINKLIBS on $(<) = $(LINKLIBS) $(ILINKLIBS) ;

  # Generate import library with the IMPLIB tool !!
  #
  DllImplib $(_implib) : $(<) ;
  Depends   $(_implib) : $(_deffile) $(<) ;
  Depends   lib        : $(_implib) ;

  DEFFILENAME on $(_implib) = $(_deffile) ;

  # clean the TDS file, since the compiler refuses to not generate it !
  MakeLocate $(<:S=.tds) : $(LOCATE_TARGET) ;
  Clean  clean : $(<:S=.tds) ;
}


rule SharedLink-LCC
{
  if "" {
  #Echo "Sorry, but generating DLLs with LCC is not supported. That's" ;
  #Echo "because the 'lcclnk' tool that comes with this compiler is" ;
  #Echo "unreliable and doesn't work as expected. For more information" ;
  #Echo "contact the FT-Jam maintainer" ;
  #Exit ;
  }

  # the 'lcclnk' tool is absolutely broken:
  #   - its -o flag doesn't work when there is a LIBRARY statement
  #     in the .def file.
  #
  #   - it uses the LIBRARY name in the .def file to determine
  #     the name of the dll and its import library, and always
  #     places them in the current directory !!
  #
  #   - if there is no LIBRARY statement, the -o flag is only
  #     used to determine where the DLL is placed, the import
  #     library will always be placed in the current directory !!
  #

  # clean the .exp file too, don't know how to get rid of it
  Clean clean : $(4:S=.exp) ;
}


rule SharedLink-WATCOM
{
  #Echo "Sorry, but building DLLs with Watcom isn't supported by this tool" ;
  #Echo "this comes from the fact that the Watcom linker isn't capable of"  ;
  #Echo "using normal .def files" ;
  #Exit ;

  local  _deffile = $(4) ;
  local  _implib  = $(3) ;

  IMPLIB on $(<) = $(_implib) ;
  DEFFILE on $(<) = $(_deffile) ;

  # clean the TDS file, since the compiler refuses to not generate it !
  MakeLocate $(<:S=.tds) : $(LOCATE_TARGET) ;
  Clean  clean : $(<:S=.tds) ;
}


rule SharedLink-DIGITALMARS
{
  #Echo "Sorry, but building DLLs with Digital Mars isn't supported at the" ;
  #Echo "moment, please contact the FT-Jam maintainers" ;
  #Exit ;
}


# /Link  image : objects ;
#
# Links _image_ from _objects_ and sets permissions on _image_ to
# $(EXEMODE). _image_ must be an actual filename; suffix is not
# supplied.
#
# Called by @Main, shouldn't be called by most people
#
rule Link
{
    MODE on $(<) = $(EXEMODE) ;
    Chmod $(<) ;
}

# /LinkLibraries image : libraries ;
#
# Makes _image_ depend on _libraries_ and includes them during linking
#
# _image_ may be referenced without a suffix in this rule invocation.
# @LinkLibraries supplies the suffix
#
# You should only use this rule with libraries created through the
# @Library rule. For external libraries, use something else (XXX)
#
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)) ;
}

# /LinkSharedLibraries image : libraries :
#
# Same as @LinkLibraries, but to link _image_ with shared libraries
# generated through the @SharedLibrary rule
#
rule LinkSharedLibraries
{
    # make library dependencies of target
    # set NEEDLIBS variable used by 'actions Main'

    local _t   = [ FAppendSuffix $(<) : $(SUFEXE) ] ;
    local _ext = $(SUFLIBSHR) ;

    if $(NT) || $(OS2)
    {
      # on NT or OS/2, we need to link agains the import library,
      # not the DLL itself !!
      #
      _ext = $(SUFLIBSHR)$(SUFLIB) ;
    }
    Depends $(_t) : $(>:S=$(_ext))  ;
    NEEDLIBS on $(_t) += $(>:S=$(_ext)) ;
}

# /Main image : sources ;
#
# Compiles _sources_ and links them into _image_. Calls @Objects and
# @MainFromObjects.
#
# _image_ may be supplied without suffix.
#
rule Main
{
    MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;
    Objects $(>) ;
}

# /MainFromObjects image : objects ;
#
# Links _objects_ into _image_. Dependency of exe.
# @MainFromObjects provides a default suffix for _image_
#
rule MainFromObjects
{
    local _s _t ;

    # Add grist to file names
    # Add suffix to exe

    _s = [ FGristFiles $(>) ] ;
    _t = [ FAppendSuffix $(<) : $(SUFEXE) ] ;

    # so 'jam foo' works when it's really foo.exe

    if $(_t) != $(<)
    {
        Depends $(<) : $(_t) ;
        NotFile $(<) ;
    }

    # make compiled sources a dependency of target

    Depends exe : $(_t) ;
    Depends $(_t) : $(_s) ;
    MakeLocate $(_t) : $(LOCATE_TARGET) ;

    Clean clean : $(_t) ;

    # special case for stupid Borland C++, which always generates a
    # .tds file for executables, even when no debug information is needed
    #
    if $(JAM_TOOLSET) = BORLANDC {
      MakeLocate $(_t:S=.tds) : $(LOCATE_TARGET) ;
      Clean  clean : $(_t:S=.tds) ;
    }

    Link $(_t) : $(_s) ;
}

# /MakeLocate  targets : directory
#
# Creates _dir_ and causes _target_ to be built into _dir_
#
# This is done by setting the target-specific variable LOCATE
# on _targets_, and arranges with @MkDir to create the target
# directory
#
rule MakeLocate
{
    # Note we grist the directory name with 'dir',
    # so that directory path components and other
    # targets don't conflict.

    if $(>)
    {
        LOCATE on $(<) = $(>) ;
        Depends $(<) : $(>[1]:G=dir) ;
        MkDir $(>[1]:G=dir) ;
    }
}

# /MkDir  dir ;
#
# Creates _dir_ and its parent directories
#
rule MkDir
{
    # Ignore timestamps on directories: we only care if they
    # exist.

    NoUpdate $(<) ;

    # Don't create . or any directory already created.

    if $(<:G=) != $(DOT) && ! $($(<)-mkdir)
    {
        # Cheesy gate to prevent multiple invocations on same dir
        # Arrange for jam dirs
        # MkDir1 has the actions

        $(<)-mkdir = true ;
        Depends dirs : $(<) ;
        MkDir1 $(<) ;

        # Recursively make parent directories.
        # $(<:P) = $(<)'s parent, & we recurse until root

        local s = $(<:P) ;

        # Don't try to create A: or A:\ on windows

        if $(NT)
        {
            switch $(s)
            {
                case *:   : s = ;
                case *:\\ : s = ;
            }
        }

        # handle "C:", "C:/", "/cygdrive" and "/cygdrive/" in Cygwin
        if $(UNIX) && $(OS) = CYGWIN
        {
            switch $(s)
            {
              case ?:   : s = ;
              case ?:/  : s = ;
              case <dir>/cygdrive   : s = ;
              case <dir>/cygdrive/  : s = ;
            }
        }

        if $(s) = $(<)
        {
        # The parent is the same as the dir.
        # We're at the root, which some OS's can't stat, so we mark
        # it as NotFile.

            NotFile $(s) ;
        }
        else if $(s:G=)
        {
        # There's a parent; recurse.

        Depends $(<) : $(s) ;
        MkDir $(s) ;
        }
    }
}

# /Object object : source ;
#
# Compile s a single _source_ file into _object_. The @Main and @Library
# rules use it to compile sources.
#
# Causes _source_ to be scanned for #include directives and calls @HdrRule
# to make all included files dependencies of _object_.
#
# Calls one of the following rules depending on the suffix to do the
# actual compilation:
#
rule Object
{
    # locate object and search for source, if wanted

    Clean clean : $(<) ;

    MakeLocate $(<) : $(LOCATE_TARGET) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;

    # Save HDRS for -I$(HDRS) on compile.
    # We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers
    # in the .c file's directory, but generated .c files (from
    # yacc, lex, etc) are located in $(LOCATE_TARGET), possibly
    # different from $(SEARCH_SOURCE).

    HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ;

    # handle #includes for source: Jam scans for headers with
    # the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE)
    # with the scanned file as the target and the found headers
    # as the sources.  HDRSEARCH is the value of SEARCH used for
    # the found header files.  Finally, if jam must deal with
    # header files of the same name in different directories,
    # they can be distinguished with HDRGRIST.

    # $(SEARCH_SOURCE:E) is where cc first looks for #include
    # "foo.h" files.  If the source file is in a distant directory,
    # look there.  Else, look in "" (the current directory).

    HDRRULE on $(>) = HdrRule ;
    HDRSCAN on $(>) = $(HDRPATTERN) ;
    HDRSEARCH on $(>) =
        $(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(STDHDRS) ;

    HDRGRIST on $(>) = $(HDRGRIST) ;

    # propagate target specific-defines

    DEFINES on $(<) += $(DEFINES) ;

    # if source is not .c, generate .c with specific rule

    switch $(>:S)
    {
        case .asm : As $(<) : $(>) ;
        case .c :   Cc $(<) : $(>) ;
        case .C :   C++ $(<) : $(>) ;
        case .cc :  C++ $(<) : $(>) ;
        case .cpp : C++ $(<) : $(>) ;
        case .cxx : C++ $(<) : $(>) ;
        case .c++ : C++ $(<) : $(>) ;
        case .C++ : C++ $(<) : $(>) ;
        case .d :   Dc $(<) : $(>) ;
        case .f :   Fortran $(<) : $(>) ;
        case .l :   Cc $(<) : $(<:S=.c) ;
                    Lex $(<:S=.c) : $(>) ;
        case .s :   As $(<) : $(>) ;
        case .y :   Cc $(<) : $(<:S=$(YACCGEN)) ;
                    Yacc $(<:S=$(YACCGEN)) : $(>) ;
        case * :    UserObject $(<) : $(>) ;
    }
}

# /ObjectCcFlags  sources : flags ;
#
# this rule is used to add compiler flags to the compilation of
# specific C sources files.
#
rule ObjectCcFlags
{
    CCFLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;
}

# /ObjectC++Flags  sources : flags ;
#
# this rule is used to add compiler flags to the compilation of
# specific C++ source files
#
rule ObjectC++Flags
{
    C++FLAGS on [ FGristFiles $(<:S=$(SUFOBJ)) ] += $(>) ;
}

# /ObjectDefines  objects : macros ;
#
# this rule is used to add macro defines to the compilation of
# specific C and C++ source files
#
rule ObjectDefines
{
    # must reformat CCDEFS according to current defines

    local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;

    DEFINES on $(s) += $(>) ;
    CCDEFS on $(s) = [ on $(s) FDefines $(DEFINES) ] ;
}

# /ObjectHdrs  sources : paths ;
#
# this rule is used to add include paths to the compilation of
# specific C and C++ source files
#
rule ObjectHdrs
{
    # Add to HDRS for HdrScan's benefit.
    # must reformat CCHDRS according to headers

    local s = [ FGristFiles $(<:S=$(SUFOBJ)) ] ;

    HDRS on $(s) += $(>) ;
    CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] ;
}

# /Objects sources ;
#
# this rule is used to compile one or more sources into object files.
# do not call it directly, it is used by the Main and Library rules
# automatically
#
rule Objects
{
    local _i ;

    for _i in [ FGristFiles $(<) ]
    {
        Object $(_i:S=$(SUFOBJ)) : $(_i) ;
        Depends obj : $(_i:S=$(SUFOBJ)) ;
    }
}

# /SharedObjects
#
# this rule is used to compile one or more sources into 'shared object
# files'. This means object files used to build either DLLs or Unix shared
# libraries.
#
# do not call this rule directly, it is called by SharedLibrary automatically
#
rule SharedObjects
{
  # temporarily override SUFOBJ with $(SUFOBJSHR) to
  #
  local SUFOBJ = $(SUFOBJSHR) ;

  # call the normal Objects rule
  #
  Objects $(<) ;

  # add the compiler-specific position-independent-code flag
  # where needed
  #
  ObjectCcFlags $(<) : $(PICFLAGS) ;

  # change the compiler invokation for all these objects
  # to use Libtool on Unix systems. We explicitely disable the
  # generation of static objects here
  #
  if $(UNIX)
  {
    libtool on $(<:S=$(SUFOBJ)) = [ LibToolFind ] ;
    CC on $(<:S=$(SUFOBJ))      = "$(libtool) --mode=compile $(CC) -dynamic" ;
  }
}


rule RmTemps
{
    Temporary $(>) ;
}

rule Setuid
{
    MODE on [ FAppendSuffix $(<) : $(SUFEXE) ] = 4711 ;
}

rule Shell
{
    Depends shell : $(<) ;
    Depends $(<) : $(>) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;
    MODE on $(<) = $(SHELLMODE) ;
    Clean clean : $(<) ;
    Chmod $(<) ;
}

rule SoftLink
{
    Depends files : $(<) ;
    Depends $(<) : $(>) ;
    SEARCH on $(>) = $(SEARCH_SOURCE) ;
    Clean clean : $(<) ;
}

rule SubDir
{
    #
    # SubDir TOP d1 d2 ... ;
    #
    # Support for a project tree spanning multiple directories.
    #
    # SubDir declares a Jamfile's location in a project tree, setting
    # Jambase variables (SEARCH_SOURCE, LOCATE_TARGET) so that source
    # files can be found.
    #
    # TOP is a user-select variable name for root of the tree, and
    # d1 d2 ...  are the directory elements that lead from the root
    # of the tree to the directory of the Jamfile.
    #
    # TOP can be set externally, but normally the first SubDir call
    # computes TOP as the path up from the current directory; the
    # path contains one ../ for each of d1 d2 ...
    #
    # SubDir reads once the project-specific rules file Jamrules
    # in the TOP directory, if present.  This can be overridden
    # with the variable TOPRULES.
    #
    # SubDir supports multiple, overlaid project trees:  SubDir
    # invocations with different TOPs can appear in the same Jamfile.
    # The location established by the first SubDir call is used set
    # the TOPs for the subsequent SubDir calls.
    #
    # SubDir's public variables:
    #
    #   $(TOP) = path from CWD to root.
    #   $(SUBDIR) = path from CWD to the directory SubDir names.
    #   $(SUBDIR_TOKENS) = path from $(TOP) to $(SUBDIR) as dir names
    #   $(SEARCH_SOURCE) = $(SUBDIR)
    #   $(LOCATE_SOURCE) = $(ALL_LOCATE_TARGET) $(SUBDIR)
    #   $(LOCATE_TARGET) = $(ALL_LOCATE_TARGET) $(SUBDIR)
    #   $(SOURCE_GRIST) = $(SUBDIR_TOKENS) with !'s
    #

    local _top = $(<[1]) ;
    local _tokens = $(<[2-]) ;

    #
    # First time through sets up relative root and includes Jamrules.
    #

        if ! $(_top)
        {
            Exit SubDir syntax error ;
        }

    if ! $($(_top)-SET)
    {
        $(_top)-SET = true ;

        # First time we've seen this TOP.
        # We'll initialize a number of internal variables:
        #
        #   $(TOP-UP) = directories from ROOT to a common point
        #   $(TOP-DOWN) = directories from common point to TOP
        #   $(TOP-ROOT) = root directory for UP/DOWN -- normally CWD
        #   $(SUBDIR_UP) = current value of $(TOP-UP)
        #   $(SUBDIR_DOWN) = current value of $(TOP-DOWN)
        #   $(SUBDIR_ROOT) = current value of $(TOP-ROOT)
        #

        if $($(_top))
        {
            # TOP externally set.
            # We'll ignore the relative (UP/DOWN) path that
            # got us here, and instead remember the hard ROOT.

            $(_top)-UP = ;
            $(_top)-DOWN = ;
            $(_top)-ROOT = $($(_top)) ;
        }
        else
    {
        # TOP not preset.

        # Establishing a new TOP.  In the simplest case,
        # (SUBDIR_UP/SUBDIR_DOWN/SUBDIR_ROOT unset), it's
        # merely a certain number of directories down from
        # the current directory, and FSubDirPath will set
        # TOP to a path consisting of ../ for each of the
        # elements of _tokens, because that represents how
        # far below TOP the current directory sits.
        #
        # In the more complicated case, the starting directory
        # isn't the directory of jam's invocation but an
        # location established by previous SubDir call.  The
        # starting directory is SUBDIR_UP directories up from
        # SUBDIR_ROOT, and then SUBDIR_DOWN directories down
        # from that.   If SUBDIR_ROOT is not set, that means
        # SUBDIR_DOWN and SUBDIR_UP represent the path from
        # the directory of jam's invocation.
        #
        # In the most complicated case, the _tokens also
        # represents directories down, because TOP is being
        # estalished in a directory other than TOP's root.
        # Hopefully, _tokens and SUBDIR_DOWN represent the
        # same final directory, relative to the new TOP and
        # the previous SubDIr's TOP.  To find the new TOP,
        # we have to chop off any common directories from
        # then ends of _tokens and SUBDIR_DOWN.  To do so,
        # we reverse each of them, call FStripCommon to
        # remove the initial common elements, and then
        # reverse them again.  After this process, if
        # both _tokens and SUBDIR_DOWN have elements, it
        # means the directory names estalished by the two
        # SubDir calls don't match, and a warning is issued.
        # All hell will likely break loose at this point,
        # since the whole SubDir scheme relies on the SubDir
        # calls accurately naming the current directory.

        # Strip common trailing elements of _tokens and SUBDIR_DOWN.

        _tokens = [ FReverse $(_tokens) ] ;
        SUBDIR_DOWN = [ FReverse $(SUBDIR_DOWN) ] ;
        FStripCommon _tokens : SUBDIR_DOWN ;
        SUBDIR_DOWN = [ FReverse $(SUBDIR_DOWN) ] ;
        _tokens = [ FReverse $(_tokens) ] ;

        if $(SUBDIR_DOWN) && $(_tokens)
        {
            Echo Warning: SubDir $(<) misplaced! ;
        }

        # We'll remember the relative (UP/DOWN) path that
        # got us here, plus any hard ROOT starting point
        # for the UP/DOWN.  If TOP is never set externally,
        # ROOT will always be "" (directory of jam's invocation).

        $(_top)-UP = $(SUBDIR_UP) $(_tokens) ;
        $(_top)-DOWN = $(SUBDIR_DOWN) ;
        $(_top)-ROOT = $(SUBDIR_ROOT:E="") ;
        $(_top) = [ FSubDirPath $(_top) ] ;
        }

        # Set subdir vars for the inclusion of the Jamrules,
        # just in case they have SubDir rules of their own.
        # Note that SUBDIR_DOWN is empty: it's all the way
        # up where the Jamrules live.  These gets overrided
        # just after the inclusion.

        SUBDIR_UP = $($(_top)-UP) ;
        SUBDIR_DOWN = ;
        SUBDIR_ROOT = $($(_top)-ROOT) ;

        # Include $(TOPRULES) or $(TOP)/Jamrules.
        # Include $(TOPRULES) if set.
        # Otherwise include $(TOP)/Jamrules if present.

        if $($(_top)RULES) {
            include $($(_top)RULES) ;
        } else {
            NoCare $(JAMRULES:R=$($(_top)):G=$(_top)) ;
            include $(JAMRULES:R=$($(_top)):G=$(_top)) ;
        }
    }

    # Get path from $(TOP) to named directory.
    # Save dir tokens for other potential uses.

    SUBDIR_UP = $($(_top)-UP) ;
    SUBDIR_DOWN = $($(_top)-DOWN) $(_tokens) ;
    SUBDIR_ROOT = $($(_top)-ROOT) ;
    SUBDIR_TOKENS = $(SUBDIR_DOWN) ;

    SUBDIR = [ FSubDirPath $(<) ] ;

    # 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 $(SUBDIR_TOKENS) ] ;

    # Reset per-directory ccflags, hdrs, etc,
    # listed in SUBDIRRESET.
    # Note use of variable expanded assignment var

    SUBDIR$(SUBDIRRESET) = ;

    # Invoke user-specific SubDir extensions,
    # rule names listed in SUBDIRRULES.
    # Note use of variable expanded rule invocation

    $(SUBDIRRULES) $(<) ;
}

rule FSubDirPath
{
    # FSubDirPath TOP d1 ... ;

    # Returns path to named directory.

    # If jam is invoked in a subdirectory of the TOP, then we
    # need to prepend a ../ for every level we must climb up
    # (TOP-UP), and then append the directory names we must
    # climb down (TOP-DOWN), plus the named directories d1 ...
    # If TOP was set externally, or computed from another TOP
    # that was, we'll have to reroot the whole thing at TOP-ROOT.

    local _r = [ FRelPath $($(<[1])-UP) : $($(<[1])-DOWN) $(<[2-]) ] ;

    return $(_r:R=$($(<[1])-ROOT)) ;
}

rule SubDirDcFlags
{
    SUBDIRDCFLAGS += $(<) ;
}

rule SubDirCcFlags
{
    SUBDIRCCFLAGS += $(<) ;
}

rule SubDirC++Flags
{
    SUBDIRC++FLAGS += $(<) ;
}

rule SubDirHdrs
{
    SUBDIRHDRS += [ FDirName $(<) ] ;
}

rule SubInclude
{
    # SubInclude TOP d1 ... ;
    #
    # Include a subdirectory's Jamfile.

    # We use SubDir to get there, in case the included Jamfile
    # either doesn't have its own SubDir (naughty) or is a subtree
    # with its own TOP.

    if ! $($(<[1]))
    {
        Exit SubInclude $(<[1]) without prior SubDir $(<[1]) ;
    }

    SubDir $(<) ;

    include $(JAMFILE:D=$(SUBDIR)) ;
}

rule SubRules
{
    # SubRules TOP d1 ... : Other-TOP ;
    #
    # Read another tree's Jamrules, by giving it's path according
    # to this tree and it's own name.

    if ! $($(<[1]))
    {
        Exit SubRules $(<[1]) without prior SubDir $(<[1]) ;
    }

    SubDir $(<) ;
    SubDir $(>) ;
}

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
#

# /FGrist path to file ;
#
# Returns a single string that is used as grist
#
rule FGrist
{
    return $(<:J=!) ;

}


rule FGristFiles
{
    return $(<:G=$(SOURCE_GRIST:E)) ;
}


rule FGristSourceFiles
{
    # Produce source file name name with grist in it,
    # if SOURCE_GRIST is set.

    # Leave header files alone, because they have a global
    # visibility.

    if ! $(SOURCE_GRIST)
    {
        return $(<) ;
    }
    else
    {
        local _i _o ;

        for _i in $(<)
        {
        switch $(_i)
        {
        case *.h :  _o += $(_i) ;
        case * :    _o += $(_i:G=$(SOURCE_GRIST)) ;
        }
        }

        return $(_o) ;
    }
}


rule FReverse
{
    if $(1) { return [ FReverse $(1[2-]) ] $(1[1]) ; }
}


rule FSubDir
{
    # If $(>) is the path to the current directory, compute the
    # path (using ../../ etc) back to that root directory.
    # Sets result in $(<)

    if ! $(<[1])
    {
        return $(DOT) ;
    }
    else
    {
        local _i _d ;

        _d = $(DOTDOT) ;

        for _i in $(<[2-])
        {
        _d = $(_d:R=$(DOTDOT)) ;
        }

        return $(_d) ;
    }
}


rule FStripCommon
{
    # FStripCommon v1 : v2 ;

    # Strip common initial elements of variables v1 and v2.
    # Modifies the variable values themselves.

    if $($(<)[1]) && $($(<)[1]) = $($(>)[1])
    {
        $(<) = $($(<)[2-]) ;
        $(>) = $($(>)[2-]) ;
        FStripCommon $(<) : $(>) ;
    }
}


rule FRelPath
{
    local _l _r ;

    # first strip off common parts

    _l = $(<) ;
    _r = $(>) ;

    FStripCommon _l : _r ;

    # now make path to root and path down

    _l = [ FSubDir $(_l) ] ;
    _r = [ FDirName $(_r) ] ;

    # Concatenate and save

    # XXX This should be better

    if $(_r) = $(DOT) {
        return $(_l) ;
    } else {
        return $(_r:R=$(_l)) ;
    }
}


rule FAppendSuffix
{
       # E.g., "FAppendSuffix yacc lex foo.bat : $(SUFEXE) ;"
       # returns (yacc,lex,foo.bat) on Unix and
       # (yacc.exe,lex.exe,foo.bat) on NT.

    if $(>)
    {
        local _i _o ;

        for _i in $(<)
        {
        if $(_i:S)
        {
            _o += $(_i) ;
        }
        else
        {
            _o += $(_i:S=$(>)) ;
        }
        }
        return $(_o) ;
    }
    else
    {
        return $(<) ;
    }
}

#
# Operating system specific utility rules
# First, the (generic) UNIX versions
#

rule FQuote { return "\\\"$(<)\\\"" ; }
rule FDefines { return -D$(<) ; }
rule FIncludes { return -I$(<) ; }

rule FDirName
{
    # Turn individual elements in $(<) into a usable path.

    local _i ;
    local _s = $(DOT) ;

    for _i in $(<)
    {
        _s = $(_i:R=$(_s)) ;
    }

    return $(_s) ;
}

if $(OS2)
{
    rule FQuote { return "\"$(<)\"" ; }
    rule FIncludes { return /I$(<) ; }
}
else if $(NT) && $(JAM_TOOLSET) != MINGW && $(JAM_TOOLSET) != LCC
{
    rule FDefines { return /D$(<) ; }
    rule FIncludes { return /I$(<) ; }
}

else if $(MAC)
{
    rule FQuote { return "\"$(<)\"" ; }
    rule FDefines { return "-define '$(<)'" ; }
    rule FIncludes { return "\"$(<:J=,)\"" ; }
}

else if $(VMS)
{
    rule FQuote { return "\"\"\"$(<)\"\"\"" ; }
    rule FDefines { return "/define=( $(<:J=,) )" ; }
    rule FIncludes { return "/inc=( $(<:J=,) )" ; }

    rule FDirName
    {
        local _s _i ;

        # Turn individual elements in $(<) into a usable path.

        if ! $(<)
        {
            _s = $(DOT) ;
        }
        else
        {
            # This handles the following cases:
            #   a -> [.a]
            #   a b c -> [.a.b.c]
            #   x: -> x:
            #   x: a -> x:[a]
            #   x:[a] b -> x:[a.b]

            switch $(<[1])
            {
                case *:* :     _s = $(<[1]) ;
                case \\[*\\] : _s = $(<[1]) ;
                case * :       _s = [.$(<[1])] ;
            }

            for _i in [.$(<[2-])]
            {
                _s = $(_i:R=$(_s)) ;
            }
        }

        return $(_s) ;
    }
}

#
# Actions
#

#
# First the defaults
#

actions updated together piecemeal Archive
{
    $(AR) $(<) $(>)
}

actions As
{
    $(AS) $(ASFLAGS) $(ASHDRS) -o $(<) $(>)
}

actions C++
{
    $(C++) -c -o $(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
}

actions Cc
{
    $(CC) -c -o $(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
}

actions Dc
{
    $(DC) -c -of$(<) $(DCFLAGS) $(DOPTIM) $(>)
}

actions Chgrp
{
    $(CHGRP) $(GROUP) $(<)
}

actions Chmod1
{
    $(CHMOD) $(MODE) $(<)
}

actions Chown
{
    $(CHOWN) $(OWNER) $(<)
}

actions piecemeal together existing Clean
{
    $(RM) $(>)
}

actions File
{
    $(CP) $(>) $(<)
}

actions GenFile1
{
    $(>[1]) $(<) $(>[2-])
}

actions Fortran
{
    $(FORTRAN) $(FORTRANFLAGS) -o $(<) $(>)
}

actions HardLink
{
    $(RM) $(<) && $(LN) $(>) $(<)
}

actions Install
{
    $(CP) $(>) $(<)
}

actions Lex
{
    $(LEX) $(>)
}

actions LexMv
{
    $(MV) lex.yy.c $(<)
}

actions Link bind NEEDLIBS
{
    $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
}

actions MkDir1
{
    $(MKDIR) $(<)
}

actions together Ranlib
{
    $(RANLIB) $(<)
}

actions quietly updated piecemeal together RmTemps
{
    $(RM) $(>)
}

actions Shell
{
    $(AWK) '
        NR == 1 { print "$(SHELLHEADER)" }
        NR == 1 && /^[#:]/ { next }
        /^##/ { next }
        { print }
    ' < $(>) > $(<)
}

actions SoftLink
{
    $(RM) $(<) && $(LN) -s $(>) $(<)
}

actions Yacc1
{
    $(YACC) $(YACCFLAGS) $(>)
}

actions YaccMv
{
    $(MV) $(YACCFILES).c $(<[1])
    $(MV) $(YACCFILES).h $(<[2])
}

#
# RELOCATE - for compilers with broken -o flags
#

if $(RELOCATE)
{
    actions C++
    {
    $(C++) -c $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
    }

    actions Cc
    {
    $(CC) -c $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
    }

    actions ignore CcMv
    {
    [ $(<) != $(>:BS=$(SUFOBJ)) ] && $(MV) $(>:BS=$(SUFOBJ)) $(<)
    }
}

#
# NOARUPDATE - can't update an archive
#

if $(NOARUPDATE)
{
    actions Archive
    {
        $(AR) $(<) $(>)
    }
}

#
# UNIX specific actions
#

if $(UNIX)
{
    actions GenFile1
    {
    PATH="$PATH:."
    $(>[1]) $(<) $(>[2-])
    }
}

#
# NT specific actions
#

if $(NT)
{
    if $(JAM_TOOLSET) = VISUALC || $(JAM_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 /Fo$(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) $(>)
        }

        actions C++
        {
            $(C++) /c /Fo$(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) /I$(STDHDRS) /Tp$(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) /DLL /DEF:$(DEFFILENAME) /IMPLIB:$(IMPLIBNAME) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }
    }
    else if $(JAM_TOOLSET) = VISUALC16
    {
        actions updated together piecemeal Archive
        {
            $(AR) $(<) -+$(>)
        }

        actions Cc
        {
            $(CC) /c /Fo$(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }

        actions C++
        {
            $(C++) /c /Fo$(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) /Tp$(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) /DLL /DEF:$(DEFFILENAME) /IMPLIB:$(IMPLIBNAME) /out:$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }
    }
    else if $(JAM_TOOLSET) = BORLANDC
    {
        actions updated together piecemeal Archive
        {
            $(AR) $(<) -+$(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) -e$(<) $(LINKFLAGS) $(UNDEFS) -L$(LINKLIBS) $(NEEDLIBS) $(>)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME
        {
            $(ILINK) $(LINKFLAGS) $(>) , $(<) ,, $(LINKLIBS:E) $(NEEDLIBS:E) , $(DEFFILENAME)
        }

        actions DllImplib bind DEFFILENAME
        {
            $(IMPLIB) -a $(<) $(>) $(DEFFILENAME)
        }

        actions C++
        {
            $(C++) -c -o$(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }

        actions Cc
        {
            $(CC) -c -o$(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }
    }
    else if $(JAM_TOOLSET) = MINGW
    {
        actions together piecemeal Archive
        {
            $(AR) $(<) $(>:T)
        }

        actions Cc
        {
            $(CC) -c -o $(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) -I$(STDHDRS) $(>)
        }

        actions C++
        {
            $(C++) -c -o $(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) -I$(STDHDRS) $(>)
        }

        actions DllLink bind DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) -shared -o $(<) $(>) $(DEFFILENAME) -Wl,--out-implib,$(IMPLIBNAME)
        }
    }
    else if $(JAM_TOOLSET) = WATCOM
    {
        actions together piecemeal Archive
        {
            $(AR) -q $(<) +-$(>)
        }

        actions Cc
        {
            $(CC) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) /Fo=$(<) -I$(STDHDRS) $(>)
        }

        actions C++
        {
            $(C++) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) /Fo=$(<) -I$(STDHDRS) $(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) -l=NT_DLL -"export=$(DEFFILENAME) option implib=$(IMPLIBNAME)" /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions Shell
        {
            $(CP) $(>) $(<)
        }
    }
    else if $(JAM_TOOLSET) = LCC
    {
        actions together piecemeal Archive
        {
            $(AR) /out:$(<) $(>)
        }

        actions Cc
        {
            $(CC) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) -Fo$(<) -I$(STDHDRS) $(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) -o $(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME
        {
            $(LINK) $(LINKFLAGS) -DLL -o $(<) $(UNDEFS) $(>) $(DEFFILENAME) $(NEEDLIBS) $(LINKLIBS)
        }

        actions ignore DllLinkMv
        {
            $(MV) $(2) $(1)
        }

        actions Shell
        {
            $(CP) $(>) $(<)
        }
    }
    else if $(JAM_TOOLSET) = DIGITALMARS
    {
        actions together piecemeal Archive
        {
            $(AR) $(<) $(>)
        }

        actions Cc
        {
            $(CC) -c $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) -o$(<) -I$(STDHDRS) $(>)
        }

        actions C++
        {
            $(C++) -c $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) -o$(<) -I$(STDHDRS) $(>)
        }

        # note: we don't generate MAP files here !
        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) $(>),$(<),, $(NEEDLIBS) $(LINKLIBS)
        }

        # note: we don't generate MAP files here !
        actions DllLink bind NEEDLIBS DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) /IMPLIB:$(IMPLIBNAME) $(>) , $(<) ,NUL, $(LINKLIBS:E) $(NEEDLIBS:E) , $(DEFFILENAME)
        }

        actions Shell
        {
            $(CP) $(>) $(<)
        }
    }
    else if $(JAM_TOOLSET) = PELLESC
    {
        actions together piecemeal Archive
        {
            $(AR) /OUT:$(<) $(>)
        }

        actions Cc
        {
            $(CC) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS)   /Fo $(<) -I$(STDHDRS)  $(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) $(LINKFLAGS) /OUT:$(<) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions DllLink bind NEEDLIBS DEFFILENAME IMPLIBNAME
        {
            $(LINK) $(LINKFLAGS) /DLL /DEF:$(DEFFILENAME) /IMPLIB:$(IMPLIBNAME) /OUT:$(<) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions Shell
        {
            $(CP) $(>) $(<)
        }
    }
}

#
# OS2 specific actions
#

else if $(OS2)
{
    if $(JAM_TOOLSET) = WATCOM
    {
        actions together piecemeal Archive
        {
            $(AR) -q $(<) +-$(>)
        }

        actions Cc
        {
            $(CC) /Fo=$(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }

        actions C++
        {
            $(C++) /Fo=$(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }

        actions Link bind NEEDLIBS
        {
            $(LINK) -q $(LINKFLAGS) /Fe=$(<) $(UNDEFS) $(>) $(NEEDLIBS) $(LINKLIBS)
        }

        actions Shell
        {
            $(CP) $(>) $(<)
        }
    }
    else if $(JAM_TOOLSET) = EMX
    {
        actions together piecemeal Archive
        {
          $(AR) $(<) $(>:T)
        }

        actions Cc
        {
            $(CC) -c -o $(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }

        actions C++
        {
            $(C++) -c -o $(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
        }
    }
}

#
# VMS specific actions
#

else if $(VMS)
{
    actions updated together piecemeal Archive
    {
        lib/replace $(<) $(>[1]) ,$(>[2-])
    }

    actions Cc
    {
        $(CC)/obj=$(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
    }

    actions C++
    {
        $(C++)/obj=$(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(CCHDRS) $(>)
    }

    actions piecemeal together existing Clean
    {
        $(RM) $(>[1]);* ,$(>[2-]);*
    }

    actions together quietly CreLib
    {
        if f$search("$(<)") .eqs. "" then lib/create $(<)
    }

    actions GenFile1
    {
        mcr $(>[1]) $(<) $(>[2-])
    }

    actions Link bind NEEDLIBS
    {
        $(LINK)/exe=$(<) $(LINKFLAGS) $(>:J=,) ,$(NEEDLIBS)/lib ,$(LINKLIBS)
    }

    actions quietly updated piecemeal together RmTemps
    {
        $(RM) $(>[1]);* ,$(>[2-]);*
    }

    actions Shell
    {
        $(CP) $(>) $(<)
    }
}

#
# Mac specifc actions
#

else if $(MAC)
{
    actions together Archive
    {
        $(LINK) -library -o $(<) $(>)
    }

    actions Cc
    {
        set -e MWCincludes $(CCHDRS)
        $(CC) -o $(<) $(CCFLAGS) $(OPTIM) $(CCDEFS) $(>)
    }

    actions C++
    {
        set -e MWCincludes $(CCHDRS)
        $(CC) -o $(<) $(C++FLAGS) $(OPTIM) $(CCDEFS) $(>)
    }

    actions Link bind NEEDLIBS
    {
        $(LINK) -o $(<) $(LINKFLAGS) $(>) $(NEEDLIBS) "$(LINKLIBS)"
    }
}

if $(WIN98)
{
    actions existing Clean
    {
        del $(>)
    }
}

#
# Backwards compatibility with jam 1, where rules were uppercased.
#

rule BULK { Bulk $(<) : $(>) ; }
rule FILE { File $(<) : $(>) ; }
rule HDRRULE { HdrRule $(<) : $(>) ; }
rule INSTALL { Install $(<) : $(>) ; }
rule LIBRARY { Library $(<) : $(>) ; }
rule LIBS { LinkLibraries $(<) : $(>) ; }
rule LINK { Link $(<) : $(>) ; }
rule MAIN { Main $(<) : $(>) ; }
rule SETUID { Setuid $(<) ; }
rule SHELL { Shell $(<) : $(>) ; }
rule UNDEFINES { Undefines $(<) : $(>) ; }

# Old INSTALL* didn't take dest directory.

rule INSTALLBIN { InstallBin $(BINDIR) : $(<) ; }
rule INSTALLLIB { InstallLib $(LIBDIR) : $(<) ; }
rule INSTALLMAN { InstallMan $(MANDIR) : $(<) ; }

# Compatibility with jam 2.2.

rule addDirName { $(<) += [ FDirName $(>) ] ; }
rule makeCommon { FStripCommon $(<) : $(>) ; }
rule _makeCommon { FStripCommon $(<) : $(>) ; }
rule makeDirName { $(<) = [ FDirName $(>) ] ; }
rule makeGrist { $(<) = [ FGrist $(>) ] ; }
rule makeGristedName { $(<) = [ FGristSourceFiles $(>) ] ; }
rule makeRelPath { $(<[1]) = [ FRelPath $(<[2-]) : $(>) ] ; }
rule makeString { $(<) = $(>:J) ; }
rule makeSubDir { $(<) = [ FSubDir $(>) ] ; }
rule makeSuffixed { $(<[1]) = [ FAppendSuffix $(>) : $(<[2]) ] ; }


#
# special package-management rules
#
# check that the package root directory is set
# otherwise, set it to $HOME/packages by default
#

# $(1)   : list variable identifier
# $(2)   : new list
#
rule _PkgAppend
{
  local result = $($(1)) ;
  local i ;

  for i in $(2)
  {
    if ! $(i) in $(result)
    {
      result += $(i) ;
    }
  }
  $(1) = $(result) ;
}


# $(1)   : list variable identifier
# $(2)   : new list
#
rule _PkgPrepend
{
  local result = $($(1)) ;
  local i ;

  for i in $(2)
  {
    if ! $(i) in $(result)
    {
      result = $(i) $(result) ;
    }
  }
  $(1) = $(result) ;
}


# $(1) : Package Name
# $(2) : optional top-level installation directory
#
rule PkgBegin
{
  if $(_PKG_NAME)
  {
    Echo "nested package declarations are not allowed. please use"
    Exit "PkgEnd to finish" $(_PKG_NAME)"'s declaration" ;
  }

  if ! $(PACKAGE_ROOT)
  {
    PACKAGE_ROOT = [ FDirName  $(HOME) packages ] ;
    Echo "PACKAGE_ROOT  variable not set, using" $(PACKAGE_ROOT) "directory" ;
  }

  _PKG_NAME = $(1[1]) ;
  _PKG_DESC = [ FDirName $(PACKAGE_ROOT) $(_PKG_NAME).pc ] ;
  _PKG_TOP  = $(2) ;
  if ! $(_PKG_TOP)
  {
    _PKG_TOP = [ FDirName $(PACKAGE_ROOT) $(_PKG_NAME) ] ;
  }

  _PKG_ORG_HDRS       = $(HDRS) ;
  _PKG_ORG_DEFINES    = $(DEFINES) ;
  _PKG_ORG_LINKLIBS   = $(LINKLIBS) ;
  _PKG_ORG_SUBDIRHDRS = $(SUBDIRHDRS) ;

   pkg-$(_PKG_NAME)-top = $(_PKG_TOP) ;

  _PKG_USES = ;
  _PKG_DEFINES = ;
  _PKG_INCLUDES = ;
  _PKG_LIBS = ;
  _PKG_DO_INSTALL = ;

  _PKG_ALL_USES = ;

  _PkgUpdate ;
}


rule PkgEnd
{
  if $(_PKG_DO_INSTALL)
  {
    _PkgGeneratePc $(_PKG_DESC) ;
    PKG on $(_PKG_DESC) = $(_PKG_NAME) ;
  }

  HDRS     = $(_PKG_ORG_HDRS) ;
  DEFINES  = $(_PKG_ORG_DEFINES) ;
  LINKLIBS = $(_PKG_ORG_LINKLIBS) ;

  SUBDIRHDRS = $(_PKG_ORG_SUBDIRHDRS) ;

  _PKG_NAME = ;
  _PKG_DO_INSTALL = ;
}

#
#
rule _PkgReverse
{
  local p ;

  result = $(1[1]) ;

  for p in $(1[2-])
  {
    result = $(p) $(result) ;
  }
  return $(result) ;
}

# $(1) : package descriptor file path
#
rule _PkgGeneratePc
{
  MkDir $(PACKAGE_ROOT) ;
  Depends $(1) : $(PACKAGE_ROOT) ;
  Depends install : $(1) ;
  Clean uninstall : $(1) ;
  Always $(1) ;   # always re-install, overwrite old version
}

if $(UNIX)
{
  actions _PkgGeneratePc
  {
    echo "# this file was generated automatically - do not edit" > $(1)
    echo "pkg-$(PKG)-uses     = $(pkg-$(PKG)-uses) ;" >> $(1)
    echo "pkg-$(PKG)-libs     = $(pkg-$(PKG)-libs:Q) ;" >> $(1)
    echo "pkg-$(PKG)-defines  = $(pkg-$(PKG)-defines) ;" >> $(1)
    echo "pkg-$(PKG)-includes = $(pkg-$(PKG)-includes:Q) ;" >> $(1)
    echo "pkg-$(PKG)-ok       = 1 ;" >> $(1)
  }
}
else
{
  actions _PkgGeneratePc
  {
    echo # this file was generated automatically - do not edit > $(1)
    echo pkg-$(PKG)-uses     = $(pkg-$(PKG)-uses) ; >> $(1)
    echo pkg-$(PKG)-libs     = $(pkg-$(PKG)-libs:Q) ; >> $(1)
    echo pkg-$(PKG)-defines  = $(pkg-$(PKG)-defines) ; >> $(1)
    echo pkg-$(PKG)-includes = $(pkg-$(PKG)-includes:Q) ; >> $(1)
    echo pkg-$(PKG)-ok       = 1 ; >> $(1)
  }
}

rule PkgInstallPc
{
  # nothing, this is now handled automatically by PkgEnd
}

# recomputes current package's settings whenever needed
#
# no arguments
#
rule _PkgUpdate
{
  local p z ;

  _PKG_ALL_DEFINES  = ;
  _PKG_ALL_INCLUDES = ;
  _PKG_ALL_LIBS     = ;
  _PKG_USE_LIBS     = ;

  for p in $(_PKG_ALL_USES)
  {
    _PkgAppend  _PKG_ALL_DEFINES  : $(pkg-$(p)-defines) ;
    _PkgAppend  _PKG_ALL_INCLUDES : $(pkg-$(p)-includes)    ;
  }

  for p in [ _PkgReverse  $(_PKG_ALL_USES) ]
  {
    local  thelibs = $(pkg-$(p)-libs) ;

    _PKG_ALL_LIBS += $(thelibs) ;
    _PKG_USE_LIBS += $(thelibs[1]) ;
  }

  _PkgAppend _PKG_ALL_DEFINES  : $(_PKG_DEFINES) ;
  _PkgAppend _PKG_ALL_INCLUDES : $(_PKG_INCLUDES) ;

  HDRS     = $(_PKG_ORG_HDRS)     $(_PKG_ALL_INCLUDES) ;
  DEFINES  = $(_PKG_ORG_DEFINES)  $(_PKG_ALL_DEFINES) ;
  LINKLIBS = $(_PKG_ORG_LINKLIBS) $(_PKG_ALL_LIBS) ;

  pkg-$(_PKG_NAME)-includes = $(_PKG_INCLUDES) ;
  pkg-$(_PKG_NAME)-defines  = $(_PKG_DEFINES) ;
  pkg-$(_PKG_NAME)-uses     = $(_PKG_USES) ;
  pkg-$(_PKG_NAME)-libs     = $(_PKG_LIBS) ;
}


# $(1) : list of packages to use
# $(2) : name of missing packages variable
#
rule _PkgUses
{
  local p ;

  for p in $(1)
  {
    if ! $(p) in $(_PKG_ALL_USES)
    {
      local  pcfile = [ FDirName $(PACKAGE_ROOT) $(p).pc ] ;

      NoCare $(pcfile) ;
      include $(pcfile) ;

      if ! $(pkg-$(p)-ok)
      {
        $(2) += $(p) ;
      }
      else if $(pkg-$(p)-uses)
      {
        _PkgUses $(pkg-$(p)-uses) ;
      }
      _PKG_ALL_USES  +=  $(p) ;
    }
  }
}


# $(1) : Package name list
#
rule PkgUses
{
  local  pkg-missing = ;

  _PkgUses $(1) : pkg-missing ;

  if $(pkg-missing)
  {
    Exit "Please install the following required packages:" $(pkg-missing) ;
  }

  _PkgPrepend _PKG_USES : $(1) ;

  _PkgUpdate ;
}


# $(1) : target directory
# $(2) : list of sources relative to $(3)
# $(3) : top source directory
#
rule _PkgMakeLocate
{
  local  top = $(3:E=$(DOT)) ;
  local  dir file ss ;
  local  dirs ;

    for ss in $(2)
    {
    file = [ FDirName $(top) $(ss:G="") ] ;
    dir  = $(1) ;

    if $(ss:D)
    {
      dir = [ FDirName $(dir) $(ss:D) ] ;
    }

      LOCATE on $(ss) = $(1) ;
      Depends $(ss) : $(dir) ;

    if ! $(dir) in $(dirs)
    {
      dirs += $(dir) ;
    }
  }

  MkDir $(dirs) ;
}

# $(1) : target directory
# $(2) : list of source files relative to $(3)
# $(3) : top source directory
#
rule _PkgInstallInto
{
  # InstallInto dir : sources ;
  local sources = $(2) ;
  local targets = $(sources:G=$(INSTALLGRIST)) ;

  # Arrange for jam install
  # Arrange for jam uninstall
  # sources are in SEARCH_SOURCE
  # targets are in dir

  Depends  install : $(targets) ;
  Clean uninstall : $(targets) ;

  _PkgMakeLocate $(1) : $(targets) : $(3) ;

  # For each source, make gristed target name
  # and Install, Chmod, Chown, and Chgrp

  for s in $(sources)
  {
    local t = $(s:G=$(INSTALLGRIST)) ;

    Depends $(t) : $(s) ;

    SEARCH on $(s) = $(3) ;

    Install $(t) : $(s) ;
    Chmod $(t) ;

    if $(OWNER) && $(CHOWN)
    {
      Chown $(t) ;
      OWNER on $(t) = $(OWNER) ;
    }

    if $(GROUP) && $(CHGRP)
    {
      Chgrp $(t) ;
      GROUP on $(t) = $(GROUP) ;
    }
  }
}

# $(1) : target directory
# $(2) : list of source binaries
# $(3) : top source directory
#
rule _PkgInstallBin
{
  local _t = [ FAppendSuffix $(>) : $(SUFEXE) ] ;

  _PkgInstallInto $(<) : $(_t) : $(3) ;
  MODE on $(_t:G=$(INSTALLGRIST)) = $(EXEMODE) ;
}


# $(1) : target directory
# $(2) : list of source shells
# $(3) : top source directory
#
rule _PkgInstallShell
{
  _PkgInstallInto $(1) : $(2) : $(3) ;
  MODE on $(2:G=$(INSTALLGRIST)) = $(SHELLMODE) ;
}


# $(1) : target directory
# $(2) : list of source files
# $(3) : top source directory
#
rule _PkgInstallFile
{
  _PkgInstallInto $(1) : $(2) : $(3) ;
  MODE on $(2:G=$(INSTALLGRIST)) = $(FILEMODE) ;
}

# $(1) : list of include paths
#
rule PkgIncludes
{
  _PKG_INCLUDES += $(1) ;
  _PkgUpdate ;
}

rule PkgDefines
{
  _PKG_DEFINES += $(1) ;
  _PkgUpdate ;
}

# $(1) : list of header files
# $(2) : top-level source directory
# $(3) : optional directory suffix
#
rule PkgInstallHeader
{
  local  dir = [ FDirName $(_PKG_TOP) include ] ;

  _PKG_DO_INSTALL = 1 ;

  _PkgInstallFile  [ FDirName $(dir) $(3) ] : $(1) : $(2)   ;
  _PkgAppend _PKG_INCLUDES : $(dir) ;
  _PkgUpdate ;
}

# $(1) : library name
#
rule PkgInstallLib
{
  local  lib = $(1:S=$(SUFLIB)) ;
  local  dir = [ FDirName $(_PKG_TOP) lib ] ;

  _PKG_DO_INSTALL = 1 ;

  _PkgInstallFile  $(dir) : $(lib) : $(DOT) ;
  _PkgPrepend _PKG_LIBS : [ FDirName $(dir) $(lib) ] ;
  _PkgUpdate ;
}


# $(1) : required library names or link flags
#
rule PkgNeedLib
{
  _PkgAppend _PKG_LIBS : $(1) ;
  _PkgUpdate ;
}


rule PkgMain
{
    MainFromObjects $(<) : $(>:S=$(SUFOBJ)) ;
    Objects $(>) ;
    LINKLIBS on $(<:S=$(SUFEXE)) += $(LINKLIBS) ;
    Depends $(<:S=$(SUFEXE)) : $(_PKG_USE_LIBS) ;
}


# $(1) : list of directories relative to $(2)
# $(2) : top search directory
# $(3) : pattern
#
rule PkgGlob
{
  local files dir ;
  files = [ GLOB [ FDirName $(2) $(1) ] : $(3) ] ;
  dir   = [ FDirName $(1) ] ;

  return  $(files:D=$(dir)) ;
}

#
# Now include the user's Jamfile.
#

include $(JAMFILE) ;
