Skip to content

Commit fb88f4d

Browse files
tkfstevengj
authored andcommitted
Use find_libpython.py in deps/build.jl (#556)
* Copy find_libpython.py from PyJulia * Call find_libpython.py from deps/build.jl * Handle the case find_libpython.py cannot find libpython * Store full path to libpython in deps.jl * Update find_libpython.py * Use --candidate-names in the fallback path * Improve debugging message * Use pythonenv in when invoking find_libpython.py * Add _linked_libpython_windows
1 parent bb7c9bb commit fb88f4d

File tree

2 files changed

+433
-65
lines changed

2 files changed

+433
-65
lines changed

Diff for: deps/build.jl

+39-65
Original file line numberDiff line numberDiff line change
@@ -47,91 +47,65 @@ pysys(python::AbstractString, var::AbstractString) = pyvar(python, "sys", var)
4747

4848
const dlprefix = Compat.Sys.iswindows() ? "" : "lib"
4949

50-
# return libpython name, libpython pointer
51-
function find_libpython(python::AbstractString)
52-
# it is ridiculous that it is this hard to find the name of libpython
53-
v = pyconfigvar(python,"VERSION","")
54-
libs = [ dlprefix*"python"*v, dlprefix*"python" ]
55-
lib = pyconfigvar(python, "LIBRARY")
56-
lib != "None" && pushfirst!(libs, splitext(lib)[1])
57-
lib = pyconfigvar(python, "LDLIBRARY")
58-
lib != "None" && pushfirst!(pushfirst!(libs, basename(lib)), lib)
59-
libs = unique(libs)
60-
61-
# it is ridiculous that it is this hard to find the path of libpython
62-
libpaths = [pyconfigvar(python, "LIBDIR"),
63-
(Compat.Sys.iswindows() ? dirname(pysys(python, "executable")) : joinpath(dirname(dirname(pysys(python, "executable"))), "lib"))]
64-
if Compat.Sys.isapple()
65-
push!(libpaths, pyconfigvar(python, "PYTHONFRAMEWORKPREFIX"))
66-
end
50+
# print out extra info to help with remote debugging
51+
const PYCALL_DEBUG_BUILD = "yes" == get(ENV, "PYCALL_DEBUG_BUILD", "no")
6752

68-
# `prefix` and `exec_prefix` are the path prefixes where python should look for python only and compiled libraries, respectively.
69-
# These are also changed when run in a virtualenv.
70-
exec_prefix = pysys(python, "exec_prefix")
71-
72-
push!(libpaths, exec_prefix)
73-
push!(libpaths, joinpath(exec_prefix, "lib"))
53+
function exec_find_libpython(python::AbstractString, options)
54+
cmd = `$python $(joinpath(@__DIR__, "find_libpython.py")) $options`
55+
if PYCALL_DEBUG_BUILD
56+
cmd = `$cmd --verbose`
57+
end
58+
return readlines(pythonenv(cmd))
59+
end
7460

75-
error_strings = String[]
61+
function show_dlopen_error(e)
62+
if PYCALL_DEBUG_BUILD
63+
println(stderr, "dlopen($libpath_lib) ==> ", e)
64+
# Using STDERR since find_libpython.py prints debugging
65+
# messages to STDERR too.
66+
end
67+
end
7668

77-
# TODO: other paths? python-config output? pyconfigvar("LDFLAGS")?
69+
# return libpython name, libpython pointer
70+
function find_libpython(python::AbstractString)
71+
dlopen_flags = Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL
7872

79-
# find libpython (we hope):
80-
for lib in libs
81-
for libpath in libpaths
82-
libpath_lib = joinpath(libpath, lib)
83-
if isfile(libpath_lib*"."*Libdl.dlext)
84-
try
85-
return (Libdl.dlopen(libpath_lib,
86-
Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL),
87-
libpath_lib)
88-
catch e
89-
push!(error_strings, string("dlopen($libpath_lib) ==> ", e))
90-
end
91-
end
73+
libpaths = exec_find_libpython(python, `--list-all`)
74+
for lib in libpaths
75+
try
76+
return (Libdl.dlopen(lib, dlopen_flags), lib)
77+
catch e
78+
show_dlopen_error(e)
9279
end
9380
end
9481

82+
# Try all candidate libpython names and let Libdl find the path.
9583
# We do this *last* because the libpython in the system
9684
# library path might be the wrong one if multiple python
9785
# versions are installed (we prefer the one in LIBDIR):
86+
libs = exec_find_libpython(python, `--candidate-names`)
9887
for lib in libs
9988
lib = splitext(lib)[1]
10089
try
101-
return (Libdl.dlopen(lib, Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL),
102-
lib)
90+
libpython = Libdl.dlopen(lib, dlopen_flags)
91+
# Store the fullpath to libpython in deps.jl. This makes
92+
# it easier for users to investigate Python setup
93+
# PyCall.jl trying to use. It also helps PyJulia to
94+
# compare libpython.
95+
return (libpython, Libdl.dlpath(libpython))
10396
catch e
104-
push!(error_strings, string("dlopen($lib) ==> ", e))
105-
end
106-
end
107-
108-
if "yes" == get(ENV, "PYCALL_DEBUG_BUILD", "no") # print out extra info to help with remote debugging
109-
println(stderr, "------------------------------------- exceptions -----------------------------------------")
110-
for s in error_strings
111-
print(s, "\n\n")
112-
end
113-
println(stderr, "---------------------------------- get_config_vars ---------------------------------------")
114-
print(stderr, read(`python -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_vars())"`, String))
115-
println(stderr, "--------------------------------- directory contents -------------------------------------")
116-
for libpath in libpaths
117-
if isdir(libpath)
118-
print(libpath, ":\n")
119-
for file in readdir(libpath)
120-
if occursin("pyth", file)
121-
println(" ", file)
122-
end
123-
end
124-
end
97+
show_dlopen_error(e)
12598
end
126-
println(stderr, "------------------------------------------------------------------------------------------")
12799
end
128100

129101
error("""
130102
Couldn't find libpython; check your PYTHON environment variable.
131103
132-
The python executable we tried was $python (= version $v);
133-
the library names we tried were $libs
134-
and the library paths we tried were $libpaths""")
104+
The python executable we tried was $python (= version $v).
105+
Re-building with
106+
ENV["PYCALL_DEBUG_BUILD"] = "yes"
107+
may provide extra information for why it failed.
108+
""")
135109
end
136110

137111
#########################################################################

0 commit comments

Comments
 (0)