|
1 | 1 | import os |
2 | 2 | import platform |
| 3 | +import warnings |
| 4 | +from pathlib import Path |
3 | 5 |
|
4 | 6 | import importlib_resources |
5 | 7 | import sys |
|
27 | 29 | raise |
28 | 30 |
|
29 | 31 | cppyy.ll.set_signals_as_exception(True) |
30 | | -cppyy.add_include_path(os.path.dirname(os.path.realpath(__file__))) |
| 32 | +CONFIG = os.getenv("OGDF_PYTHON_MODE", "release") |
| 33 | +if CONFIG not in ("release", "debug"): |
| 34 | + warnings.warn(f"Environment variable OGDF_PYTHON_MODE set to '{CONFIG}' instead of 'debug' or 'release'.") |
| 35 | + |
| 36 | + |
| 37 | +## Search Utils |
| 38 | + |
| 39 | +def get_library_path(name=None): |
| 40 | + if name is None: |
| 41 | + if CONFIG == "release": |
| 42 | + return get_library_path("OGDF") or get_library_path("libOGDF") |
| 43 | + else: |
| 44 | + return get_library_path(f"OGDF-{CONFIG}") or get_library_path(f"libOGDF-{CONFIG}") |
| 45 | + return cppyy.gbl.gSystem.FindDynamicLibrary(cppyy.gbl.CppyyLegacy.TString(name), True) |
| 46 | + |
| 47 | + |
| 48 | +def get_loaded_library_path(name="OGDF"): |
| 49 | + from pathlib import Path |
| 50 | + return [s for s in cppyy.gbl.gSystem.GetLibraries().split(" ") if name in Path(s).name] |
| 51 | + |
| 52 | + |
| 53 | +def get_base_include_path(include="ogdf/basic/Graph.h"): |
| 54 | + path = get_include_path(include) |
| 55 | + if path: |
| 56 | + path = path.removesuffix(include) |
| 57 | + return path |
| 58 | + |
| 59 | + |
| 60 | +def get_include_path(name="ogdf/basic/Graph.h"): |
| 61 | + import ctypes |
| 62 | + s = ctypes.c_char_p() |
| 63 | + if cppyy.gbl.gSystem.IsFileInIncludePath(name, s): |
| 64 | + return s.value.decode() |
| 65 | + else: |
| 66 | + return None |
| 67 | + |
| 68 | + |
| 69 | +def call_if_exists(func, path): |
| 70 | + if Path(path).is_dir(): |
| 71 | + func(str(path)) |
| 72 | + return True |
| 73 | + return False |
| 74 | + |
| 75 | + |
| 76 | +## Setup Search Paths |
31 | 77 |
|
32 | 78 | if "OGDF_INSTALL_DIR" in os.environ: |
33 | | - INSTALL_DIR = os.path.expanduser(os.getenv("OGDF_INSTALL_DIR")) |
34 | | - cppyy.add_include_path(os.path.join(INSTALL_DIR, "include")) |
35 | | - cppyy.add_library_path(os.path.join(INSTALL_DIR, "lib")) # TODO windows? |
| 79 | + INSTALL_DIR = Path(os.getenv("OGDF_INSTALL_DIR")).absolute() |
| 80 | + if call_if_exists(cppyy.add_library_path, INSTALL_DIR / "lib64"): |
| 81 | + call_if_exists(cppyy.add_library_path, INSTALL_DIR / "lib") |
| 82 | + else: |
| 83 | + cppyy.add_library_path(str(INSTALL_DIR / "lib")) |
| 84 | + cppyy.add_include_path(str(INSTALL_DIR / "include")) |
| 85 | + |
36 | 86 | if "OGDF_BUILD_DIR" in os.environ: |
37 | | - BUILD_DIR = os.path.expanduser(os.getenv("OGDF_BUILD_DIR")) |
38 | | - cppyy.add_include_path(os.path.join(BUILD_DIR, "include")) |
39 | | - cppyy.add_include_path(os.path.join(BUILD_DIR, "..", "include")) # TODO not canonical |
40 | | - cppyy.add_library_path(BUILD_DIR) |
| 87 | + BUILD_DIR = Path(os.getenv("OGDF_BUILD_DIR")).absolute() |
| 88 | + cppyy.add_library_path(str(BUILD_DIR)) |
| 89 | + call_if_exists(cppyy.add_library_path, BUILD_DIR / "Debug") |
| 90 | + call_if_exists(cppyy.add_library_path, BUILD_DIR / "Release") |
| 91 | + cppyy.add_include_path(str(BUILD_DIR / "include")) |
| 92 | + for line in open(BUILD_DIR / "CMakeCache.txt"): |
| 93 | + line = line.strip() |
| 94 | + if line.startswith("OGDF-PROJECT_SOURCE_DIR"): |
| 95 | + key, _, val = line.partition("=") |
| 96 | + cppyy.add_include_path(str(Path(val) / "include")) |
| 97 | + |
41 | 98 | try: |
42 | 99 | wheel_inst_dir = importlib_resources.files("ogdf_wheel") / "install" |
43 | 100 | if wheel_inst_dir.is_dir(): |
44 | 101 | cppyy.add_library_path(str(wheel_inst_dir / "lib")) |
45 | | - cppyy.add_library_path(str(wheel_inst_dir / "bin")) |
46 | 102 | cppyy.add_include_path(str(wheel_inst_dir / "include")) |
47 | 103 | except ImportError: |
48 | 104 | pass |
49 | 105 |
|
| 106 | +## Now do the actual loading |
| 107 | + |
50 | 108 | cppyy.cppdef("#undef NDEBUG") |
| 109 | +if CONFIG == "release": |
| 110 | + cppyy.cppdef("#define NDEBUG") |
| 111 | +cppyy.include("cassert") |
51 | 112 | cppyy.cppdef("#define OGDF_INSTALL") |
| 113 | + |
52 | 114 | try: |
53 | | - cppyy.load_library("OGDF") |
| 115 | + if CONFIG == "release": |
| 116 | + cppyy.load_library("COIN") |
| 117 | + cppyy.load_library("OGDF") |
| 118 | + else: |
| 119 | + cppyy.load_library(f"COIN-{CONFIG}") |
| 120 | + cppyy.load_library(f"OGDF-{CONFIG}") |
| 121 | + |
| 122 | + config_autogen_h = "ogdf/basic/internal/config_autogen.h" |
| 123 | + if not get_include_path(config_autogen_h): |
| 124 | + # try to find the config-dependent header include path if it isn't correctly configured yet |
| 125 | + config_include = get_include_path(f"ogdf-{CONFIG}/{config_autogen_h}") |
| 126 | + if config_include: |
| 127 | + config_include = config_include.removesuffix(config_autogen_h) |
| 128 | + cppyy.add_include_path(config_include) |
| 129 | + cppyy.include(config_autogen_h) |
54 | 130 |
|
55 | | - cppyy.include("ogdf/basic/internal/config_autogen.h") |
56 | 131 | cppyy.include("ogdf/basic/internal/config.h") |
57 | 132 | cppyy.include("ogdf/basic/Graph.h") |
58 | | - try: |
59 | | - cppyy.include("ogdf/cluster/ClusterGraphObserver.h") # otherwise only pre-declared |
60 | | - except ImportError: |
61 | | - pass # gone in newer versions |
62 | 133 | cppyy.include("ogdf/fileformats/GraphIO.h") |
63 | 134 | cppyy.include("ogdf/basic/LayoutStandards.h") |
64 | | -except: |
65 | | - print( # TODO check if the issue is really that the file couldn't be found |
66 | | - "ogdf-python couldn't load OGDF. " |
| 135 | +except (RuntimeError, ImportError) as e: |
| 136 | + raise ImportError( |
| 137 | + f"ogdf-python couldn't load OGDF in mode '{CONFIG}'.\n" |
| 138 | + "Please check the above underlying error and check that the below search paths contain " |
| 139 | + "OGDF headers and shared libraries in the correct release/debug configuration.\n" |
67 | 140 | "If you haven't installed OGDF globally to your system, " |
68 | | - "please set environment variables OGDF_INSTALL_DIR or OGDF_BUILD_DIR. " |
69 | | - "The current search path is:\n%s\n" |
70 | | - "The current include path is:\n%s\n" % |
71 | | - (cppyy.gbl.gSystem.GetDynamicPath(), cppyy.gbl.gInterpreter.GetIncludePath()), |
72 | | - file=sys.stderr) |
73 | | - raise |
| 141 | + "you can use the environment variables OGDF_INSTALL_DIR or OGDF_BUILD_DIR.\n" |
| 142 | + f"The current search path is:\n{cppyy.gbl.gSystem.GetDynamicPath()}\n" |
| 143 | + f"The current include path is:\n{cppyy.gbl.gInterpreter.GetIncludePath()}\n" |
| 144 | + ) from e |
74 | 145 |
|
75 | | -if cppyy.gbl.ogdf.debugMode: |
76 | | - cppyy.cppdef("#undef NDEBUG") |
| 146 | +if CONFIG == "release": |
| 147 | + if cppyy.gbl.ogdf.debugMode: |
| 148 | + warnings.warn("Attempted to load OGDF release build, but the found library was built in debug mode.") |
77 | 149 | else: |
78 | | - cppyy.cppdef("#define NDEBUG") |
79 | | -cppyy.include("cassert") |
| 150 | + if not cppyy.gbl.ogdf.debugMode: |
| 151 | + warnings.warn("Attempted to load OGDF debug build, but the found library was built in release mode.") |
80 | 152 |
|
81 | | -# Load re-exports |
| 153 | +## Load re-exports |
82 | 154 | from cppyy import include as cppinclude, cppdef, cppexec, nullptr |
83 | 155 | from cppyy.gbl import ogdf |
84 | 156 |
|
85 | | -__all__ = ["cppyy", "cppinclude", "cppdef", "cppexec", "nullptr", "ogdf"] |
| 157 | +__all__ = ["cppyy", "cppinclude", "cppdef", "cppexec", "nullptr", "ogdf", |
| 158 | + "get_library_path", "get_loaded_library_path", "get_include_path", "get_base_include_path"] |
86 | 159 | __keep_imports = [cppyy, cppinclude, cppdef, cppexec, nullptr, ogdf] |
0 commit comments