import modules ;
import option ;
import os ;
import path ;
import project ;

#Shell with trailing line removed http://lists.boost.org/boost-build/2007/08/17051.php
rule trim-nl ( str extras * ) {
return [ MATCH "([^
]*)" : $(str) ] $(extras) ;
}
rule _shell ( cmd : extras * ) {
  return [ trim-nl [ SHELL $(cmd) : $(extras) ] ] ;
}

cxxflags = [ os.environ "CXXFLAGS" ] ;
cflags = [ os.environ "CFLAGS" ] ;
ldflags = [ os.environ "LDFLAGS" ] ;

#Run g++ with empty main and these arguments to see if it passes.  
rule test_flags ( flags * ) {
  flags = $(cxxflags) $(ldflags) $(flags) ;
  local cmd = "bash -c \"g++ "$(flags:J=" ")" -x c++ - <<<'int main() {}' -o /dev/null >/dev/null 2>/dev/null\"" ;
  local ret = [ SHELL $(cmd) : exit-status ] ;
  if --debug-configuration in [ modules.peek : ARGV ] {
    echo $(cmd) ;
    echo $(ret) ;
  }
  if $(ret[2]) = 0 {
    return true ;
  } else {
    return ;
  }
}

rule test_header ( name ) {
  return [ test_flags "-include $(name)" ] ;
}

rule test_library ( name ) {
  return [ test_flags "-l$(name)" ] ;
}

{
  local cleaning = [ option.get "clean" : : yes ] ;
  cleaning ?= [ option.get "clean-all" : no : yes ] ;
  if "clean" in [ modules.peek : ARGV ] {
    cleaning = yes ;
  }
  constant CLEANING : $(cleaning) ;
}

#Determine if a library can be compiled statically.  
rule auto-shared ( name : additional * ) {
  additional ?= "" ;
  if [ test_flags $(additional)" -static -l"$(name) ] {
    return ;
  } else {
    return "<link>shared" ;
  }
}

# MacPorts' default location is /opt/local -- use this if no path is given.
with-macports = [ option.get "with-macports" : : "/opt/local" ] ;
if $(with-macports) {
  using darwin ;
  ECHO "Using --with-macports=$(with-macports), implying use of darwin GCC" ;

  L-boost-search = -L$(with-macports)/lib ;
  boost-search = <search>$(with-macports)/lib ;
  I-boost-include = -I$(with-macports)/include ;
  boost-include = <include>$(with-macports)/include ;
}
else {
  with-boost = [ option.get "with-boost" ] ;
  with-boost ?= [ os.environ "BOOST_ROOT" ] ;
  if $(with-boost) {
    L-boost-search = -L$(with-boost)/lib" "-L$(with-boost)/lib64 ;
    boost-search = <search>$(with-boost)/lib <search>$(with-boost)/lib64 ;
    I-boost-include = -I$(with-boost)/include ;
    boost-include = <include>$(with-boost)/include ;
  } else {
    L-boost-search = "" ;
    boost-search = ;
    I-boost-include = "" ;
    boost-include = ;
  }
}

requirements = ;
 
#Are we linking static binaries against shared boost?
boost-auto-shared = [ auto-shared "boost_program_options" : $(L-boost-search) ] ;
#Convenience rule for boost libraries.  Defines library boost_$(name).  
rule boost-lib ( name macro ) {
  #Link multi-threaded programs against the -mt version if available.  Old 
  #versions of boost do not have -mt tagged versions of all libraries.   Sadly,
  #boost.jam does not handle this correctly.  
  if [ test_flags $(L-boost-search)" -lboost_"$(name)"-mt" ] {
#    if [ test_flags $(L-boost-search)" -lboost_"$(name) ] {
#      lib inner_boost_$(name) : : <threading>single $(boost-search) <name>boost_$(name) ;
#      lib inner_boost_$(name) : : <threading>multi $(boost-search) <name>boost_$(name)-mt ;
#    } else {
      if ! <threading>multi in $(requirements) {
        requirements += <threading>multi ;
      }
      lib inner_boost_$(name) : : <threading>multi $(boost-search) <name>boost_$(name)-mt ;
#    }
  } else {
    lib inner_boost_$(name) : : $(boost-search) <name>boost_$(name) ;
  }

  alias boost_$(name) : inner_boost_$(name) : $(boost-auto-shared) : : <link>shared:<define>BOOST_$(macro) $(boost-include) ;
}

