import libtbx.load_env
import libtbx.env_config
from libtbx import easy_run
from libtbx.utils import getenv_bool
from libtbx.str_utils import show_string
from libtbx.path import norm_join, full_command_path
import sys, os
op = os.path

if (hasattr(Environment, "Clone")):
  Environment.Copy = Environment.Clone # XXX backward compatibility 2008_03_30

def SCons_Action_FunctionAction_get_contents(self, target, source, env,
                                                   dict=None):
  import cPickle, string
  try:
    code = cPickle.dumps(self.execfunction)
  except (TypeError, cPickle.PicklingError):
    code = self.execfunction.func_name
  return str(code) + env.subst(string.join(map(lambda v: '${'+v+'}',
                                               self.varlist)))
if (1):
  import SCons.Action
  SCons.Action.FunctionAction.get_contents = \
  SCons_Action_FunctionAction_get_contents

class empty(object):

  def __init__(self):
    open(libtbx.env.under_build("include_paths"), "w")

  def __setattr__(self, key, value):
    self.__dict__.__setitem__(key, value)
    if (key.endswith("_common_includes")):
      include_paths = libtbx.env_config.unique_paths(paths=value)
      for path in include_paths:
        if (path.find(os.pathsep) >= 0):
          raise RuntimeError(
            'include path with embedded "%s" character: %s' % (
              os.pathsep, show_string(path)))
      print >> open(libtbx.env.under_build("include_paths"), "a"), \
        key, os.pathsep.join(include_paths)

  def _():
    def fget(self):
      return self.__dict__.setdefault("_cflags_base", [])
    def fset(self, value):
      self._cflags_base = value
    return locals()
  cflags_base = property(**_())


def set_python_include_and_libs(env_etc):
  env_etc.python_include = libtbx.env_config.python_include_path()
  if (sys.platform == "win32"):
    env_etc.libs_python = ["python" + sys.version[0] + sys.version[2]]
    env_etc.libpath_python = [sys.prefix + r"\libs"]
  else:
    env_etc.libs_python = []
    env_etc.libpath_python = []
  if (env_etc.compiler.startswith("darwin_")):
    env_etc.python_framework = "/".join(
      env_etc.python_include.split("/")[:-2] + ["Python"])

def determine_pointer_size(env_base, env_etc): # currently unused
  test_code = """\
#include <iostream>
int main()
{
  std::cout << sizeof(void*) << std::endl;
  return 0;
}
"""
  env = env_base.Clone(
    CFLAGS=env_etc.cflags_base,
    CCFLAGS=env_etc.ccflags_base,
    CXXFLAGS=env_etc.cxxflags_base)
  conf = env.Configure()
  flag, output = conf.TryRun(test_code, extension='.cpp')
  conf.Finish()
  if (not flag): return None
  return int(output)

def determine_cpp0x_flag():
  result = getenv_bool(variable_name="LIBTBX_CPP0X", default=None)
  if (result is None):
    result = libtbx.env.under_dist(
      module_name="boost",
      default=None,
      path=".svn",
      test=op.isdir)
  return result

env_etc = empty()
env_etc.no_boost_python = ARGUMENTS.get("no_boost_python")
if (env_etc.no_boost_python is not None):
  env_etc.no_boost_python = bool(int(env_etc.no_boost_python))
else:
  env_etc.no_boost_python = \
    not libtbx.env.build_options.build_boost_python_extensions
env_etc.norm_join = norm_join
env_etc.gcc_version = None
env_etc.icc_version = None

env_etc.include_registry = libtbx.env_config.include_registry() \
  .scan_boost(flag=libtbx.env.build_options.scan_boost)

# XXX backward compatibility 2007-11-18
def disable_strict_aliasing(env): pass
env_etc.disable_strict_aliasing = disable_strict_aliasing

def patch_scons_env_for_ad_hoc_debug(env, O123_replacement=["-O0", "-g"]):
  assert not env_etc.compiler.startswith("win")
  for kw in ["CCFLAGS", "SHCCFLAGS"]:
    dbg_ccflags = []
    for f in env[kw]:
      if   (f in ["-O1", "-O2", "-O3"]):
        dbg_ccflags.extend(O123_replacement)
      elif (f not in ["-ffast-math", "-funroll-loops"]):
        dbg_ccflags.append(f)
    env.Replace(**{kw: dbg_ccflags})
env_etc.patch_scons_env_for_ad_hoc_debug = patch_scons_env_for_ad_hoc_debug

def patch_scons_env(env, key, remove=set(), replace={}):
  remaining = []
  have_changes = False
  for v in env[key]:
    if (v in remove):
      have_changes = True
    elif (v in replace):
      remaining.append(replace[v])
      have_changes = True
    else:
      remaining.append(v)
  if (have_changes):
    env.Replace(**{key: remaining})
env_etc.patch_scons_env = patch_scons_env

compiler = libtbx.env.build_options.compiler
if (sys.platform == "win32"):
  if (compiler == "default"):
    env_etc.compiler = "win32_cl"
  else:
    env_etc.compiler = "win32_"+compiler
    compiler = None
