#  (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
#  distribute this software is granted provided this copyright notice appears in
#  all copies. This software is provided "as is" without express or implied
#  warranty, and with no claim as to its suitability for any purpose.

# Keep a record so that no module is included multiple times
module local loaded-modules ;
module local loading-modules ;
module local untested ;

# meant to be invoked from import when no __test__ rule is defined in a given
# module
local rule no_test_defined
{
    ECHO warning: no __test__ rule defined in module [ CALLER_MODULE ] ;
}

# return the binding of the given module
rule binding ( module )
{
    return $($(module).__binding__) ;
}

# load the indicated module if it is not already loaded.
rule load ( module-name )
{
    if ! ( $(module-name) in $(loaded-modules) )
    {
        loaded-modules += $(module-name) ;
        loading-modules += $(module-name) ;
        local suppress-test = $(untested[1]) ; # suppress tests until all recursive loads are complete.
        untested += $(module-name) ; # add the module to the stack of untested modules
        
        module $(module-name)
        {
            module local __name__ = $(module-name) ;
            
            # Prepare a default behavior, in case no __test__ is defined.
            IMPORT modules : no_test_defined : $(module-name) : __test__ ;

            # Add some grist so that the module will have a unique target name
            local module-target = $(module-name:G=module@:S=.jam) ;
            
            SEARCH on $(module-target) = $(BOOST_BUILD_PATH) ;
            BINDRULE on $(module-target) = modules.record-binding ;
            include $(module-name:G=module@:S=.jam) ;

        }
        loading-modules = $(loading-modules[1--2]) ;
        
        if ! $(suppress-test) && $(BOOST_BUILD_TEST)-is-nonempty
        {
            # run any pending tests
            for local m in $(untested)
            {
                ECHO testing module $(m)... ;
                module $(m)
                {
                    __test__ ;
                }
            }
            untested = ;
        }
    }
    else if $(module-name) in $(loading-modules)
    {
        ECHO loading \"$(module-name)\" ;
        ECHO circular module loading dependency: ;
        EXIT $(loading-modules) $(module-name) ;
    }
}

# This helper is used by load (above) to record the binding (path) of
# each loaded module.
rule record-binding ( module-target : binding )
{
    module local $(module-target:G=:S=).__binding__ = $(binding) ;
}

# load the indicated module and import rule names into the current
# module. Any members of rules-opt will be available without
# qualification in the caller's module. Any members of rename-opt will
# be taken as the names of the rules in the caller's module, in place
# of the names they have in the imported module. If rules-opt = '*',
# all rules from the indicated module are imported into the caller's
# module. If rename-opt is supplied, it must have the same number of
# elements as rules-opt.
rule import ( module-name : rules-opt * : rename-opt * )
{
    load $(module-name) ;
    
    local source-names = $(rules-opt) ;
    if $(rules-opt) = *
    {
        source-names = [ RULENAMES module-name ] ;
    }
    
    local target-names = $(rename-opt) ;
    target-names ?= $(source-names) ;
    IMPORT $(module-name) : $(source-names) : [ CALLER_MODULE ] : $(target-names) ;
}

# Returns the module-local value of a variable.
rule peek ( module-name variable )
{
    module $(module-name)
    {
        return $($(variable)) ;
    }
}

local rule __test__ ( )
{
    import assert ;

    module modules.__test__
    {
        module local foo = bar ;
    }
    assert.result bar :  peek modules.__test__ foo ;
}
