# Part of the A-A-P recipe executive: Setup using MingW

# Copyright (c) 2002-2003 stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING

"""mingw.py

This module sets up variables and actions for using the MingW compiler tools.

The following variables are used to find the tools.

 - MINGPREFIX  - prefix of tool names.  For example, if this is set to 
                 "mingw32-", the tool will look for a C compiler called
                 "mingw32-gcc".  The default is to try various prefixes until
                 the gcc tool is found.
 - MINGCC      - name, or full path, of the MingW C compiler.  The default
                 is $(MINGW_PREFIX)gcc
 - MINGCXX     - name, or full path, of the MingW C++ compiler.  The default
                 is $(MINGW_PREFIX)g++
 - MINGAR      - name, or full path, of the MingW version of ar.  The default
                 is $(MINGW_PREFIX)ar
 - MINGRANLIB  - name, or full path, of the MingW version of ranlib.  The
                 default is $(MINGW_PREFIX)ranlib
 - MINGDLLTOOL - name, or full path, of the MingW version of dlltool.
                 The default is $(MINGW_PREFIX)dlltool
"""

from RecPython import *
import Global
from Action import action_add
from Dictlist import str2dictlist
from RecPos import RecPos

def _guess_prefix():
    """
    Guess the prefix of the tools in the MingW toolchain, by testing for the
    existence of the "gcc" tool at a predefined set of locations.
    If the MINGPREFIX variable is set, simply returns its value.
    If MINGCC is set, tries to guess the prefix from its value.
    If MINGCXX is set, tries to guess the prefix from its value.

    In addition, the following prefixes are tried (in order):

     - "mingw32-" (XXX - where is this used - was in old version of this file)
     - "i586-mingw32msvc-gcc-" (Used for the mingw cross compiler on Debian.)
     - "" (This is tested last, since if mingw is installed as a cross
           compiler, there may well be a non-mingw gcc installed.)

    If the gcc tool cannot be found at any of the prefixes, returns None.
    If the gcc tool is found, returns a list of prefixes at which it is found.
    """
    rd = Global.globals
    prefix = rd.get("MINGPREFIX")
    if prefix:
        # If MINGPREFIX is set, try it (and only it)
        prefixes = [ prefix ]
    else:
        # Standard set of prefixes to try
        # An entry should be added to this list whenever a user complains
        # about mingw not being found on their system.  The list in the
        # documentation about should be kept in sync, and explanations of the
        # situtation the prefix is used in should also be added.
        prefixes = [ "mingw32-", "i586-mingw32msvc-", "" ]

        # If MINGCC is set, try to extract the prefix from that
        path = rd.get("MINGCC")
        if path:
            if path.endswith("gcc"):
                prefixes = [ path[:-3] ] + prefixes

        # If MINGCXX is set, try to extract the prefix from that
        path = rd.get("MINGCXX")
        if path:
            if path.endswith("g++"):
                prefixes = [ path[:-3] ] + prefixes

    # Check if a 'gcc' exists at any of the possible prefixes
    results = []
    for prefix in prefixes:
        name = prefix + 'gcc'
        if program_path(name):
            results.append(prefix)

    # No mingw found
    if len(results) == 0:
        return None
    return results

def exists():
    """
    Return TRUE when the MingW toolchain can be found.
    This will look for the gcc tool at MINGPREFIX if it is set.
    Otherwise, it will try to guess the prefix from the MINGCC and MINGCXX
    variables and also look through predefined set of possible paths.
    If it finds a suitable candidate, it will then check that the tool
    actually is the mingw compiler by running it with the --version argument
    and checking for the existence of the string "mingw" (case insensitively)
    in the output.
    """
    rd = Global.globals
    msg_log(rd, "Checking for MingW")

    # Check if we can find an appropriate prefix
    prefixes = _guess_prefix()
    if prefixes == None:
        msg_log(rd, "No mingw compiler found")
        return None

    for prefix in prefixes:
        msg_log(rd, "Guessing MingW prefix as '%s'" % prefix)

        # See if there is a gcc at the guessed prefix
        path = program_path(prefix + 'gcc')
        if not path:
            continue

        # Add double quotes if the name contains a space.
        if " " in path:
            prog = '"%s"' % path
        else:
            prog = path

        # Run gcc with --version to see if it really is MingW
        ok, out = redir_system(prog + ' --version', 0)
        if not ok:
            msg_log(rd, "%s returned error code" % path)
            continue
        import string, re
        firstline = string.split(out, "\n", 1)[0]
        if not re.search("mingw", firstline, re.IGNORECASE):
            msg_log(rd, "%s doesn't look like MingW compiler" % path)
            msg_log(rd, "version line: %s" % firstline)
            continue

        msg_log(rd, "MingW compiler found at '%s'" % path)

        # Remember the prefix for define_actions
        rd["MINGPREFIX"] = prefix
        return 1

    return None