elif (sys.platform.startswith("darwin")):
  if (compiler == "default"):
    env_etc.compiler = "darwin_c++"
  else:
    env_etc.compiler = "darwin_"+compiler
    compiler = None
elif (os.name == "posix"):
  if (compiler == "default"):
    env_etc.compiler = "unix_gcc"
  else:
    env_etc.compiler = "unix_"+compiler
    compiler = None
if (compiler not in ("default", None)):
  sys.tracebacklimit = 0
  raise RuntimeError("Compiler not supported on this platform: %s" % compiler)
supported_compilers = (
  "win32_cl",
  "win32_icc",
  "win32_mingw",
  "unix_mingw", # cross-compiling works only with
                # --build-boost-python-extensions=False
  "unix_gcc",
  "unix_gcc4",
  "unix_clang",
  "unix_icc",
  "unix_icpc",
  "darwin_c++",
  "darwin_gcc",
  "darwin_gcc-4.2", # tested with the "GCC 4.2 Developer Preview 1"
                    # distributed by Apple
                    # and the GCC 4.2 shipping with XCode 3.1
  "darwin_clang", # successfull compilation of phenix + smtbx
                  # with LLVM trunk revision 111316 and all tests pass.
                  # It will hopefully be so with the soon-to-be-released
                  # LLVM 2.8 which will most likely ship with the first
                  # official release of XCode 4 (currently in Preview).
)
if (not env_etc.compiler in supported_compilers):
  sys.tracebacklimit = 0
  raise RuntimeError("Unknown platform/compiler: %s. Choices are: %s" % (
    env_etc.compiler, ", ".join(supported_compilers)))
if (env_etc.compiler == "unix_icpc"):
  # backward compatibility for CCP4
  env_etc.compiler = "unix_icc"

static_exe = libtbx.env.build_options.static_exe

def gcc_common_warn_options():
  result = [
    "-Wall",
    "-Wno-sign-compare",
    "-Wno-unknown-pragmas",
    "-Wno-parentheses"]
  if (    env_etc.gcc_version is not None
      and env_etc.gcc_version >= 40000):
    result.append("-Winit-self")
    if (    env_etc.gcc_version >= 40300
        and env_etc.gcc_version < 40501):
      # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
      result.append("-Wno-array-bounds")
  return result

def clang_common_warn_options():
  return [ '-Wmost', '-Wno-unknown-pragmas', '-Wno-logical-op-parentheses' ]

def enable_more_warnings(env):
  """ Enable more warning for debug_level == 0 """
  if (env_etc._enable_more_warnings == "gcc"):
    # Here we rely on the fact that at debug_level == 0 implies the -w flag
    for flags in ["CCFLAGS", "SHCCFLAGS", "CXXFLAGS", "SHCXXFLAGS"]:
      have_change = False
      patched_flags = []
      for item in env[flags]:
        if (item == "-w"):
          patched_flags.extend(gcc_common_warn_options())
          have_change = True
        else:
          patched_flags.append(item)
      if (have_change):
        env.Replace(**{flags: patched_flags})
  elif env_etc._enable_more_warnings == "clang":
    # For clang, debug_level == 0 has no special flag
    # So search for -W flags instead
    for flags in ["CCFLAGS", "SHCCFLAGS", "CXXFLAGS", "SHCXXFLAGS"]:
      has_warnings_already = False
      for item in env[flags]:
        if item.startswith('-W'):
          has_warnings_already = True
          break
      if not has_warnings_already:
        env.Append(**{flags: clang_common_warn_options()})

def setup_clang_warning(env_etc, warning_level):
  env_etc.__dict__.setdefault("cflags_base", [])

  if warning_level == 0:
    # - pure C code is only 3rd party library we don't care cleaning up bad
    #   programming style: hence no warning
    #
    # - for our C++ code, Boost recent versions started to use C++0x features
    #   that clang warns about, so disable that. Disable array bound checking
    #   as Boost is again too tricky for clang to reason on
    env_etc.cflags_base.extend([ '-w' ])
    env_etc.ccflags_base.extend([ '-Wno-c++0x-extensions',
                                  '-Wno-array-bounds' ])
    env_etc._enable_more_warnings = "clang"
  elif warning_level == 1:
    # not sure about whether -Wmost is the best choice yet
    # time will tell
    env_etc.ccflags_base.extend([ '-Wmost' ])
  else:
    # idem
    env_etc.ccflags_base.extend([ '-Wmost', '-Werror' ])


env_etc.enable_more_warnings = enable_more_warnings
env_etc._enable_more_warnings = None

is_64bit_architecture = libtbx.env_config.is_64bit_architecture()

