# This file is used to build the Bionic library with the Jam build
# tool. For info, see www.perforce.com/jam/jam.html
#

BIONIC_TOP ?= $(DOT) ;

DEBUG = 1 ;

# pattern used for automatic heade inclusion detection
HDRPATTERN = "^[ 	]*#[ 	]*include[ 	]*[<\"]([^\">]*)[\">].*$" ;


# debugging support, simply define the DEBUG variable to activate verbose output
rule Debug
{
    if $(DEBUG) {
        Echo $(1) ;
    }
}

# return all elements from $(1) that are not in $(2)
rule Filter  list : filter
{
    local result = ;
    local item ;
    for item in $(list) {
        if ! $(item) in $(filter) {
            result += $(item) ;
        }
    }
    return $(result) ;
}


# reverse a list of elements
rule Reverse  list
{
    local  result = ;
    local  item ;

    for item in $(list) {
        result = $(item) $(result) ;
    }
    return $(result) ;
}


# decompose a path into a list of elements
rule PathDecompose  dir
{
    local  result ;

    while $(dir:D)
    {
        if ! $(dir:BS) {  # for rooted paths like "/foo"
            break ;
        }
        result = $(dir:BS) $(result) ;
        dir    = $(dir:D) ;
    }
    result = $(dir) $(result) ;
    return $(result) ;
}


# simply a file path, i.e. get rid of . or .. when possible
rule _PathSimplify  dir
{
    local  result = ;
    local  dir2 d ;

    dir  = [ PathDecompose $(dir) ] ;

    # get rid of any single dot
    dir2 = ;
    for d in $(dir) {
        if $(d) = "." {
            continue ;
        }
        dir2 += $(d) ;
    }

    # get rid of .. when possible
    for d in $(dir2) {
        if $(d) = ".." && $(result) {
            result = $(result[2-]) ;
        }
        else
            result = $(d) $(result) ;
    }

    # now invert the result
    result = [ Reverse $(result) ] ;
    if ! $(result) {
        result = "." ;
    }
    return $(result:J="/") ;
}


rule PathSimplify  dirs
{
    local result ;
    local d ;
    for d in $(dirs) {
        result += [ _PathSimplify $(d) ] ;
    }
    return $(result) ;
}


# retrieve list of subdirectories
rule ListSubDirs  paths
{
    local  result  = ;
    local  entry ;
    for entry in [ Glob $(paths) : * ] {
        if ! $(entry:S) {
            result += $(entry) ;
        }
    }
    return [ PathSimplify $(result) ] ;
}


# retrieve list of sources in a given directory
rule ListSources  path
{
    return [ Glob $(path) : *.S *.c ] ;
}


# find the prebuilt directory
#
if ! $(TOP) {
    Echo "Please define TOP as the root of your device build tree" ;
    Exit ;
}

Debug "OS is" $(OS) ;
Debug "CPU is" $(CPU) ;

if $(OS) = LINUX
{
    PREBUILT = $(TOP)/prebuilt/Linux ;
}
else if $(OS) = MACOSX
{
    switch $(CPU) {
        case i386 : PREBUILT = $(TOP)/prebuilt/darwin-x86 ; break ;
        case ppc  : PREBUILT = $(TOP)/prebuilt/darwin-ppc ; break ;
        case *    : Echo "unsupported CPU" "$(CPU) !!" ;
                    Echo "Please contact digit@google.com for help" ;
                    Exit ;
    }
}
else
{
    Echo "Unsupported operating system" $(OS) ;
    Echo "Please contact digit@google.com for help" ;
    Exit ;
}

Debug "TOP is" $(TOP) ;
Debug "PREBUILT is" $(PREBUILT) ;


# check architectures and setup toolchain variables
#
SUPPORTED_ARCHS = x86 arm ;

ARCH ?= $(SUPPORTED_ARCHS) ;

if ! $(ARCH) in $(SUPPORTED_ARCHS) {
    Echo "The variable ARCH contains an unsupported value, use one or more of these instead" ;
    Echo "separated by spaces:" $(SUPPORTED_ARCHS) ;
    Exit ;
}

x86_TOOLSET_PREFIX ?= "" ;
arm_TOOLSET_PREFIX ?= $(TOP)/prebuilt/Linux/toolchain-4.1.1/bin/arm-elf- ;

for arch in $(ARCH) {
    CC_$(arch)  = $($(arch)_TOOLSET_PREFIX)gcc ;
    C++_$(arch) = $($(arch)_TOOLSET_PREFIX)g++ ;
    AR_$(arch)  = $($(arch)_TOOLSET_PREFIX)ar ;
}


# the list of arch-independent source subdirectories
BIONIC_SRC_SUBDIRS = string ;
BIONIC_x86_SUBDIRS = ;
BIONIC_arm_SUBDIRS = ;

CFLAGS   = -O0 -g -W ;



# find sources in a given list of subdirectories
rule FindSources  dirs
{
    local dir ;

    for dir in $(dirs)
    {
        local LOCAL_SRC NO_LOCAL_SRC ;

        if [ Glob $(dir) : rules.jam ] {
            include $(dir)/rules.jam ;
            if $(LOCAL_SRC) {
                _sources = $(LOCAL_SRC) ;
            }
            else {
                _sources = [ Glob $(dir) : *.S *.c ] ;
                _sources = $(_sources:BS) ;
            }
            if $(NO_LOCAL_SRC) {
                _sources = [ Filter $(_sources) : $(NO_LOCAL_SRC) ] ;
            }
            sources += $(dir)/$(_sources) ;
        }
        else
            sources += [ ListSources $(dir) ] ;
    }
}

