diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b89207d..1f1e3a6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,10 +39,10 @@ jobs: brew update -f brew install libomp llvm echo " - export PATH=\"/usr/local/opt/llvm/bin:\$PATH\" + export PATH=\"$(brew --prefix)/opt/llvm/bin:\$PATH\" export DYLD_LIBRARY_PATH=\"$(brew --prefix)/lib:$DYLD_LIBRARY_PATH\" - export LDFLAGS=\"-L/usr/local/opt/llvm/lib\" - export CPPFLAGS=\"-I/usr/local/opt/llvm/include\" + export LDFLAGS=\"-L$(brew --prefix)/opt/llvm/lib\" + export CPPFLAGS=\"-I$(brew --prefix)/opt/llvm/include\" export LD=ld.lld export AR=llvm-ar export RANLIB=llvm-ranlib diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 59188cd..21476ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,10 +34,10 @@ jobs: brew update -f brew install libomp llvm echo " - export PATH=\"/usr/local/opt/llvm/bin:\$PATH\" + export PATH=\"$(brew --prefix)/opt/llvm/bin:\$PATH\" export DYLD_LIBRARY_PATH=\"$(brew --prefix)/lib:$DYLD_LIBRARY_PATH\" - export LDFLAGS=\"-L/usr/local/opt/llvm/lib\" - export CPPFLAGS=\"-I/usr/local/opt/llvm/include\" + export LDFLAGS=\"-L$(brew --prefix)/opt/llvm/lib\" + export CPPFLAGS=\"-I$(brew --prefix)/opt/llvm/include\" export LD=ld.lld export AR=llvm-ar export RANLIB=llvm-ranlib diff --git a/COVERAGE.md b/COVERAGE.md index 67e438c..2af47f4 100644 --- a/COVERAGE.md +++ b/COVERAGE.md @@ -3,8 +3,8 @@ | src/hatch_cython/\_\_init\_\_.py | 2 | 0 | 0 | 0 | 100% | | src/hatch_cython/config/\_\_init\_\_.py | 2 | 0 | 0 | 0 | 100% | | src/hatch_cython/config/autoimport.py | 9 | 0 | 2 | 0 | 100% | -| src/hatch_cython/config/config.py | 139 | 9 | 58 | 5 | 92% | -| src/hatch_cython/config/defaults.py | 6 | 0 | 0 | 0 | 100% | +| src/hatch_cython/config/config.py | 142 | 9 | 60 | 6 | 92% | +| src/hatch_cython/config/defaults.py | 28 | 3 | 10 | 2 | 87% | | src/hatch_cython/config/files.py | 35 | 1 | 16 | 2 | 94% | | src/hatch_cython/config/flags.py | 70 | 1 | 24 | 0 | 99% | | src/hatch_cython/config/includes.py | 15 | 1 | 8 | 1 | 91% | @@ -17,4 +17,4 @@ | src/hatch_cython/plugin.py | 243 | 12 | 156 | 10 | 94% | | src/hatch_cython/temp.py | 13 | 0 | 2 | 0 | 100% | | src/hatch_cython/utils.py | 44 | 4 | 20 | 1 | 89% | -| **TOTAL** | **748** | **38** | **355** | **26** | **94%** | +| **TOTAL** | **773** | **41** | **367** | **29** | **94%** | diff --git a/README.md b/README.md index b5ab3c4..74391ed 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,10 @@ templated_win = { supported = ["int", "float", "complex"] } templated_win_x86_64 = { supported = ["int", "float", "np.double"]} ``` +## Notes + +- MacOS users with brew installed will have `brew --prefix` libs and include paths added in compilation step. Code parsing is found [here](./src/hatch_cython/config/defaults.py#L11) + ## Development ### Requirements diff --git a/src/hatch_cython/config/config.py b/src/hatch_cython/config/config.py index acc794c..e695c61 100644 --- a/src/hatch_cython/config/config.py +++ b/src/hatch_cython/config/config.py @@ -8,7 +8,7 @@ from hatchling.builders.hooks.plugin.interface import BuildHookInterface from hatch_cython.config.autoimport import Autoimport -from hatch_cython.config.defaults import get_default_compile, get_default_link +from hatch_cython.config.defaults import brew_path, get_default_compile, get_default_link from hatch_cython.config.files import FileArgs from hatch_cython.config.flags import EnvFlags, parse_env_args from hatch_cython.config.includes import parse_includes @@ -89,12 +89,25 @@ def parse_from_dict(cls: BuildHookInterface): PlatformArgs(arg="/openmp", platforms="windows"), PlatformArgs(arg="-fopenmp", platforms="linux"), PlatformArgs(arg="-lomp", platforms="darwin", marker=LTPY311, apply_to_marker=running_in_ci), - PlatformArgs( - arg="-L/usr/local/opt/llvm/lib/c++ -Wl,-rpath,/usr/local/opt/llvm/lib/c++", - platforms=["darwin"], - depends_path=True, - ), ] + + brew = brew_path() + if brew: + link.extend( + [ + PlatformArgs( + arg=f"-L{brew}/opt/llvm/lib/c++ -Wl,-rpath,{brew}/llvm/lib/c++", + platforms=["darwin"], + depends_path=True, + ), + PlatformArgs( + arg="-L/usr/local/opt/llvm/lib/c++ -Wl,-rpath,/usr/local/llvm/lib/c++", + platforms=["darwin"], + depends_path=True, + ), + ] + ) + cma = ({*cfg.compile_args}).union({*comp}) cfg.compile_args = list(cma) seb = ({*cfg.extra_link_args}).union({*link}) @@ -231,7 +244,7 @@ def flush(it): # side effect list(map(flush, args.values())) - return flat + return list({*flat}) def asdict(self): d = asdict(self) diff --git a/src/hatch_cython/config/defaults.py b/src/hatch_cython/config/defaults.py index b455ea4..7fa0fd0 100644 --- a/src/hatch_cython/config/defaults.py +++ b/src/hatch_cython/config/defaults.py @@ -1,18 +1,53 @@ +from subprocess import CalledProcessError, check_output + from hatch_cython.config.platform import PlatformArgs from hatch_cython.constants import POSIX_CORE +from hatch_cython.utils import aarch, memo, plat + +BREW = "brew" + + +@memo +def brew_path(): + if plat() == "darwin": + # no user input - S603 is false positive + try: + proc = check_output([BREW, "--prefix"]) # noqa: S603 + except CalledProcessError: + proc = None + dec = proc.decode().replace("\n", "") if proc else None + if dec and dec != "": + return dec + return "/opt/homebrew" if aarch() == "arm64" else "/usr/local" def get_default_link(): - return [ - PlatformArgs(arg="-L/opt/homebrew/lib", platforms=POSIX_CORE, depends_path=True), + base = [ PlatformArgs(arg="-L/usr/local/lib", platforms=POSIX_CORE, depends_path=True), PlatformArgs(arg="-L/usr/local/opt", platforms=POSIX_CORE, depends_path=True), ] + brew = brew_path() + if brew: + base.extend( + [ + PlatformArgs(arg=f"-L{brew}/opt", platforms=POSIX_CORE, depends_path=True), + PlatformArgs(arg=f"-L{brew}/lib", platforms=POSIX_CORE, depends_path=True), + ] + ) + return base + def get_default_compile(): - return [ + base = [ PlatformArgs(arg="-O2"), - PlatformArgs(arg="-I/opt/homebrew/include", platforms=POSIX_CORE, depends_path=True), PlatformArgs(arg="-I/usr/local/include", platforms=POSIX_CORE, depends_path=True), ] + brew = brew_path() + if brew: + base.extend( + [ + PlatformArgs(arg=f"-I{brew}/include", platforms=POSIX_CORE, depends_path=True), + ] + ) + return base diff --git a/test_libraries/simple_structure/hatch.toml b/test_libraries/simple_structure/hatch.toml index 57aa1db..eaaf858 100644 --- a/test_libraries/simple_structure/hatch.toml +++ b/test_libraries/simple_structure/hatch.toml @@ -19,12 +19,6 @@ compile_args = [ "linux", "darwin" ], arg = "-Wcpp"}, - {platforms = [ - "darwin" - ], arch = "x86_64", arg = "-arch x86_64"}, - {platforms = [ - "darwin" - ], arch = "arm64", arg = "-arch arm64"}, {platforms = [ "darwin" ], arch = "x86_64", arg = "-I/usr/local/opt/llvm/include", depends_path = true, marker = "python_version <= '3.10'"}, diff --git a/test_libraries/src_structure/hatch.toml b/test_libraries/src_structure/hatch.toml index e4c9520..eaa1fc2 100644 --- a/test_libraries/src_structure/hatch.toml +++ b/test_libraries/src_structure/hatch.toml @@ -19,12 +19,6 @@ compile_args = [ "linux", "darwin" ], arg = "-Wcpp"}, - {platforms = [ - "darwin" - ], arch = "x86_64", arg = "-arch x86_64"}, - {platforms = [ - "darwin" - ], arch = "arm64", arg = "-arch arm64"}, {platforms = [ "darwin" ], arch = "x86_64", arg = "-I/usr/local/opt/llvm/include", depends_path = true, marker = "python_version <= '3.10'"}, diff --git a/tests/test_config.py b/tests/test_config.py index cd14508..6f5118b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,10 +4,21 @@ from toml import loads from hatch_cython.config import parse_from_dict +from hatch_cython.config.defaults import brew_path +from hatch_cython.utils import aarch, plat from .utils import arch_platform, import_module, patch_path, pyversion +def test_brew_path(): + if plat() == "darwin" and aarch() == "x86_64": + assert brew_path() == "/usr/local" + elif plat() == "darwin" and aarch() == "arm64": + assert brew_path() == "/usr/local" + else: + assert brew_path() is None + + def test_config_parser(): data = """ [options] @@ -77,114 +88,146 @@ def getcfg(): with pyversion("3", "9"), arch_platform("arm64", "darwin"), patch_path("arm64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/opt/homebrew/include", - "-I/abc/def", - "-Wcpp", - "-L/usr/local/opt/llvm/include", - "-py39", - "-O3", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/abc/def", + "-py39", + "-Wcpp", + "-I/opt/homebrew/include", + "-O3", + "-L/usr/local/opt/llvm/include", + ] + ) with arch_platform("x86_64", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-std=c++17", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-LC://abc/def"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-std=c++17", + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted(["-LC://abc/def"]) with arch_platform("x86_64", "linux"), patch_path("x86_64", "/usr/local/opt"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-I/abc/def", - "-Wcpp", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt", "-L/etc/ssl/ssl.h"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/usr/local/include", + "-I/abc/def", + "-Wcpp", + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + ["-L/usr/local/lib", "-L/usr/local/opt", "-L/etc/ssl/ssl.h"] + ) with arch_platform("x86_64", "darwin"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-I/abc/def", - "-Wcpp", - "-L/usr/local/opt/llvm/include", - "-O2", - ] - assert cfg.compile_links_for_platform == [ - "-L/usr/local/lib", - "-L/usr/local/opt", - "-L/usr/local/opt/llvm/lib", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/usr/local/include", + "-I/abc/def", + "-Wcpp", + "-L/usr/local/opt/llvm/include", + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/lib", + "-L/usr/local/opt", + "-L/usr/local/opt/llvm/lib", + ] + ) with arch_platform("arm64", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-std=c++17", - "-O3", - ] - assert cfg.compile_links_for_platform == ["-LC://abc/def", "-L/usr/include/cpu/simd.h"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-std=c++17", + "-O3", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted(["-LC://abc/def", "-L/usr/include/cpu/simd.h"]) with arch_platform("arm64", "linux"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-I/abc/def", - "-Wcpp", - "-O3", - ] - assert cfg.compile_links_for_platform == [ - "-L/usr/local/lib", - "-L/usr/local/opt", - "-L/etc/ssl/ssl.h", - "-L/usr/include/cpu/simd.h", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/usr/local/include", + "-I/abc/def", + "-Wcpp", + "-O3", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/lib", + "-L/usr/local/opt", + "-L/etc/ssl/ssl.h", + "-L/usr/include/cpu/simd.h", + ] + ) with arch_platform("arm64", "darwin"), patch_path("arm64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/opt/homebrew/include", - "-I/abc/def", - "-Wcpp", - "-L/usr/local/opt/llvm/include", - "-O3", - ] - assert cfg.compile_links_for_platform == [ - "-L/opt/homebrew/lib", - "-L/usr/local/opt/llvm/lib", - "-L/usr/include/cpu/simd.h", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/opt/homebrew/include", + "-I/abc/def", + "-Wcpp", + "-L/usr/local/opt/llvm/include", + "-O3", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/opt/homebrew/lib", + "-L/usr/local/opt/llvm/lib", + "-L/usr/include/cpu/simd.h", + ] + ) with arch_platform("", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == ["-std=c++17", "-O1"] - assert cfg.compile_links_for_platform == [ - "-LC://abc/def", - ] + assert sorted(cfg.compile_args_for_platform) == sorted(["-std=c++17", "-O1"]) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-LC://abc/def", + ] + ) with arch_platform("", "linux"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == ["-I/usr/local/include", "-I/abc/def", "-Wcpp", "-O1"] - assert cfg.compile_links_for_platform == [ - "-L/usr/local/lib", - "-L/usr/local/opt", - "-L/etc/ssl/ssl.h", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + ["-I/usr/local/include", "-I/abc/def", "-Wcpp", "-O1"] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/lib", + "-L/usr/local/opt", + "-L/etc/ssl/ssl.h", + ] + ) with arch_platform("", "darwin"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-I/abc/def", - "-Wcpp", - "-L/usr/local/opt/llvm/include", - "-O1", - ] - assert cfg.compile_links_for_platform == [ - "-L/usr/local/lib", - "-L/usr/local/opt", - "-L/usr/local/opt/llvm/lib", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/usr/local/include", + "-I/abc/def", + "-Wcpp", + "-L/usr/local/opt/llvm/include", + "-O1", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/lib", + "-L/usr/local/opt", + "-L/usr/local/opt/llvm/lib", + ] + ) cfg = getcfg() @@ -210,70 +253,96 @@ def getcfg(): with arch_platform("x86_64", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-O2", - ] - assert cfg.compile_links_for_platform == [] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted([]) with arch_platform("x86_64", "linux"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt"] + assert sorted(cfg.compile_args_for_platform) == sorted(["-I/usr/local/include", "-O2"]) + assert sorted(cfg.compile_links_for_platform) == sorted(["-L/usr/local/opt", "-L/usr/local/lib"]) with arch_platform("x86_64", "darwin"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-O2", + "-I/usr/local/include", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted(["-L/usr/local/opt", "-L/usr/local/lib"]) with arch_platform("arm64", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-O2", - ] - assert cfg.compile_links_for_platform == [] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted([]) with arch_platform("arm64", "linux"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/usr/local/include", + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted(["-L/usr/local/lib", "-L/usr/local/opt"]) with arch_platform("arm64", "darwin"), patch_path("arm64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/opt/homebrew/include", - "-O2", - ] - assert cfg.compile_links_for_platform == [ - "-L/opt/homebrew/lib", - ] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-I/opt/homebrew/include", + "-O2", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/opt/homebrew/lib", + ] + ) with arch_platform("", "windows"): cfg = getcfg() - assert cfg.compile_args_for_platform == ["-O2"] - assert cfg.compile_links_for_platform == [] + assert sorted(cfg.compile_args_for_platform) == sorted(["-O2"]) + assert sorted(cfg.compile_links_for_platform) == sorted([]) with arch_platform("", "linux"), patch_path("x86_64", "/etc/ssl/ssl.h"): cfg = getcfg() - assert cfg.compile_args_for_platform == ["-I/usr/local/include", "-O2"] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-O2", + "-I/usr/local/include", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/opt", + "-L/usr/local/lib", + ] + ) with arch_platform("", "darwin"), patch_path("x86_64"): cfg = getcfg() - assert cfg.compile_args_for_platform == [ - "-I/usr/local/include", - "-O2", - ] - assert cfg.compile_links_for_platform == ["-L/usr/local/lib", "-L/usr/local/opt"] + assert sorted(cfg.compile_args_for_platform) == sorted( + [ + "-O2", + "-I/usr/local/include", + ] + ) + assert sorted(cfg.compile_links_for_platform) == sorted( + [ + "-L/usr/local/opt", + "-L/usr/local/lib", + ] + ) cfg = getcfg() assert cfg.compile_kwargs == {} diff --git a/tests/utils.py b/tests/utils.py index c1e7f8b..57bc60e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -33,6 +33,12 @@ def wrap(path): yield +@contextmanager +def patch_brew(prefix): + with patch("hatch_cython.config.defaults.brew_path", lambda: prefix): + yield + + @contextmanager def arch_platform(arch: str, platform: str): def aarchgetter(): @@ -41,6 +47,10 @@ def aarchgetter(): def platformgetter(): return platform + expect_brew = None + if platform == "darwin": + expect_brew = "/usr/local" if arch == "x86_64" else "/opt/homebrew" + try: with patch("hatch_cython.utils.plat", platformgetter): with patch("hatch_cython.utils.plat", platformgetter): @@ -48,7 +58,8 @@ def platformgetter(): with patch("hatch_cython.plugin.plat", platformgetter): with patch("hatch_cython.utils.aarch", aarchgetter): with patch("hatch_cython.config.platform.aarch", aarchgetter): - yield + with patch_brew(expect_brew): + yield finally: print(f"Clean {arch}-{platform}") # noqa: T201 del aarchgetter, platformgetter