if (sys.platform == "win32" or
      env_etc.compiler.endswith("mingw")):  # cross-compiling for Windows
  if is_64bit_architecture:
    target_arch = 'x86_64'
  else:
    target_arch = 'x86'
  env_etc.static_libraries = 1
  env_etc.static_bpl = 0
  set_python_include_and_libs(env_etc)
  env_etc.extension_module_suffix = ".pyd"
  if (env_etc.compiler == "win32_cl"):
    cl_exe = full_command_path("cl.exe")
    if (cl_exe is None):
      raise RuntimeError("cl.exe not on PATH")
    VS_UNICODE_OUTPUT = os.environ.get('VS_UNICODE_OUTPUT')
    if VS_UNICODE_OUTPUT is not None:
      # Stop output from being redirected to IDE
      del os.environ['VS_UNICODE_OUTPUT']
    cl_info = easy_run.fully_buffered(
      command='"%s"' % cl_exe,
      join_stdout_stderr=True,
      stdout_splitlines=False).stdout_buffer
    if VS_UNICODE_OUTPUT is not None:
      # Reset VS_UNICODE_OUTPUT
      os.environ['VS_UNICODE_OUTPUT'] = VS_UNICODE_OUTPUT
    if (cl_info.find("Version 13.10") >= 0):
      env_base = Environment(ENV=os.environ, MSVC_VERSION="7.1")
      env_etc.have_manifest_tool = False
    elif (cl_info.find("Version 14.00") >= 0):
      env_base = Environment(
        ENV=os.environ, MSVC_VERSION="8.0", TARGET_ARCH=target_arch)
      env_etc.have_manifest_tool = True
    elif (cl_info.find("Version 15.00") >= 0):
      env_base = Environment(
        ENV=os.environ, MSVC_VERSION="9.0", TARGET_ARCH=target_arch)
      env_etc.have_manifest_tool = True
    elif (cl_info.find("Version 16.00") >= 0):
      env_base = Environment(
        ENV=os.environ, MSVC_VERSION="10.0", TARGET_ARCH=target_arch)
      env_etc.have_manifest_tool = True
    else:
      raise RuntimeError("""\
Unknown or unsupported cl.exe version.

Minimum version is 7.1 (Visual Studio 2003).
For newer versions, please adjust this SConscript.
""")
    print "MSVC_VERSION:", env_base["MSVC_VERSION"]
    env_base.Replace(
      SHCC="cl",
      SHCXX="cl",
      SHLINK="link",
    )
    env_etc.c_link = "link"
    env_etc.ccflags_base = [
      "/nologo",
      "/D_SECURE_SCL=0",
      "/D_CRT_SECURE_NO_DEPRECATE",
      "/DNOMINMAX",
      "/wd4996", # warning C4996: ... was declared deprecated
      "/wd4068", # warning C4068: unknown pragma
      "/Z7", # produce program database file containing debugging symbols.
             # Harmless for release builds
      "/Zm800"]
    if (libtbx.env.build_options.debug_symbols):
      if (not env_etc.no_boost_python):
        print """\
libtbx.scons: Warning: compiling with /MDd:
                       Boost.Python extensions will work reliably
                       only with a debug build of Python."""
      env_etc.ccflags_base.extend([
        "/MDd",
        "/D_DEBUG",
        "/Z7", # see above
        "/D_HAS_ITERATOR_DEBUGGING=0"])
          # speed up memory allocation and deallocation in debug mode
    else:
      env_etc.ccflags_base.append("/MD")
    env_etc.cxxflags_base = [
      "/GR",
      "/EHsc"]
    if (libtbx.env.build_options.optimization):
      opts = ["/DNDEBUG", "/O2"]
      if (    libtbx.env.build_options.msvc_arch_flag is not None
          and not is_64bit_architecture):
        opts.append("/arch:%s" %libtbx.env.build_options.msvc_arch_flag)
      if (env_base["MSVC_VERSION"] != "7.1"):
        opts.append("/fp:fast")
    else:
      opts = ["/Od"]
    opts.append("/DBOOST_ALL_NO_LIB")
    if not libtbx.env.build_options.enable_boost_threads:
      opts.append('/DBOOST_DISABLE_THREADS')
    env_etc.ccflags_base.extend(opts)
    env_etc.shlinkflags = ["/nologo", "/incremental:no", "/dll", "/MANIFEST"]
    if (libtbx.env.build_options.debug_symbols):
      env_etc.shlinkflags.append("/DEBUG")
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
    env_etc.libm = []
  elif (env_etc.compiler == "win32_icc"):
    cl_exe = full_command_path("icl.exe")
    if (cl_exe is None):
      raise RuntimeError("cl.exe not on PATH")
    cl_info = easy_run.fully_buffered(
      command='"%s"' % cl_exe,
      join_stdout_stderr=True,
      stdout_splitlines=False).stdout_buffer
    if (cl_info.find("Version 13.10") >= 0):
      env_base = Environment(ENV=os.environ, MSVC_VERSION="7.1")
    elif (cl_info.find("Version 14.00") >= 0):
      env_base = Environment(ENV=os.environ, MSVC_VERSION="8.0")
    else:
      env_base = Environment(ENV=os.environ)
    print "MSVC_VERSION:", env_base["MSVC_VERSION"]
    env_etc.have_manifest_tool = op.isfile(
      op.join(op.dirname(cl_exe), "mt.exe"))
    env_base.Replace(
      CC="icl",
      CXX="icl",
      SHCC="icl",
      SHCXX="icl",
      SHLINK="link",
    )
    env_etc.ccflags_base = """
      /nologo
      /D_SECURE_SCL=0
      /D_CRT_SECURE_NO_DEPRECATE
      /wd4996
      /Zm800
    """.split()
    env_etc.cxxflags_base = """
      /GR
      /EHsc
      /DBOOST_ALL_NO_LIB
      /DBOOST_DISABLE_THREADS
    """.split()
    if (libtbx.env.build_options.optimization):
      opts = [
        "/DNDEBUG", "/Ob2", "/Ox", "/Oi", "/Ot", "/GT", "/MD", "/EHsc",
        "/fp:fast"]
      if (    libtbx.env.build_options.msvc_arch_flag is not None
          and not is_64bit_architecture):
        opts.append("/arch:%s" %libtbx.env.build_options.msvc_arch_flag)
    else:
      opts = ["/Od"]
    if (libtbx.env.build_options.debug_symbols):
      raise RuntimeError("Debug build not supported.")
    env_etc.ccflags_base.extend(opts)
    env_etc.shlinkflags = "/nologo /incremental:no /dll"
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
    env_etc.libm = []

  elif env_etc.compiler.endswith("_mingw"):
    if env_etc.compiler.startswith('win'):
      env_base = Environment(ENV=os.environ, tools=['mingw'])
    else:
      libtbx_dist_path = libtbx.env.under_dist(module_name="libtbx", path="")
      env_base = Environment(ENV=os.environ, tools=['crossmingw'],
                                             toolpath=[libtbx_dist_path])
      assert env_base['CC'] != "gcc", \
        "Cross-compiler gcc not found. How is it prefixed? (see crossmingw.py)"
    # the options here are mostly copied from unix_gcc
    env_etc.libm = ["m"]
    env_etc.static_libraries = 0
    env_etc.static_bpl = 0
    env_etc.gcc_version = libtbx.env_config.get_gcc_version(
                                                 command_name=env_base['CC'])
    env_etc.shlibsuffix = env_base['SHLIBSUFFIX']
    env_etc.c_link = env_base['CC']
    env_etc.ccflags_base = [
      "-fPIC",
      "-fno-strict-aliasing"]
    env_etc.cxxflags_base = []
    if (env_etc.gcc_version >= 40400 and determine_cpp0x_flag()):
      env_etc.cxxflags_base.append("-std=c++0x")
    if (libtbx.env.build_options.warning_level == 0):
      warn_options = ["-w"]
      env_etc._enable_more_warnings = "gcc"
    elif (libtbx.env.build_options.warning_level == 1):
      warn_options = gcc_common_warn_options()
    else:
      warn_options = gcc_common_warn_options() + ["-Werror"]
    env_etc.ccflags_base.extend(warn_options)
    if (libtbx.env.build_options.optimization):
      opts = ["-DNDEBUG", "-O3", "-ffast-math"]
      if (env_etc.gcc_version >= 40100 and is_64bit_architecture):
        opts.insert(2, "-funroll-loops")
      # -march=native can make some tests crashing
      #if (env_etc.gcc_version >= 40300):
      #  opts.insert(1, "-march=native")
    else:
      opts = ["-O0", "-fno-inline"]
    if (libtbx.env.build_options.mode == "profile"):
      opts.insert(0, "-pg")
      opts.insert(1, "-ggdb")
    elif (libtbx.env.build_options.debug_symbols):
      opts.insert(0, "-g")
    opts.append("-DBOOST_ALL_NO_LIB")
    if (libtbx.env.build_options.enable_boost_threads):
      opts.append("-pthread")
    else:
      opts.append('-DBOOST_DISABLE_THREADS')
    env_etc.ccflags_base.extend(opts)
    if (static_exe):
      env_base.Prepend(LINKFLAGS=["-static"])
      static_exe = None
      lkfl = []
    elif libtbx.env.build_options.debug_symbols:
      lkfl = ["-rdynamic"]
    else:
      lkfl = ["-s"]
    env_etc.shlinkflags = ["-shared"]
    if (libtbx.env.build_options.mode == "profile"):
      env_base.Prepend(LINKFLAGS=["-pg"])
      env_etc.shlinkflags.append("-pg")
    env_base.Append(LINKFLAGS=lkfl)
    env_etc.shlinkflags.extend(lkfl)
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
    env_etc.mac_os_use_dsymutil = False

  else:
    sys.tracebacklimit = 0
    raise RuntimeError("Unknown compiler choice: %s" % env_etc.compiler)
