#!/usr/bin/env python3
'''
This tool is used to generate user-config.jam and site-config.jam
for boost library automatically. 
'''
import os
import os.path

__author__    = "Tianjiao Yin (ytj000@gamil.com)"
__date__      = "Fri, 24 Jun 2011 18:58:58 +0800"
__copyright__ = "Copyright 2011 Tianjiao Yin"
__license__   = "WTFPLv2"
__version__   = "0.3"

# Default path
site_path = "/etc/site-config.jam"
boostbook_path = "/usr/share/boostbook"

# Constant default path
QT_PATH = "/usr/share/qt" # useless useless
DOCBOOK_PATH = "/usr/share/xml/docbook/"

# Supporting list
TOOLS_LIST = ("specific_gcc", "python", "mpi", "boostbook")

# "using name ;" if name is executable
# Trivial tools don't need a special function.
# May be I should remove this and write functions for all;
TRIVIAL_TOOLS = ("gcc", "doxygen", "quickbook")

SITE_DATA = r'''# Copyright 2003, 2005 Douglas Gregor
# Copyright 2004 John Maddock
# Copyright 2002, 2003, 2004, 2007 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://w...content-available-to-author-only...t.org/LICENSE_1_0.txt)

#   This file is used to configure your Boost.Build installation. You can modify
# this file in place, or you can place it in a permanent location so that it
# does not get overwritten should you get a new version of Boost.Build. See:
#
#   http://w...content-available-to-author-only...t.org/boost-build2/doc/html/bbv2/overview/configuration.html
#
# for documentation about possible permanent locations.

#   This file specifies which toolsets (C++ compilers), libraries, and other
# tools are available. Often, you should be able to just uncomment existing
# example lines and adjust them to taste. The complete list of supported tools,
# and configuration instructions can be found at:
#
#   http://b...content-available-to-author-only...t.org/boost-build2/doc/html/bbv2/reference/tools.html
#

#   This file uses Jam language syntax to describe available tools. Mostly,
# there are 'using' lines, that contain the name of the used tools, and
# parameters to pass to those tools -- where paremeters are separated by
# semicolons. Important syntax notes:
#
#   - Both ':' and ';' must be separated from other tokens by whitespace
#   - The '\' symbol is a quote character, so when specifying Windows paths you
#     should use '/' or '\\' instead.
#
# More details about the syntax can be found at:
#
#   http://b...content-available-to-author-only...t.org/boost-build2/doc/html/bbv2/advanced.html#bbv2.advanced.jam_language
#


# -------------------
# MSVC configuration.
# -------------------

# Configure msvc (default version, searched for in standard locations and PATH).
# using msvc ;

# Configure specific msvc version (searched for in standard locations and PATH).
# using msvc : 8.0 ;


# ----------------------
# Borland configuration.
# ----------------------
# using borland ;


# ----------------------
# STLPort configuration.
# ----------------------

#   Configure specifying location of STLPort headers. Libraries must be either
# not needed or available to the compiler by default.
# using stlport : : /usr/include/stlport ;

# Configure specifying location of both headers and libraries explicitly.
# using stlport : : /usr/include/stlport /usr/lib ;


# ------------------
# GCC configuration.
# ------------------

# Configure gcc (default version).
{gcc}

# Configure specific gcc version, giving alternative name to use.
{specific_gcc}


# -----------------
# QT configuration.
# -----------------

# Configure assuming QTDIR gives the installation prefix.
# using qt ;

# Configure with an explicit installation prefix.
# using qt : /usr/opt/qt ;

# ---------------------
# Python configuration.
# ---------------------

# Configure specific Python version.
{python}


# ------------------
# MPI configuration.
# ------------------
{mpi}


# ------------------------
# BoostBook configuration.
# ------------------------
{boostbook}


# ----------------------
# Doxygen configuration.
# ----------------------
{doxygen}


# ------------------------
# Quickbook configuration.
# ------------------------
{quickbook}


# --------------------
# Extra configuration.
# --------------------
'''

def get_all_exec():
    '''
    Find all executable files.
    Return file_names_dict, paths_list
    '''
    def is_exe(fpath):
        return os.path.exists(fpath) and os.access(fpath, os.X_OK)
    rec = set()
    ans = []
    for path in os.environ["PATH"].split(os.pathsep):
        if os.path.exists(path):
            for name in os.listdir(path):
                exe_file = os.path.join(path, name)
                if name not in rec and is_exe(exe_file):
                    ans.append(exe_file)
                    rec.add(name)
    return rec, ans

all_name, all_path = get_all_exec()