# Compile a given object file from a source
rule Compile  object : source
{
    Depends $(object) : $(source) ;
    Depends bionic : $(object) ;
    Clean clean : $(object) ;

    MakeLocate $(object) : $(OUT) ;


    CC on $(object)       = $(CC_$(arch)) ;
    CFLAGS on $(object)   = $(CFLAGS) ;
    INCLUDES on $(object) = $(INCLUDES) ;
    DEFINES on $(object)  = $(DEFINES) ;

    HDRRULE on $(>) = HdrRule ;
    HDRSCAN on $(>) = $(HDRPATTERN) ;
    HDRSEARCH on $(>) = $(INCLUDES) ;
    HDRGRIST on $(>) = $(HDRGRIST) ;
}


actions Compile
{
    $(CC) -c -o $(1) $(CFLAGS) -I$(INCLUDES) -D$(DEFINES) $(2)
}


rule RmTemps
{
    Temporary $(2) ;
}

actions quietly updated piecemeal together RmTemps
{
    rm -f $(2)
}

actions Archive
{
    $(AR) ru $(1) $(2)
}

rule Library  library : objects
{
    local  obj ;

    if ! $(library:S) {
        library = $(library:S=.a) ;
    }
    library = $(library:G=<$(arch)>) ;

    Depends all : $(library) ;

    if ! $(library:D) {
        MakeLocate $(library) $(library)($(objects:BS)) : $(OUT) ;
    }

    Depends $(library) : $(library)($(objects:BS)) ;
    for obj in $(objects) {
        Depends $(library)($(obj:BS)) : $(obj) ;
    }

    Clean clean : $(library) ;

    AR on $(library) = $(AR_$(arch)) ;
    Archive $(library) : $(objects) ;

    RmTemps $(library) : $(objects) ;
}


rule  ProcessDir
{
    local CFLAGS   = $(CFLAGS) ;
    local DEFINES  = $(DEFINES) ;
    local INCLUDES = $(INCLUDES) ;
    local local_rules = [ Glob $(1) : rules.jam ] ;
    local source sources ;

    if $(local_rules) {
        local LOCAL_CFLAGS LOCAL_DEFINES LOCAL_INCLUDES LOCAL_SRC NO_LOCAL_SRC ;

        include $(local_rules) ;
        CFLAGS   += $(LOCAL_CFLAGS) ;
        DEFINES  += $(LOCAL_DEFINES) ;
        INCLUDES += $(LOCAL_INCLUDES) ;

        if $(LOCAL_SRC) {
            sources = $(LOCAL_SRC) ;
        }
        else {
            sources = [ Glob $(1) : *.S *.c ] ;
            sources = $(sources:BS) ;
        }

        if $(NO_LOCAL_SRC) {
            sources = [ Filter $(sources) : $(NO_LOCAL_SRC) ] ;
        }

        sources = $(1)/$(sources) ;
    }
    else
        sources = [ Glob $(1) : *.S *.c ] ;

    for source in $(sources) {
        local name = $(source:B) ;

        if $(source:S) = ".S" {
            # record the list of assembler sources
            ASSEMBLER_SOURCES += $(name) ;
        }
        else if $(source:S) = ".c" && $(name) in $(ASSEMBLER_SOURCES) {
            # skip C source file if corresponding assembler exists
            continue ;
        }

        objname = <$(arch)>$(name).o  ;

        Compile $(objname) : $(source) ;
        ALL_OBJECTS += $(objname) ;
    }
}

rule ProcessDirs
{
    local  dir ;
    for dir in $(1) {
        ProcessDir $(dir) ;
    }
}

INCLUDES_x86 = /usr/src/linux/include ;

INCLUDES_arm = ../kernel_headers
               include/arch/arm
               include/bits32
               ;

INCLUDES = include stdio string stdlib .
           ../msun/include
           ;

DEFINES  = ANDROID_CHANGES
           USE_LOCKS
           REALLOC_ZERO_BYTES_FREES
           _LIBC=1
           SOFTFLOAT
           FLOATING_POINT
           NEED_PSELECT=1
           ANDROID
           ;

CFLAGS_x86 = ;


for arch in $(ARCH)
{
    local ARCH_DIR = $(BIONIC_TOP)/arch-$(arch) ;
    local INCLUDES = $(INCLUDES_$(arch)) $(ARCH_DIR)/include $(INCLUDES) ;
    local DEFINES  = $(DEFINES_$(arch)) $(DEFINES) ARCH=$(arch)  ;
    local CFLAGS   = $(CFLAGS) $(CFLAGS_$(arch)) ;
    local OUT      = out/$(arch) ;
    local ASSEMBLER_SOURCES ALL_OBJECTS ;

    ProcessDirs [ ListSubDirs $(ARCH_DIR) ] ;
    ProcessDirs stdlib stdio unistd string tzcode inet ;
    ProcessDirs [ ListSubDirs netbsd ] ;
    ProcessDirs bionic ;

    Library bionic : $(ALL_OBJECTS) ;
}

BIONIC_SEARCH = $(BIONIC_TOP)/include ;



# /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) ;
}