/////////////////////////////////////////////////////////////////////////////
// Name:        compiler.cpp
// Purpose:     wxCompilerSettings
// Author:      Francesco Montorsi, Julian Smart
// Modified by:
// Created:     2006-11-07
// RCS-ID:      $Id: compiler.cpp,v 1.9 2007/02/01 20:49:50 frm Exp $
// Copyright:   (c) Francesco Montorsi, Julian Smart
// Licence:     wxWidgets licence
/////////////////////////////////////////////////////////////////////////////


// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// includes
#include "wx/config.h"

#include "wxp/packagecmd.h"
#include "wxp/compiler.h"
#include "wxp/wxp.h"


// static
wxPackageCompilerFormat wxCompilerSettings::s_defaultFormat = wxPCF_INVALID;
wxPathList wxCompilerSettings::s_paths[wxPCF_MAX][wxPCPT_MAX];



// definitions

wxString wxPackageCompilerFormatName[] =
{
    _T("borland"),
    _T("msvc"),
    _T("watcom"),
    _T("mingw"),
    _T("msys"),

    _T("gnu"),
    _T("autoconf")
};

wxString wxPackageCMakeFormatName[] =
{
    _T("Borland Makefiles"),
    _T("NMake Makefiles"),
    _T("Wactom WMake"),
    _T("MinGW Makefiles"),
    _T("MSYS Makefiles"),

    _T("Unix Makefiles"),
    _T("NOTSUPPORTED")
};

wxString wxPackageMakeName[] =
{
    wxT("make.exe"),
    wxT("nmake.exe"),
    wxT("wmake.exe"),
    wxT("mingw32-make.exe"),
    wxT("make"),            // msys is like Unix

    // no extensions: we are on Unix for these formats !
    wxT("make"),
    wxT("make")
};

wxString wxPackageCompilerPathTypeName[] =
{
    wxT("bin"),
    wxT("include"),
    wxT("lib")
};

// name of the standard extensions for makefiles of the respective formats
// NB: this is bakefile-specific as CMake always generates makefiles
//     called 'Makefile' regardless of the selected compiler.
wxString wxBakefileMakefileExtName[] =
{
    wxT("bcc"),
    wxT("vc"),
    wxT("wat"),
    wxT("mingw"),
    wxT(""),        // MSYS (??)

    wxT(""),        // GNU makefile
    wxT("")         // Autoconf
};

// other code should use wxGetFullPackageBuildSystemTypeList() !
#define wxPBST_ALL  (wxPBST_BAKEFILE|wxPBST_CMAKE|wxPBST_SCONS)

// The mask of the build systems which support the compilers
// listed in #wxPackageCompilerFormat.
// NB: for scons this array has been constructed looking at the
//     list of tools supported out-of-the-box in the
//     "Construction environment" section of its manpage.
int s_nBuildSystemTypeMask[wxPCF_MAX] =
{
    wxPBST_ALL,                        // wxPCF_BORLAND
    wxPBST_ALL,                        // wxPCF_MSVC
    wxPBST_BAKEFILE|wxPBST_CMAKE,      // wxPCF_WATCOM
    wxPBST_ALL,                        // wxPCF_MINGW
    wxPBST_CMAKE,                      // wxPCF_MSYS
    wxPBST_ALL,                        // wxPCF_GNUMAKEFILE
    wxPBST_BAKEFILE                    // wxPCF_AUTOCONF
};

#undef wxPBST_ALL

// The mask of the platforms of the compilers listed in #wxPackageCompilerFormat.
long s_nPlatformMask[wxPCF_MAX] =
{
    wxOS_WINDOWS,
    wxOS_WINDOWS,
    wxOS_WINDOWS,
    wxOS_WINDOWS,
    wxOS_WINDOWS,
    wxOS_UNIX,
    wxOS_UNIX
};



// ----------------------------------------------------------------------------
// string2enum helpers
// ----------------------------------------------------------------------------