def specific_gcc():
    gccs = []
    for i in all_name:
        if len(i) > 4 and i[:4] == "g++-":
            gccs.append("using gcc : {} : {} ;\n".format(i[4:], i))
    return "\n".join(gccs)



def python():

    def getpyver(cmd):
        '''
        Get python version
        getpyver(python_path) --> python_version

        Simple example:
            getpyver("python3") --> "3.2"
        '''
        import subprocess
        p = subprocess.Popen((cmd, "--version"), stderr=subprocess.PIPE)
        p.wait();
        p = p.stderr.read().decode('ascii')
        p = p.split()[1].split('.')
        return p[0] + '.' + p[1]

    def get_cpy(cmd):
        '''
        Get python configure.
        '''
        pyv = getpyver(cmd)
        pyinc = "/usr/include/python{}mu".format(pyv)
        if not os.path.isdir(pyinc):
            pyinc = "/usr/include/python{}".format(pyv)
        for i in all_path:
            fpath, fname = os.path.split(i)
            if "python{}".format(pyv[0]) == fname:
                loca = i;
                break
        py = "using python : {} : {} : {} : /usr/lib ;"
        return py.format(pyv, loca, pyinc)

    pys = []

    # We choose python2 as default, so python2 must appear before than python3.
    if "python2" in all_name:
        pys.append(get_cpy("python2"))
    if "python3" in all_name:
        pys.append(get_cpy("python3"))
    return "\n".join(pys)

def mpi():
    if "mpicc" in all_name:
        return "using mpi ;"
    return ""

def boostbook():
    if not os.path.isdir(boostbook_path): 
        return ''
    if not os.path.isdir(DOCBOOK_PATH):
        return ''
    s = "using boostbook\n: {DOCBOOK_XSL_DIR}\n: {DOCBOOK_DTD_DIR}\n: {BOOSTBOOK_DIR} ;"
    ret = {}
    path = (os.path.join(DOCBOOK_PATH, name) for name in os.listdir(DOCBOOK_PATH))
    l = [i for i in path if os.path.isdir(i)]
    xsl, xml = [], []
    for i in l:
        if "xsl" in i:
            xsl.append(i)
        if "xml" in i:
            xml.append(i)

    if not xsl or not xml:
        return ""

    # Find the newest version
    def version_key(version):
        import re
        return re.split('\D*', version)
    
    ret["BOOSTBOOK_DIR"] = boostbook_path
    ret["DOCBOOK_XSL_DIR"] = max(xsl, key = version_key)
    ret["DOCBOOK_DTD_DIR"] = max(xml, key = version_key)
    return s.format(**ret)

def site_config():
    conf = {}
    for name in TRIVIAL_TOOLS:
        conf[name] = "using {} ;".format(name) if name in all_name else ""
    for name in TOOLS_LIST:
        conf[name] = globals()[name]()
    return conf

def main():
    """
    Generate configuration file for boost build automatically.
    """
    global site_path
    global boostbook_path
    import argparse
    parser = argparse.ArgumentParser(
            # display before help
            description=main.__doc__,
            # display after help
            epilog=("License {}. This is free software. "
                "If you are lucky, this program will not delete your /usr. "
                "However, It has not been tested widely."
                ).format(__license__),
            )
    parser.add_argument('-s', '--setup',
            required=True,
            action='store_true',
            help="You have to input this argument, otherwise it will not proceed."
            )
    parser.add_argument('--version', 
            action='version', 
            version='%(prog)s ' + __version__
            )
    parser.add_argument('-o',
            default=site_path,
            help="Place the configuration into %(metavar)s (default: %(default)s)",
            metavar="<file>"
            )
    parser.add_argument('-b', "--boostbook_path",
            default=boostbook_path,
            help="Boost book directory (default: %(default)s)",
            metavar="<path>"
            )
    parser.add_argument('-a', "--add_tools",
            nargs=2,
            # Bad idea:
            # If using nargs='*' instead,
            # No need to type quotation marks.
            # However, it will confuse the user.
            action='append',
            help=('Force to add a tool. '
                'Simple usage: -a gcc "using gcc : : \\"distcc g++\\" ;" '
                ),
            metavar=("<name>", "<information>")
            )
    args = parser.parse_args()
    site_path = args.o
    boostbook_path = args.boostbook_path
    add_conf = {}
    if args.add_tools:
        for i in args.add_tools:
            add_conf[i[0]] = " ".join(i[1:])
    conf = site_config()
    conf.update(add_conf)
    data = SITE_DATA
    for i in conf:
        if i not in TOOLS_LIST and i not in TRIVIAL_TOOLS:
            data += "{" + i + "}\n"
    data = data.format(**conf)
    open(site_path,"w").write(data)

if __name__ == "__main__":
    main()