else:
  env_etc.mac_cpu = None
  env_etc.mac_cpu_is_g4 = None
  env_etc.mac_os_version = None
  env_etc.mac_os_use_dsymutil = False
  if (sys.platform.startswith("darwin")):
    env_etc.mac_cpu = easy_run.fully_buffered(
      command="/usr/bin/uname -p") \
      .raise_if_errors() \
      .stdout_lines[0].strip()
    def mac_cpu_is_g4():
      if (env_etc.mac_cpu != "powerpc"): return False
      lines = easy_run.fully_buffered(
        command="/usr/sbin/system_profiler SPHardwareDataType") \
        .raise_if_errors() \
        .stdout_lines
      for line in lines:
        line = line.strip()
        if (   line.startswith("Machine Name: ")
            or line.startswith("Machine Model: ")
            or line.startswith("CPU Type: ")):
          if (line.find(" G4 ") > 0 or line.endswith(" G4")):
            return True
          break
      return False
    env_etc.mac_cpu_is_g4 = mac_cpu_is_g4()
    env_etc.mac_os_version = ".".join(easy_run.fully_buffered(
      command="/usr/bin/sw_vers -productVersion")
      .raise_if_errors()
      .stdout_lines[0].strip().split(".")[:2])
    if (env_etc.mac_os_version == "10.3"):
      os.environ["MACOSX_DEPLOYMENT_TARGET"] = env_etc.mac_os_version
    elif (env_etc.mac_os_version == "10.4"):
      pass
    elif (libtbx.env.build_options.debug_symbols):
      env_etc.mac_os_use_dsymutil = True
  env_base = Environment(
    ENV=os.environ,
    tools=['cc','g++','gnulink','ar'])
  set_python_include_and_libs(env_etc)
  if (sys.platform.startswith("darwin")):
    env_etc.shlibsuffix = ".dylib"
    env_etc.extension_module_suffix = ".so"
    env_base.Append(LIBSUFFIXES=[".dylib"])
  else:
    env_etc.shlibsuffix = ".so"
    env_etc.extension_module_suffix = ".so"
  env_etc.libm = []
  env_etc.libm.append("m")
  if (env_etc.compiler in ["unix_gcc", "unix_gcc4"]):
    env_etc.static_libraries = 0
    env_etc.static_bpl = 0
    cc = env_etc.compiler.replace("unix_", "")
    cxx = cc.replace("gcc", "g++")
    env_etc.gcc_version = libtbx.env_config.get_gcc_version(command_name=cc)
    env_base.Replace(
      CC=cc,
      SHCC=cc,
      CXX=cxx,
      LINK=cxx,
      SHCXX=cxx,
      SHLINK=cxx,
      SHLIBSUFFIX=env_etc.shlibsuffix,
    )
    env_etc.c_link = cc
    env_etc.ccflags_base = [
      "-fPIC",
      "-fno-strict-aliasing"]
    env_etc.cxxflags_base = []
    if (env_etc.gcc_version >= 40400 and determine_cpp0x_flag()):
      env_etc.cxxflags_base.append("-std=c++0x")
    if (libtbx.env.build_options.warning_level == 0):
      warn_options = ["-w"]
      env_etc._enable_more_warnings = "gcc"
    elif (libtbx.env.build_options.warning_level == 1):
      warn_options = gcc_common_warn_options()
    else:
      warn_options = gcc_common_warn_options() + ["-Werror"]
    env_etc.ccflags_base.extend(warn_options)
    if (libtbx.env.build_options.optimization):
      opts = ["-DNDEBUG", "-O3", "-ffast-math"]
      if (env_etc.gcc_version >= 40100 and is_64bit_architecture):
        opts.insert(2, "-funroll-loops")
      if (env_etc.gcc_version >= 40300):
        opts.insert(1, "-march=native")
    else:
      opts = ["-O0", "-fno-inline"]
    if (libtbx.env.build_options.mode == "profile"):
      opts.insert(0, "-pg")
      opts.insert(1, "-ggdb")
    elif (libtbx.env.build_options.debug_symbols):
      opts.insert(0, "-g")
    opts.append("-DBOOST_ALL_NO_LIB")
    if (libtbx.env.build_options.enable_boost_threads):
      opts.append("-pthread")
    else:
      opts.append('-DBOOST_DISABLE_THREADS')
    env_etc.ccflags_base.extend(opts)
    if (static_exe):
      env_base.Prepend(LINKFLAGS=["-static"])
      static_exe = None
      lkfl = []

    elif libtbx.env.build_options.debug_symbols:
      lkfl = ["-rdynamic"]

    else:
      lkfl = ["-s"]
    env_etc.shlinkflags = ["-shared"]
    if (libtbx.env.build_options.mode == "profile"):
      env_base.Prepend(LINKFLAGS=["-pg"])
      env_etc.shlinkflags.append("-pg")
    env_base.Append(LINKFLAGS=lkfl)
    env_etc.shlinkflags.extend(lkfl)
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
  elif (env_etc.compiler == "unix_clang"):
    env_etc.static_libraries = 0
    env_etc.static_bpl = 0
    cc = "clang"
    cxx = "clang++"
    env_base.Replace(
      CC=cc,
      SHCC=cc,
      CXX=cxx,
      LINK=cxx,
      SHCXX=cxx,
      SHLINK=cxx,
      SHLIBSUFFIX=env_etc.shlibsuffix)
    env_etc.c_link = cc
    env_etc.ccflags_base = [
      "-fPIC",
      "-fno-strict-aliasing"]
    env_etc.cxxflags_base = []
    setup_clang_warning(env_etc, libtbx.env.build_options.warning_level)
    if (libtbx.env.build_options.optimization):
      opts = [
        "-DNDEBUG",
        "-O3",
        "-ffast-math"]
    else:
      opts = ["-O0", "-fno-inline"]
    if (libtbx.env.build_options.debug_symbols):
      opts.insert(0, "-g")
    opts.append("-DBOOST_ALL_NO_LIB")
    opts.append('-DBOOST_DISABLE_THREADS')
    env_etc.ccflags_base.extend(opts)
    if (static_exe):
      env_base.Prepend(LINKFLAGS=["-static"])
      static_exe = None
    env_etc.shlinkflags = ["-shared"]
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
  elif (env_etc.compiler == "unix_icc"):
    env_etc.static_libraries = 0
    env_etc.static_bpl = 0
    env_etc.icc_version = libtbx.env_config.get_gcc_version(command_name="icc")
    env_base.Replace(
      CC="icc",
      SHCC="icc",
      CXX="icpc",
      LINK="icpc",
      SHCXX="icpc",
      SHLINK="icpc",
    )
    env_etc.c_link = "icc"
    env_etc.ccflags_base = [
      "-fPIC", "-fno-strict-aliasing", "-vec_report0", "-wd161,167"]
    env_etc.cxxflags_base = ["-Wno-deprecated"]
    if (env_etc.icc_version >= 100000 and determine_cpp0x_flag()):
      env_etc.cxxflags_base.append("-std=c++0x")
    if (libtbx.env.build_options.optimization):
      opts = ["-DNDEBUG", "-O2"]
    else:
      opts = ["-O0"]
    if (libtbx.env.build_options.debug_symbols):
      opts.insert(0, "-g")
    opts.append("-DBOOST_ALL_NO_LIB")
    if not libtbx.env.build_options.enable_boost_threads:
      opts.append('-DBOOST_DISABLE_THREADS')
    env_etc.ccflags_base.extend(opts)
    env_etc.shlinkflags = ["-shared"]
    env_etc.shlinkflags_bpl = list(env_etc.shlinkflags)
    if (static_exe):
      env_base.Prepend(LINKFLAGS=["-Bstatic"])
      static_exe = None
  elif env_etc.compiler.startswith("darwin"):
    env_etc.gcc_version = libtbx.env_config.get_gcc_version()
    env_etc.static_libraries = 1
    env_etc.static_bpl = 0
    if env_etc.compiler == "darwin_c++":
      cc = "cc"
      cxx = "c++"
    else:
      cc  = env_etc.compiler.replace('darwin_','')
      if cc == 'clang':
        cxx = 'clang++'
      else:
        cxx = cc.replace('gcc', 'g++')
        if env_etc.compiler.endswith("4.2"):
          if libtbx.env.build_options.enable_boost_threads:
            build_nb = libtbx.env_config.get_darwin_gcc_build_number(gcc=cxx)
            min_build_nb = 5564 # XCode 3.1 beta distributed as part of
                                # the iPhone SDK and XCode 3.1
            if build_nb < min_build_nb:
              raise RuntimeError("Apple GCC 4.2 (build %i) incompatible with "
                                 "--enable_boost_threads" % build_nb)
    link_flags = ["-w"] # suppress "source/lib does not exist" warning
    if (env_etc.gcc_version >= 40201 and libtbx.env.build_options.force_32bit) :
      link_flags.extend(["-arch", "i386"])
    env_base.Replace(
      CC=cc,
      SHCC=cc,
      CXX=cxx,
      LINK=cxx,
      LINKFLAGS=link_flags,
      SHCXX=cxx,
      SHLINK=cxx,
      SHLIBSUFFIX=env_etc.shlibsuffix,
    )
    env_etc.c_link = "cc"
    env_etc.ccflags_base = [
      "-fPIC",
      "-fno-strict-aliasing"]
    if cxx == "clang++":
      setup_clang_warning(env_etc, libtbx.env.build_options.warning_level)
    else:
      if (libtbx.env.build_options.warning_level == 0):
        warn_options = ["-w"]
        env_etc._enable_more_warnings = "gcc"
      elif (libtbx.env.build_options.warning_level == 1):
        warn_options = gcc_common_warn_options()
      else:
        warn_options = gcc_common_warn_options() + ["-Werror"]
      env_etc.ccflags_base.extend(warn_options)
    if (cxx == "c++"):
      env_etc.ccflags_base.extend(["-no-cpp-precomp"])
      if (env_etc.gcc_version < 40201):
        env_etc.ccflags_base.append("-Wno-long-double")
    env_etc.cxxflags_base = []
    if (env_etc.gcc_version < 40000):
      env_etc.cxxflags_base.append("-fcoalesce-templates")
    env_etc.cxxflags_base.append("-DBOOST_ALL_NO_LIB")
    if not libtbx.env.build_options.enable_boost_threads:
      env_etc.cxxflags_base.append("-DBOOST_DISABLE_THREADS")
    if (libtbx.env.build_options.optimization):
      opts = ["-DNDEBUG", "-O3", "-ffast-math"]
      if (cxx == "c++"
            and env_etc.gcc_version == 40000):
        opts[1] = "-O2" # Apple's optimizer is broken
      elif (cxx == "c++"
              and env_etc.gcc_version == 40201
                and libtbx.env.build_options.force_32bit):
        opts[1] = "-O1" # Apple's optimizer is even more broken
      elif (libtbx.env.build_options.max_optimized):
        if cxx != "clang++": opts[1] = "-fast"
        if (env_etc.mac_cpu_is_g4):
          opts.insert(1, "-mcpu=G4")
    else:
      opts = ["-O0", "-fno-inline"]
    if (libtbx.env.build_options.debug_symbols):
      opts.insert(0, "-g")
      if env_etc.compiler == "darwin_gcc-4.2":
        build_nb = libtbx.env_config.get_darwin_gcc_build_number(gcc="g++-4.2")
        # The gcc 4.2 shipped by Apple with all versions of XCode
        # on Leopard is plagued by bug 27574:
        # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27574
        # Workaround till Apple applies the patch that has already landed
        # on GCC 4.2 branch: use the old stabs debugging info format instead
        # of the new now native dwarf.
        # Beware: valgrind does not support stabs on Darwin: just use
        # gcc 4.0.1 for code to be tested by valgrind
        if build_nb < 5646:
          # fixed on 10.6 (Snow Leopard) whose Dev Tools start with that build
          opts.insert(0, "-gstabs+")
    env_etc.shlinkflags = []
    env_etc.shlinkflags_bpl = [
      "-w", # suppress "source/lib does not exist" warning
      "-bundle",
      "-undefined", "dynamic_lookup"]
    if (env_etc.gcc_version >= 40201 and libtbx.env.build_options.force_32bit):
      env_etc.ccflags_base.extend(["-arch", "i386"])
      env_etc.cxxflags_base.extend(["-arch", "i386"])
      env_etc.shlinkflags.extend(["-arch", "i386"])
      env_etc.shlinkflags_bpl.extend(["-arch", "i386"])
    env_etc.ccflags_base.extend(opts)
    if (static_exe):
      # no action required
      static_exe = None
  else:
    sys.tracebacklimit = 0
    raise RuntimeError("Unknown compiler choice: %s" % env_etc.compiler)
  # user options for any compiler from the environment variables
  # at the time of configure
  opts = libtbx.env.build_options
  if( opts.use_environment_flags ):
    print "libtbx.scons: using flags from initial environment: "
    print "              CXXFLAGS = ", opts.env_cxxflags
    print "              CFLAGS = ", opts.env_cflags
    print "              CPPFLAGS = ", opts.env_cppflags
    flg = opts.env_cxxflags.split(" ")
    if( hasattr(env_etc, "cxxflags_base") ):
      env_etc.cxxflags_base.extend(flg)
    else:
      env_etc.cxxflags_base = flg
    flg = opts.env_cflags.split(" ")
    if( hasattr(env_etc, "ccflags_base") ):
      env_etc.ccflags_base.extend(flg)
    else:
      env_etc.ccflags_base = flg
    flg = opts.env_cppflags.split(" ")
    env_etc.ccflags_base.extend(flg)