#Argument is e.g. 103600
rule boost ( min-version ) {
  local cmd = "bash -c \"g++ "$(I-boost-include)" -dM -x c++ -E /dev/null -include boost/version.hpp 2>/dev/null |grep '#define BOOST_VERSION '\"" ;
  local boost-shell = [ SHELL "$(cmd)" : exit-status ] ;
  if $(boost-shell[2]) != 0 && $(CLEANING) = no {
    echo Failed to run "$(cmd)" ;
    exit Boost does not seem to be installed or g++ is confused. : 1 ;
  }
  boost-version = [ MATCH "#define BOOST_VERSION ([0-9]*)" : $(boost-shell[1]) ] ;
  if $(boost-version) < $(min-version) && $(CLEANING) = no {
    exit You have Boost $(boost-version).  This package requires Boost at least $(min-version) (and preferably newer). : 1 ;
  }
  #See tools/build/v2/contrib/boost.jam in a boost distribution for a table of macros to define.   
  boost-lib thread THREAD_DYN_DLL ;
  boost-lib program_options PROGRAM_OPTIONS_DYN_LINK ;
  boost-lib unit_test_framework TEST_DYN_LINK ;
  boost-lib iostreams IOSTREAMS_DYN_LINK ;
  boost-lib serialization SERIALIZATION_DYN_LINK ;
  boost-lib mpi MPI_DYN_LINK ;
}
 
#Link normally to a library, but sometimes static isn't installed so fall back to dynamic.
rule external-lib ( name : search-path * ) {
  lib $(name) : : [ auto-shared $(name) : "-L"$(search-path) ] <search>$(search-path) ;
}

#Write the current command line to previous.sh.  This does not do shell escaping.  
{
  local build-log = $(TOP)/previous.sh ;
  if ! [ path.exists $(build-log) ] {
    SHELL "touch $(build-log) && chmod +x $(build-log)" ;
  }
  local script = [ modules.peek : ARGV ] ;
  if $(script[1]) = "./jam-files/bjam" {
    #The ./bjam shell script calls ./jam-files/bjam so that appears in argv but
    #we want ./bjam to appear so the environment variables are set correctly.  
    script = "./bjam "$(script[2-]:J=" ") ;
  } else {
    script = $(script:J=" ") ;
  }
  script = "#!/bin/sh\n$(script)\n" ;
  local ignored = @($(build-log):E=$(script)) ;
}

{
  #Boost jam's static clang for Linux is buggy.
  requirements += <cxxflags>$(cxxflags) <cflags>$(cflags) <linkflags>$(ldflags) <os>LINUX,<toolset>clang:<link>shared ;

  #libSegFault prints a stack trace on segfault.  Link against it if available.  
  if [ test_flags "-lSegFault" ] {
    external-lib SegFault ;
    requirements += <library>SegFault ;
  }
}

if [ option.get "git" : : "yes" ] {
  local revision = [ _shell "git rev-parse --verify HEAD |head -c 7" ] ;
  constant GITTAG : "/"$(revision) ;
} else {
  constant GITTAG : "" ;
}

prefix = [ option.get "prefix" ] ;
if $(prefix) {
  prefix = [ path.root $(prefix) [ path.pwd ] ] ;
} else {
  prefix = $(TOP)/dist$(GITTAG) ;
}

bindir = [ option.get "bindir" : $(prefix)/bin ] ;
libdir = [ option.get "libdir" : $(prefix)/lib ] ;
rule install-bin-libs ( deps * ) {
  install prefix-bin : $(deps) : <location>$(bindir) <install-dependencies>on <install-type>EXE <link>shared:<dll-path>$(libdir) ;
  install prefix-lib : $(deps) : <location>$(libdir) <install-dependencies>on <install-type>LIB <link>shared:<dll-path>$(libdir) ;
}
rule install-headers ( name : list * : source-root ? ) {
  local includedir = [ option.get "includedir" : $(prefix)/include ] ;
  source-root ?= "." ;
  install $(name) : $(list) : <location>$(includedir) <install-source-root>$(source-root) ;
}

rule build-projects ( projects * ) {
  for p in $(projects) {
    build-project $(p) ;
  }
}