#!/usr/bin/env python
#     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
#
#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
#

"""

This is the main program of Nuitka, it checks the options and then translates
one or more modules to a C++ source code using Python C/API in a build directory
compiles it to either an executable or an extension module that can contain
other modules.

"""

import sys, os, subprocess

from logging import warning

# LIBDIR trick start (marker for removal on platforms that don't need it)
libdir = '@LIBDIR@'

# Two cases:
if libdir != '@' 'LIBDIR' '@':
    # Changed by our distutils hook, then use the given path.

    if not os.path.isabs( libdir ):
        libdir = os.path.join(
            os.path.dirname( os.path.realpath( __file__ ) ),
            libdir
        )
        libdir = os.path.abspath( libdir )

    sys.path.insert(
        0,
        libdir
    )
else:
    # Unchanged, running from checkout, use the parent directory, the nuitka
    # package ought be there.
    sys.path.insert(
        0,
        os.path.normpath(
            os.path.join(
                os.path.dirname( __file__ ),
                ".."
            )
        )
    )
# LIBDIR trick end (marker for removal on platforms that don't need it)

import logging
logging.basicConfig( format = 'Nuitka:%(levelname)s:%(message)s' )

# We don't care, and these are triggered by run time calculations of "range" and
# others, while on python2.7 they are disabled by default.
import warnings
warnings.simplefilter( "ignore", DeprecationWarning )

from nuitka import Options

intended_version = Options.getIntendedPythonVersion()

# Make sure on the promise to execute with a specific version. When compiled,
# this may not be supported.
if intended_version is not None:
    current_version = "%d.%d" % ( sys.version_info[0], sys.version_info[1] )

    if current_version != intended_version:
        assert intended_version in ( "2.6", "2.7", "3.2", "3.3" )

        if os.name == "nt":
            python_binary = r"C:\Python%s\python.exe" % \
              intended_version.replace( ".", "" )
        else:
            python_binary = "/usr/bin/python" + intended_version

        args = [ python_binary, os.path.basename( python_binary ) ] + sys.argv

        # That's the API of execl, pylint: disable=W0142
        os.execl( *args )


from nuitka import MainControl, SyntaxErrors, Importing

positional_args = Options.getPositionalArgs()
assert len( positional_args ) > 0

filename = Options.getPositionalArgs()[0]

# Inform the importing layer about the main script directory, so it can use it
# when attempting to follow imports.
Importing.setMainScriptDirectory(
    main_dir = os.path.dirname( os.path.abspath( filename ) )
)

# Turn that source code into a node tree structure.
try:
    tree = MainControl.createNodeTree(
        filename = filename
    )
except (SyntaxError, IndentationError) as e:
    if Options.isFullCompat() and e.args[0].startswith( "unknown encoding:" ):
        # False alarm, pylint: disable=E0611
        from nuitka.Utils import python_version

        if ( python_version >= 332 and os.name != "nt" ) or \
           "2.7.5+" in sys.version:
            complaint = "no-exist"
        else:
            complaint = "with BOM"

        e.args = (
            "encoding problem: %s" % complaint,
            ( e.args[1][0], 1, None, None )
        )

    sys.exit( SyntaxErrors.formatOutput( e ) )

if Options.shallDumpBuiltTree():
    MainControl.dumpTree( tree )
elif Options.shallDumpBuiltTreeXML():
    MainControl.dumpTreeXML( tree )
elif Options.shallDisplayBuiltTree():
    MainControl.displayTree( tree )
else:
    import shutil

    result, options = MainControl.compileTree( tree )

    # Exit if compilation failed.
    if not result:
        sys.exit( 1 )

    # Remove the source directory (now build directory too) if asked to.
    if Options.isRemoveBuildDir():
        shutil.rmtree( MainControl.getSourceDirectoryPath( tree ) )

    # Pack and copy files in portable mode
    if Options.isPortableMode():
        from nuitka import PortableSetup
        if not PortableSetup.setup( filename, Options.getOutputDir() ):
            sys.exit( 1 )

    # Sanity check, warn people if "__main__" is used in the compiled module, it
    # may not be the appropiate usage.
    if Options.shallMakeModule() and Options.shallExecuteImmediately():
        for variable in tree.getVariables():
            if variable.getName() == "__name__":
                warning( """\
Compiling to extension module, which will not have '__name__' as '__main__', \
did you intend '--exe' or to use 'nuitka-python' instead.""" )
                break

    # Modules should not be executable, but Scons creates them like it, fix it
    # up here.
    if os.name != "nt" and Options.shallMakeModule():
        subprocess.call(
            (
                "chmod",
                "-x",
                options[ "result_name" ] + ".so"
            )
        )

    # Execute the module immediately if option was given.
    if Options.shallExecuteImmediately():
        if Options.shallMakeModule():
            MainControl.executeModule(
                tree       = tree,
                clean_path = Options.shallClearPythonPathEnvironment()
            )
        else:
            MainControl.executeMain(
                binary_filename = options[ "result_name" ] + ".exe",
                tree            = tree,
                clean_path      = Options.shallClearPythonPathEnvironment()
            )