if ARGUMENTS.get('TERSE') == '1':
  compiling_message = "Compiling $TARGET"
  linking_message   = "Linking $TARGET"
  env_base['CCCOMSTR']     = compiling_message
  env_base['CXXCOMSTR']    = compiling_message
  env_base['LINKCOMSTR']   = linking_message
  env_base['SHCCCOMSTR']   = compiling_message
  env_base['SHCXXCOMSTR']  = compiling_message
  env_base['SHLINKCOMSTR'] = linking_message

env_base.Replace(LIBPATH=["#lib"])
env_base.Replace(SHOBJSUFFIX=env_base["OBJSUFFIX"])

# XXX backward compatibility 2009-08-21
# future: remove ccflags_base, cxxflags_base from env_etc
def env_base_sync_with_env_etc():
  env_base.Replace(
    CFLAGS=env_etc.cflags_base,
    CCFLAGS=env_etc.ccflags_base,
    CXXFLAGS=env_etc.cxxflags_base,
    SHCFLAGS=env_etc.cflags_base,
    SHCCFLAGS=env_etc.ccflags_base,
    SHCXXFLAGS=env_etc.cxxflags_base)
env_base_sync_with_env_etc()

if (static_exe):
  sys.tracebacklimit = 0
  raise RuntimeError("Static executables not supported on this platforms.")

