diff --git a/pyodide_build/pywasmcross.py b/pyodide_build/pywasmcross.py index 52aa6effd..561d3f738 100755 --- a/pyodide_build/pywasmcross.py +++ b/pyodide_build/pywasmcross.py @@ -264,12 +264,26 @@ def handle_command(line, args, dryrun=False): elif new_args[0] == "em++": new_args.extend(args.cflags.split() + args.cxxflags.split()) + optflags_valid = [f"-O{tok}" for tok in "01234sz"] + optflag = None + # Identify the optflag (e.g. -O3) in cflags/cxxflags/ldflags. Last one has + # priority. + for arg in new_args[::-1]: + if arg in optflags_valid: + optflag = arg + break + lapack_dir = None used_libs = set() # Go through and adjust arguments for arg in line[1:]: + if arg in optflags_valid and optflag is not None and arg != optflag: + # There are multiple contradictory optflags provided, use the one + # from cflags/cxxflags/ldflags + continue + if arg.startswith("-I"): if ( str(Path(arg[2:]).resolve()).startswith(sys.prefix + "/include/python") diff --git a/pyodide_build/tests/test_pywasmcross.py b/pyodide_build/tests/test_pywasmcross.py index d2f1d5c1a..c6411862b 100644 --- a/pyodide_build/tests/test_pywasmcross.py +++ b/pyodide_build/tests/test_pywasmcross.py @@ -1,7 +1,9 @@ -from collections import namedtuple from pathlib import Path import sys import argparse +from dataclasses import dataclass + +import pytest sys.path.append(str(Path(__file__).parents[2])) @@ -10,6 +12,18 @@ from pyodide_build.pywasmcross import f2c # noqa: E402 from pyodide_build.pywasmcross import make_parser +@dataclass +class BuildArgs: + """An object to hold build arguments""" + + cflags: str = "" + cxxflags: str = "" + ldflags: str = "" + host: str = "" + replace_libs: str = "" + install_dir: str = "" + + def _args_wrapper(func): """Convert function to take as input / return a string instead of a list of arguments @@ -33,12 +47,7 @@ f2c_wrap = _args_wrapper(f2c) def test_handle_command(): - Args = namedtuple( - "args", ["cflags", "cxxflags", "ldflags", "host", "replace_libs", "install_dir"] - ) - args = Args( - cflags="", cxxflags="", ldflags="", host="", replace_libs="", install_dir="" - ) + args = BuildArgs() assert handle_command_wrap("gcc -print-multiarch", args) is None assert handle_command_wrap("gcc test.c", args) == "emcc test.c" assert ( @@ -47,13 +56,10 @@ def test_handle_command(): ) # check cxxflags injection and cpp detection - args = Args( + args = BuildArgs( cflags="-I./lib2", cxxflags="-std=c++11", ldflags="-lm", - host="", - replace_libs="", - install_dir="", ) assert ( handle_command_wrap("gcc -I./lib1 test.cpp -o test.o", args) @@ -61,7 +67,7 @@ def test_handle_command(): ) # check ldflags injection - args = Args( + args = BuildArgs( cflags="", cxxflags="", ldflags="-lm", host="", replace_libs="", install_dir="" ) assert ( @@ -70,13 +76,8 @@ def test_handle_command(): ) # check library replacement and removal of double libraries - args = Args( - cflags="", - cxxflags="", - ldflags="", - host="", + args = BuildArgs( replace_libs="bob=fred", - install_dir="", ) assert ( handle_command_wrap("gcc -shared test.o -lbob -ljim -ljim -o test.so", args) @@ -87,6 +88,25 @@ def test_handle_command(): assert handle_command_wrap("gcc /usr/file.c", args) is None +@pytest.mark.parametrize( + "in_ext, out_ext, executable, flag_name", + [ + (".c", ".o", "emcc", "cflags"), + (".cpp", ".o", "em++", "cxxflags"), + (".c", ".so", "emcc", "ldflags"), + ], +) +def test_handle_command_optflags(in_ext, out_ext, executable, flag_name): + # Make sure that when multiple optflags are present those in cflags, + # cxxflags, or ldflags has priority + + args = BuildArgs(**{flag_name: "-Oz"}) + assert ( + handle_command_wrap(f"gcc -O3 test.{in_ext} -o test.{out_ext}", args) + == f"{executable} -Oz test.{in_ext} -o test.{out_ext}" + ) + + def test_f2c(): assert f2c_wrap("gfortran test.f") == "gfortran test.c" assert f2c_wrap("gcc test.c") is None @@ -98,12 +118,7 @@ def test_f2c(): def test_conda_compiler_compat(): - Args = namedtuple( - "args", ["cflags", "cxxflags", "ldflags", "host", "replace_libs", "install_dir"] - ) - args = Args( - cflags="", cxxflags="", ldflags="", host="", replace_libs="", install_dir="" - ) + args = BuildArgs() assert handle_command_wrap( "gcc -shared -c test.o -B /compiler_compat -o test.so", args ) == ("emcc -c test.o -o test.so")