Fix pywasmcross when pyodide_build is installed out of tree (#2824)

Symlinking cc to pywasmcross.py only works if pywasmcross.py has
execute permissions. When we install the package with pip, it will not
set execute permissions on pywasmcross.py. It does set execute flags
on entrypoints. Thus, define an entrypoint called _pywasmcross which
calls pywasmcross.main. If a script called _pywasmcross exists, we are
using an out-of-tree install so symlink cc to _pywasmcross. Otherwise,
we should be in tree and pywasmcross.py should have the execute flag
set, so symlink cc to pywasmcross.py.

On the other side, if __main__.__file__ is in a folder named pyodide_build
or bin we are being invoked normally, otherwise we are being invoked via
a symlink.
This commit is contained in:
Hood Chatham 2022-07-01 22:00:27 -07:00 committed by GitHub
parent e7ad0ce944
commit b0a28579bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 19 deletions

View File

@ -12,33 +12,45 @@ import json
import os
import sys
from pathlib import Path, PurePosixPath
from typing import Any
IS_MAIN = __name__ == "__main__"
if IS_MAIN:
from __main__ import __file__ as INVOKED_PATH_STR
INVOKED_PATH = Path(INVOKED_PATH_STR)
SYMLINKS = {"cc", "c++", "ld", "ar", "gcc", "gfortran", "cargo"}
IS_COMPILER_INVOCATION = INVOKED_PATH.name in SYMLINKS
if IS_COMPILER_INVOCATION:
# If possible load from environment variable, if necessary load from disk.
if "PYWASMCROSS_ARGS" in os.environ:
PYWASMCROSS_ARGS = json.loads(os.environ["PYWASMCROSS_ARGS"])
else:
with open(Path(__file__).parent / "pywasmcross_env.json") as f:
try:
with open(INVOKED_PATH.parent / "pywasmcross_env.json") as f:
PYWASMCROSS_ARGS = json.load(f)
except FileNotFoundError:
raise RuntimeError(
"Invalid invocation: can't find PYWASMCROSS_ARGS."
f" Invoked from {INVOKED_PATH}."
)
# restore __name__ so that relative imports work as we expect
__name__ = PYWASMCROSS_ARGS.pop("orig__name__")
sys.path = PYWASMCROSS_ARGS.pop("PYTHONPATH")
os.environ["PATH"] = PYWASMCROSS_ARGS.pop("PATH")
# restore __name__ so that relative imports work as we expect
__name__ = PYWASMCROSS_ARGS.pop("orig__name__")
import re
import shutil
import subprocess
from collections import namedtuple
from contextlib import contextmanager
from tempfile import TemporaryDirectory
from typing import Any, Iterator, Literal, MutableMapping, NoReturn
from typing import Iterator, Literal, MutableMapping, NoReturn
from pyodide_build import common
from pyodide_build._f2c_fixes import fix_f2c_input, fix_f2c_output, scipy_fixes
symlinks = {"cc", "c++", "ld", "ar", "gcc", "gfortran", "cargo"}
def symlink_dir() -> Path:
return Path(common.get_make_flag("TOOLSDIR")) / "symlinks"
@ -68,13 +80,17 @@ def make_command_wrapper_symlinks(
exist.
"""
exec_path = Path(__file__).resolve()
for symlink in symlinks:
for symlink in SYMLINKS:
symlink_path = symlink_dir / 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)
pywasmcross_exe = shutil.which("_pywasmcross")
if pywasmcross_exe:
symlink_path.symlink_to(pywasmcross_exe)
else:
symlink_path.symlink_to(exec_path)
except FileExistsError:
pass
if symlink == "c++":
@ -609,13 +625,13 @@ def environment_substitute_args(
return subbed_args
if IS_MAIN:
REPLAY_ARGS = ReplayArgs(**PYWASMCROSS_ARGS)
def compiler_main():
replay_args = ReplayArgs(**PYWASMCROSS_ARGS)
basename = Path(sys.argv[0]).name
args = list(sys.argv)
args[0] = basename
if basename in symlinks:
sys.exit(handle_command(args, REPLAY_ARGS))
else:
raise Exception(f"Unexpected invocation '{basename}'")
sys.exit(handle_command(args, replay_args))
if IS_COMPILER_INVOCATION:
compiler_main()

View File

@ -2,4 +2,10 @@
import setuptools
if __name__ == "__main__":
setuptools.setup()
setuptools.setup(
entry_points={
"console_scripts": [
"_pywasmcross = pyodide_build.pywasmcross:compiler_main",
]
}
)