Beispiel #1
0
def make_parser(parser):
    basename = Path(sys.argv[0]).name
    if basename in symlinks:
        # skip parsing of all arguments
        parser._actions = []
    else:
        parser.description = (
            "Cross compile a Python distutils package. "
            "Run from the root directory of the package's source")
        parser.add_argument(
            "--cflags",
            type=str,
            nargs="?",
            default=common.get_make_flag("SIDE_MODULE_CFLAGS"),
            help="Extra compiling flags",
            action=EnvironmentRewritingArgument,
        )
        parser.add_argument(
            "--cxxflags",
            type=str,
            nargs="?",
            default=common.get_make_flag("SIDE_MODULE_CXXFLAGS"),
            help="Extra C++ specific compiling flags",
            action=EnvironmentRewritingArgument,
        )
        parser.add_argument(
            "--ldflags",
            type=str,
            nargs="?",
            default=common.get_make_flag("SIDE_MODULE_LDFLAGS"),
            help="Extra linking flags",
            action=EnvironmentRewritingArgument,
        )
        parser.add_argument(
            "--target",
            type=str,
            nargs="?",
            default=common.get_make_flag("TARGETPYTHONROOT"),
            help="The path to the target Python installation",
        )
        parser.add_argument(
            "--install-dir",
            type=str,
            nargs="?",
            default="",
            help=
            ("Directory for installing built host packages. Defaults to setup.py "
             "default. Set to 'skip' to skip installation. Installation is "
             "needed if you want to build other packages that depend on this one."
             ),
        )
        parser.add_argument(
            "--replace-libs",
            type=str,
            nargs="?",
            default="",
            help="Libraries to replace in final link",
            action=EnvironmentRewritingArgument,
        )
    return parser
Beispiel #2
0
def test_wheel_paths():
    from pathlib import Path

    old_version = "cp38"
    PYMAJOR = int(get_make_flag("PYMAJOR"))
    PYMINOR = int(get_make_flag("PYMINOR"))
    current_version = f"cp{PYMAJOR}{PYMINOR}"
    future_version = f"cp{PYMAJOR}{PYMINOR + 1}"
    strings = []

    for interp in [
            old_version,
            current_version,
            future_version,
            "py3",
            "py2",
            "py2.py3",
    ]:
        for abi in [interp, "abi3", "none"]:
            for arch in ["emscripten_wasm32", "linux_x86_64", "any"]:
                strings.append(f"wrapt-1.13.3-{interp}-{abi}-{arch}.whl")

    paths = [Path(x) for x in strings]
    assert [x.stem.split("-", 2)[-1] for x in find_matching_wheels(paths)] == [
        f"{current_version}-{current_version}-emscripten_wasm32",
        f"{current_version}-abi3-emscripten_wasm32",
        f"{current_version}-none-emscripten_wasm32",
        f"{old_version}-abi3-emscripten_wasm32",
        "py3-none-emscripten_wasm32",
        "py2.py3-none-emscripten_wasm32",
        "py3-none-any",
        "py2.py3-none-any",
    ]
Beispiel #3
0
def capture_command(command: str, args: list[str]) -> int:
    """
    This is called when this script is called through a symlink that looks like
    a compiler or linker.

    It writes the arguments to the build.log, and then delegates to the real
    native compiler or linker (unless it decides to skip host compilation).

    Returns
    -------
        The exit code of the real native compiler invocation
    """
    TOOLSDIR = Path(common.get_make_flag("TOOLSDIR"))
    # Remove the symlink compiler from the PATH, so we can delegate to the
    # native compiler
    env = dict(os.environ)
    path = env["PATH"]
    while str(TOOLSDIR) + ":" in path:
        path = path.replace(str(TOOLSDIR) + ":", "")
    env["PATH"] = path

    skip_host = "SKIP_HOST" in os.environ

    # Skip compilations of C/Fortran extensions for the target environment.
    # We still need to generate the output files for distutils to continue
    # the build.
    # TODO: This may need slight tuning for new projects. In particular,
    #       currently ar is not skipped, so a known failure would happen when
    #       we create some object files (that are empty as gcc is skipped), on
    #       which we run the actual ar command.
    skip = False
    if (command in ["gcc", "cc", "c++", "gfortran", "ld"] and "-o" in args
            # do not skip numpy as it is needed as build time
            # dependency by other packages (e.g. matplotlib)
            and skip_host):
        out_idx = args.index("-o")
        if (out_idx + 1) < len(args):
            # get the index of the output file path
            out_idx += 1
            with open(args[out_idx], "wb") as fh:
                fh.write(b"")
            skip = True

    with open("build.log", "a") as fd:
        # TODO: store skip status in the build.log
        json.dump([command] + args, fd)
        fd.write("\n")

    if skip:
        return 0
    compiler_command = [command]
    if shutil.which("ccache") is not None:
        # Enable ccache if it's installed
        compiler_command.insert(0, "ccache")

    return subprocess.run(compiler_command + args, env=env).returncode
