210 lines
7.2 KiB
Smarty
210 lines
7.2 KiB
Smarty
# -*- Python -*-
|
|
"""Repository rule for Python autoconfiguration.
|
|
|
|
`python_configure` depends on the following environment variables:
|
|
|
|
* `NUMPY_INCLUDE_PATH`: Location of Numpy libraries.
|
|
* `PYTHON_BIN_PATH`: location of python binary.
|
|
* `PYTHON_INCLUDE_PATH`: Location of python binaries.
|
|
"""
|
|
|
|
_NUMPY_INCLUDE_PATH = "NUMPY_INCLUDE_PATH"
|
|
_PYTHON_BIN_PATH = "PYTHON_BIN_PATH"
|
|
_PYTHON_INCLUDE_PATH = "PYTHON_INCLUDE_PATH"
|
|
|
|
|
|
def _tpl(repository_ctx, tpl, substitutions={}, out=None):
|
|
if not out:
|
|
out = tpl
|
|
repository_ctx.template(
|
|
out,
|
|
Label("//third_party/py:%s.tpl" % tpl),
|
|
substitutions)
|
|
|
|
|
|
def _python_configure_warning(msg):
|
|
"""Output warning message during auto configuration."""
|
|
yellow = "\033[1;33m"
|
|
no_color = "\033[0m"
|
|
print("\n%sPython Configuration Warning:%s %s\n" % (yellow, no_color, msg))
|
|
|
|
|
|
def _python_configure_fail(msg):
|
|
"""Output failure message when auto configuration fails."""
|
|
red = "\033[0;31m"
|
|
no_color = "\033[0m"
|
|
fail("\n%sPython Configuration Error:%s %s\n" % (red, no_color, msg))
|
|
|
|
|
|
def _get_env_var(repository_ctx, name, default = None, enable_warning = True):
|
|
"""Find an environment variable in system path."""
|
|
if name in repository_ctx.os.environ:
|
|
return repository_ctx.os.environ[name]
|
|
if default != None:
|
|
if enable_warning:
|
|
_python_configure_warning(
|
|
"'%s' environment variable is not set, using '%s' as default" % (name, default))
|
|
return default
|
|
_python_configure_fail("'%s' environment variable is not set" % name)
|
|
|
|
|
|
def _is_windows(repository_ctx):
|
|
"""Returns true if the host operating system is windows."""
|
|
os_name = repository_ctx.os.name.lower()
|
|
if os_name.find("windows") != -1:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _symlink_genrule_for_dir(repository_ctx, src_dir, dest_dir, genrule_name):
|
|
"""returns a genrule to symlink all files in a directory."""
|
|
# Get the list of files under this directory
|
|
find_result = None
|
|
line_break = None
|
|
if _is_windows(repository_ctx):
|
|
line_break = '\r\n'
|
|
find_result = repository_ctx.execute([
|
|
"dir", src_dir, "/b", "/s", "/a-d",
|
|
])
|
|
else:
|
|
line_break = '\n'
|
|
find_result = repository_ctx.execute([
|
|
"find", src_dir, "-follow", "-type", "f",
|
|
])
|
|
# Create a list with the src_dir stripped to use for outputs.
|
|
dest_files = find_result.stdout.replace(src_dir, '').split(line_break)
|
|
src_files = find_result.stdout.split(line_break)
|
|
command = []
|
|
command_windows = []
|
|
outs = []
|
|
outs_windows = []
|
|
for i in range(len(dest_files)):
|
|
if dest_files[i] != "":
|
|
command.append('ln -s ' + src_files[i] + ' $(@D)/' +
|
|
dest_dir + dest_files[i])
|
|
# ln -sf is actually implemented as copying in msys since creating
|
|
# symbolic links is privileged on Windows. But copying is too slow, so
|
|
# invoke mklink to create junctions on Windows.
|
|
command_windows.append('mklink /J ' + src_files[i] + ' $(@D)/' +
|
|
dest_dir + dest_files[i])
|
|
outs.append(' "' + dest_dir + dest_files[i] + '",')
|
|
outs_windows.append(' "' + dest_dir + '_windows' +
|
|
dest_files[i] + '",')
|
|
genrule = _genrule(src_dir, genrule_name, ' && '.join(command),
|
|
'\n'.join(outs))
|
|
genrule_windows = _genrule(src_dir, genrule_name + '_windows',
|
|
"cmd /c \"" + ' && '.join(command_windows) + "\"",
|
|
'\n'.join(outs_windows))
|
|
return genrule + '\n' + genrule_windows
|
|
|
|
|
|
def _genrule(src_dir, genrule_name, command, outs):
|
|
"""Returns a string with a genrule.
|
|
|
|
Genrule executes the given command and produces the given outputs.
|
|
"""
|
|
return (
|
|
'genrule(\n' +
|
|
' name = "' +
|
|
genrule_name + '",\n' +
|
|
' outs = [\n' +
|
|
outs +
|
|
' ],\n' +
|
|
' cmd = """\n' +
|
|
command +
|
|
' """,\n' +
|
|
')\n'
|
|
)
|
|
|
|
|
|
def _check_python_bin(repository_ctx, python_bin):
|
|
"""Checks the python bin path."""
|
|
cmd = '[[ -x "%s" ]] && [[ ! -d "%s" ]]' % (python_bin, python_bin)
|
|
result = repository_ctx.execute(["bash", "-c", cmd])
|
|
if result.return_code == 1:
|
|
_python_configure_fail(
|
|
"PYTHON_BIN_PATH is not executable. Is it the python binary?")
|
|
|
|
|
|
def _get_python_include(repository_ctx, python_bin):
|
|
"""Gets the python include path."""
|
|
result = repository_ctx.execute([python_bin, "-c",
|
|
'from __future__ import print_function;' +
|
|
'from distutils import sysconfig;' +
|
|
'print(sysconfig.get_python_inc())'])
|
|
if result == "":
|
|
_python_configure_fail(
|
|
"Problem getting python include path. Is distutils installed?")
|
|
return result.stdout.splitlines()[0]
|
|
|
|
|
|
def _get_numpy_include(repository_ctx, python_bin):
|
|
"""Gets the numpy include path."""
|
|
result = repository_ctx.execute([python_bin, "-c",
|
|
'from __future__ import print_function;' +
|
|
'import numpy;' +
|
|
' print(numpy.get_include());'])
|
|
if result == "":
|
|
_python_configure_fail(
|
|
"Problem getting numpy include path. Is numpy installed?")
|
|
return result.stdout.splitlines()[0]
|
|
|
|
|
|
def _create_python_repository(repository_ctx):
|
|
"""Creates the repository containing files set up to build with Python."""
|
|
python_include = None
|
|
numpy_include = None
|
|
# If local checks were requested, the python and numpy include will be auto
|
|
# detected on the host config (using _PYTHON_BIN_PATH).
|
|
if repository_ctx.attr.local_checks:
|
|
python_bin = _get_env_var(repository_ctx, _PYTHON_BIN_PATH)
|
|
_check_python_bin(repository_ctx, python_bin)
|
|
python_include = _get_python_include(repository_ctx, python_bin)
|
|
numpy_include = _get_numpy_include(repository_ctx, python_bin) + '/numpy'
|
|
else:
|
|
# Otherwise, we assume user provides all paths (via ENV or attrs)
|
|
python_include = _get_env_var(repository_ctx, _PYTHON_INCLUDE_PATH,
|
|
repository_ctx.attr.python_include)
|
|
numpy_include = _get_env_var(repository_ctx, _NUMPY_INCLUDE_PATH,
|
|
repository_ctx.attr.numpy_include) + '/numpy'
|
|
|
|
python_include_rule = _symlink_genrule_for_dir(
|
|
repository_ctx, python_include, 'python_include', 'python_include')
|
|
numpy_include_rule = _symlink_genrule_for_dir(
|
|
repository_ctx, numpy_include, 'numpy_include', 'numpy_include')
|
|
_tpl(repository_ctx, "BUILD", {
|
|
"%{PYTHON_INCLUDE_GENRULE}": python_include_rule,
|
|
"%{NUMPY_INCLUDE_GENRULE}": numpy_include_rule,
|
|
})
|
|
|
|
|
|
def _python_autoconf_impl(repository_ctx):
|
|
"""Implementation of the python_autoconf repository rule."""
|
|
_create_python_repository(repository_ctx)
|
|
|
|
|
|
python_configure = repository_rule(
|
|
implementation = _python_autoconf_impl,
|
|
attrs = {
|
|
"local_checks": attr.bool(mandatory = False, default = True),
|
|
"python_include": attr.string(mandatory = False),
|
|
"numpy_include": attr.string(mandatory = False),
|
|
},
|
|
environ = [
|
|
_PYTHON_BIN_PATH,
|
|
_PYTHON_INCLUDE_PATH,
|
|
_NUMPY_INCLUDE_PATH,
|
|
],
|
|
)
|
|
"""Detects and configures the local Python.
|
|
|
|
Add the following to your WORKSPACE FILE:
|
|
|
|
```python
|
|
python_configure(name = "local_config_python")
|
|
```
|
|
|
|
Args:
|
|
name: A unique name for this workspace rule.
|
|
"""
|