if (env_etc.static_libraries == 0
    and libtbx.env.build_options.static_libraries):
  env_etc.static_libraries = 1
env_etc.static_exe = libtbx.env.build_options.static_exe

env_etc.libtbx_build = libtbx.env.build_path
env_etc.libtbx_include = libtbx.env.under_build("include")
env_etc.libtbx_lib = abs(libtbx.env.lib_path)

def enable_openmp_if_possible():
  test_code = r"""%s
#include <iostream>
#include <iomanip>
int main() {
  double e, pi;
  const int N=100000;
  #pragma omp parallel sections shared(e, pi)
  {
    #pragma omp section
    {
      e = 1;
      double a = 1;
      for(int i=1; i<N; ++i) {
        a /= i;
        e += a;
      }
    }
    #pragma omp section
    {
      pi = 0;
      double a=1, b=3;
      for(int i=1; i<2*N; ++i) {
        pi += 1/a - 1/b;
        a += 4;
        b += 4;
      }
      pi *= 4;
    }
  }
  std::cout << std::setprecision(6) << "e=" << e << ", pi=" << pi << "\n";
}
""" % ['', '#include <omp.h>'][int(sys.platform == 'win32')]
  #
  env_etc.have_openmp = False
  if (env_etc.compiler == "win32_cl" and env_base["MSVC_VERSION"] == "7.1"):
    print "libtbx.scons: OpenMP is not available."
    return
  if env_etc.compiler == "darwin_clang":
    print "libtbx.scons: OpenMP is not supported by clang yet."
    return
  if (libtbx.env.build_options.enable_boost_threads):
    if (libtbx.env.build_options.enable_openmp_if_possible):
      print "libtbx.scons: OpenMP is disabled because Boost.Thread is enabled."
    return
  if (not libtbx.env.build_options.enable_openmp_if_possible):
    print "libtbx.scons: OpenMP is disabled."
    return
  from libtbx.utils import select_matching
  compiler_options, linker_options = select_matching(
    key=env_etc.compiler,
    choices=[
      ('^win32_cl$',  [['/openmp'], []]),
      ('^win32_icc$', [['/Qopenmp']]*2),
      ('^unix_icc$',  [['-openmp']]*2),
      ('darwin',      [['-fopenmp']]*2),
      ('gcc',         [['-fopenmp']]*2),
      ('mingw',       [['-fopenmp']]*2),
    ],
    default=[None]*2)
  if (compiler_options is None):
    print "libtbx.scons: OpenMP is not supported."
    return
  env = env_base.Clone()
  env.Append(CCFLAGS=compiler_options)
  env.Append(LINKFLAGS=linker_options)
  conf = env.Configure()
  flag, output = conf.TryRun(test_code, extension='.cpp')
  conf.Finish()
  if (not flag or output.strip() != "e=2.71828, pi=3.14159"):
    print "libtbx.scons: OpenMP is not usable."
    return
  msg = "libtbx.scons: OpenMP is not usable (in shared libraries)."
  if (    sys.platform == 'linux2'
      and not env_etc.no_boost_python
      and env_etc.gcc_version is not None
      and env_etc.gcc_version < 40300):
    # C.f. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=28482
    print msg
    return
  print "libtbx.scons: OpenMP is available."
  env_etc.have_openmp = True
  env_etc.ccflags_base.extend(compiler_options)
  env_base.Append(LINKFLAGS=linker_options)
  env_etc.shlinkflags.extend(linker_options)
  env_etc.shlinkflags_bpl.extend(linker_options)
  env_base_sync_with_env_etc()

