-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
determine libpython at Pkg.build time, not at runtime (for #167) (fixes
#91)
- Loading branch information
Showing
15 changed files
with
490 additions
and
663 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
deps/deps.jl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
language: python | ||
python: | ||
- 2.6 | ||
- 2.7 | ||
- 3.2 | ||
- 3.3 | ||
|
@@ -22,7 +21,7 @@ before_install: | |
- git config --global user.email "[email protected]" | ||
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi | ||
script: | ||
- julia -e 'versioninfo(); Pkg.init(); Pkg.clone(pwd())' | ||
- julia -e 'versioninfo(); Pkg.init(); Pkg.clone(pwd()); Pkg.build("PyCall")' | ||
- if [ $JULIAVERSION = "julianightlies" ]; then julia --code-coverage test/runtests.jl; fi | ||
- if [ $JULIAVERSION = "juliareleases" ]; then julia test/runtests.jl; fi | ||
after_success: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
# In this file, we figure out how to link to Python (surprisingly complicated) | ||
# and generate a deps/deps.jl file with the libpython name and other information | ||
# needed for static compilation of PyCall. | ||
|
||
# As a result, if you switch to a different version or path of Python, you | ||
# will probably need to re-run Pkg.build("PyCall"). | ||
|
||
# remove deps.jl if it exists, in case build.jl fails | ||
isfile("deps.jl") && rm("deps.jl") | ||
|
||
using Compat | ||
|
||
######################################################################### | ||
|
||
# set PYTHONIOENCODING when running python executable, so that | ||
# we get UTF-8 encoded text as output (this is not the default on Windows). | ||
ENV["PYTHONIOENCODING"] = "UTF-8" | ||
|
||
pyconfigvar(python::AbstractString, var::AbstractString) = chomp(readall(`$python -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('$var'))"`)) | ||
pyconfigvar(python, var, default) = let v = pyconfigvar(python, var) | ||
v == "None" ? default : v | ||
end | ||
|
||
pysys(python::AbstractString, var::AbstractString) = chomp(readall(`$python -c "import sys; print(sys.$var)"`)) | ||
|
||
######################################################################### | ||
|
||
const dlprefix = @windows? "" : "lib" | ||
|
||
# return libpython name, libpython pointer | ||
function find_libpython(python::AbstractString) | ||
# it is ridiculous that it is this hard to find the name of libpython | ||
v = pyconfigvar(python,"VERSION","") | ||
libs = [ dlprefix*"python"*v*"."*Libdl.dlext, dlprefix*"python."*Libdl.dlext ] | ||
lib = pyconfigvar(python, "LIBRARY") | ||
lib != "None" && unshift!(libs, splitext(lib)[1]*"."*Libdl.dlext) | ||
lib = pyconfigvar(python, "LDLIBRARY") | ||
lib != "None" && unshift!(unshift!(libs, basename(lib)), lib) | ||
libs = unique(libs) | ||
|
||
libpaths = [pyconfigvar(python, "LIBDIR"), | ||
(@windows ? dirname(pysys(python, "executable")) : joinpath(dirname(dirname(pysys(python, "executable"))), "lib"))] | ||
@osx_only push!(libpaths, pyconfigvar(python, "PYTHONFRAMEWORKPREFIX")) | ||
|
||
# `prefix` and `exec_prefix` are the path prefixes where python should look for python only and compiled libraries, respectively. | ||
# These are also changed when run in a virtualenv. | ||
exec_prefix = pyconfigvar(python, "exec_prefix") | ||
# Since we only use `libpaths` to find the python dynamic library, we should only add `exec_prefix` to it. | ||
push!(libpaths, exec_prefix) | ||
if !haskey(ENV, "PYTHONHOME") | ||
# PYTHONHOME tells python where to look for both pure python | ||
# and binary modules. When it is set, it replaces both | ||
# `prefix` and `exec_prefix` and we thus need to set it to | ||
# both in case they differ. This is also what the | ||
# documentation recommends. However, they are documented | ||
# to always be the same on Windows, where it causes | ||
# problems if we try to include both. | ||
ENV["PYTHONHOME"] = @windows? exec_prefix : pyconfigvar(python, "prefix") * ":" * exec_prefix | ||
# Unfortunately, setting PYTHONHOME screws up Canopy's Python distro | ||
try | ||
run(pipe(`$python -c "import site"`, stdout=DevNull, stderr=DevNull)) | ||
catch | ||
pop!(ENV, "PYTHONHOME") | ||
end | ||
end | ||
# TODO: look in python-config output? pyconfigvar("LDFLAGS")? | ||
for lib in libs | ||
for libpath in libpaths | ||
libpath_lib = joinpath(libpath, lib) | ||
if isfile(libpath_lib) | ||
try | ||
return (Libdl.dlopen(libpath_lib, | ||
Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL), | ||
libpath_lib) | ||
end | ||
end | ||
end | ||
end | ||
|
||
# We do this *last* because the libpython in the system | ||
# library path might be the wrong one if multiple python | ||
# versions are installed (we prefer the one in LIBDIR): | ||
for lib in libs | ||
lib = splitext(lib)[1] | ||
try | ||
return (Libdl.dlopen(lib, Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL), | ||
lib) | ||
end | ||
end | ||
error("Couldn't find libpython; check your PYTHON environment variable") | ||
end | ||
|
||
######################################################################### | ||
|
||
hassym(lib, sym) = Libdl.dlsym_e(lib, sym) != C_NULL | ||
|
||
# call dlsym_e on a sequence of symbols and return the symbol that gives | ||
# the first non-null result | ||
function findsym(lib, syms...) | ||
for sym in syms | ||
if hassym(lib, sym) | ||
return sym | ||
end | ||
end | ||
error("no symbol found from: ", syms) | ||
end | ||
|
||
######################################################################### | ||
|
||
# need to be able to get the version before Python is initialized | ||
Py_GetVersion(libpy) = bytestring(ccall(Libdl.dlsym(libpy, :Py_GetVersion), Ptr{Uint8}, ())) | ||
|
||
const python = get(ENV, "PYTHON", "python") | ||
const (libpython, libpy_name) = find_libpython(python) | ||
const programname = pysys(python, "executable") | ||
|
||
# cache the Python version as a Julia VersionNumber | ||
const pyversion = convert(VersionNumber, split(Py_GetVersion(libpython))[1]) | ||
|
||
if pyversion < v"2.7" | ||
error("Python 2.7 or later is required for PyCall") | ||
end | ||
|
||
# PyUnicode_* may actually be a #define for another symbol, so | ||
# we cache the correct dlsym | ||
const PyUnicode_AsUTF8String = | ||
findsym(libpython, :PyUnicode_AsUTF8String, :PyUnicodeUCS4_AsUTF8String, :PyUnicodeUCS2_AsUTF8String) | ||
const PyUnicode_DecodeUTF8 = | ||
findsym(libpython, :PyUnicode_DecodeUTF8, :PyUnicodeUCS4_DecodeUTF8, :PyUnicodeUCS2_DecodeUTF8) | ||
|
||
# Python 2/3 compatibility: cache symbols for renamed functions | ||
if hassym(libpython, :PyString_FromString) | ||
const PyString_FromString = :PyString_FromString | ||
const PyString_AsString = :PyString_AsString | ||
const PyString_Size = :PyString_Size | ||
const PyString_Type = :PyString_Type | ||
else | ||
const PyString_FromString = :PyBytes_FromString | ||
const PyString_AsString = :PyBytes_AsString | ||
const PyString_Size = :PyBytes_Size | ||
const PyString_Type = :PyBytes_Type | ||
end | ||
if hassym(libpython, :PyInt_Type) | ||
const PyInt_Type = :PyInt_Type | ||
const PyInt_FromSize_t = :PyInt_FromSize_t | ||
const PyInt_FromSsize_t = :PyInt_FromSsize_t | ||
const PyInt_AsSsize_t = :PyInt_AsSsize_t | ||
else | ||
const PyInt_Type = :PyLong_Type | ||
const PyInt_FromSize_t = :PyLong_FromSize_t | ||
const PyInt_FromSsize_t = :PyLong_FromSsize_t | ||
const PyInt_AsSsize_t = :PyLong_AsSsize_t | ||
end | ||
|
||
# hashes changed from long to intptr_t in Python 3.2 | ||
const Py_hash_t = pyversion < v"3.2" ? Clong:Int | ||
|
||
# whether to use unicode for strings by default, ala Python 3 | ||
const pyunicode_literals = pyversion >= v"3.0" | ||
|
||
open("deps.jl", "w") do f | ||
print(f, """ | ||
const python = "$(escape_string(python))" | ||
const libpython = "$(escape_string(libpy_name))" | ||
const pyprogramname = $(pyversion.major < 3 ? "bytestring" : "wstring")("$(escape_string(programname))") | ||
const pyversion_build = $(repr(pyversion)) | ||
const PYTHONHOME = "$(escape_string(get(ENV, "PYTHONHOME", nothing)))" | ||
const PyUnicode_AsUTF8String = :$PyUnicode_AsUTF8String | ||
const PyUnicode_DecodeUTF8 = :$PyUnicode_DecodeUTF8 | ||
const PyString_FromString = :$PyString_FromString | ||
const PyString_AsString = :$PyString_AsString | ||
const PyString_Size = :$PyString_Size | ||
const PyString_Type = :$PyString_Type | ||
const PyInt_Type = :$PyInt_Type | ||
const PyInt_FromSize_t = :$PyInt_FromSize_t | ||
const PyInt_FromSsize_t = :$PyInt_FromSsize_t | ||
const PyInt_AsSsize_t = :$PyInt_AsSsize_t | ||
const Py_hash_t = $Py_hash_t | ||
const pyunicode_literals = $pyunicode_literals | ||
""") | ||
end |
Oops, something went wrong.