#===--- SwiftBuildSupport.py - Utilities for Swift build scripts -----------===#
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
#===------------------------------------------------------------------------===#

from __future__ import print_function

import ConfigParser
import os
import pipes
import subprocess
import sys


HOME = os.environ.get("HOME", "/")


def _get_default_source_root():
    result = ""

    # Are we in a Swift checkout? Start from this file and check its parent
    # directories.
    #
    # $SWIFT_SOURCE_ROOT/swift/utils/SwiftBuildSupport.py
    (swift_path, parent_dirname) = os.path.split(os.path.dirname(__file__))
    if parent_dirname != "utils":
        return result
    if not os.path.exists(os.path.join(swift_path, 'CMakeLists.txt')):
        return result
    result = os.path.dirname(swift_path)

    # Are we in an LLVM checkout? Start from the Swift checkout and check /its/
    # parent directories.
    #
    # $SWIFT_SOURCE_ROOT/llvm/tools/swift/utils/SwiftBuildSupport.py
    (llvm_path, parent_dirname) = os.path.split(result)
    if parent_dirname != "tools":
        return result
    if not os.path.exists(os.path.join(llvm_path, 'CMakeLists.txt')):
        return result
    result = os.path.dirname(llvm_path)

    return result

# Set SWIFT_SOURCE_ROOT in your environment to control where the sources
# are found.
SWIFT_SOURCE_ROOT = os.environ.get(
    "SWIFT_SOURCE_ROOT", _get_default_source_root())

# Set SWIFT_BUILD_ROOT to a directory that will contain a subdirectory
# for each build configuration
SWIFT_BUILD_ROOT = os.environ.get(
    "SWIFT_BUILD_ROOT", os.path.join(SWIFT_SOURCE_ROOT, "build"))


def print_with_argv0(message):
    print(sys.argv[0] + ": " + message)


def bad_usage(message):
    print_with_argv0(message)
    print("Run '" + pipes.quote(sys.argv[0]) + " --help' for more information.")
    sys.exit(1)

def quote_shell_command(args):
    return " ".join([ pipes.quote(a) for a in args ])


def check_call(args, print_command=False, verbose=False):
    if print_command:
        print(os.getcwd() + "$ " + quote_shell_command(args))
    try:
        return subprocess.check_call(args)
    except subprocess.CalledProcessError as e:
        if verbose:
            print_with_argv0(e.strerror)
        else:
            print_with_argv0(
                "command terminated with a non-zero exit status " +
                str(e.returncode) + ", aborting")
        sys.stdout.flush()
        sys.exit(1)
    except OSError as e:
        print_with_argv0("could not execute '" + quote_shell_command(args) +
            "': " + e.strerror)
        sys.stdout.flush()
        sys.exit(1)


def check_output(args, print_command=False, verbose=False):
    if print_command:
        print(os.getcwd() + "$ " + quote_shell_command(args))
    try:
        return subprocess.check_output(args)
    except subprocess.CalledProcessError as e:
        if verbose:
            print_with_argv0(e.strerror)
        else:
            print_with_argv0(
                "command terminated with a non-zero exit status " +
                str(e.returncode) + ", aborting")
        sys.stdout.flush()
        sys.exit(1)
    except OSError as e:
        print_with_argv0("could not execute '" + quote_shell_command(args) +
            "': " + e.strerror)
        sys.stdout.flush()
        sys.exit(1)


def _load_preset_files_impl(preset_file_names, substitutions={}):
    config = ConfigParser.SafeConfigParser(substitutions, allow_no_value=True)
    if config.read(preset_file_names) == []:
        print_with_argv0(
            "preset file not found (tried " + str(preset_file_names) + ")")
        sys.exit(1)
    return config


_PRESET_PREFIX = "preset: "

def _get_preset_options_impl(config, substitutions, preset_name):
    section_name = _PRESET_PREFIX + preset_name
    if section_name not in config.sections():
        return (None, None)

    build_script_opts = []
    build_script_impl_opts = []
    dash_dash_seen = False
    for o, a in config.items(section_name):
        if o in substitutions:
            continue

        opt = None
        if o == "mixin-preset":
            # Split on newlines and filter out empty lines.
            mixins = filter(None, [m.strip() for m in a.splitlines()])
            for mixin in mixins:
                (base_build_script_opts, base_build_script_impl_opts) = \
                    _get_preset_options_impl(config, substitutions, mixin)
                build_script_opts += base_build_script_opts
                build_script_impl_opts += base_build_script_impl_opts
        elif o == "dash-dash":
            dash_dash_seen = True
        elif a == "":
            opt = "--" + o
        else:
            opt = "--" + o + "=" + a

        if opt:
            if not dash_dash_seen:
                build_script_opts.append(opt)
            else:
                build_script_impl_opts.append(opt)
    return (build_script_opts, build_script_impl_opts)


def get_preset_options(substitutions, preset_file_names, preset_name):
    config = _load_preset_files_impl(preset_file_names, substitutions)

    (build_script_opts, build_script_impl_opts) = \
        _get_preset_options_impl(config, substitutions, preset_name)
    if build_script_opts is None:
        print_with_argv0("preset '" + preset_name + "' not found")
        sys.exit(1)

    return build_script_opts + [ "--" ] + build_script_impl_opts


def get_all_preset_names(preset_file_names):
    config = _load_preset_files_impl(preset_file_names)
    return [ name[len(_PRESET_PREFIX):] for name in config.sections()
             if name.startswith(_PRESET_PREFIX) ]


# A context manager for changing the current working directory.
#
#     with WorkingDirectory('/tmp'):
#         ... do work in /tmp...
class WorkingDirectory(object):
    def __init__(self, new_cwd):
        self.new_cwd = new_cwd

    def __enter__(self):
        self.old_cwd = os.getcwd()
        os.chdir(self.new_cwd)

    def __exit__(self, type, value, traceback):
        os.chdir(self.old_cwd)

