Skip to content

Commit

Permalink
[bazel] Introduce a "py_import" rule, analogous to "java_import"
Browse files Browse the repository at this point in the history
This allows us to use wheels and other tar.gz third party libraries in
our python code. This will prove useful.
  • Loading branch information
shs96c committed Aug 13, 2019
1 parent 811e42d commit 6681d50
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
3 changes: 3 additions & 0 deletions py/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
load("//py:import.bzl", _py_import = "py_import")

py_import = _py_import
100 changes: 100 additions & 0 deletions py/import.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
def _py_import_impl(ctx):
# Unpack the file somewhere, and present as a python library. We need to
# know all the files in the zip, and that's problematic. For now, we might
# be able to get away with just creating and declaring the directory.

root = ctx.actions.declare_directory("%s-pyroot" % ctx.attr.name)
args = ctx.actions.args()

if ctx.file.wheel.path.endswith(".zip") or ctx.file.wheel.path.endswith(".whl"):
args.add("x")
args.add(ctx.file.wheel.path)
args.add_all(["-d", root.path])

ctx.actions.run(
outputs = [root],
inputs = [ctx.file.wheel],
arguments = [args],
executable = ctx.executable._zip,
)
elif ctx.file.wheel.path.endswith(".tar.gz"):
args.add(ctx.file.wheel.path)
args.add(root.path)

ctx.actions.run(
outputs = [root],
inputs = [ctx.file.wheel],
arguments = [args],
executable = ctx.executable._untar,
)
else:
fail("Unrecognised file extension: %s" % ctx.attr.wheel)

runfiles = ctx.runfiles(files = [root])
for dep in ctx.attr.deps:
runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)

imports = depset(
items = [
"%s/%s/%s-pyroot" % (ctx.workspace_name, ctx.label.package, ctx.label.name),
],
transitive = [dep[PyInfo].imports for dep in ctx.attr.deps],
)
transitive_sources = depset(
items = [],
transitive = [dep[PyInfo].transitive_sources for dep in ctx.attr.deps],
)

py_srcs = ctx.attr.srcs_version

info = PyInfo(
imports = imports,
has_py2_only_sources = py_srcs == "PY2",
has_py3_only_sources = py_srcs == "PY3",
transitive_sources = transitive_sources,
uses_shared_libraries = not ctx.attr.zip_safe,
)

return [
DefaultInfo(
files = depset(items = [root]),
default_runfiles = runfiles,
),
info,
]

py_import = rule(
_py_import_impl,
attrs = {
"wheel": attr.label(
allow_single_file = True,
mandatory = True,
),
"zip_safe": attr.bool(
default = True,
),
"python_version": attr.string(
default = "PY3",
values = ["PY2", "PY3"],
),
"srcs_version": attr.string(
default = "PY2AND3",
values = ["PY2", "PY3", "PY2AND3"],
),
"deps": attr.label_list(
allow_empty = True,
providers = [PyInfo],
),
"_zip": attr.label(
allow_single_file = True,
cfg = "host",
default = "@bazel_tools//tools/zip:zipper",
executable = True,
),
"_untar": attr.label(
cfg = "exec",
default = "//py:untar",
executable = True,
),
},
)
26 changes: 26 additions & 0 deletions py/untar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
import sys
import tarfile

if __name__ == '__main__':
outdir = sys.argv[2]
if not os.path.exists(outdir):
os.makedirs(outdir)

tar = tarfile.open(sys.argv[1])
for member in tar.getmembers():
parts = member.name.split("/")
parts.pop(0)
if not len(parts):
continue

basepath = os.path.join(*parts)
basepath = os.path.normpath(basepath)
member.name = basepath

dir = os.path.join(outdir, os.path.dirname(basepath))
if not os.path.exists(dir):
os.makedirs(dir)

tar.extract(member, outdir)
tar.close()

0 comments on commit 6681d50

Please sign in to comment.