wxIMPLEMENT_STRING2COMBINEABLE_ENUM(PackageCompilerFormat, wxPCF_MAX)
wxIMPLEMENT_STRING2COMBINEABLE_ENUM(PackageBuildSystemType, wxPBST_MAX)
wxIMPLEMENT_STRING2ENUM(PackageCompilerPathType, wxPCPT_MAX)

// Global utility function to extract info from previous arrays.
// Use these functions instead of directly accessing them.

bool wxIsCompilerFormatSupportedBy(wxPackageCompilerFormat fmt,
                                   wxPackageBuildSystemType buildsys)
{
    wxASSERT(fmt != wxPCF_INVALID);
    return (wxGetBuildSystemsSupportingCompilerFormat(fmt) & buildsys) != 0;
}

int wxGetBuildSystemsSupportingCompilerFormat(wxPackageCompilerFormat fmt)
{
    wxASSERT(fmt != wxPCF_INVALID);
    return s_nBuildSystemTypeMask[wxPackageCompilerFormat2Idx(fmt)];
}

bool wxIsCompilerFormatSupportedByThisPlatform(wxPackageCompilerFormat fmt)
{
    wxASSERT(fmt != wxPCF_INVALID);
    wxPlatformInfo current;

    long osmask = s_nPlatformMask[wxPackageCompilerFormat2Idx(fmt)];
    return (osmask & current.GetOperatingSystemId()) != 0;
}

wxString wxPackageCompilerFormat2BakefileFormatName(wxPackageCompilerFormat f)
{
    // default name coincides with bakefile format names
    return wxPackageCompilerFormat2String(f);
}

wxString wxPackageCompilerFormat2CMakeFormatName(wxPackageCompilerFormat f)
{
    // default name coincides with bakefile format names
    return wxPackageCMakeFormatName[wxPackageCompilerFormat2Idx(f)];
}





// ----------------------------------------------------------------------------
// wxCompilerSettings
// ----------------------------------------------------------------------------

// static
bool wxCompilerSettings::IsValidPathList(wxPackageCompilerFormat fmt,
                                         const wxPathList &pl)
{
    wxArrayString arr = GetRequiredToolsList(fmt);

    // search path list for required programs
    for (size_t i=0; i < arr.GetCount(); i++)
    {
        wxString str = arr[i];
        if (!str.IsEmpty() && pl.FindAbsoluteValidPath(str).IsEmpty())
            return false;
    }

    return true;
}

void wxCompilerSettings::UpdateEnvVar(const wxString &name, wxPackageCompilerPathType t)
{
    wxString paths;
    for (size_t j=0; j<GetCompilerPaths(t).GetCount(); j++)
        paths += GetCompilerPaths(t).Item(j) + wxT(";");
    if (!paths.IsEmpty() && paths.Last() == wxT(';'))
        paths.RemoveLast();

    wxString value = paths, oldvalue;
    if (wxGetEnv(name, &oldvalue))
        value += wxT(";") + oldvalue;
    wxSetEnv(name, value);
}

void wxCompilerSettings::UpdateEnvVars()
{
    UpdateEnvVar(wxT("PATH"), wxPCPT_BIN);
    UpdateEnvVar(wxT("INCLUDE"), wxPCPT_INCLUDE);
    UpdateEnvVar(wxT("LIB"), wxPCPT_LIB);

    // some compilers prefer LIBPATH as env var:
    UpdateEnvVar(wxT("LIBPATH"), wxPCPT_LIB);
}

