diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c414625..e1bc832e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unreleased +* Added option `lib` to JuliaCall. Setting this will skip the discovery subprocess. * Bug fixes. ## 0.9.34 (2026-05-18) diff --git a/docs/src/juliacall.md b/docs/src/juliacall.md index c49e150f..55ad5868 100644 --- a/docs/src/juliacall.md +++ b/docs/src/juliacall.md @@ -142,6 +142,7 @@ be configured in two ways: | `-X juliacall-heap-size-hint=` | `PYTHON_JULIACALL_HEAP_SIZE_HINT=` | Hint for initial heap size in bytes. | | `-X juliacall-exe=` | `PYTHON_JULIACALL_EXE=` | Path to Julia binary to use (overrides JuliaPkg). | | `-X juliacall-project=` | `PYTHON_JULIACALL_PROJECT=` | Path to the Julia project to use (overrides JuliaPkg). | +| `-X juliacall-lib=` | `PYTHON_JULIACALL_LIB=` | Path to libjulia. Set to skip the relatively slow discovery process. | | `-X juliacall-trace-compile=` | `PYTHON_JULIACALL_TRACE_COMPILE=` | Print precompile statements. | | `-X juliacall-trace-compile-timing` | `PYTHON_JULIACALL_TRACE_COMPILE_TIMING=` | Include timings with precompile statements. | diff --git a/pysrc/juliacall/__init__.py b/pysrc/juliacall/__init__.py index bdd25c4f..33991fae 100644 --- a/pysrc/juliacall/__init__.py +++ b/pysrc/juliacall/__init__.py @@ -179,39 +179,45 @@ def args_from_config(config): CONFIG['opt_handle_signals'] = choice('handle_signals', ['yes', 'no'])[0] CONFIG['opt_startup_file'] = choice('startup_file', ['yes', 'no'])[0] CONFIG['opt_heap_size_hint'] = option('heap_size_hint')[0] - CONFIG['project'] = path_option('project', check_exists=True)[0] - CONFIG['exepath'] = executable_option('exe')[0] + CONFIG['project'] = project = path_option('project', check_exists=True)[0] + CONFIG['libpath'] = libpath = path_option('lib', check_exists=True)[0] + CONFIG['exepath'] = exepath = executable_option('exe')[0] # Stop if we already initialised if CONFIG['inited']: return - have_exepath = CONFIG['exepath'] is not None - have_project = CONFIG['project'] is not None - if have_exepath and have_project: + if (exepath is None) and (bindir is not None): + # if bindir is set then set exepath={bindir}/julia + CONFIG['exepath'] = exepath = os.path.join(bindir, 'julia.exe' if os.name == 'nt' else 'julia') + if (exepath is not None) and (project is not None): pass - elif (not have_exepath) and (not have_project): + elif (exepath is None) and (project is None): # we don't import this at the top level because it is not required when # juliacall is loaded by PythonCall and won't be available, or if both # `exepath` and `project` are set by the user. import juliapkg # Find the Julia executable and project - CONFIG['exepath'] = juliapkg.executable() - CONFIG['project'] = juliapkg.project() + CONFIG['exepath'] = exepath = juliapkg.executable() + CONFIG['project'] = project = juliapkg.project() else: raise Exception("Both PYTHON_JULIACALL_PROJECT and PYTHON_JULIACALL_EXE must be set together, not only one of them.") - - exepath = CONFIG['exepath'] - project = CONFIG['project'] - - # Find the Julia library - cmd = [exepath, '--project='+project, '--startup-file=no', '-O0', '--compile=min', - '-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\0", Sys.BINDIR)'] - libpath, default_bindir = subprocess.run(cmd, check=True, capture_output=True, encoding='utf8').stdout.split('\0') + if (libpath is not None) and (exepath is None): + raise Exception("PYTHON_JULIACALL_EXE is required if PYTHON_JULIACALL_LIB is set.") + + # Find the Julia library, if not specified. + if libpath is None: + cmd = [exepath, '--project='+project, '--startup-file=no', '-O0', '--compile=min', + '-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\0", Sys.BINDIR)'] + libpath, found_bindir = subprocess.run(cmd, check=True, capture_output=True, encoding='utf8').stdout.split('\0') + CONFIG['libpath'] = libpath + if bindir is None: + CONFIG['bindir'] = bindir = found_bindir + if bindir is None: + bindir = os.path.dirname(exepath) assert os.path.exists(libpath) - assert os.path.exists(default_bindir) - CONFIG['libpath'] = libpath + assert os.path.exists(bindir) # Add the Julia library directory to the PATH on Windows so Julia's system libraries can # be found. They are normally found because they are in the same directory as julia.exe, @@ -242,7 +248,7 @@ def args_from_config(config): jl_init.argtypes = [c.c_char_p, c.c_char_p] jl_init.restype = None jl_init( - (default_bindir if bindir is None else bindir).encode('utf8'), + None if bindir is None else bindir.encode('utf8'), None if sysimg is None else sysimg.encode('utf8'), )