diff options
author | Damien Martin-Guillerez <dmarting@google.com> | 2015-10-09 14:10:42 +0000 |
---|---|---|
committer | Kristina Chodorow <kchodorow@google.com> | 2015-10-09 14:42:58 +0000 |
commit | 80245bcdcb77493fbe3d6789c98b96c570159960 (patch) | |
tree | e72446d9ff497eb8d0fdb12892f7bb11303be606 /tools | |
parent | ec07479061adae837c460a1dabc5ee396bfc9abf (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/BUILD | 3 | ||||
-rw-r--r-- | tools/build_defs/docker/BUILD | 29 | ||||
-rw-r--r-- | tools/build_defs/docker/create_image.py | 2 | ||||
-rw-r--r-- | tools/build_defs/docker/docker.bzl | 2 | ||||
-rw-r--r-- | tools/build_defs/docker/join_layers.py | 2 | ||||
-rw-r--r-- | tools/build_defs/docker/testdata/BUILD | 9 | ||||
-rw-r--r-- | tools/build_defs/docker/testdata/extras_gen.py | 2 | ||||
-rw-r--r-- | tools/build_defs/docker/testdata/strip_top.py | 2 | ||||
-rw-r--r-- | tools/build_defs/pkg/BUILD | 112 | ||||
-rw-r--r-- | tools/build_defs/pkg/README.md | 365 | ||||
-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-x | tools/build_defs/pkg/build_test.sh | 83 | ||||
-rw-r--r-- | tools/build_defs/pkg/make_deb.py | 198 | ||||
-rw-r--r-- | tools/build_defs/pkg/pkg.bzl | 223 | ||||
-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) | bin | 10240 -> 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) | bin | 134 -> 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) | bin | 158 -> 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) | bin | 192 -> 192 bytes | |||
-rw-r--r-- | tools/build_defs/pkg/testenv.py | 22 | ||||
-rwxr-xr-x | tools/build_defs/pkg/testenv.sh | 25 |
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 Binary files differindex 70b043bd9d..70b043bd9d 100644 --- a/tools/build_defs/docker/testdata/archive/tar_test.tar +++ b/tools/build_defs/pkg/testdata/tar_test.tar diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.bz2 b/tools/build_defs/pkg/testdata/tar_test.tar.bz2 Binary files differindex c6f7edf5a7..c6f7edf5a7 100644 --- a/tools/build_defs/docker/testdata/archive/tar_test.tar.bz2 +++ b/tools/build_defs/pkg/testdata/tar_test.tar.bz2 diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.gz b/tools/build_defs/pkg/testdata/tar_test.tar.gz Binary files differindex 4b851716dd..4b851716dd 100644 --- a/tools/build_defs/docker/testdata/archive/tar_test.tar.gz +++ b/tools/build_defs/pkg/testdata/tar_test.tar.gz diff --git a/tools/build_defs/docker/testdata/archive/tar_test.tar.xz b/tools/build_defs/pkg/testdata/tar_test.tar.xz Binary files differindex 1ea3c8b836..1ea3c8b836 100644 --- a/tools/build_defs/docker/testdata/archive/tar_test.tar.xz +++ b/tools/build_defs/pkg/testdata/tar_test.tar.xz 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" |