def define_actions():
    """
    Define the actions that MingW can accomplish.
    """
    rd = Global.globals
    prefix = rd.get("MINGPREFIX")
    if prefix == None:
        # Just in case someone's cleared the variable since :usetool mingw
        prefix = ""
    if not rd.get("MINGCC"):
        rd["MINGCC"] = prefix + "gcc"
    if not rd.get("MINGCXX"):
        rd["MINGCXX"] = prefix + "g++"
    if not rd.get("MINGAR"):
        rd["MINGAR"] = prefix + "ar"
    if not rd.get("MINGRANLIB"):
        rd["MINGRANLIB"] = prefix + "ranlib"
    if not rd.get("MINGDLLTOOL"):
        rd["MINGDLLTOOL"] = prefix + "dlltool"

    # Must set the prefixes to the values used on windows
    rd["OBJSUF"] = ".obj"
    rd["LIBOBJSUF"] = ".obj"
    rd["DLLOBJSUF"] = ".sob"
    rd["EXESUF"] = ".exe"
    rd["LIBPRE"] = ""
    rd["LIBSUF"] = ".lib"
    rd["DLLPRE"] = ""
    rd["DLLSUF"] = ".dll"
    rd["LNKSUF"] = ""

    # dllobjects don't need to be compiled differently with mingw: -fPIC
    # has no effect (other than to display a warning message).
    rpstack = [ RecPos("compile_mingw_c action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "compile_mingw_c object,libobject,dllobject c"),
            ":buildcheck $OPTIMIZE $?DEBUG\n"
            ":sys $MINGCC $CPPFLAGS $?DEFINE $?INCLUDE `cflags_normal()` "
                "$CFLAGS -o $target -c $source")

    rpstack = [ RecPos("compile_mingw_cxx action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "compile_mingw_cxx object,libobject,dllobject cpp"),
            ":buildcheck $OPTIMIZE $?DEBUG\n"
            ":sys $MINGCXX $CPPFLAGS $?DEFINE $?INCLUDE `cflags_normal()` "
                "$CXXFLAGS -o $target -c $source")

    rpstack = [ RecPos("build_mingw_c action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "build_mingw_c object,dllobject,libobject"),
            ":sys $MINGCC $?LDFLAGS `cflags_normal()` -o $target $source $?LIBS")

    rpstack = [ RecPos("build_mingw_cxx action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "build_mingw_cxx object,dllobject,libobject"),
            ":sys $MINGCXX $?LDFLAGS `cflags_normal()` -o $target $source $?LIBS")

    rpstack = [ RecPos("build_mingw_lib action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "build_mingw_lib default"),
            ":sys $MINGAR $ARFLAGS $target $source\n"
            ":sys $MINGRANLIB $?RANLIBFLAGS $target")

    # building a .dll also produces a .lib and a _exports.o
    # FIXME - should this be reflected in the dependencies?
    rpstack = [ RecPos("build_mingw_dll action") ]
    action_add(rpstack, rd, str2dictlist(rpstack, "build_mingw_dll default"),
            "dllexports = `sufreplace('', '_exports.o', target)` \n"
            "dlllib = `sufreplace('', '.lib', target)` \n"
            ":sys $MINGDLLTOOL -e $dllexports -l $dlllib $source\n"
            ":sys $MINGCC -shared $?LDFLAGS `cflags_normal()` -o $target $dllexports $source $?LIBS")


def use_actions(scope):
    """
    Setup variables so that the default actions use the MingW actions.
    """
    scope["C_COMPILE_ACTION"] = "compile_mingw_c"
    scope["CXX_COMPILE_ACTION"] = "compile_mingw_cxx"
    scope["C_BUILD_ACTION"] = "build_mingw_c"
    scope["CXX_BUILD_ACTION"] = "build_mingw_cxx"
    scope["BUILDLIB_ACTION"] = "build_mingw_lib"
    scope["BUILDDLL_ACTION"] = "build_mingw_dll"

    # Attempt using gcc for dependency checks.
    scope["HASGCC"] = ""
    scope["HASGCCXX"] = ""

# vim: set sw=4 et sts=4 tw=79 fo+=l:
