Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
workspace(name = "pybind11_bazel")
2 changes: 1 addition & 1 deletion build_defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def pybind_extension(
"//conditions:default": ["-Wl,-Bsymbolic"],
}),
linkshared = 1,
tags = tags + ["local"],
tags = tags,
deps = deps + PYBIND_DEPS,
**kwargs
)
Expand Down
113 changes: 72 additions & 41 deletions python_configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,52 @@

`python_configure` depends on the following environment variables:

* `PYTHON_BIN_PATH`: location of python binary.
* `PYTHON_BIN_PATH`: Location of python binary.
* `PYTHON_LIB_PATH`: Location of python libraries.

These can be directly set with the `python_interpreter_target` and
`python_library_target` respectively.
"""

_BAZEL_SH = "BAZEL_SH"
_PYTHON_BIN_PATH = "PYTHON_BIN_PATH"
_PYTHON_CONFIG_BIN_PATH = "PYTHON_CONFIG_BIN_PATH"
_PYTHON_LIB_PATH = "PYTHON_LIB_PATH"

# TODO: Move scripts to respective files and expose to rule.
_INCLUDE_SCRIPT = """
from __future__ import print_function
from sysconfig import get_path
print(get_path("include"))"""

_LIBRARY_SCRIPT = """
from __future__ import print_function
import site
import os

try:
input = raw_input
except NameError:
pass

python_paths = []
if os.getenv('PYTHONPATH') is not None:
python_paths = os.getenv('PYTHONPATH').split(':')
try:
library_paths = site.getsitepackages()
except AttributeError:
from sysconfig import get_path
library_paths = [get_path("purelib")]

all_paths = set(python_paths + library_paths)
paths = []
for path in all_paths:
if os.path.isdir(path):
paths.append(path)
if len(paths) >=1:
print(paths[0])
"""

def _tpl(repository_ctx, tpl, substitutions = {}, out = None):
if not out:
out = tpl
Expand Down Expand Up @@ -96,7 +133,8 @@ def _genrule(src_dir, genrule_name, command, outs, local):
"genrule(\n" +
' name = "' +
genrule_name + '",\n' + (
" local = 1,\n" if local else "") +
" local = 1,\n" if local else ""
) +
" outs = [\n" +
outs +
"\n ],\n" +
Expand Down Expand Up @@ -194,37 +232,26 @@ def _get_bash_bin(repository_ctx):

def _get_python_lib(repository_ctx, python_bin):
"""Gets the python lib path."""
python_lib = repository_ctx.os.environ.get(_PYTHON_LIB_PATH)
if python_lib != None:
return python_lib
print_lib = ("<<END\n" +
"from __future__ import print_function\n" +
"import site\n" +
"import os\n" +
"\n" +
"try:\n" +
" input = raw_input\n" +
"except NameError:\n" +
" pass\n" +
"\n" +
"python_paths = []\n" +
"if os.getenv('PYTHONPATH') is not None:\n" +
" python_paths = os.getenv('PYTHONPATH').split(':')\n" +
"try:\n" +
" library_paths = site.getsitepackages()\n" +
"except AttributeError:\n" +
" from distutils.sysconfig import get_python_lib\n" +
" library_paths = [get_python_lib()]\n" +
"all_paths = set(python_paths + library_paths)\n" +
"paths = []\n" +
"for path in all_paths:\n" +
" if os.path.isdir(path):\n" +
" paths.append(path)\n" +
"if len(paths) >=1:\n" +
" print(paths[0])\n" +
"END")
cmd = "%s - %s" % (python_bin, print_lib)
result = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", cmd])
if repository_ctx.attr.python_library_target:
return str(repository_ctx.path(repository_ctx.attr.python_library_target))

# If an interpreter is explicitly passed down, we should should not regard
# the environmental variable.
if repository_ctx.attr.python_interpreter_target == None:
python_lib = repository_ctx.os.environ.get(_PYTHON_LIB_PATH)
if python_lib:
return python_lib

result = _execute(
repository_ctx,
[
python_bin,
"-c",
_LIBRARY_SCRIPT,
],
error_msg = "Unable to detect library path.",
error_details = ("Is Python installed correctly?"),
)
return result.stdout.strip("\n")

def _check_python_lib(repository_ctx, python_lib):
Expand All @@ -251,14 +278,11 @@ def _get_python_include(repository_ctx, python_bin):
[
python_bin,
"-c",
"from __future__ import print_function;" +
"from distutils import sysconfig;" +
"print(sysconfig.get_python_inc())",
_INCLUDE_SCRIPT,
],
error_msg = "Problem getting python include path.",
error_details = ("Is the Python binary path set up right? " +
"(See ./configure or " + _PYTHON_BIN_PATH + ".) " +
"Is distutils installed?"),
"(See ./configure or " + _PYTHON_BIN_PATH + ".)"),
)
return result.stdout.splitlines()[0]

Expand Down Expand Up @@ -314,6 +338,7 @@ def _get_embed_flags(repository_ctx, python_config):
# See https://github.com/python/cpython/pull/13500
link_cmd = python_config + " --ldflags --embed"

# TODO: Resolve ctx.execute vs _execute.
err = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", err_cmd]).stdout.strip("\n")
compiler_flags = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", comp_cmd]).stdout.strip("\n")
linker_flags = repository_ctx.execute([_get_bash_bin(repository_ctx), "-c", link_cmd]).stdout.strip("\n")
Expand Down Expand Up @@ -395,8 +420,9 @@ python_configure = repository_rule(
_PYTHON_LIB_PATH,
],
attrs = {
"python_version": attr.string(default=""),
"python_version": attr.string(default = ""),
"python_interpreter_target": attr.label(),
"python_library_target": attr.string(),
},
)
"""Detects and configures the local Python.
Expand All @@ -416,6 +442,11 @@ Args:
By default, will build for whatever Python version is returned by
`which python`.
python_interpreter_target: If set to a target providing a Python binary,
will configure for that Python version instead of the installed
binary. This configuration takes precedence over python_version.
this will configure for that Python version instead of the installed
binary. This configuration takes precedence over python_version. In
addition, setting this label will ignore `PYTHON_LIB_PATH`- if needed
use `python_library_target` to explicitly set the desired library.
python_library_target: If this string is set, this will override the
environmental variable `PYTHON_LIB_PATH`. Otherwise implicit discovery,
is used from the availible binary.
"""