diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1450454..36426c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,18 +3,14 @@ name: Python Tests -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] +on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] # macos-latest + os: [ubuntu-latest, windows-latest, macos-latest] python: ['3.9', '3.8', '3.7', '3.6', pypy3] steps: diff --git a/clr_loader/__init__.py b/clr_loader/__init__.py index 3616345..a67f4a9 100644 --- a/clr_loader/__init__.py +++ b/clr_loader/__init__.py @@ -50,7 +50,7 @@ def __getitem__(self, path): return self.get_assembly(path) -def get_mono(domain=None): +def get_mono(domain=None, config_file=None, path=None, gc=None): from .mono import Mono impl = Mono(domain=domain) diff --git a/clr_loader/ffi/__init__.py b/clr_loader/ffi/__init__.py index 7befd2d..d1daa11 100644 --- a/clr_loader/ffi/__init__.py +++ b/clr_loader/ffi/__init__.py @@ -36,7 +36,9 @@ def load_hostfxr(dotnet_root): def load_mono(path=None, gc=None): # Preload C++ standard library, Mono needs that and doesn't properly link against it - ffi.dlopen("stdc++", ffi.RTLD_GLOBAL) + if sys.platform.startswith("linux"): + ffi.dlopen("stdc++", ffi.RTLD_GLOBAL) + if path is None: from ctypes.util import find_library diff --git a/clr_loader/hostfxr.py b/clr_loader/hostfxr.py index 00e6aa4..6709908 100644 --- a/clr_loader/hostfxr.py +++ b/clr_loader/hostfxr.py @@ -16,13 +16,28 @@ def __init__(self, runtime_config, dotnet_root=None): if not dotnet_root: dotnet_root = os.environ.get("DOTNET_ROOT", None) + if not dotnet_root and sys.platform == 'win32': + # On Windows, the host library is stored separately from dotnet.exe for x86 + if sys.maxsize > 2 ** 32: + possible_root = os.path.join(os.environ.get("ProgramFiles"), "dotnet") + else: + possible_root = os.path.join(os.environ.get("ProgramFiles(x86)"), "dotnet") + + if os.path.isdir(possible_root): + dotnet_root = possible_root + if not dotnet_root: dotnet_path = shutil.which("dotnet") if not dotnet_path: raise RuntimeError("Can not determine dotnet root") try: - dotnet_tmp_path = os.readlink(dotnet_path) + # Pypy does not provide os.readlink right now + if hasattr(os, "readlink"): + dotnet_tmp_path = os.readlink(dotnet_path) + else: + dotnet_tmp_path = dotnet_path + if os.path.isabs(dotnet_tmp_path): dotnet_path = dotnet_tmp_path else: diff --git a/clr_loader/mono.py b/clr_loader/mono.py index f85f096..c5d2f6a 100644 --- a/clr_loader/mono.py +++ b/clr_loader/mono.py @@ -11,9 +11,9 @@ class Mono: - def __init__(self, domain=None, config_file=None): + def __init__(self, domain=None, config_file=None, path=None, gc=None): self._assemblies = {} - initialize(config_file=config_file) + initialize(config_file=config_file, path=path, gc=gc) if domain is None: self._domain = _ROOT_DOMAIN @@ -84,7 +84,7 @@ def initialize(config_file, path=None, gc=None): global _MONO, _ROOT_DOMAIN if _MONO is None: _MONO = load_mono(path=path, gc=gc) - + if config_file is None: config_file = ffi.NULL else: diff --git a/example/example.csproj b/example/example.csproj index d7763f1..f30cfb7 100644 --- a/example/example.csproj +++ b/example/example.csproj @@ -3,4 +3,10 @@ netcoreapp31;netstandard20 true + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/test_common.py b/tests/test_common.py index aa420e4..7788d34 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -1,33 +1,51 @@ import pytest from subprocess import check_call import os -from cffi import FFI -NULL = FFI().NULL +import sys + + +@pytest.fixture(scope="session") +def example_netstandard(tmpdir_factory): + return build_example(tmpdir_factory, "netstandard20") @pytest.fixture(scope="session") -def example_dll(tmpdir_factory): - out = str(tmpdir_factory.mktemp("example")) +def example_netcore(tmpdir_factory): + return build_example(tmpdir_factory, "netcoreapp31") + +def build_example(tmpdir_factory, framework): + out = str(tmpdir_factory.mktemp(f"example-{framework}")) proj_path = os.path.join(os.path.dirname(__file__), "../example") - check_call(["dotnet", "build", proj_path, "-o", out, "-f", "netcoreapp31"]) + check_call(["dotnet", "build", proj_path, "-o", out, "-f", framework]) return out -def test_mono(example_dll): +@pytest.mark.xfail +def test_mono(example_netstandard): from clr_loader import get_mono mono = get_mono() - asm = mono.get_assembly(os.path.join(example_dll, "example.dll")) + asm = mono.get_assembly(os.path.join(example_netstandard, "example.dll")) run_tests(asm) -def test_coreclr(example_dll): +def test_coreclr(example_netcore): from clr_loader import get_coreclr - coreclr = get_coreclr(os.path.join(example_dll, "example.runtimeconfig.json")) - asm = coreclr.get_assembly(os.path.join(example_dll, "example.dll")) + coreclr = get_coreclr(os.path.join(example_netcore, "example.runtimeconfig.json")) + asm = coreclr.get_assembly(os.path.join(example_netcore, "example.dll")) + + run_tests(asm) + + +@pytest.mark.skipif(sys.platform != 'win32', reason=".NET Framework only exists on Windows") +def test_netfx(example_netstandard): + from clr_loader import get_netfx + + netfx = get_netfx() + asm = netfx.get_assembly(os.path.join(example_netstandard, "example.dll")) run_tests(asm)