wxStringHashMap wxCompilerSettings::GetSubstitutionHashMap(wxPackageBuildSystemType t) const
{
    wxStringHashMap ret;
    if (s_selFormat == wxPCF_INVALID)
        return ret;     // return an empty hashmap

    size_t n = wxPackageCompilerFormat2Idx(s_selFormat);
    bool needExe = (wxPlatformInfo().GetOperatingSystemId() & wxOS_WINDOWS) != 0;

    ret[wxT("make")] = wxPackageMakeName[n];
    ret[wxT("cmake")] = needExe ? wxT("cmake.exe") : wxT("cmake");
    ret[wxT("scons")] = needExe ? wxT("scons.exe") : wxT("scons");

    // the names of the user-selected formats:
    ret[wxT("format-bakefile")] = wxPackageCompilerFormat2BakefileFormatName(s_selFormat);
    ret[wxT("format-cmake")] = wxPackageCompilerFormat2CMakeFormatName(s_selFormat);

    // the makefile extensions:
    switch (t)
    {
    case wxPBST_BAKEFILE:
        ret[wxT("makefileext"] = wxBakefileMakefileExtName[n]);
        break;

    default:
        break;
    }

    // the default values for the compiler-specific options:
    wxStringHashMap opts(GetOptionsWithDefaults(t));
    for( wxStringHashMap::iterator it = opts.begin(); it != opts.end(); ++it )
    {
        wxString key = it->first, value = it->second;
        ret[key + wxT("-default")] = value;
    }

    return ret;
}

// static
bool wxCompilerSettings::LoadGlobals(wxConfigBase *p, const wxString &path)
{
    wxString defformat =
#if defined(__WXMSW__)
            wxT("msvc");
#elif defined(__UNIX__)
            wxT("gnu");
#endif

    // load format & buildsys
    wxString fmtstr = p->Read(path + wxT("/CompilerFormats/DefaultFormat"), defformat);
    SetDefaultFormat(wxString2PackageCompilerFormat(fmtstr));

    // load paths
    for (size_t i=0; i < wxPCF_MAX; i++)
    {
        for (size_t j=0; j < wxPCPT_MAX; j++)
        {
            wxString str = 
                path + wxT("/CompilerPaths/") +
                wxPackageCompilerPathType2String((wxPackageCompilerPathType)j) + wxT("/") +
                wxPackageCompilerFormat2String(wxIdx2PackageCompilerFormat(i));

            // VERY IMPORTANT: do not include the space in the tokens list as it
            //                 is often used in paths
            s_paths[i][j].Add(wxStringTokenize(p->Read(str, wxEmptyString), wxT(",")));
        }
    }

    return true;
}

bool wxCompilerSettings::Load(wxConfigBase *p, const wxString &path)
{
    // load selected format & buildsys
    wxString fmtstr = p->Read(path + wxT("/CompilerFormats/SelectedFormat"), wxT("msvc"));
    SetSelFormat(wxString2PackageCompilerFormat(fmtstr));

    return true;
}

// static
void wxCompilerSettings::SaveGlobals(wxConfigBase *p, const wxString &path)
{
    p->Write(path + wxT("/CompilerFormats/DefaultFormat"),
             wxPackageCompilerFormat2String(GetDefaultFormat()));

    // save paths
    for (size_t i=0; i < wxPCF_MAX; i++)
    {
        for (size_t k=0; k < wxPCPT_MAX; k++)
        {
            wxString str = 
                path + wxT("/CompilerPaths/") +
                wxPackageCompilerPathType2String((wxPackageCompilerPathType)k) + wxT("/") +
                wxPackageCompilerFormat2String(wxIdx2PackageCompilerFormat(i));

            p->Write(str, wxArray2String(s_paths[i][k]));
        }
    }
}

void wxCompilerSettings::Save(wxConfigBase *p, const wxString &path) const
{
    // save format
    wxString fmt(wxPackageCompilerFormat2String(s_selFormat));
    p->Write(path + wxT("/CompilerFormats/SelectedFormat"), fmt);
}




// ----------------------------------------------------------------------------
// wxCompilerSettings == COMPILER-DEPENDENT STUFF
// All the code below does not only differ among different compilers but even
// for different versions of the same compiler!
// ----------------------------------------------------------------------------