Beispiel #4
0
def capture_compile(args):
    TOOLSDIR = Path(common.get_make_flag("TOOLSDIR"))
    env = dict(os.environ)
    make_symlinks(env)
    env["PATH"] = str(TOOLSDIR) + ":" + os.environ["PATH"]

    cmd = [sys.executable, "setup.py", "install"]
    if args.install_dir == "skip":
        cmd[-1] = "build"
    elif args.install_dir != "":
        cmd.extend(["--home", args.install_dir])

    result = subprocess.run(cmd, env=env)
    if result.returncode != 0:
        build_log_path = Path("build.log")
        if build_log_path.exists():
            build_log_path.unlink()
        sys.exit(result.returncode)
Beispiel #5
0
def make_symlinks(env):
    """
    Makes sure all of the symlinks that make this script look like a compiler
    exist.
    """
    TOOLSDIR = Path(common.get_make_flag("TOOLSDIR"))
    exec_path = Path(__file__).resolve()
    for symlink in symlinks:
        symlink_path = TOOLSDIR / symlink
        if os.path.lexists(symlink_path) and not symlink_path.exists():
            # remove broken symlink so it can be re-created
            symlink_path.unlink()
        try:
            symlink_path.symlink_to(exec_path)
        except FileExistsError:
            pass
        if symlink == "c++":
            var = "CXX"
        else:
            var = symlink.upper()
        env[var] = symlink
Beispiel #6
0
def capture_compile(*, host_install_dir: str, skip_host: bool, env: dict[str,
                                                                         str]):
    TOOLSDIR = Path(common.get_make_flag("TOOLSDIR"))
    env = dict(env)
    env["PATH"] = str(TOOLSDIR) + ":" + env["PATH"]
    capture_make_command_wrapper_symlinks(env)

    cmd = [sys.executable, "setup.py"]
    if skip_host:
        env["SKIP_HOST"] = "1"
        cmd.append("build")
    else:
        assert host_install_dir, "Missing host_install_dir"
        cmd.extend(["install", "--home", host_install_dir])

    result = subprocess.run(cmd, env=env)
    if result.returncode != 0:
        build_log_path = Path("build.log")
        if build_log_path.exists():
            build_log_path.unlink()
        result.check_returncode()
    clean_out_native_artifacts()
Beispiel #7
0
def test_get_make_flag():
    assert len(get_make_flag("SIDE_MODULE_LDFLAGS")) > 0
    assert len(get_make_flag("SIDE_MODULE_CFLAGS")) > 0
    # n.b. right now CXXFLAGS is empty so don't check length here, just check it returns
    get_make_flag("SIDE_MODULE_CXXFLAGS")
Beispiel #8
0
import argparse
import importlib.machinery
import json
import os
from pathlib import Path, PurePosixPath
import re
import subprocess
import shutil
import sys

# absolute import is necessary as this file will be symlinked
# under tools
from pyodide_build import common
from pyodide_build._f2c_fixes import fix_f2c_clapack_calls

TOOLSDIR = Path(common.get_make_flag("TOOLSDIR"))
symlinks = set(["cc", "c++", "ld", "ar", "gcc", "gfortran"])


class EnvironmentRewritingArgument(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        for e_name, e_value in os.environ.items():
            values = values.replace(f"$({e_name})", e_value)
        setattr(namespace, self.dest, values)


def collect_args(basename):
    """
    This is called when this script is called through a symlink that looks like
    a compiler or linker.
Beispiel #9
0
def symlink_dir():
    return Path(common.get_make_flag("TOOLSDIR")) / "symlinks"