John Cater has uploaded this change for review.
Add script to generate an RPM file Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/rpm.bzl 6 files changed, 297 insertions(+), 76 deletions(-)
diff --git a/scripts/packages/fedora/BUILD b/scripts/packages/fedora/BUILD
index 36d8690..4cffb30 100644
--- a/scripts/packages/fedora/BUILD
+++ b/scripts/packages/fedora/BUILD
@@ -5,26 +5,18 @@
srcs = glob(["**"]),
)
-genrule(
- name = "bazel-fedora",
- srcs = [
- "bazel.spec",
+load("//tools/build_defs/pkg:rpm.bzl", "pkg_rpm")
+
+pkg_rpm(
+ name = "bazel",
+ spec_file = "bazel.spec",
+ architecture = "x86_64",
+ changelog = "//:changelog-file",
+ version_file = "//scripts/packages:version.txt",
+ data = [
"//scripts/packages:bazel",
"//scripts/packages:bazel-real",
- "//scripts/packages:debian/bazel.bazelrc",
+ "//scripts/packages:bazel.bazelrc",
"//scripts:bash_completion",
],
- outs = [
- "bazel-fedora.rpm",
- ],
- cmd = """
- $(location :build_rpm.sh) \
- $(location :bazel.spec) \
- $(location bazel-fedora.rpm) \
- $(location //scripts/packages:bazel) \
- $(location //scripts/packages:bazel-real) \
- $(location //scripts/packages:debian/bazel.bazelrc) \
- $(location //scripts:bash_completion)
- """,
- tools = ["build_rpm.sh"],
)
diff --git a/scripts/packages/fedora/bazel.spec b/scripts/packages/fedora/bazel.spec
index 0d69e61..14cabfc 100644
--- a/scripts/packages/fedora/bazel.spec
+++ b/scripts/packages/fedora/bazel.spec
@@ -30,7 +30,7 @@
install -m 755 bazel-real %{buildroot}%{_bindir}/bazel-real
mkdir -p %{buildroot}%{_sysconfdir}/
install -m 644 bazel.bazelrc %{buildroot}%{_sysconfdir}/bazel.bazelrc
-mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d
+mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d/
install -m 644 bazel-complete.bash %{buildroot}%{_sysconfdir}/bash_completion.d/bazel
%files
@@ -40,4 +40,6 @@
%{_sysconfdir}/bash_completion.d/bazel
%changelog
-# TODO: Include changelog.
+* Fri Jan 27 2017 jca...@google.com - devel-1
+- Initial package version released.
+
diff --git a/scripts/packages/fedora/build_rpm.sh b/scripts/packages/fedora/build_rpm.sh
deleted file mode 100755
index 525c858..0000000
--- a/scripts/packages/fedora/build_rpm.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-# Copyright 2016 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.
-
-set -eu
-
-# Build a RPM archive from the Bazel sources.
-
-# Usage: build_rpm.sh spec_file dest_file [other files]
-
-spec_file=$1
-shift
-dest_file=$1
-shift
-
-echo "Building ${dest_file} from ${spec_file}."
-WORK_DIR="${PWD}/bazel-fedora"
-
-# Copy needed sources.
-rm -rf ${WORK_DIR}
-mkdir -p ${WORK_DIR}/SOURCES
-mkdir -p ${WORK_DIR}/BUILD
-cp $spec_file ${WORK_DIR}
-for i in "$@"; do
- cp $i $WORK_DIR/BUILD
-done
-
-# Build the RPM.
-rpmbuild \
- --define "_topdir ${WORK_DIR}" \
- --define "_tmppath /tmp" \
- -bb ${spec_file} > rpmbuild.log 2>&1
-if [ $? -ne 0 ]; then
- err=$?
- echo "Error in rpmbuild:"
- cat rpmbuild.log
- exit $err
-fi
-out_file=$(grep '^Wrote:' rpmbuild.log | cut -d ' ' -f 2)
-
-# Copy output back to the destination.
-cp $out_file $dest_file
-echo "Created $dest_file"
-
diff --git a/tools/build_defs/pkg/BUILD b/tools/build_defs/pkg/BUILD
index 1bc4786..63579d9 100644
--- a/tools/build_defs/pkg/BUILD
+++ b/tools/build_defs/pkg/BUILD
@@ -66,6 +66,16 @@
],
)
+# Used by pkg_rpm in rpm.bzl.
+py_binary(
+ name = "make_rpm",
+ srcs = ["make_rpm.py"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "@bazel_tools//third_party/py/gflags",
+ ],
+)
+
# tests
load("//tools/build_defs/pkg:pkg.bzl", "pkg_deb", "pkg_tar")
diff --git a/tools/build_defs/pkg/make_rpm.py b/tools/build_defs/pkg/make_rpm.py
new file mode 100644
index 0000000..91a58ad
--- /dev/null
+++ b/tools/build_defs/pkg/make_rpm.py
@@ -0,0 +1,190 @@
+# Copyright 2017 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 an RPM package."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import contextlib
+import fileinput
+import os
+import re
+import shutil
+import subprocess
+import sys
+from tempfile import mkdtemp
+
+from third_party.py import gflags
+
+
+gflags.DEFINE_string('name', '',
+ 'The name of the software being packaged.')
+gflags.DEFINE_string('version', '',
+ 'The version of the software being packaged.')
+gflags.DEFINE_string('arch', '',
+ 'The CPU architecture of the software being packaged.')
+
+gflags.DEFINE_string('spec_file', '',
+ 'The file containing the RPM specification.')
+gflags.DEFINE_string('out_file', '',
+ 'The destination to save the resulting RPM file to.')
+
+# Debugging options.
+gflags.DEFINE_string('debug_tempdir', None,
+ 'A directory to use instead of the temporary directory.')
+
+
+# Setup to safely create a temporary directory and clean it up when done.
+...@contextlib.contextmanager
+def Cd(newdir, cleanup=lambda: True):
+ prevdir = os.getcwd()
+ os.chdir(os.path.expanduser(newdir))
+ try:
+ yield
+ finally:
+ os.chdir(prevdir)
+ cleanup()
+
+
+...@contextlib.contextmanager
+def Tempdir():
+ if FLAGS.debug_tempdir:
+ with Cd(FLAGS.debug_tempdir):
+ yield FLAGS.debug_tempdir
+ else:
+ dirpath = mkdtemp()
+
+ def Cleanup():
+ shutil.rmtree(dirpath)
+
+ with Cd(dirpath, Cleanup):
+ yield dirpath
+
+
+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
+
+
+class RpmBuilder(object):
+ """A helper class to manage building the RPM file."""
+
+ SOURCE_DIR = 'SOURCES'
+ BUILD_DIR = 'BUILD'
+ TEMP_DIR = 'TMP'
+ DIRS = [SOURCE_DIR, BUILD_DIR, TEMP_DIR]
+ WROTE_FILE_RE = re.compile(r'Wrote: (?P<rpm_path>.+)', re.MULTILINE)
+
+ def __init__(self, name, version, arch):
+ self.name = name
+ self.version = GetFlagValue(version)
+ self.arch = arch
+ self.files = []
+ self.rpm_path = None
+
+ def AddFiles(self, files):
+ """Add a set of files to the current RPM."""
+ self.files += files
+
+ def SetupWorkdir(self, spec_file, original_dir):
+ """Create the needed structure in the workdir."""
+
+ # Create directory structure.
+ for name in RpmBuilder.DIRS:
+ if not os.path.exists(name):
+ os.makedirs(name, 0777)
+
+ shutil.copy(spec_file, '.')
+
+ # Copy the files.
+ for f in self.files:
+ shutil.copy(os.path.join(original_dir, f), RpmBuilder.BUILD_DIR)
+
+ # Copy the spec file, updating with the correct version.
+ self.spec_file = os.path.basename(spec_file)
+ with open(self.spec_file, 'w') as output:
+ for line in fileinput.input(spec_file):
+ if self.version and line.startswith('Version: '):
+ line = 'Version: %s\n' % self.version
+ output.write(line)
+
+ def CallRpmBuild(self, dirname):
+ """Call rpmbuild with the correct arguments."""
+
+ args = [
+ 'rpmbuild',
+ '--define', '_topdir %s' % dirname,
+ '--define', '_tmppath %s/TMP' % dirname,
+ '--bb',
+ self.spec_file,
+ ]
+ #print('Calling: %s' % ' '.join(args))
+ p = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output = p.communicate()[0]
+
+ if p.returncode == 0:
+ # Find the created file.
+ m = RpmBuilder.WROTE_FILE_RE.search(output)
+ if m:
+ self.rpm_path = m.group('rpm_path')
+
+ if p.returncode != 0 or not self.rpm_path:
+ print('Error calling rpmbuild:')
+ print(output)
+
+ # Return the status.
+ return p.returncode
+
+ def SaveResult(self, out_file):
+ """Save the result RPM out of the temporary working directory."""
+
+ if self.rpm_path:
+ shutil.copy(self.rpm_path, out_file)
+ print('Saved RPM file to %s' % out_file)
+ else:
+ print('No RPM file created.')
+
+ def Build(self, spec_file, out_file):
+ """Build the RPM described by the spec_file."""
+ print('Building RPM for %s at %s' % (self.name, out_file))
+
+ original_dir = os.getcwd()
+ spec_file = os.path.join(original_dir, spec_file)
+ out_file = os.path.join(original_dir, out_file)
+ with Tempdir() as dirname:
+ self.SetupWorkdir(spec_file, original_dir)
+ status = self.CallRpmBuild(dirname)
+ self.SaveResult(out_file)
+
+ return status
+
+
+def main(argv=()):
+ builder = RpmBuilder(FLAGS.name, FLAGS.version, FLAGS.arch)
+ builder.AddFiles(argv[1:])
+ return builder.Build(FLAGS.spec_file, FLAGS.out_file)
+
+
+if __name__ == '__main__':
+ FLAGS = gflags.FLAGS
+ main(FLAGS(sys.argv))
+
diff --git a/tools/build_defs/pkg/rpm.bzl b/tools/build_defs/pkg/rpm.bzl
new file mode 100644
index 0000000..d4899a2
--- /dev/null
+++ b/tools/build_defs/pkg/rpm.bzl
@@ -0,0 +1,83 @@
+# Copyright 2017 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 to create RPM archives."""
+
+rpm_filetype = [".rpm"]
+spec_filetype = [".spec"]
+
+def _pkg_rpm_impl(ctx):
+ """Implements to pkg_rpm rule."""
+
+ files = []
+ args = ["--name=" + ctx.label.name]
+
+ # Version 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.architecture:
+ args += ["--arch=" + ctx.attr.architecture]
+
+ if ctx.attr.spec_file:
+ args += ["--spec_file=" + ctx.file.spec_file.path]
+ files += [ctx.file.spec_file]
+ else:
+ fail("spec_file was not specified")
+
+ args += ["--out_file=" + ctx.outputs.out.path]
+
+ # Add data files.
+ files += [ctx.file.changelog] + ctx.files.data
+ args += [ctx.file.changelog.path]
+ for f in ctx.files.data:
+ args += [f.path]
+
+ # Call the generator script.
+ # TODO(katre): Generate a source RPM.
+ ctx.action(
+ executable = ctx.executable._make_rpm,
+ arguments = args,
+ inputs = files,
+ outputs = [ctx.outputs.out],
+ mnemonic = "MakeRpm")
+
+# Define the rule.
+pkg_rpm = rule(
+ implementation = _pkg_rpm_impl,
+ attrs = {
+ "spec_file" : attr.label(mandatory=True, allow_files=spec_filetype, single_file=True),
+ "architecture": attr.string(default="all"),
+ "version_file": attr.label(allow_files=True, single_file=True),
+ "version": attr.string(),
+ "changelog" : attr.label(mandatory=True, allow_files=True, single_file=True),
+ "data": attr.label_list(mandatory=True, allow_files=True),
+
+ # Implicit dependencies.
+ "_make_rpm": attr.label(
+ default=Label("//tools/build_defs/pkg:make_rpm"),
+ cfg="host",
+ executable=True,
+ allow_files=True),
+ },
+ outputs = {
+ "out": "%{name}.rpm",
+ },
+ executable = False)
To view, visit change 8590. To unsubscribe, visit settings.
John Cater uploaded patch set #2 to this change.
Create a pkg_rpm rule, and use it to generate a releasable RPM for Bazel. Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/rpm.bzl 6 files changed, 297 insertions(+), 76 deletions(-)
To view, visit change 8590. To unsubscribe, visit settings.
John Cater posted comments on this change.
Patch set 2:Verified +1
Damien Martin-guillerez posted comments on this change.
Patch set 2:Verified +1
Also edit the README of tools/build_defs/pkg to add the new rule.
(1 comment)
File tools/build_defs/pkg/rpm.bzl:
Patch Set #2, Line 62: # Define the rule.
Add skydoc (see https://skydoc.bazel.build)
To view, visit change 8590. To unsubscribe, visit settings.
Kristina Chodorow posted comments on this change.
Patch set 2:
(4 comments)
File tools/build_defs/pkg/make_rpm.py:
Patch Set #2, Line 93: WROTE_FILE_RE
Test.
Patch Set #2, Line 98: self.arch = arch
This isn't actually used anywhere.
Patch Set #2, Line 106: SetupWorkdir
Test.
File tools/build_defs/pkg/rpm.bzl:
Patch Set #2, Line 81: "out": "%{name}.rpm",
Did you ever find out what was going on with the multiple outputs not working?
To view, visit change 8590. To unsubscribe, visit settings.
John Cater uploaded patch set #3 to this change.
Add script to generate an RPM file Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/make_rpm_test.py A tools/build_defs/pkg/rpm.bzl 7 files changed, 457 insertions(+), 76 deletions(-)
To view, visit change 8590. To unsubscribe, visit settings.
John Cater posted comments on this change.
Patch set 3:-Verified
(5 comments)
File tools/build_defs/pkg/make_rpm.py:
Patch Set #2, Line 93: Args:
Done
Patch Set #2, Line 98: the prefix.
Added some TODOs to use this, currently it can only generate a package for the host architecture, whatever that is.
Patch Set #2, Line 106: break
Done
File tools/build_defs/pkg/rpm.bzl:
Patch Set #2, Line 62: # Link the RPM to the expected output name.
Done
Patch Set #2, Line 81: default=Label("//tools/build_defs/pkg:make_rpm"),
Yeah, I filed https://github.com/bazelbuild/bazel/issues/2467 about it just now.
To view, visit change 8590. To unsubscribe, visit settings.
Damien Martin-guillerez removed a vote from this change.
To view, visit change 8590. To unsubscribe, visit settings.
Damien Martin-guillerez removed Bazel CI from this change.
To view, visit change 8590. To unsubscribe, visit settings.
Damien Martin-guillerez posted comments on this change.
Patch set 3:Code-Review +1
Also please modify pkg/README.md to add the new rule.
(2 comments)
Fri Jan 27 2017 jca...@google.com
- devel-1 - Initial package version released.
Shouldn't that be Bazel changelog?
File tools/build_defs/pkg/make_rpm.py:
Patch Set #3, Line 47: ef Cd(newdir, cleanup=lambda: True):
Here and there, the linter will complain if you don't put docstring to your method.
To view, visit change 8590. To unsubscribe, visit settings.
John Cater uploaded patch set #4 to this change.
Add script to generate an RPM file Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD M tools/build_defs/pkg/README.md A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/make_rpm_test.py A tools/build_defs/pkg/rpm.bzl 8 files changed, 548 insertions(+), 76 deletions(-)
To view, visit change 8590. To unsubscribe, visit settings.
John Cater posted comments on this change.
Patch set 3:
Patch Set 3: Code-Review+1
(2 comments)
Also please modify pkg/README.md to add the new rule.
Updated README.md, sorry about that.
(2 comments)
File scripts/packages/fedora/bazel.spec:
Patch Set #3, Line 44: - Initial package version released.
No, the changelog in the spec file is specifically for changes to the packaging, not the contents. If we added new files or changed the dependencies that would go here, not the actual changes to Bazel.
File tools/build_defs/pkg/make_rpm.py:
Patch Set #3, Line 47: def Cd(newdir, cleanup=lambda: True):
Done
To view, visit change 8590. To unsubscribe, visit settings.
Kristina Chodorow posted comments on this change.
Patch set 4:Code-Review +2
Damien Martin-guillerez posted comments on this change.
To view, visit change 8590. To unsubscribe, visit settings.
Kristina Chodorow uploaded patch set #5 to this change.
Add pkg_rpm build rule to help generating RPM packages. RELNOTES: Adds pkg_rpm rule for generating RPM packages. -- Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c Reviewed-on: https://cr.bazel.build/8590 PiperOrigin-RevId: 146477490 MOS_MIGRATED_REVID=146477490 --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD M tools/build_defs/pkg/README.md A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/make_rpm_test.py A tools/build_defs/pkg/rpm.bzl 8 files changed, 546 insertions(+), 78 deletions(-)
To view, visit change 8590. To unsubscribe, visit settings.
Kristina Chodorow merged this change.
Add pkg_rpm build rule to help generating RPM packages. RELNOTES: Adds pkg_rpm rule for generating RPM packages. -- Change-Id: I2e83161e29218700bbe7e62186b0b1667e555a7c Reviewed-on: https://cr.bazel.build/8590 PiperOrigin-RevId
: 146477490 MOS_MIGRATED_REVID=146477490 --- M scripts/packages/fedora/BUILD M scripts/packages/fedora/bazel.spec D scripts/packages/fedora/build_rpm.sh M tools/build_defs/pkg/BUILD M tools/build_defs/pkg/README.md A tools/build_defs/pkg/make_rpm.py A tools/build_defs/pkg/make_rpm_test.py A tools/build_defs/pkg/rpm.bzl 8 files changed, 546 insertions(+), 78 deletions(-)
diff --git a/scripts/packages/fedora/BUILD b/scripts/packages/fedora/BUILD
index 36d8690..2ee91d0 100644
--- a/scripts/packages/fedora/BUILD
+++ b/scripts/packages/fedora/BUILD
@@ -5,26 +5,18 @@
srcs = glob(["**"]),
)
-genrule(
- name = "bazel-fedora",
- srcs = [
- "bazel.spec",
- "//scripts/packages:bazel",
- "//scripts/packages:bazel-real",
- "//scripts/packages:debian/bazel.bazelrc",
+load("//tools/build_defs/pkg:rpm.bzl", "pkg_rpm")
+
+pkg_rpm(
+ name = "bazel",
+ architecture = "x86_64",
+ changelog = "//:changelog-file",
+ data = [
"//scripts:bash_completion",
+ "//scripts/packages:bazel",
+ "//scripts/packages:bazel.bazelrc",
+ "//scripts/packages:bazel-real",
],
- outs = [
- "bazel-fedora.rpm",
- ],
- cmd = """
- $(location :build_rpm.sh) \
- $(location :bazel.spec) \
- $(location bazel-fedora.rpm) \
- $(location //scripts/packages:bazel) \
- $(location //scripts/packages:bazel-real) \
- $(location //scripts/packages:debian/bazel.bazelrc) \
- $(location //scripts:bash_completion)
- """,
- tools = ["build_rpm.sh"],
+ spec_file = "bazel.spec",
+ version_file = "//scripts/packages:version.txt",
)
diff --git a/scripts/packages/fedora/bazel.spec b/scripts/packages/fedora/bazel.spec
index 0d69e61..14cabfc 100644
--- a/scripts/packages/fedora/bazel.spec
+++ b/scripts/packages/fedora/bazel.spec
@@ -30,7 +30,7 @@
install -m 755 bazel-real %{buildroot}%{_bindir}/bazel-real
mkdir -p %{buildroot}%{_sysconfdir}/
install -m 644 bazel.bazelrc %{buildroot}%{_sysconfdir}/bazel.bazelrc
-mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d
+mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d/
install -m 644 bazel-complete.bash %{buildroot}%{_sysconfdir}/bash_completion.d/bazel
%files
@@ -40,4 +40,6 @@
%{_sysconfdir}/bash_completion.d/bazel
%changelog
-# TODO: Include changelog.
+* Fri Jan 27 2017 jca...@google.com - devel-1
+- Initial package version released.
+
diff --git a/scripts/packages/fedora/build_rpm.sh b/scripts/packages/fedora/build_rpm.sh
deleted file mode 100755
index 525c858..0000000
--- a/scripts/packages/fedora/build_rpm.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-# Copyright 2016 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.
-
-set -eu
-
-# Build a RPM archive from the Bazel sources.
-
-# Usage: build_rpm.sh spec_file dest_file [other files]
-
-spec_file=$1
-shift
-dest_file=$1
-shift
-
-echo "Building ${dest_file} from ${spec_file}."
-WORK_DIR="${PWD}/bazel-fedora"
-
-# Copy needed sources.
-rm -rf ${WORK_DIR}
-mkdir -p ${WORK_DIR}/SOURCES
-mkdir -p ${WORK_DIR}/BUILD
-cp $spec_file ${WORK_DIR}
-for i in "$@"; do
- cp $i $WORK_DIR/BUILD
-done
-
-# Build the RPM.
-rpmbuild \
- --define "_topdir ${WORK_DIR}" \
- --define "_tmppath /tmp" \
- -bb ${spec_file} > rpmbuild.log 2>&1
-if [ $? -ne 0 ]; then
- err=$?
- echo "Error in rpmbuild:"
- cat rpmbuild.log
- exit $err
-fi
-out_file=$(grep '^Wrote:' rpmbuild.log | cut -d ' ' -f 2)
-
-# Copy output back to the destination.
-cp $out_file $dest_file
-echo "Created $dest_file"
-
diff --git a/tools/build_defs/pkg/BUILD b/tools/build_defs/pkg/BUILD
index 1bc4786..266a205 100644
--- a/tools/build_defs/pkg/BUILD
+++ b/tools/build_defs/pkg/BUILD
@@ -66,6 +66,24 @@
],
)
+# Used by pkg_rpm in rpm.bzl.
+py_binary(
+ name = "make_rpm",
+ srcs = ["make_rpm.py"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "@bazel_tools//third_party/py/gflags",
+ ],
+)
+
+py_test(
+ name = "make_rpm_test",
+ srcs = ["make_rpm_test.py"],
+ deps = [
+ ":make_rpm",
+ ],
+)
+
# tests
load("//tools/build_defs/pkg:pkg.bzl", "pkg_deb", "pkg_tar")
diff --git a/tools/build_defs/pkg/README.md b/tools/build_defs/pkg/README.md
index 8fda666..4369777 100644
--- a/tools/build_defs/pkg/README.md
+++ b/tools/build_defs/pkg/README.md
@@ -5,6 +5,7 @@
<ul>
<li><a href="#pkg_tar">pkg_tar</a></li>
<li><a href="#pkg_deb">pkg_deb</a></li>
+ <li><a href="#pkg_rpm">pkg_rpm</a></li>
</ul>
</div>
@@ -386,3 +387,72 @@
</tbody>
</tbody>
</table>
+
+<a name="pkg_rpm"></a>
+### pkg_rpm
+
+```python
+pkg_rpm(name, spec_file, architecture, version, version_file, changelog, data)
+```
+
+Create an RPM package. See <a
+href="http://rpm.org/documentation.html">http://rpm.org/documentation.html</a>
+for more details on this.
+
+<table class="table table-condensed table-bordered table-params">
+ <colgroup>
+ <col class="col-param" />
+ <col class="param-description" />
+ </colgroup>
+ <thead>
+ <tr>
+ <th colspan="2">Attributes</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>name</code></td>
+ <td>
+ <code>Name, required</code>
+ <p>A unique name for this rule. Used to name the output package.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>spec_file</code></td>
+ <td>
+ <code>File, required</code>
+ <p>The RPM specification file used to generate the package.</p>
+ <p>
+ See <a href="http://rpm.org/max-rpm-snapshot/s1-rpm-build-creating-spec-file.html">http://rpm.org/max-rpm-snapshot/s1-rpm-build-creating-spec-file.html</a>.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>architecture</code></td>
+ <td>
+ <code>String, default to 'all'</code>
+ <p>The architecture that this package target.</p>
+ </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>data</code></td>
+ <td>
+ <code>Files, required</code>
+ <p>
+ Files to include in the generated package.
+ </p>
+ </td>
+ </tr>
+ </tbody>
+ </tbody>
+</table>
diff --git a/tools/build_defs/pkg/make_rpm.py b/tools/build_defs/pkg/make_rpm.py
new file mode 100644
index 0000000..9f0f40a
--- /dev/null
+++ b/tools/build_defs/pkg/make_rpm.py
@@ -0,0 +1,228 @@
+# Copyright 2017 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 an RPM package."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import contextlib
+import fileinput
+import os
+import re
+import shutil
+import subprocess
+import sys
+from tempfile import mkdtemp
+
+from third_party.py import gflags
+
+gflags.DEFINE_string('name', '', 'The name of the software being packaged.')
+gflags.DEFINE_string('version', '',
+ 'The version of the software being packaged.')
+gflags.DEFINE_string('arch', '',
+ 'The CPU architecture of the software being packaged.')
+
+gflags.DEFINE_string('spec_file', '',
+ 'The file containing the RPM specification.')
+gflags.DEFINE_string('out_file', '',
+ 'The destination to save the resulting RPM file to.')
+
+
+# Setup to safely create a temporary directory and clean it up when done.
+...@contextlib.contextmanager
+def Cd(newdir, cleanup=lambda: True):
+ """Change the current working directory.
+
+ This will run the provided cleanup function when the context exits and the
+ previous working directory is restored.
+
+ Args:
+ newdir: The directory to change to. This must already exist.
+ cleanup: An optional cleanup function to be executed when the context exits.
+
+ Yields:
+ Nothing.
+ """
+
+ prevdir = os.getcwd()
+ os.chdir(os.path.expanduser(newdir))
+ try:
+ yield
+ finally:
+ os.chdir(prevdir)
+ cleanup()
+
+
+...@contextlib.contextmanager
+def Tempdir():
+ """Create a new temporary directory and change to it.
+
+ The temporary directory will be removed when the context exits.
+
+ Yields:
+ The full path of the temporary directory.
+ """
+
+ dirpath = mkdtemp()
+
+ def Cleanup():
+ shutil.rmtree(dirpath)
+
+ with Cd(dirpath, Cleanup):
+ yield dirpath
+
+
+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
+
+
+WROTE_FILE_RE = re.compile(r'Wrote: (?P<rpm_path>.+)', re.MULTILINE)
+
+
+def FindOutputFile(log):
+ """Find the written file from the log information."""
+
+ m = WROTE_FILE_RE.search(log)
+ if m:
+ return m.group('rpm_path')
+ return None
+
+
+def CopyAndRewrite(input_file, output_file, replacements=None):
+ """Copies the given file and optionally rewrites with replacements.
+
+ Args:
+ input_file: The file to copy.
+ output_file: The file to write to.
+ replacements: A dictionary of replacements.
+ Keys are prefixes scan for, values are the replacements to write after
+ the prefix.
+ """
+ with open(output_file, 'w') as output:
+ for line in fileinput.input(input_file):
+ if replacements:
+ for prefix, text in replacements.iteritems():
+ if line.startswith(prefix):
+ line = prefix + ' ' + text + '\n'
+ break
+ output.write(line)
+
+
+class RpmBuilder(object):
+ """A helper class to manage building the RPM file."""
+
+ SOURCE_DIR = 'SOURCES'
+ BUILD_DIR = 'BUILD'
+ TEMP_DIR = 'TMP'
+ DIRS = [SOURCE_DIR, BUILD_DIR, TEMP_DIR]
+
+ def __init__(self, name, version, arch):
+ self.name = name
+ self.version = GetFlagValue(version)
+ self.arch = arch
+ self.files = []
+ self.rpm_path = None
+
+ def AddFiles(self, files):
+ """Add a set of files to the current RPM."""
+ self.files += files
+
+ def SetupWorkdir(self, spec_file, original_dir):
+ """Create the needed structure in the workdir."""
+
+ # Create directory structure.
+ for name in RpmBuilder.DIRS:
+ if not os.path.exists(name):
+ os.makedirs(name, 0777)
+
+ shutil.copy(os.path.join(original_dir, spec_file), os.getcwd())
+
+ # Copy the files.
+ for f in self.files:
+ shutil.copy(os.path.join(original_dir, f), RpmBuilder.BUILD_DIR)
+
+ # Copy the spec file, updating with the correct version.
+ spec_origin = os.path.join(original_dir, spec_file)
+ self.spec_file = os.path.basename(spec_file)
+ replacements = {}
+ if self.version:
+ replacements['Version:'] = self.version
+ CopyAndRewrite(spec_origin, self.spec_file, replacements)
+
+ def CallRpmBuild(self, dirname):
+ """Call rpmbuild with the correct arguments."""
+
+ args = [
+ 'rpmbuild',
+ '--define',
+ '_topdir %s' % dirname,
+ '--define',
+ '_tmppath %s/TMP' % dirname,
+ '--bb',
+ self.spec_file,
+ ]
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ output = p.communicate()[0]
+
+ if p.returncode == 0:
+ # Find the created file.
+ self.rpm_path = FindOutputFile(output)
+
+ if p.returncode != 0 or not self.rpm_path:
+ print('Error calling rpmbuild:')
+ print(output)
+
+ # Return the status.
+ return p.returncode
+
+ def SaveResult(self, out_file):
+ """Save the result RPM out of the temporary working directory."""
+
+ if self.rpm_path:
+ shutil.copy(self.rpm_path, out_file)
+ print('Saved RPM file to %s' % out_file)
+ else:
+ print('No RPM file created.')
+
+ def Build(self, spec_file, out_file):
+ """Build the RPM described by the spec_file."""
+ print('Building RPM for %s at %s' % (self.name, out_file))
+
+ original_dir = os.getcwd()
+ spec_file = os.path.join(original_dir, spec_file)
+ out_file = os.path.join(original_dir, out_file)
+ with Tempdir() as dirname:
+ self.SetupWorkdir(spec_file, original_dir)
+ status = self.CallRpmBuild(dirname)
+ self.SaveResult(out_file)
+
+ return status
+
+
+def main(argv=()):
+ builder = RpmBuilder(FLAGS.name, FLAGS.version, FLAGS.arch)
+ builder.AddFiles(argv[1:])
+ return builder.Build(FLAGS.spec_file, FLAGS.out_file)
+
+
+if __name__ == '__main__':
+ FLAGS = gflags.FLAGS
+ main(FLAGS(sys.argv))
diff --git a/tools/build_defs/pkg/make_rpm_test.py b/tools/build_defs/pkg/make_rpm_test.py
new file mode 100644
index 0000000..f8f7710
--- /dev/null
+++ b/tools/build_defs/pkg/make_rpm_test.py
@@ -0,0 +1,105 @@
+# Copyright 2017 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.
+"""Tests for make_rpm."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import unittest
+
+from tools.build_defs.pkg import make_rpm
+
+
+def WriteFile(filename, *contents):
+ with open(filename, 'w') as text_file:
+ text_file.write('\n'.join(contents))
+
+
+def DirExists(dirname):
+ return os.path.exists(dirname) and os.path.isdir(dirname)
+
+
+def FileExists(filename):
+ return os.path.exists(filename) and not os.path.isdir(filename)
+
+
+def FileContents(filename):
+ with open(filename, 'r') as text_file:
+ return [s.strip() for s in text_file.readlines()]
+
+
+class MakeRpmTest(unittest.TestCase):
+
+ def testFindOutputFile(self):
+ log = """
+ Lots of data.
+ Wrote: /path/to/file/here.rpm
+ More data present.
+ """
+
+ result = make_rpm.FindOutputFile(log)
+ self.assertEquals('/path/to/file/here.rpm', result)
+
+ def testFindOutputFile_missing(self):
+ log = """
+ Lots of data.
+ More data present.
+ """
+
+ result = make_rpm.FindOutputFile(log)
+ self.assertEquals(None, result)
+
+ def testCopyAndRewrite(self):
+ with make_rpm.Tempdir():
+ WriteFile('test.txt', 'Some: data1', 'Other: data2', 'More: data3')
+ make_rpm.CopyAndRewrite('test.txt', 'out.txt', {
+ 'Some:': 'data1a',
+ 'More:': 'data3a',
+ })
+
+ self.assertTrue(FileExists('out.txt'))
+ self.assertItemsEqual(['Some: data1a', 'Other: data2', 'More: data3a'],
+ FileContents('out.txt'))
+
+ def testSetupWorkdir(self):
+ builder = make_rpm.RpmBuilder('test', '1.0', 'x86')
+ with make_rpm.Tempdir() as outer:
+ # Create spec_file, test files.
+ WriteFile('test.spec', 'Name: test', 'Version: 0.1', 'Summary: test data')
+ WriteFile('file1.txt', 'Hello')
+ WriteFile('file2.txt', 'Goodbye')
+ builder.AddFiles(['file1.txt', 'file2.txt'])
+
+ with make_rpm.Tempdir():
+ # Call RpmBuilder.
+ builder.SetupWorkdir('test.spec', outer)
+
+ # Make sure files exist.
+ self.assertTrue(DirExists('SOURCES'))
+ self.assertTrue(DirExists('BUILD'))
+ self.assertTrue(DirExists('TMP'))
+ self.assertTrue(FileExists('test.spec'))
+ self.assertItemsEqual(
+ ['Name: test', 'Version: 1.0', 'Summary: test data'],
+ FileContents('test.spec'))
+ self.assertTrue(FileExists('BUILD/file1.txt'))
+ self.assertItemsEqual(['Hello'], FileContents('BUILD/file1.txt'))
+ self.assertTrue(FileExists('BUILD/file2.txt'))
+ self.assertItemsEqual(['Goodbye'], FileContents('BUILD/file2.txt'))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/build_defs/pkg/rpm.bzl b/tools/build_defs/pkg/rpm.bzl
new file mode 100644
index 0000000..2322dfa
--- /dev/null
+++ b/tools/build_defs/pkg/rpm.bzl
@@ -0,0 +1,109 @@
+# Copyright 2017 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]
+
+ # Version 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.architecture:
+ args += ["--arch=" + ctx.attr.architecture]
+
+ if ctx.attr.spec_file:
+ args += ["--spec_file=" + ctx.file.spec_file.path]
+ files += [ctx.file.spec_file]
+ else:
+ fail("spec_file was not specified")
+
+ args += ["--out_file=" + ctx.outputs.rpm.path]
+
+ # Add data files.
+ files += [ctx.file.changelog] + ctx.files.data
+ args += [ctx.file.changelog.path]
+ for f in ctx.files.data:
+ args += [f.path]
+
+ # Call the generator script.
+ # TODO(katre): Generate a source RPM.
+ ctx.action(
+ executable = ctx.executable._make_rpm,
+ arguments = args,
+ inputs = files,
+ outputs = [ctx.outputs.rpm],
+ mnemonic = "MakeRpm")
+
+ # Link the RPM to the expected output name.
+ ctx.action(
+ command = "ln -s %s %s" % (ctx.outputs.rpm.basename, ctx.outputs.out.path),
+ inputs = [ctx.outputs.rpm],
+ outputs = [ctx.outputs.out])
+
+# Define the rule.
+pkg_rpm = rule(
+ implementation = _pkg_rpm_impl,
+ attrs = {
+ "spec_file" : attr.label(mandatory=True, allow_files=spec_filetype, single_file=True),
+ "architecture": attr.string(default="all"),
+ "version_file": attr.label(allow_files=True, single_file=True),
+ "version": attr.string(),
+ "changelog" : attr.label(mandatory=True, allow_files=True, single_file=True),
+ "data": attr.label_list(mandatory=True, allow_files=True),
+
+ # Implicit dependencies.
+ "_make_rpm": attr.label(
+ default=Label("//tools/build_defs/pkg:make_rpm"),
+ cfg="host",
+ executable=True,
+ allow_files=True),
+ },
+ outputs = {
+ "out": "%{name}.rpm",
+ "rpm": "%{name}-%{architecture}.rpm",
+ },
+ executable = False)
+"""Creates an RPM format package from the data files.
+
+This runs rpmbuild (and requires it to be installed beforehand) to generate
+an RPM package based on the spec_file and data attributes.
+
+Args:
+ spec_file: The RPM spec file to use. If the version or version_file
+ attributes are provided, the Version in the spec will be overwritten.
+ Any Sources listed in the spec file must be provided as data dependencies.
+ version: The version of the package to generate. This will overwrite any
+ Version provided in the spec file. Only specify one of version and
+ version_file.
+ version_file: A file containing the version of the package to generate. This
+ will overwrite any Version provided in the spec file. Only specify one of
+ version and version_file.
+ changelog: A changelog file to include. This will not be written to the spec
+ file, which should only list changes to the packaging, not the software itself.
+ data: List all files to be included in the package here.
+"""
To view, visit change 8590. To unsubscribe, visit settings.