aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar Damien Martin-Guillerez <dmarting@google.com>2015-10-09 14:10:42 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-10-09 14:42:58 +0000
commit80245bcdcb77493fbe3d6789c98b96c570159960 (patch)
treee72446d9ff497eb8d0fdb12892f7bb11303be606 /tools
parentec07479061adae837c460a1dabc5ee396bfc9abf (diff)
Introduce debian and tar packaging rules
This refactor a bit the docker rules to reuse the tarball construction. Also introduce the debian archive for the release process. RELNOTES[NEW]: Debian and tar packaging is now supported (see tools/build_defs/pkg/README.md). -- MOS_MIGRATED_REVID=105053604
Diffstat (limited to 'tools')
-rw-r--r--tools/BUILD3
-rw-r--r--tools/build_defs/docker/BUILD29
-rw-r--r--tools/build_defs/docker/create_image.py2
-rw-r--r--tools/build_defs/docker/docker.bzl2
-rw-r--r--tools/build_defs/docker/join_layers.py2
-rw-r--r--tools/build_defs/docker/testdata/BUILD9
-rw-r--r--tools/build_defs/docker/testdata/extras_gen.py2
-rw-r--r--tools/build_defs/docker/testdata/strip_top.py2
-rw-r--r--tools/build_defs/pkg/BUILD112
-rw-r--r--tools/build_defs/pkg/README.md365
-rw-r--r--tools/build_defs/pkg/archive.py (renamed from tools/build_defs/docker/archive.py)69
-rw-r--r--tools/build_defs/pkg/archive_test.py (renamed from tools/build_defs/docker/archive_test.py)13
-rw-r--r--tools/build_defs/pkg/build_tar.py (renamed from tools/build_defs/docker/build_layer.py)97
-rwxr-xr-xtools/build_defs/pkg/build_test.sh83
-rw-r--r--tools/build_defs/pkg/make_deb.py198
-rw-r--r--tools/build_defs/pkg/pkg.bzl223
-rw-r--r--tools/build_defs/pkg/testdata/a.ar (renamed from tools/build_defs/docker/testdata/archive/a.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/a_ab.ar (renamed from tools/build_defs/docker/testdata/archive/a_ab.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/a_b.ar (renamed from tools/build_defs/docker/testdata/archive/a_b.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/a_b_ab.ar (renamed from tools/build_defs/docker/testdata/archive/a_b_ab.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/ab.ar (renamed from tools/build_defs/docker/testdata/archive/ab.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/b.ar (renamed from tools/build_defs/docker/testdata/archive/b.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/empty.ar (renamed from tools/build_defs/docker/testdata/archive/empty.ar)0
-rw-r--r--tools/build_defs/pkg/testdata/tar_test.tar (renamed from tools/build_defs/docker/testdata/archive/tar_test.tar)bin10240 -> 10240 bytes
-rw-r--r--tools/build_defs/pkg/testdata/tar_test.tar.bz2 (renamed from tools/build_defs/docker/testdata/archive/tar_test.tar.bz2)bin134 -> 134 bytes
-rw-r--r--tools/build_defs/pkg/testdata/tar_test.tar.gz (renamed from tools/build_defs/docker/testdata/archive/tar_test.tar.gz)bin158 -> 158 bytes
-rw-r--r--tools/build_defs/pkg/testdata/tar_test.tar.xz (renamed from tools/build_defs/docker/testdata/archive/tar_test.tar.xz)bin192 -> 192 bytes
-rw-r--r--tools/build_defs/pkg/testenv.py22
-rwxr-xr-xtools/build_defs/pkg/testenv.sh25
29 files changed, 1154 insertions, 104 deletions
diff --git a/tools/BUILD b/tools/BUILD
index dd25580ccb..8413b3df8a 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -23,8 +23,9 @@ filegroup(
name = "package-srcs",
srcs = glob(["**"]) + [
"//tools/build_defs/d:srcs",
- "//tools/build_defs/jsonnet:srcs",
"//tools/build_defs/docker:srcs",
+ "//tools/build_defs/jsonnet:srcs",
+ "//tools/build_defs/pkg:srcs",
"//tools/build_defs/sass:srcs",
"//tools/build_rules/appengine:srcs",
"//tools/build_rules/closure:srcs",
diff --git a/tools/build_defs/docker/BUILD b/tools/build_defs/docker/BUILD
index ca1aad1151..43a11b4490 100644
--- a/tools/build_defs/docker/BUILD
+++ b/tools/build_defs/docker/BUILD
@@ -52,21 +52,6 @@ sh_test(
)
# Used by docker_build and friends
-py_library(
- name = "archive",
- srcs = ["archive.py"],
- visibility = ["//tools/build_defs/docker:__subpackages__"],
-)
-
-py_test(
- name = "archive_test",
- srcs = [
- "archive_test.py",
- "testenv.py",
- ],
- data = ["//tools/build_defs/docker/testdata:archive_testdata"],
- deps = [":archive"],
-)
py_binary(
name = "rewrite_json",
@@ -84,16 +69,6 @@ py_test(
)
py_binary(
- name = "build_layer",
- srcs = ["build_layer.py"],
- visibility = ["//visibility:public"],
- deps = [
- ":archive",
- "//third_party/py/gflags",
- ],
-)
-
-py_binary(
name = "sha256",
srcs = ["sha256.py"],
visibility = ["//visibility:public"],
@@ -104,8 +79,8 @@ py_binary(
srcs = ["create_image.py"],
visibility = ["//visibility:public"],
deps = [
- ":archive",
"//third_party/py/gflags",
+ "//tools/build_defs/pkg:archive",
],
)
@@ -114,8 +89,8 @@ py_binary(
srcs = ["join_layers.py"],
visibility = ["//visibility:public"],
deps = [
- ":archive",
"//third_party/py/gflags",
+ "//tools/build_defs/pkg:archive",
],
)
diff --git a/tools/build_defs/docker/create_image.py b/tools/build_defs/docker/create_image.py
index c8af607807..59c486fd7a 100644
--- a/tools/build_defs/docker/create_image.py
+++ b/tools/build_defs/docker/create_image.py
@@ -16,7 +16,7 @@
import sys
import tarfile
-from tools.build_defs.docker import archive
+from tools.build_defs.pkg import archive
from third_party.py import gflags
# Hardcoded docker versions that we are claiming to be.
diff --git a/tools/build_defs/docker/docker.bzl b/tools/build_defs/docker/docker.bzl
index 238490ca42..53bfd68854 100644
--- a/tools/build_defs/docker/docker.bzl
+++ b/tools/build_defs/docker/docker.bzl
@@ -304,7 +304,7 @@ docker_build_ = rule(
"repository": attr.string(default="bazel"),
# Implicit dependencies.
"_build_layer": attr.label(
- default=Label("//tools/build_defs/docker:build_layer"),
+ default=Label("//tools/build_defs/pkg:build_tar"),
cfg=HOST_CFG,
executable=True,
allow_files=True),
diff --git a/tools/build_defs/docker/join_layers.py b/tools/build_defs/docker/join_layers.py
index 361a6a6667..d0273a6085 100644
--- a/tools/build_defs/docker/join_layers.py
+++ b/tools/build_defs/docker/join_layers.py
@@ -22,7 +22,7 @@
import os.path
import sys
-from tools.build_defs.docker import archive
+from tools.build_defs.pkg import archive
from third_party.py import gflags
gflags.DEFINE_string('output', None, 'The output file, mandatory')
diff --git a/tools/build_defs/docker/testdata/BUILD b/tools/build_defs/docker/testdata/BUILD
index c18fbfacf2..71a67b66ee 100644
--- a/tools/build_defs/docker/testdata/BUILD
+++ b/tools/build_defs/docker/testdata/BUILD
@@ -13,11 +13,6 @@ filegroup(
],
)
-filegroup(
- name = "archive_testdata",
- srcs = glob(["archive/**"]),
-)
-
genrule(
name = "gen",
outs = ["gen.out"],
@@ -144,7 +139,7 @@ docker_build(
py_binary(
name = "extras_gen",
srcs = ["extras_gen.py"],
- deps = ["//tools/build_defs/docker:archive"],
+ deps = ["//tools/build_defs/pkg:archive"],
)
genrule(
@@ -190,7 +185,7 @@ docker_build(
py_binary(
name = "strip_top",
srcs = ["strip_top.py"],
- deps = ["//tools/build_defs/docker:archive"],
+ deps = ["//tools/build_defs/pkg:archive"],
)
[genrule(
diff --git a/tools/build_defs/docker/testdata/extras_gen.py b/tools/build_defs/docker/testdata/extras_gen.py
index b4229716ec..36b33e828d 100644
--- a/tools/build_defs/docker/testdata/extras_gen.py
+++ b/tools/build_defs/docker/testdata/extras_gen.py
@@ -16,7 +16,7 @@ import datetime
import sys
import tarfile
-from tools.build_defs.docker import archive
+from tools.build_defs.pkg import archive
if __name__ == '__main__':
mtime = int(datetime.datetime.now().strftime('%s'))
diff --git a/tools/build_defs/docker/testdata/strip_top.py b/tools/build_defs/docker/testdata/strip_top.py
index 1f660649ac..1b5c8ce305 100644
--- a/tools/build_defs/docker/testdata/strip_top.py
+++ b/tools/build_defs/docker/testdata/strip_top.py
@@ -14,7 +14,7 @@
"""A simple cross-platform helper to remove top from a tar file."""
import sys
-from tools.build_defs.docker import archive
+from tools.build_defs.pkg import archive
if __name__ == '__main__':
with archive.TarFileWriter(sys.argv[2]) as f:
diff --git a/tools/build_defs/pkg/BUILD b/tools/build_defs/pkg/BUILD
new file mode 100644
index 0000000000..f50b465039
--- /dev/null
+++ b/tools/build_defs/pkg/BUILD
@@ -0,0 +1,112 @@
+licenses(["notice"]) # Apache 2.0
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//tools:__pkg__"],
+)
+
+# Used by pkg_deb
+py_library(
+ name = "archive",
+ srcs = ["archive.py"],
+ visibility = [
+ "//tools/build_defs/docker:__subpackages__",
+ ],
+)
+
+filegroup(
+ name = "archive_testdata",
+ srcs = glob(["testdata/**"]),
+)
+
+py_test(
+ name = "archive_test",
+ srcs = [
+ "archive_test.py",
+ "testenv.py",
+ ],
+ data = [":archive_testdata"],
+ deps = [":archive"],
+)
+
+py_binary(
+ name = "build_tar",
+ srcs = ["build_tar.py"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":archive",
+ "//third_party/py/gflags",
+ ],
+)
+
+py_binary(
+ name = "make_deb",
+ srcs = ["make_deb.py"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":archive",
+ "//third_party/py/gflags",
+ ],
+)
+
+# tests
+load("/tools/build_defs/pkg/pkg", "pkg_deb", "pkg_tar")
+
+genrule(
+ name = "generate_files",
+ outs = [
+ "etc/nsswitch.conf",
+ "usr/titi",
+ ],
+ cmd = "for i in $(OUTS); do echo 1 >$$i; done",
+)
+
+[pkg_tar(
+ name = "test-tar-%s" % ext[1:],
+ extension = "tar%s" % ext,
+ files = [
+ ":etc/nsswitch.conf",
+ ":usr/titi",
+ ],
+ mode = "0644",
+ modes = {"usr/titi": "0755"},
+ package_dir = "/",
+ strip_prefix = ".",
+ symlinks = {"usr/bin/java": "/path/to/bin/java"},
+) for ext in [
+ "",
+ ".gz",
+ ".bz2",
+]]
+
+pkg_deb(
+ name = "test-deb",
+ data = ":test-tar-gz.tar.gz",
+ depends = [
+ "dep1",
+ "dep2",
+ ],
+ description = "toto",
+ maintainer = "someone@somewhere.com",
+ package = "titi",
+ version = "test",
+)
+
+sh_test(
+ name = "build_test",
+ size = "medium",
+ srcs = [
+ "build_test.sh",
+ ],
+ data = [
+ "testenv.sh",
+ ":test-deb.deb",
+ ":test-tar-.tar",
+ ":test-tar-bz2.tar.bz2",
+ ":test-tar-gz.tar.gz",
+ ],
+ deps = [
+ "//src/test/shell:bashunit",
+ ],
+)
diff --git a/tools/build_defs/pkg/README.md b/tools/build_defs/pkg/README.md
new file mode 100644
index 0000000000..3bc016b476
--- /dev/null
+++ b/tools/build_defs/pkg/README.md
@@ -0,0 +1,365 @@
+# Packaging for Bazel
+
+## Overview
+
+These build rules are used for building various packaging such as tarball
+and debian package.
+
+* [Basic Example](#basic-example)
+* [Build Rule Reference](#reference)
+ * [`pkg_tar`](#pkg_tar)
+ * [`pkg_deb`](#pkg_deb)
+* [Future work](#future)
+
+<a name="basic-example"></a>
+## Basic Example
+
+This example is a simplification of the debian packaging of Bazel:
+
+```python
+
+load("/tools/build_defs/pkg/pkg", "pkg_tar", "pkg_deb")
+
+pkg_tar(
+ name = "bazel-bin",
+ data_path = "/src",
+ directory = "/usr/bin",
+ files = ["//src:bazel"],
+ mode = "0755",
+)
+
+pkg_tar(
+ name = "bazel-tools",
+ data_path = "/",
+ directory = "/usr/share/lib/bazel/tools",
+ files = ["//tools:package-srcs"],
+ mode = "0644",
+ modes = {"tools/build_defs/docker/build_test.sh": "0755"},
+)
+
+pkg_tar(
+ name = "debian-data",
+ extension = "tar.gz",
+ tars = [
+ ":bazel-bin",
+ ":bazel-tools",
+ ],
+)
+
+pkg_deb(
+ name = "bazel-debian",
+ architecture = "amd64",
+ built_using = "bazel (0.1.1)",
+ data = ":debian-data",
+ depends = [
+ "zlib1g-dev",
+ "unzip",
+ ],
+ description_file = "debian/description",
+ homepage = "http://bazel.io",
+ maintainer = "The Bazel Authors <bazel-dev@googlegroups.com>",
+ package = "bazel",
+ version = "0.1.1",
+)
+```
+
+Here, the Debian package is built from three `pkg_tar` targets:
+
+ - `bazel-bin` creates a tarball with the main binary (mode `0755`) in
+ `/usr/bin`,
+ - `bazel-tools` create a tarball with the base workspace (mode `0644`) to
+ `/usr/share/bazel/tools` ; the `modes` attribute let us specifies executable
+ files,
+ - `debian-data` creates a gzip-compressed tarball that merge the three previous
+ tarballs.
+
+`debian-data` is then used for the data content of the debian archive created by
+`pkg_deb`.
+
+<a name="reference"></a>
+## Build Rule Reference [reference]
+
+<a name="pkg_tar"></a>
+### `pkg_tar`
+
+`pkg_tar(name, extension, data_path, directory, files, mode, modes, tars, debs, symlinks)`
+
+Creates a tar file from a list of inputs.
+
+<table>
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>name</code></td>
+ <td>
+ <code>Name, required</code>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>extension</code></td>
+ <td>
+ <code>String, default to 'tar'</code>
+ <p>
+ The extension for the resulting tarball. The output
+ file will be '<i>name<i>.<i>extension<i>'. This extension
+ also decide on the compression: if set to <code>tar.gz</code>
+ or <code>tgz</code> then gzip compression will be used and
+ if set to <code>tar.bz2</code> or <code>tar.bzip2</code> then
+ bzip2 compression will be used.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>strip_prefix</code></td>
+ <td>
+ <code>String, optional</code>
+ <p>Root path of the files.</p>
+ <p>
+ The directory structure from the files is preserved inside the
+ tarball but a prefix path determined by <code>strip_prefix</code>
+ is removed from the directory structure. This path can
+ be absolute from the workspace root if starting with a <code>/</code> or
+ relative to the rule's directory. A relative path may starts with "./"
+ (or be ".") but cannot use go up with "..". By default, the
+ <code>data_path</code> attribute is unused and all files are supposed to have no
+ prefix.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>package_dir</code></td>
+ <td>
+ <code>String, optional</code>
+ <p>Target directory.</p>
+ <p>
+ The directory in which to expand the specified files, defaulting to '/'.
+ Only makes sense accompanying files.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>files</code></td>
+ <td>
+ <code>List of files, optional</code>
+ <p>File to add to the layer.</p>
+ <p>
+ A list of files that should be included in the docker image.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>mode</code></td>
+ <td>
+ <code>String, default to 0555</code>
+ <p>
+ Set the mode of files added by the <code>files</code> attribute.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>modes</code></td>
+ <td>
+ <code>Dictionary, default to '{}'</code>
+ <p>
+ A string dictionary to change default mode of specific files from
+ <code>files</code>. Each key should be a path to a file before
+ appending the prefix <code>directory</code> and the corresponding
+ value the octal permission of to apply to the file.
+ </p>
+ <p>
+ <code>
+ modes = {
+ "tools/py/2to3.sh": "0755
+ ...
+ },
+ </code>
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>tars</code></td>
+ <td>
+ <code>List of files, optional</code>
+ <p>Tar file to extract in the layer.</p>
+ <p>
+ A list of tar files to merge into the output tarball.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>symlinks</code></td>
+ <td>
+ <code>Dictionary, optional</code>
+ <p>Symlinks to create in the output tarball.</p>
+ <p>
+ <code>
+ symlinks = {
+ "/path/to/link": "/path/to/target",
+ ...
+ },
+ </code>
+ </p>
+ </td>
+ </tr>
+ </tbody>
+ </tbody>
+</table>
+
+<a name="pkg_deb"></a>
+### `pkg_deb`
+
+`pkg_deb(name, data, package, architecture, maintainer, preinst, postinst,
+ prerm, postrm, version, version_file, description, description_file,
+ built_using, built_using_file, priority, section, homepage, depends,
+ suggests, enhances, predepends, recommends)`
+
+Create a debian package. See <a
+href="http://www.debian.org/doc/debian-policy/ch-controlfields.html">http://www.debian.org/doc/debian-policy/ch-controlfields.html</a>
+for more details on this.
+
+<table>
+ <thead>
+ <tr>
+ <th>Attribute</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>name</code></td>
+ <td>
+ <code>Name, required</code>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>data</code></td>
+ <td>
+ <code>File, required</code>
+ <p>
+ A tar file that contains the data for the debian package (basically
+ the list of files that will be installed by this package).
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>package</code></td>
+ <td>
+ <code>String, required</code>
+ <p>The name of the package.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>architecture</code></td>
+ <td>
+ <code>String, default to 'all'</code>
+ <p>The architecture that this package target.</p>
+ <p>
+ See <a href="http://www.debian.org/ports/">http://www.debian.org/ports/</a>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>maintainer</code></td>
+ <td>
+ <code>String, required</code>
+ <p>The maintainer of the package.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>preinst</code>, <code>postinst</code>, <code>prerm</code> and <code>postrm</code></td>
+ <td>
+ <code>Files, optional</code>
+ <p>
+ Respectively, the pre-install, post-install, pre-remove and
+ post-remove scripts for the package.
+ </p>
+ <p>
+ See <a href="http://www.debian.org/doc/debian-policy/ch-maintainerscripts.html">http://www.debian.org/doc/debian-policy/ch-maintainerscripts.html</a>.
+ </td>
+ </tr>
+ <tr>
+ <td><code>version</code>, <code>version_file</code></td>
+ <td>
+ <code>String or File, required</code>
+ <p>
+ The package version provided either inline (with <code>version</code>)
+ or from a file (with <code>version_file</code>).
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>description</code>, <code>description_file</code></td>
+ <td>
+ <code>String or File, required</code>
+ <p>
+ The package description provided either inline (with <code>description</code>)
+ or from a file (with <code>description_file</code>).
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>built_using</code>, <code>built_using_file</code></td>
+ <td>
+ <code>String or File, default to 'Bazel'</code>
+ <p>
+ The tool that were used to build this package provided either inline
+ (with <code>built_using</code>) or from a file (with <code>built_using_file</code>).
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>priority</code></td>
+ <td>
+ <code>String, default to 'optional'</code>
+ <p>The priority of the package.</p>
+ <p>
+ See <a href="http://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities">http://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities</a>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>section</code></td>
+ <td>
+ <code>String, default to 'contrib/devel'</code>
+ <p>The section of the package.</p>
+ <p>
+ See <a href="http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections">http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections</a>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>homepage</code></td>
+ <td>
+ <code>String, optional</code>
+ <p>The homepage of the project.</p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>depends</code>, <code>suggests</code>, <code>enhances</code>,
+ <code>predepends</code> and <code>recommends</code>.
+ </td>
+ <td>
+ <code>String list, optional</code>
+ <p>The list of dependencies in the project.</p>
+ <p>
+ See <a href="http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps">http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps</a>.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+ </tbody>
+</table>
+
+<a name="future"></a>
+# Future work
+
+ - Support more format, especially `pkg_zip`.
+ - Maybe a bit more integration with the `docker_build` rule.
diff --git a/tools/build_defs/docker/archive.py b/tools/build_defs/pkg/archive.py
index 0a32e404aa..95d9e73ca2 100644
--- a/tools/build_defs/docker/archive.py
+++ b/tools/build_defs/pkg/archive.py
@@ -34,6 +34,7 @@ class SimpleArFile(object):
Upon error, this class will raise a ArError exception.
"""
+
# TODO(dmarting): We should use a standard library instead but python 2.7
# does not have AR reading library.
@@ -98,8 +99,14 @@ class TarFileWriter(object):
class Error(Exception):
pass
- def __init__(self, name):
- self.tar = tarfile.open(name=name, mode='w')
+ def __init__(self, name, compression=''):
+ if compression in ['tgz', 'gz']:
+ mode = 'w:gz'
+ elif compression in ['bzip2', 'bz2']:
+ mode = 'w:bz2'
+ else:
+ mode = 'w:'
+ self.tar = tarfile.open(name=name, mode=mode)
self.members = set([])
def __enter__(self):
@@ -108,8 +115,16 @@ class TarFileWriter(object):
def __exit__(self, t, v, traceback):
self.close()
- def add_dir(self, name, path, uid=0, gid=0, uname='', gname='',
- mtime=0, mode=None, depth=100):
+ def add_dir(self,
+ name,
+ path,
+ uid=0,
+ gid=0,
+ uname='',
+ gname='',
+ mtime=0,
+ mode=None,
+ depth=100):
"""Recursively add a directory.
Args:
@@ -137,8 +152,14 @@ class TarFileWriter(object):
# Add the x bit to directories to prevent non-traversable directories.
# The x bit is set only to if the read bit is set.
dirmode = (mode | ((0444 & mode) >> 2)) if mode else mode
- self.add_file(name + '/', tarfile.DIRTYPE, uid=uid, gid=gid,
- uname=uname, gname=gname, mtime=mtime, mode=dirmode)
+ self.add_file(name + '/',
+ tarfile.DIRTYPE,
+ uid=uid,
+ gid=gid,
+ uname=uname,
+ gname=gname,
+ mtime=mtime,
+ mode=dirmode)
if depth <= 0:
raise self.Error('Recursion depth exceeded, probably in '
'an infinite directory loop.')
@@ -148,11 +169,18 @@ class TarFileWriter(object):
for f in filelist:
new_name = os.path.join(name, f)
new_path = os.path.join(path, f)
- self.add_dir(new_name, new_path, uid, gid, uname, gname, mtime,
- mode, depth-1)
+ self.add_dir(new_name, new_path, uid, gid, uname, gname, mtime, mode,
+ depth - 1)
else:
- self.add_file(name, tarfile.REGTYPE, file_content=path, uid=uid, gid=gid,
- uname=uname, gname=gname, mtime=mtime, mode=mode)
+ self.add_file(name,
+ tarfile.REGTYPE,
+ file_content=path,
+ uid=uid,
+ gid=gid,
+ uname=uname,
+ gname=gname,
+ mtime=mtime,
+ mode=mode)
def _addfile(self, info, fileobj=None):
"""Add a file in the tar file if there is no conflict."""
@@ -166,8 +194,17 @@ class TarFileWriter(object):
print('Duplicate file in archive: %s, '
'picking first occurrence' % info.name)
- def add_file(self, name, kind=tarfile.REGTYPE, content=None, link=None,
- file_content=None, uid=0, gid=0, uname='', gname='', mtime=0,
+ def add_file(self,
+ name,
+ kind=tarfile.REGTYPE,
+ content=None,
+ link=None,
+ file_content=None,
+ uid=0,
+ gid=0,
+ uname='',
+ gname='',
+ mtime=0,
mode=None):
"""Add a file to the current tar.
@@ -214,8 +251,12 @@ class TarFileWriter(object):
else:
self._addfile(tarinfo)
- def add_tar(self, tar, rootuid=None, rootgid=None,
- numeric=False, name_filter=None):
+ def add_tar(self,
+ tar,
+ rootuid=None,
+ rootgid=None,
+ numeric=False,
+ name_filter=None):
"""Merge a tar content into the current tar, stripping timestamp.
Args:
diff --git a/tools/build_defs/docker/archive_test.py b/tools/build_defs/pkg/archive_test.py
index 3c0553c9eb..375a27dd9e 100644
--- a/tools/build_defs/docker/archive_test.py
+++ b/tools/build_defs/pkg/archive_test.py
@@ -18,8 +18,8 @@ import os.path
import tarfile
import unittest
-from tools.build_defs.docker import archive
-from tools.build_defs.docker import testenv
+from tools.build_defs.pkg import archive
+from tools.build_defs.pkg import testenv
class SimpleArFileTest(unittest.TestCase):
@@ -59,13 +59,11 @@ class SimpleArFileTest(unittest.TestCase):
self.fail("Missing file %s in archive %s" % (content[i], arfile))
def testEmptyArFile(self):
- self.assertArFileContent(os.path.join(testenv.TESTDATA_PATH,
- "archive", "empty.ar"),
+ self.assertArFileContent(os.path.join(testenv.TESTDATA_PATH, "empty.ar"),
[])
def assertSimpleFileContent(self, names):
- datafile = os.path.join(testenv.TESTDATA_PATH, "archive",
- "_".join(names) + ".ar")
+ datafile = os.path.join(testenv.TESTDATA_PATH, "_".join(names) + ".ar")
content = [{"filename": n, "size": len(n), "data": n} for n in names]
self.assertArFileContent(datafile, content)
@@ -200,8 +198,7 @@ class TarFileWriterTest(unittest.TestCase):
]
for ext in ["", ".gz", ".bz2", ".xz"]:
with archive.TarFileWriter(self.tempfile) as f:
- f.add_tar(os.path.join(testenv.TESTDATA_PATH, "archive",
- "tar_test.tar" + ext),
+ f.add_tar(os.path.join(testenv.TESTDATA_PATH, "tar_test.tar" + ext),
name_filter=lambda n: n != "./b")
self.assertTarFileContent(self.tempfile, content)
diff --git a/tools/build_defs/docker/build_layer.py b/tools/build_defs/pkg/build_tar.py
index a968f53034..75ad1610e8 100644
--- a/tools/build_defs/docker/build_layer.py
+++ b/tools/build_defs/pkg/build_tar.py
@@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""This tool build docker layer tar file from a list of inputs."""
+"""This tool build tar files from a list of inputs."""
import os
import os.path
@@ -19,29 +19,20 @@ import sys
import tarfile
import tempfile
-from tools.build_defs.docker import archive
+from tools.build_defs.pkg import archive
from third_party.py import gflags
-gflags.DEFINE_string(
- 'output', None,
- 'The output file, mandatory')
+gflags.DEFINE_string('output', None, 'The output file, mandatory')
gflags.MarkFlagAsRequired('output')
-gflags.DEFINE_multistring(
- 'file', [],
- 'A file to add to the layer')
+gflags.DEFINE_multistring('file', [], 'A file to add to the layer')
gflags.DEFINE_string(
- 'mode', None,
- 'Force the mode on the added files (in octal).')
+ 'mode', None, 'Force the mode on the added files (in octal).')
-gflags.DEFINE_multistring(
- 'tar', [],
- 'A tar file to add to the layer')
+gflags.DEFINE_multistring('tar', [], 'A tar file to add to the layer')
-gflags.DEFINE_multistring(
- 'deb', [],
- 'A debian package to add to the layer')
+gflags.DEFINE_multistring('deb', [], 'A debian package to add to the layer')
gflags.DEFINE_multistring(
'link', [],
@@ -52,31 +43,39 @@ gflags.RegisterValidator(
message='--link value should contains a : separator')
gflags.DEFINE_string(
- 'directory', None,
- 'Directory in which to store the file inside the layer')
+ 'directory', None, 'Directory in which to store the file inside the layer')
+
+gflags.DEFINE_string(
+ 'compression', None, 'Compression (`gz` or `bz2`), default is none.')
+
+gflags.DEFINE_multistring(
+ 'modes', None,
+ 'Specific mode to apply to specific file (from the file argument),'
+ ' e.g., path/to/file=0455.')
FLAGS = gflags.FLAGS
-class DockerLayer(object):
+class TarFile(object):
"""A class to generates a Docker layer."""
class DebError(Exception):
pass
- def __init__(self, output, directory):
+ def __init__(self, output, directory, compression):
self.directory = directory
self.output = output
+ self.compression = compression
def __enter__(self):
- self.tarfile = archive.TarFileWriter(self.output)
+ self.tarfile = archive.TarFileWriter(self.output, self.compression)
return self
def __exit__(self, t, v, traceback):
self.tarfile.close()
def add_file(self, f, destfile, mode=None):
- """Add a file to the layer.
+ """Add a file to the tar file.
Args:
f: the file to add to the layer
@@ -96,19 +95,19 @@ class DockerLayer(object):
self.tarfile.add_file(dest, file_content=f, mode=mode)
def add_tar(self, tar):
- """Add a tar file to the layer.
+ """Merge a tar file into the destination tar file.
- All files presents in that tar will be added to the layer under
- the same paths. No user name nor group name will be added to
- the layer.
+ All files presents in that tar will be added to the output file
+ under the same paths. No user name nor group name will be added to
+ the output.
Args:
- tar: the tar file to add to the layer
+ tar: the tar file to add
"""
self.tarfile.add_tar(tar, numeric=True)
def add_link(self, symlink, destination):
- """Add a symbolic link pointing to `destination` in the layer.
+ """Add a symbolic link pointing to `destination`.
Args:
symlink: the name of the symbolic link to add.
@@ -117,20 +116,17 @@ class DockerLayer(object):
self.tarfile.add_file(symlink, tarfile.SYMTYPE, link=destination)
def add_deb(self, deb):
- """Extract a debian package in the layer.
+ """Extract a debian package in the output tar.
All files presents in that debian package will be added to the
- layer under the same paths. No user name nor group names will
- be added to the layer.
+ output tar under the same paths. No user name nor group names will
+ be added to the output.
Args:
- deb: the tar file to add to the layer
+ deb: the tar file to add
Raises:
DebError: if the format of the deb archive is incorrect.
-
- This method does not support LZMA (data.tar.xz or data.tar.lzma)
- for the data in the deb package. Using Python 3 would fix it.
"""
with archive.SimpleArFile(deb) as arfile:
current = arfile.next()
@@ -146,21 +142,38 @@ class DockerLayer(object):
def main(unused_argv):
- force_mode = None
+ # Parse modes arguments
+ default_mode = None
if FLAGS.mode:
# Convert from octal
- force_mode = int(FLAGS.mode, 8)
- with DockerLayer(FLAGS.output, FLAGS.directory) as layer:
+ default_mode = int(FLAGS.mode, 8)
+
+ mode_map = {}
+ if FLAGS.modes:
+ for filemode in FLAGS.modes:
+ (f, mode) = filemode.split('=', 1)
+ if f[0] == '/':
+ f = f[1:]
+ mode_map[f] = int(mode, 8)
+
+ # Add objects to the tar file
+ with TarFile(FLAGS.output, FLAGS.directory, FLAGS.compression) as output:
for f in FLAGS.file:
(inf, tof) = f.split('=', 1)
- layer.add_file(inf, tof, force_mode)
+ mode = default_mode
+ if tof[0] == '/' and (tof[1:] in mode_map):
+ mode = mode_map[tof[1:]]
+ elif tof in mode_map:
+ mode = mode_map[tof]
+ output.add_file(inf, tof, mode)
for tar in FLAGS.tar:
- layer.add_tar(tar)
+ output.add_tar(tar)
for deb in FLAGS.deb:
- layer.add_deb(deb)
+ output.add_deb(deb)
for link in FLAGS.link:
l = link.split(':', 1)
- layer.add_link(l[0], l[1])
+ output.add_link(l[0], l[1])
+
if __name__ == '__main__':
main(FLAGS(sys.argv))
diff --git a/tools/build_defs/pkg/build_test.sh b/tools/build_defs/pkg/build_test.sh
new file mode 100755
index 0000000000..daa6125447
--- /dev/null
+++ b/tools/build_defs/pkg/build_test.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unit tests for pkg_deb and pkg_tar
+
+DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+source ${DIR}/testenv.sh || { echo "testenv.sh not found!" >&2; exit 1; }
+
+function get_tar_listing() {
+ local input=$1
+ local test_data="${TEST_DATA_DIR}/${input}"
+ tar tvf "${test_data}" | sed -e 's/^.*:00 //'
+}
+
+function get_tar_permission() {
+ local input=$1
+ local file=$2
+ local test_data="${TEST_DATA_DIR}/${input}"
+ tar tvf "${test_data}" | fgrep "00 $file" | cut -d " " -f 1
+}
+
+function get_deb_listing() {
+ local input=$1
+ local test_data="${TEST_DATA_DIR}/${input}"
+ dpkg-deb -c "${test_data}" | sed -e 's/^.*:00 //'
+}
+
+function get_deb_description() {
+ local input=$1
+ local test_data="${TEST_DATA_DIR}/${input}"
+ dpkg-deb -I "${test_data}"
+}
+
+function get_deb_permission() {
+ local input=$1
+ local file=$2
+ local test_data="${TEST_DATA_DIR}/${input}"
+ dpkg-deb -c "${test_data}" | fgrep "00 $file" | cut -d " " -f 1
+}
+
+
+function test_tar() {
+ local listing="./etc/nsswitch.conf
+./usr/titi
+./usr/bin/java -> /path/to/bin/java"
+ for i in "" ".gz" ".bz2"; do
+ check_eq "$listing" "$(get_tar_listing test-tar-${i:1}.tar$i)"
+ check_eq "-rwxr-xr-x" "$(get_tar_permission test-tar-${i:1}.tar$i ./usr/titi)"
+ check_eq "-rw-r--r--" "$(get_tar_permission test-tar-${i:1}.tar$i ./etc/nsswitch.conf)"
+ done;
+}
+
+function test_deb() {
+ if ! (which dpkg-deb); then
+ echo "Unable to run test for debian, no dpkg-deb!" >&2
+ return 0
+ fi
+ local listing="./etc/nsswitch.conf
+./usr/titi
+./usr/bin/java -> /path/to/bin/java"
+ check_eq "$listing" "$(get_deb_listing test-deb.deb)"
+ check_eq "-rwxr-xr-x" "$(get_deb_permission test-deb.deb ./usr/titi)"
+ check_eq "-rw-r--r--" "$(get_deb_permission test-deb.deb ./etc/nsswitch.conf)"
+ get_deb_description test-deb.deb >$TEST_log
+ expect_log "Description: toto"
+ expect_log "Package: titi"
+ expect_log "Depends: dep1, dep2"
+}
+
+run_suite "build_test"
diff --git a/tools/build_defs/pkg/make_deb.py b/tools/build_defs/pkg/make_deb.py
new file mode 100644
index 0000000000..d502e52e3b
--- /dev/null
+++ b/tools/build_defs/pkg/make_deb.py
@@ -0,0 +1,198 @@
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""A simple cross-platform helper to create a debian package."""
+
+import os.path
+from StringIO import StringIO
+import sys
+import tarfile
+import textwrap
+
+from third_party.py import gflags
+
+# list of debian fields : (name, mandatory, wrap[, default])
+# see http://www.debian.org/doc/debian-policy/ch-controlfields.html
+DEBIAN_FIELDS = [
+ ('Package', True, False),
+ ('Version', True, False),
+ ('Section', False, False, 'contrib/devel'),
+ ('Priority', False, False, 'optional'),
+ ('Architecture', True, False, 'all'),
+ ('Depends', False, True, []),
+ ('Recommends', False, True, []),
+ ('Suggests', False, True, []),
+ ('Enhances', False, True, []),
+ ('Pre-Depends', False, True, []),
+ ('Installed-Size', False, False),
+ ('Maintainer', True, False),
+ ('Description', True, True),
+ ('Homepage', False, False),
+ ('Built-Using', False, False, 'Bazel')
+ ]
+
+gflags.DEFINE_string(
+ 'output', None,
+ 'The output file, mandatory')
+gflags.MarkFlagAsRequired('output')
+
+gflags.DEFINE_string('data', None,
+ 'Path to the data tarball, mandatory')
+gflags.MarkFlagAsRequired('data')
+
+gflags.DEFINE_string('preinst', None,
+ 'The preinst script (prefix with @ to provide a path).')
+gflags.DEFINE_string('postinst', None,
+ 'The postinst script (prefix with @ to provide a path).')
+gflags.DEFINE_string('prerm', None,
+ 'The prerm script (prefix with @ to provide a path).')
+gflags.DEFINE_string('postrm', None,
+ 'The postrm script (prefix with @ to provide a path).')
+
+
+def MakeGflags():
+ for field in DEBIAN_FIELDS:
+ fieldname = field[0].replace('-', '_').lower()
+ msg = 'The value for the %s content header entry.' % field[0]
+ if len(field) > 3:
+ if type(field[3]) is list:
+ gflags.DEFINE_multistring(fieldname, field[3], msg)
+ else:
+ gflags.DEFINE_string(fieldname, field[3], msg)
+ else:
+ gflags.DEFINE_string(fieldname, None, msg)
+ if field[1]:
+ gflags.MarkFlagAsRequired(fieldname)
+
+
+def AddArFileEntry(fileobj, filename,
+ content='', timestamp=0,
+ owner_id=0, group_id=0, mode=0644):
+ """Add a AR file entry to fileobj."""
+ fileobj.write((filename + '/').ljust(16)) # filename (SysV)
+ fileobj.write(str(timestamp).ljust(12)) # timestamp
+ fileobj.write(str(owner_id).ljust(6)) # owner id
+ fileobj.write(str(group_id).ljust(6)) # group id
+ fileobj.write(oct(mode).ljust(8)) # mode
+ fileobj.write(str(len(content)).ljust(10)) # size
+ fileobj.write('\x60\x0a') # end of file entry
+ fileobj.write(content)
+ if len(content) % 2 != 0:
+ fileobj.write('\n') # 2-byte alignment padding
+
+
+def MakeDebianControlField(name, value, wrap=False):
+ """Add a field to a debian control file."""
+ result = name + ': '
+ if type(value) is list:
+ value = ', '.join(value)
+ if wrap:
+ result += ' '.join(value.split('\n'))
+ result = textwrap.fill(result)
+ else:
+ result += value
+ return result.replace('\n', '\n ') + '\n'
+
+
+def CreateDebControl(extrafiles=None, **kwargs):
+ """Create the control.tar.gz file."""
+ # create the control file
+ controlfile = ''
+ for values in DEBIAN_FIELDS:
+ fieldname = values[0]
+ key = fieldname[0].lower() + fieldname[1:].replace('-', '')
+ if values[1] or (key in kwargs and kwargs[key]):
+ controlfile += MakeDebianControlField(fieldname, kwargs[key], values[2])
+ # Create the control.tar file
+ tar = StringIO()
+ with tarfile.open('control.tar.gz', mode='w:gz', fileobj=tar) as f:
+ tarinfo = tarfile.TarInfo('control')
+ tarinfo.size = len(controlfile)
+ f.addfile(tarinfo, fileobj=StringIO(controlfile))
+ if extrafiles:
+ for name in extrafiles:
+ tarinfo = tarfile.TarInfo(name)
+ tarinfo.size = len(extrafiles[name])
+ f.addfile(tarinfo, fileobj=StringIO(extrafiles[name]))
+ control = tar.getvalue()
+ tar.close()
+ return control
+
+
+def CreateDeb(output, data,
+ preinst=None, postinst=None, prerm=None, postrm=None, **kwargs):
+ """Create a full debian package."""
+ extrafiles = {}
+ if preinst:
+ extrafiles['preinst'] = preinst
+ if postinst:
+ extrafiles['postinst'] = postinst
+ if prerm:
+ extrafiles['prerm'] = prerm
+ if postrm:
+ extrafiles['postrm'] = postrm
+ control = CreateDebControl(extrafiles=extrafiles, **kwargs)
+
+ # Write the final AR archive (the deb package)
+ with open(output, 'w') as f:
+ f.write('!<arch>\n') # Magic AR header
+ AddArFileEntry(f, 'debian-binary', '2.0\n')
+ AddArFileEntry(f, 'control.tar.gz', control)
+ # Tries to presever the extension name
+ ext = os.path.basename(data).split('.')[-2:]
+ if len(ext) < 2:
+ ext = 'tar'
+ elif ext[1] == 'tgz':
+ ext = 'tar.gz'
+ elif ext[1] == 'tar.bzip2':
+ ext = 'tar.bz2'
+ else:
+ ext = '.'.join(ext)
+ if ext not in ['tar.bz2', 'tar.gz', 'tar.xz', 'tar.lzma']:
+ ext = 'tar'
+ with open(data, 'r') as datafile:
+ data = datafile.read()
+ AddArFileEntry(f, 'data.' + ext, data)
+
+
+def GetFlagValue(flagvalue, strip=True):
+ if flagvalue:
+ if flagvalue[0] == '@':
+ with open(flagvalue[1:], 'r') as f:
+ flagvalue = f.read()
+ if strip:
+ return flagvalue.strip()
+ return flagvalue
+
+
+def main(unused_argv):
+ CreateDeb(FLAGS.output, FLAGS.data,
+ preinst=GetFlagValue(FLAGS.preinst, False),
+ postinst=GetFlagValue(FLAGS.postinst, False),
+ prerm=GetFlagValue(FLAGS.prerm, False),
+ postrm=GetFlagValue(FLAGS.postrm, False),
+ package=FLAGS.package, version=GetFlagValue(FLAGS.version),
+ description=GetFlagValue(FLAGS.description),
+ maintainer=FLAGS.maintainer,
+ section=FLAGS.section, architecture=FLAGS.architecture,
+ depends=FLAGS.depends, suggests=FLAGS.suggests,
+ enhances=FLAGS.enhances, preDepends=FLAGS.pre_depends,
+ recommends=FLAGS.recommends, homepage=FLAGS.homepage,
+ builtUsing=GetFlagValue(FLAGS.built_using),
+ priority=FLAGS.priority,
+ installedSize=GetFlagValue(FLAGS.installed_size))
+
+if __name__ == '__main__':
+ MakeGflags()
+ FLAGS = gflags.FLAGS
+ main(FLAGS(sys.argv))
diff --git a/tools/build_defs/pkg/pkg.bzl b/tools/build_defs/pkg/pkg.bzl
new file mode 100644
index 0000000000..d8a69644a0
--- /dev/null
+++ b/tools/build_defs/pkg/pkg.bzl
@@ -0,0 +1,223 @@
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Rules for manipulation of various packaging."""
+
+# Filetype to restrict inputs
+tar_filetype = FileType([".tar", ".tar.gz", ".tgz", ".tar.xz"])
+deb_filetype = FileType([".deb"])
+
+def _short_path_dirname(path):
+ """Returns the directory's name of the short path of an artifact."""
+ sp = path.short_path
+ return sp[:sp.rfind("/")]
+
+def _dest_path(f, strip_prefix):
+ """Returns the short path of f, stripped of strip_prefix."""
+ if strip_prefix == None:
+ # If no strip_prefix was specified, use the package of the
+ # given input as the strip_prefix.
+ strip_prefix = _short_path_dirname(f)
+ if not strip_prefix:
+ return f.short_path
+ if f.short_path.startswith(strip_prefix):
+ return f.short_path[len(strip_prefix):]
+ return f.short_path
+
+def _compute_data_path(out, data_path):
+ """Compute the relative data path prefix from the data_path attribute."""
+ if data_path:
+ # Strip ./ from the beginning if specified.
+ # There is no way to handle .// correctly (no function that would make
+ # that possible and Skylark is not turing complete) so just consider it
+ # as an absolute path.
+ if data_path[0:2] == "./":
+ data_path = data_path[2:]
+ if data_path[0] == "/": # Absolute path
+ return data_path[1:]
+ elif not data_path or data_path == ".": # Relative to current package
+ return _short_path_dirname(out)
+ else: # Relative to a sub-directory
+ return _short_path_dirname(out) + "/" + data_path
+ else:
+ return None
+
+def _pkg_tar_impl(ctx):
+ """Implementation of the pkg_tar rule."""
+ # Compute the relative path
+ data_path = _compute_data_path(ctx.outputs.out, ctx.attr.strip_prefix)
+
+ build_tar = ctx.executable._build_tar
+ args = [
+ "--output=" + ctx.outputs.out.path,
+ "--directory=" + ctx.attr.package_dir,
+ "--mode=" + ctx.attr.mode,
+ ]
+ args += ["--file=%s=%s" % (f.path, _dest_path(f, data_path))
+ for f in ctx.files.files]
+ if ctx.attr.modes:
+ args += ["--modes=%s=%s" % (key, ctx.attr.modes[key]) for key in ctx.attr.modes]
+ if ctx.attr.extension:
+ dotPos = ctx.attr.extension.find('.')
+ if dotPos > 0:
+ dotPos += 1
+ args += ["--compression=%s" % ctx.attr.extension[dotPos:]]
+ args += ["--tar=" + f.path for f in ctx.files.deps]
+ args += ["--link=%s:%s" % (k, ctx.attr.symlinks[k])
+ for k in ctx.attr.symlinks]
+
+ ctx.action(
+ executable = build_tar,
+ arguments = args,
+ inputs = ctx.files.files + ctx.files.deps,
+ outputs = [ctx.outputs.out],
+ mnemonic="PackageTar"
+ )
+
+
+def _pkg_deb_impl(ctx):
+ """The implementation for the pkg_deb rule."""
+ files = [ctx.file.data]
+ args = [
+ "--output=" + ctx.outputs.out.path,
+ "--data=" + ctx.file.data.path,
+ "--package=" + ctx.attr.package,
+ "--architecture=" + ctx.attr.architecture,
+ "--maintainer=" + ctx.attr.maintainer,
+ ]
+ if ctx.attr.preinst:
+ args += ["--preinst=@" + ctx.file.preinst.path]
+ files += [ctx.file.preinst]
+ if ctx.attr.postinst:
+ args += ["--postinst=@" + ctx.file.postinst.path]
+ files += [ctx.file.postinst]
+ if ctx.attr.prerm:
+ args += ["--prerm=@" + ctx.file.prerm.path]
+ files += [ctx.file.prerm]
+ if ctx.attr.postrm:
+ args += ["--postrm=@" + ctx.file.postrm.path]
+ files += [ctx.file.postrm]
+
+ # Version and description can be specified by a file or inlined
+ if ctx.attr.version_file:
+ if ctx.attr.version:
+ fail("Both version and version_file attributes were specified")
+ args += ["--version=@" + ctx.file.version_file.path]
+ files += [ctx.file.version_file]
+ elif ctx.attr.version:
+ args += ["--version=" + ctx.attr.version]
+ else:
+ fail("Neither version_file nor version attribute was specified")
+
+ if ctx.attr.description_file:
+ if ctx.attr.description:
+ fail("Both description and description_file attributes were specified")
+ args += ["--description=@" + ctx.file.description_file.path]
+ files += [ctx.file.description_file]
+ elif ctx.attr.description:
+ args += ["--description=" + ctx.attr.description]
+ else:
+ fail("Neither description_file nor description attribute was specified")
+
+ # Built using can also be specified by a file or inlined (but is not mandatory)
+ if ctx.attr.built_using_file:
+ if ctx.attr.built_using:
+ fail("Both build_using and built_using_file attributes were specified")
+ args += ["--built_using=@" + ctx.file.built_using_file.path]
+ files += [ctx.file.built_using_file]
+ elif ctx.attr.built_using:
+ args += ["--built_using=" + ctx.attr.built_using]
+
+ if ctx.attr.priority:
+ args += ["--priority=" + ctx.attr.priority]
+ if ctx.attr.section:
+ args += ["--section=" + ctx.attr.section]
+ if ctx.attr.homepage:
+ args += ["--homepage=" + ctx.attr.homepage]
+
+ args += ["--depends=" + d for d in ctx.attr.depends]
+ args += ["--suggests=" + d for d in ctx.attr.suggests]
+ args += ["--enhances=" + d for d in ctx.attr.enhances]
+ args += ["--pre_depends=" + d for d in ctx.attr.predepends]
+ args += ["--recommends=" + d for d in ctx.attr.recommends]
+
+ ctx.action(
+ executable = ctx.executable._make_deb,
+ arguments = args,
+ inputs = files,
+ outputs = [ctx.outputs.out],
+ mnemonic="MakeDeb"
+ )
+
+# A rule for creating a tar file, see README.md
+pkg_tar = rule(
+ implementation = _pkg_tar_impl,
+ attrs = {
+ "strip_prefix": attr.string(),
+ "package_dir": attr.string(default="/"),
+ "deps": attr.label_list(allow_files=tar_filetype),
+ "files": attr.label_list(allow_files=True),
+ "mode": attr.string(default="0555"),
+ "modes": attr.string_dict(),
+ "extension": attr.string(default="tar"),
+ "symlinks": attr.string_dict(),
+ # Implicit dependencies.
+ "_build_tar": attr.label(
+ default=Label("//tools/build_defs/pkg:build_tar"),
+ cfg=HOST_CFG,
+ executable=True,
+ allow_files=True)
+ },
+ outputs = {
+ "out": "%{name}.%{extension}",
+ },
+ executable = False)
+
+
+# A rule for creating a deb file, see README.md
+pkg_deb = rule(
+ implementation = _pkg_deb_impl,
+ attrs = {
+ "data": attr.label(mandatory=True, allow_files=tar_filetype, single_file=True),
+ "package": attr.string(mandatory=True),
+ "architecture": attr.string(default="all"),
+ "maintainer": attr.string(mandatory=True),
+ "preinst": attr.label(allow_files=True, single_file=True),
+ "postinst": attr.label(allow_files=True, single_file=True),
+ "prerm": attr.label(allow_files=True, single_file=True),
+ "postrm": attr.label(allow_files=True, single_file=True),
+ "version_file": attr.label(allow_files=True, single_file=True),
+ "version": attr.string(),
+ "description_file": attr.label(allow_files=True, single_file=True),
+ "description": attr.string(),
+ "built_using_file": attr.label(allow_files=True, single_file=True),
+ "built_using": attr.string(),
+ "priority": attr.string(),
+ "section": attr.string(),
+ "homepage": attr.string(),
+ "depends": attr.string_list(default=[]),
+ "suggests": attr.string_list(default=[]),
+ "enhances": attr.string_list(default=[]),
+ "predepends": attr.string_list(default=[]),
+ "recommends": attr.string_list(default=[]),
+ # Implicit dependencies.
+ "_make_deb": attr.label(
+ default=Label("//tools/build_defs/pkg:make_deb"),
+ cfg=HOST_CFG,
+ executable=True,
+ allow_files=True)
+ },
+ outputs = {
+ "out": "%{name}.deb",
+ },
+ executable = False)
diff --git a/tools/build_defs/docker/testdata/archive/a.ar b/tools/build_defs/pkg/testdata/a.ar
index d2594dffca..d2594dffca 100644
--- a/tools/build_defs/docker/testdata/archive/a.ar
+++ b/tools/build_defs/pkg/testdata/a.ar
diff --git a/tools/build_defs/docker/testdata/archive/a_ab.ar b/tools/build_defs/pkg/testdata/a_ab.ar
index f6b72174ab..f6b72174ab 100644
--- a/tools/build_defs/docker/testdata/archive/a_ab.ar
+++ b/tools/build_defs/pkg/testdata/a_ab.ar
diff --git a/tools/build_defs/docker/testdata/archive/a_b.ar b/tools/build_defs/pkg/testdata/a_b.ar
index 6f2d79c439..6f2d79c439 100644
--- a/tools/build_defs/docker/testdata/archive/a_b.ar
+++ b/tools/build_defs/pkg/testdata/a_b.ar
diff --git a/tools/build_defs/docker/testdata/archive/a_b_ab.ar b/tools/build_defs/pkg/testdata/a_b_ab.ar
index 07dc3e71f1..07dc3e71f1 100644
--- a/tools/build_defs/docker/testdata/archive/a_b_ab.ar
+++ b/tools/build_defs/pkg/testdata/a_b_ab.ar
diff --git a/tools/build_defs/docker/testdata/archive/ab.ar b/tools/build_defs/pkg/testdata/ab.ar
index 29f56beed2..29f56beed2 100644
--- a/tools/build_defs/docker/testdata/archive/ab.ar
+++ b/tools/build_defs/pkg/testdata/ab.ar
diff --git a/tools/build_defs/docker/testdata/archive/b.ar b/tools/build_defs/pkg/testdata/b.ar
index 3f41a7eb70..3f41a7eb70 100644
--- a/tools/build_defs/docker/testdata/archive/b.ar
+++ b/tools/build_defs/pkg/testdata/b.ar
diff --git a/tools/build_defs/docker/testdata/archive/empty.ar b/tools/build_defs/pkg/testdata/empty.ar
index 8b277f0dd5..8b277f0dd5 100644
--- a/tools/build_defs/docker/testdata/archive/empty.ar
+++ b/tools/build_defs/pkg/testdata/empty.ar
diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar b/tools/build_defs/pkg/testdata/tar_test.tar
index 70b043bd9d..70b043bd9d 100644
--- a/tools/build_defs/docker/testdata/archive/tar_test.tar
+++ b/tools/build_defs/pkg/testdata/tar_test.tar
Binary files differ
diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.bz2 b/tools/build_defs/pkg/testdata/tar_test.tar.bz2
index c6f7edf5a7..c6f7edf5a7 100644
--- a/tools/build_defs/docker/testdata/archive/tar_test.tar.bz2
+++ b/tools/build_defs/pkg/testdata/tar_test.tar.bz2
Binary files differ
diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.gz b/tools/build_defs/pkg/testdata/tar_test.tar.gz
index 4b851716dd..4b851716dd 100644
--- a/tools/build_defs/docker/testdata/archive/tar_test.tar.gz
+++ b/tools/build_defs/pkg/testdata/tar_test.tar.gz
Binary files differ
diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.xz b/tools/build_defs/pkg/testdata/tar_test.tar.xz
index 1ea3c8b836..1ea3c8b836 100644
--- a/tools/build_defs/docker/testdata/archive/tar_test.tar.xz
+++ b/tools/build_defs/pkg/testdata/tar_test.tar.xz
Binary files differ
diff --git a/tools/build_defs/pkg/testenv.py b/tools/build_defs/pkg/testenv.py
new file mode 100644
index 0000000000..8a18528be2
--- /dev/null
+++ b/tools/build_defs/pkg/testenv.py
@@ -0,0 +1,22 @@
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Path to the test data."""
+
+import os
+import os.path
+
+TESTDATA_PATH = os.path.join(
+ os.getcwd(),
+ "tools/build_defs/pkg/testdata",
+ )
diff --git a/tools/build_defs/pkg/testenv.sh b/tools/build_defs/pkg/testenv.sh
new file mode 100755
index 0000000000..0a61815bb6
--- /dev/null
+++ b/tools/build_defs/pkg/testenv.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright 2015 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Integration test for docker, test environment.
+
+[ -z "$TEST_SRCDIR" ] && { echo "TEST_SRCDIR not set!" >&2; exit 1; }
+
+# Load the unit-testing framework
+source "${TEST_SRCDIR}/src/test/shell/unittest.bash" || \
+ { echo "Failed to source unittest.bash" >&2; exit 1; }
+
+readonly TEST_DATA_DIR="${TEST_SRCDIR}/tools/build_defs/pkg"