def enable_cuda_if_possible():
  env_etc.enable_cuda = False
  if (libtbx.env.build_options.enable_cuda):

    cuda_version = easy_run.fully_buffered(command='nvcc --version',
                                           join_stdout_stderr=True).stdout_lines
    if ('command not found' in cuda_version[0]):
      raise RuntimeError('nvcc was not found')
    for line in cuda_version:
      if ('release' in line):
        break
    major = None
    cuda_version = line.split(',')
    if (len(cuda_version) >= 2):
      flds = cuda_version[1].split()
      if (len(flds) >= 2):
        fld = flds[1].split('.')[0]
        try: major = int(fld)
        except ValueError: pass
    if ( (major is None) or (major < 4) ):
      raise RuntimeError(
        'CUDA Toolkit 4.0 and higher is required.'
        ' Current CUDA version: %s' % line)
    env_etc.enable_cuda = True

    env_base['NVCC'] = 'nvcc'

    linker_flags = env_base['SHLINKFLAGS']
    for flag in linker_flags:
      if (flag.find(' ') >= 0):
        raise RuntimeError(
          'Not implemented: wrapping of linker flags with'
          ' embedded spaces: %s' % flag)
    linker_flags = ' '.join(linker_flags)
    env_base['NVCCSHLINKFLAGS'] = ['-shared','--linker-options',linker_flags]

    cc_flags = env_base['CCFLAGS']
    for flag in cc_flags:
      if (flag.find(' ') >= 0):
        raise RuntimeError(
          'Not implemented: wrapping of compiler flags with'
          ' embedded spaces: %s' % flag)
    cc_flags = ' '.join(cc_flags)
    env_base['NVCCFLAGS'] = ['-arch=compute_20','-use_fast_math','--shared',
                             '--compiler-options',cc_flags]

    shared_object_builder =\
      Builder\
      (action = '$NVCC -c -o $TARGET $_CPPINCFLAGS $NVCCFLAGS $SOURCES',
       suffix = '.o',
       src_suffix = '.cu',
       source_scanner = SCons.Scanner.C.CScanner())
    shared_library_builder =\
      Builder\
      (action ='$NVCC -o $TARGET $NVCCSHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
       suffix = '$SHLIBSUFFIX',
       src_suffix = '.o')

    env_base['BUILDERS']['cudaSharedLibrary'] = shared_library_builder
    env_base['BUILDERS']['cudaSharedLibrary'].add_src_builder(shared_object_builder)

enable_openmp_if_possible()
enable_cuda_if_possible()

Export("env_base", "env_etc")
