#!/usr/bin/env python
# This script contains helper functions to work with the third_party subrepo.

import os
import sys
from os import path
from util import find_exts, make_env, remove_and_symlink, rmtree, root_path, run


# Helper function that returns the full path to a subpath of the repo root.
def root(*subpath_parts):
    return path.normpath(path.join(root_path, *subpath_parts))


# Helper function that returns the full path to a file/dir in third_party.
def tp(*subpath_parts):
    return root("third_party", *subpath_parts)


third_party_path = tp()
depot_tools_path = tp("depot_tools")
rust_crates_path = tp("rust_crates")
gn_path = tp(depot_tools_path, "gn")
ninja_path = tp(depot_tools_path, "ninja")


# This function creates or modifies an environment so that it matches the
# expectations of various google tools (gn, gclient, etc).
def google_env(env=None, merge_env={}, depot_tools_path=depot_tools_path):
    env = make_env(env=env, merge_env=merge_env)
    # Depot_tools to be in the PATH, before Python.
    path_prefix = depot_tools_path + os.path.pathsep
    if not env['PATH'].startswith(path_prefix):
        env['PATH'] = path_prefix + env['PATH']
    # We're not using Google's internal infrastructure.
    if os.name == 'nt' and not 'DEPOT_TOOLS_WIN_TOOLCHAIN' in env:
        env['DEPOT_TOOLS_WIN_TOOLCHAIN'] = "0"
    return env


def fix_symlinks():
    # Ensure the third_party directory exists.
    try:
        os.makedirs(third_party_path)
    except:
        pass

    # Make symlinks to Yarn metadata living in the root repo.
    remove_and_symlink("../package.json", tp("package.json"))
    remove_and_symlink("../yarn.lock", tp("yarn.lock"))

    # TODO(ry) Is it possible to remove these symlinks?
    remove_and_symlink("v8/third_party/googletest", tp("googletest"), True)
    remove_and_symlink("v8/third_party/jinja2", tp("jinja2"), True)
    remove_and_symlink("v8/third_party/llvm-build", tp("llvm-build"), True)
    remove_and_symlink("v8/third_party/markupsafe", tp("markupsafe"), True)

    # On Windows, git doesn't create the right type of symlink if the symlink
    # and it's target are in different repos. Here we fix the symlinks that exist
    # in the root repo while their target is in the third_party repo.
    remove_and_symlink("third_party/node_modules", root("node_modules"), True)
    remove_and_symlink("third_party/v8/build", root("build"), True)
    remove_and_symlink("third_party/v8/buildtools", root("buildtools"), True)
    remove_and_symlink("third_party/v8/build_overrides",
                       root("build_overrides"), True)
    remove_and_symlink("third_party/v8/testing", root("testing"), True)
    remove_and_symlink("../third_party/v8/tools/clang", root("tools/clang"),
                       True)


# Run Yarn to install JavaScript dependencies.
def run_yarn():
    run(["yarn"], cwd=third_party_path)


# Run Cargo to install Rust dependencies.
def run_cargo():
    # Deletes the cargo index lockfile; it appears that cargo itself doesn't do it.
    # If the lockfile ends up in the git repo, it'll make cargo hang for everyone
    # else who tries to run sync_third_party.
    def delete_lockfile():
        lockfiles = find_exts(
            path.join(rust_crates_path, "registry/index"), '.cargo-index-lock')
        for lockfile in lockfiles:
            os.remove(lockfile)

    # Delete the index lockfile in case someone accidentally checked it in.
    delete_lockfile()

    run(["cargo", "fetch", "--manifest-path=" + root("Cargo.toml")],
        cwd=third_party_path,
        merge_env={'CARGO_HOME': rust_crates_path})

    # Delete the lockfile again so it doesn't end up in the git repo.
    delete_lockfile()


# Run gclient to install other dependencies.
def run_gclient_sync():
    # Depot_tools will normally try to self-update, which will fail because
    # it's not checked out from it's own git repository; gclient will then try
    # to fix things up and not succeed, and and we'll end up with a huge mess.
    # To work around this, we rename the `depot_tools` directory to
    # `{root_path}/depot_tools_temp` first, and we set DEPOT_TOOLS_UPDATE=0 in
    # the environment so depot_tools doesn't attempt to self-update.
    # Since depot_tools is listed in .gclient_entries, gclient will install a
    # fresh copy in `third_party/depot_tools`.
    # If it all works out, we remove the depot_tools_temp directory afterwards.
    depot_tools_temp_path = root("depot_tools_temp")

    # Rename depot_tools to depot_tools_temp.
    try:
        os.rename(depot_tools_path, depot_tools_temp_path)
    except:
        # If renaming failed, and the depot_tools_temp directory already exists,
        # assume that it's still there because a prior run_gclient_sync() call
        # failed half-way, before we got the chance to remove the temp dir.
        # We'll use whatever is in the temp dir that was already there.
        # If not, the user can recover by removing the temp directory manually.
        if path.isdir(depot_tools_temp_path):
            pass
        else:
            raise

    args = [
        "gclient", "sync", "--reset", "--shallow", "--no-history", "--nohooks"
    ]
    envs = {
        'DEPOT_TOOLS_UPDATE': "0",
        'GCLIENT_FILE': root("gclient_config.py")
    }
    env = google_env(depot_tools_path=depot_tools_temp_path, merge_env=envs)
    run(args, cwd=third_party_path, env=env)

    # Delete the depot_tools_temp directory, but not before verifying that
    # gclient did indeed install a fresh copy.
    # Also check that `{depot_tools_temp_path}/gclient.py` exists, so a typo in
    # this script won't accidentally blow out someone's home dir.
    if (path.isdir(path.join(depot_tools_path, ".git"))
            and path.isfile(path.join(depot_tools_path, "gclient.py"))
            and path.isfile(path.join(depot_tools_temp_path, "gclient.py"))):
        rmtree(depot_tools_temp_path)


# Download gn from Google storage.
def download_gn():
    if sys.platform == 'win32':
        sha1_file = "v8/buildtools/win/gn.exe.sha1"
    elif sys.platform == 'darwin':
        sha1_file = "v8/buildtools/mac/gn.sha1"
    elif sys.platform.startswith('linux'):
        sha1_file = "v8/buildtools/linux64/gn.sha1"

    run([
        "python",
        tp('depot_tools/download_from_google_storage.py'),
        '--platform=' + sys.platform,
        '--no_auth',
        '--bucket=chromium-gn',
        '--sha1_file',
        tp(sha1_file),
    ],
        env=google_env())


# Download clang by calling the clang update script.
def download_clang():
    run(['python',
         tp('v8/tools/clang/scripts/update.py'), '--if-needed'],
        env=google_env())


def maybe_download_sysroot():
    if sys.platform.startswith('linux'):
        run([
            'python',
            tp('v8/build/linux/sysroot_scripts/install-sysroot.py'),
            '--arch=amd64'
        ],
            env=google_env())