// static
wxArrayString wxCompilerSettings::GetRequiredToolsList(wxPackageCompilerFormat fmt)
{
    wxArrayString ret;

    switch (fmt)
    {
    case wxPCF_BORLAND:
        ret.Add(wxT("bcc32.exe"));
        ret.Add(wxT("make.exe"));
        ret.Add(wxT("ilink32.exe"));
        ret.Add(wxT("tlib.exe"));
        break;

    case wxPCF_MSVC:
        ret.Add(wxT("cl.exe"));
        ret.Add(wxT("rc.exe"));
        ret.Add(wxT("nmake.exe"));
        ret.Add(wxT("mspdb71.dll"));
        break;

    case wxPCF_WATCOM:
        ret.Add(wxT("wmake.exe"));
        ret.Add(wxT("wpp386.exe"));
        ret.Add(wxT("wcc386.exe"));
        ret.Add(wxT("wlink.exe"));
        ret.Add(wxT("wrc.exe"));
        break;

    case wxPCF_MINGW:
        ret.Add(wxT("g++.exe"));
        ret.Add(wxT("gcc.exe"));
        ret.Add(wxT("mingw32-make.exe"));
        ret.Add(wxT("ld.exe"));
        ret.Add(wxT("windres.exe"));
        break;

    case wxPCF_MSYS:
        // ??
        break;

    case wxPCF_GNUMAKEFILE:
        ret.Add(wxT("make"));
        ret.Add(wxT("gcc"));
        ret.Add(wxT("g++"));
        ret.Add(wxT("ar"));
        break;

    case wxPCF_AUTOCONF:
        // autoconf will automatically detect
        // the available tools
        break;

    default:
        wxFAIL;
    }

    return ret;
}

wxStringHashMap wxCompilerSettings::GetOptionsWithDefaults(wxPackageBuildSystemType bs) const
{
    wxStringHashMap h;

    switch (bs)
    {
    case wxPBST_BAKEFILE:
        h[wxT("CFLAGS")] = wxEmptyString;
        h[wxT("CXXFLAGS")] = wxEmptyString;
        h[wxT("CPPFLAGS")] = wxEmptyString;
        h[wxT("LDFLAGS")] = wxEmptyString;
        switch (s_selFormat)
        {
        case wxPCF_BORLAND:
            h[wxT("CC")] = wxT("bcc32.exe");
            h[wxT("CXX")] = wxT("bcc32.exe");
            h[wxT("CPPFLAGS")] = wxT("-a8 -g0");
            break;
        case wxPCF_MSVC:
            h[wxT("CC")] = wxT("cl.exe");
            h[wxT("CXX")] = wxT("cl.exe");
            break;
        case wxPCF_WATCOM:
            h[wxT("CC")] = wxT("wcc386.exe");
            h[wxT("CXX")] = wxT("wpp386.exe");
            break;
        case wxPCF_MINGW:
            h[wxT("CC")] = wxT("gcc.exe");
            h[wxT("CXX")] = wxT("g++.exe");
            break;
        case wxPCF_GNUMAKEFILE:
            h[wxT("CC")] = wxT("gcc");
            h[wxT("CXX")] = wxT("g++");
            break;
        case wxPCF_AUTOCONF:
            // Autoconf will automatically detect the
            // required tools
            h[wxT("CC")] = wxEmptyString;
            h[wxT("CXX")] = wxEmptyString;
            break;

        default:
            wxFAIL;
            break;
        }
        break;

    case wxPBST_CMAKE:
        h[wxT("CMAKE_C_FLAGS")] = wxEmptyString;
        h[wxT("CMAKE_CXX_FLAGS")] = wxEmptyString;
        h[wxT("CMAKE_MODULE_LINKER_FLAGS")] = wxEmptyString;
        h[wxT("CMAKE_SHARED_LINKER_FLAGS")] = wxEmptyString;
        h[wxT("CMAKE_EXE_LINKER_FLAGS")] = wxEmptyString;
        switch (s_selFormat)
        {
        case wxPCF_BORLAND:
            h[wxT("CMAKE_C_COMPILER")] = wxT("bcc32.exe");
            h[wxT("CMAKE_CXX_COMPILER")] = wxT("bcc32.exe");
            break;
        case wxPCF_MSVC:
            h[wxT("CMAKE_C_COMPILER")] = wxT("cl.exe");
            h[wxT("CMAKE_CXX_COMPILER")] = wxT("cl.exe");
            break;

        default:
            // FIXME
            break;
        }
        break;

    case wxPBST_SCONS:
        // FIXME
        break;

    default:
        wxFAIL;
        break;
    }

    return h;
}

