# Copyright (c) 2010 Vladimir Prus.
#
# Use, modification and distribution is subject to the Boost Software
# License Version 1.0. (See accompanying file LICENSE_1_0.txt or
# http://www.boost.org/LICENSE_1_0.txt)

# This module defines function to help with two main tasks:
#
# - Discovering build-time configuration for the purposes of adjusting the build
#   process.
# - Reporting what is built, and how it is configured.

import "class" : new ;
import common ;
import path ;
import property ;
import property-set ;
import targets ;
import config-cache ;


rule log-summary ( )
{
}


.width = 30 ;

rule set-width ( width )
{
    .width = $(width) ;
}


# Declare that the components specified by the parameter exist.
#
rule register-components ( components * )
{
    .components += $(components) ;
}


# Declare that the components specified by the parameters will be built.
#
rule components-building ( components * )
{
    .built-components += $(components) ;
}


# Report something about component configuration that the user should better
# know.
#
rule log-component-configuration ( component : message )
{
    # FIXME: Implement per-property-set logs.
    .component-logs.$(component) += $(message) ;
}


rule log-check-result ( result )
{
    if ! $(.announced-checks)
    {
        ECHO "Performing configuration checks\n" ;
        .announced-checks = 1 ;
    }

    ECHO $(result) ;
    # FIXME: Unfinished code. Nothing seems to set .check-results at the moment.
    #.check-results += $(result) ;
}


rule log-library-search-result ( library : result )
{
    local x = [ PAD "    - $(library)" : $(.width) ] ;
    log-check-result "$(x) : $(result)" ;
}


rule print-component-configuration ( )
{
    # FIXME: See what was intended with this initial assignment.
    # local c = [ sequence.unique $(.components) ] ;

    ECHO "\nComponent configuration:\n" ;
    local c ;
    for c in $(.components)
    {
        local s ;
        if $(c) in $(.built-components)
        {
            s = "building" ;
        }
        else
        {
            s = "not building" ;
        }
        ECHO [ PAD "    - $(c)" : $(.width) ] ": $(s)" ;
        for local m in $(.component-logs.$(c))
        {
            ECHO "        -" $(m) ;
        }
    }
    ECHO ;
}


rule print-configure-checks-summary ( )
{
    # FIXME: The problem with this approach is that the user sees the checks
    # summary when all checks are done, and has no progress reporting while the
    # checks are being executed.
    if $(.check-results)
    {
        ECHO "Configuration checks summary\n" ;
        for local r in $(.check-results)
        {
            ECHO $(r) ;
        }
        ECHO ;
    }
}


# Attempt to build a metatarget named by 'metatarget-reference' in context of
# 'project' with properties 'ps'. Returns non-empty value if build is OK.
#
rule builds-raw ( metatarget-reference : project : ps : what : retry ? )
{
    local result ;

    if ! $(retry) && ! $(.$(what)-tested.$(ps))
    {
        .$(what)-tested.$(ps) = true ;

        local cache-name = $(what) [ $(ps).raw ] ;
        cache-name = $(cache-name:J=-) ;
        local value = [ config-cache.get $(cache-name) ] ;
        
        local targets = [ targets.generate-from-reference 
            $(metatarget-reference) : $(project) : $(ps) ] ;

        local jam-targets ;
        for local t in $(targets[2-])
        {
            jam-targets += [ $(t).actualize ] ;
        }
             
        if $(value)
        {
            local x = [ PAD "    - $(what)" : $(.width) ] ; 
            if $(value) = true
            {
                .$(what)-supported.$(ps) = yes ;
                result = true ;
                log-check-result "$(x) : yes (cached)" ;
            }
            else
            {
                log-check-result "$(x) : no  (cached)" ;
            }
        }       
        else if ! UPDATE_NOW in [ RULENAMES ]
        {
            # Cannot determine. Assume existance.
        }
        else
        {
            local x = [ PAD "    - $(what)" : $(.width) ] ;
            if [ UPDATE_NOW $(jam-targets) :
                 $(.log-fd) : ignore-minus-n : ignore-minus-q ]
            {
                .$(what)-supported.$(ps) = yes ;
                result = true ;
                log-check-result "$(x) : yes" ;
            }
            else
            {
                log-check-result "$(x) : no" ;
            }
        }
        if ! $(value)
        {
            if $(result)
            {
                config-cache.set $(cache-name) : true ;
            }
            else
            {
                config-cache.set $(cache-name) : false ;
            }
        }        
        return $(result) ;
    }
    else
    {
        return $(.$(what)-supported.$(ps)) ;
    }
}

rule builds ( metatarget-reference : properties * : what ? : retry ? )
{
    # FIXME: This should not be hardcoded. Other checks might want to consider a
    # different set of features as relevant.
    local toolset = [ property.select <toolset> : $(properties) ] ;
    local toolset-version-property = "<toolset-$(toolset:G=):version>" ;
    local relevant = [ property.select <target-os> <toolset>
        $(toolset-version-property) <address-model> <architecture> :
        $(properties) ] ;
    local ps = [ property-set.create $(relevant) ] ;
    local t = [ targets.current ] ;
    local p = [ $(t).project ] ;

    if ! $(what)
    {
        local resolved = [ targets.resolve-reference $(metatarget-reference) : $(p) ] ;
        local name = [ $(resolved[1]).name ] ;
        what = "$(name) builds" ;
    }

    return [ builds-raw $(metatarget-reference) : $(p) : $(ps) : $(what) :
        $(retry) ] ;
}


# Called by Boost.Build startup code to specify the file to receive the
# configuration check results. Should never be called by user code.
#
rule set-log-file ( log-file )
{
    path.makedirs [ path.parent $(log-file) ] ;
    .log-fd = [ FILE_OPEN $(log-file) : "w" ] ;
}


# Frontend rules

class check-target-builds-worker
{
    import configure ;
    import property-set ;
    import targets ;
    import property ;

    rule __init__ ( target message ? : true-properties * : false-properties * )
    {
        self.target = $(target) ;
        self.message = $(message) ;
        self.true-properties = $(true-properties) ;
        self.false-properties = $(false-properties) ;
    }

    rule check ( properties * )
    {
        local choosen ;
        if [ configure.builds $(self.target) : $(properties) : $(self.message) ]
        {
            choosen = $(self.true-properties) ;
        }
        else
        {
            choosen = $(self.false-properties) ;
        }
        return [ property.evaluate-conditionals-in-context $(choosen) :
            $(properties) ] ;
    }
}


rule check-target-builds ( target message ? : true-properties * :
    false-properties * )
{
    local instance = [ new check-target-builds-worker $(target) $(message) :
        $(true-properties) : $(false-properties) ] ;
    return <conditional>@$(instance).check ;
}


IMPORT $(__name__) : check-target-builds :  : check-target-builds ;
