Move dist rule to bazel_common_rules.
Test: presubmits
Bug: 197589841
Change-Id: I2cb506b3409a083da2ce4bf426adc0f8b126d5cc
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..4de1e69
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,8 @@
+# Platform build
+include platform/build/soong:/OWNERS
+
+# Kernel build
+#
+# The kernel follows a different branching model than that of platform, which
+# needs the 'master' branch to be explicitly declared.
+include kernel/build:master:/kleaf/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..21dbb5a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# Bazel Common Rules
+
+This directory contains common Bazel rules and tools shared between the Platform
+and Kernel builds.
+
+For platform-specific rules, place them in the platform checkout's
+[//build/bazel/rules](https://android.googlesource.com/platform/build/bazel/+/master/rules/)
+directory.
+
+For kernel-specific rules, place them in kernel checkout's [//build/kleaf
+directory](https://android.googlesource.com/kernel/build/+/master/kleaf/).
diff --git a/dist/BUILD b/dist/BUILD
new file mode 100644
index 0000000..ee3a4ca
--- /dev/null
+++ b/dist/BUILD
@@ -0,0 +1,11 @@
+load(":dist.bzl", "copy_to_dist_dir")
+exports_files(["dist.py"])
+
+# bazel run --package_path=out/soong/workspace //build/bazel_common_rules/dist:dist_bionic_example -- --dist_dir=/tmp/dist
+copy_to_dist_dir(
+ name = "dist_bionic_example",
+ data = [
+ "//bionic/libc:libc",
+ "//bionic/libdl:libdl",
+ ],
+)
diff --git a/dist/dist.bzl b/dist/dist.bzl
new file mode 100644
index 0000000..a4e29d6
--- /dev/null
+++ b/dist/dist.bzl
@@ -0,0 +1,68 @@
+# Rule to support Bazel in copying its output files to the dist dir outside of
+# the standard Bazel output user root.
+
+load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
+
+def _generate_dist_manifest_impl(ctx):
+ # Create a manifest of dist files to differentiate them from other runfiles.
+ dist_manifest = ctx.actions.declare_file(ctx.attr.name + "_dist_manifest.txt")
+ dist_manifest_content = ""
+ all_dist_files = []
+ for f in ctx.attr.data:
+ dist_files = f[DefaultInfo].files.to_list()
+ all_dist_files += dist_files
+ dist_manifest_content += "\n".join([dist_file.short_path for dist_file in dist_files])
+ ctx.actions.write(
+ output = dist_manifest,
+ content = dist_manifest_content,
+ )
+
+ # Create the runfiles object.
+ runfiles = ctx.runfiles(files = [dist_manifest] + all_dist_files)
+
+ return [DefaultInfo(runfiles = runfiles)]
+
+
+_generate_dist_manifest = rule(
+ implementation = _generate_dist_manifest_impl,
+ doc = """Generate a manifest of files to be dist to a directory.""",
+ attrs = {
+ "data": attr.label_list(
+ mandatory = True,
+ allow_files = True,
+ doc = """Files or targets to copy to the dist dir.
+
+In the case of targets, the rule copies the list of `files` from the target's DefaultInfo provider.
+""",
+ ),
+ },
+)
+
+def copy_to_dist_dir(name, data):
+ """A dist rule to copy files out of Bazel's output directory into a custom location.
+
+Example:
+ bazel run //path/to/my:dist_target -- --dist_dir=/tmp/dist
+"""
+ _generate_dist_manifest(
+ name = name + "_dist_manifest",
+ data = data,
+ )
+
+ copy_file(
+ name = name + "_dist_tool",
+ src = "//build/bazel_common_rules/dist:dist.py",
+ out = name + "_dist.py",
+ )
+
+ # The dist py_binary tool must be colocated in the same package as the
+ # dist_manifest so that the runfiles directory is the same, and that the
+ # dist_manifest is in the data runfiles of the dist tool.
+ native.py_binary(
+ name = name,
+ main = name + "_dist.py",
+ srcs = [name + "_dist.py"],
+ python_version = "PY3",
+ visibility = ["//visibility:public"],
+ data = [name + "_dist_manifest"],
+ )
diff --git a/dist/dist.py b/dist/dist.py
new file mode 100644
index 0000000..c49dfd5
--- /dev/null
+++ b/dist/dist.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# 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 Python script to copy files from Bazel's dist rule to a user specified dist directory.
+
+READ ME FIRST.
+
+This script is only meant to be executed with `bazel run`. `bazel build <this
+script>` doesn't actually copy the files, you'd have to `bazel run` a
+copy_to_dist_dir target.
+
+This script copies files from Bazel's output tree into a directory specified by
+the user. It does not check if the dist dir already contains the file, and will
+simply overwrite it.
+
+One approach is to wipe the dist dir every time this script runs, but that may
+be overly destructive and best left to an explicit rm -rf call outside of this
+script.
+
+Another approach is to error out if the file being copied already exist in the dist dir,
+or perform some kind of content hash checking.
+"""
+
+
+import argparse
+import glob
+import os
+import shutil
+import sys
+
+
+def files_to_dist():
+ # Assume that dist.bzl is in the same package as dist.py
+ runfiles_directory = os.path.dirname(__file__)
+ dist_manifests = glob.glob(os.path.join(runfiles_directory, "*_dist_manifest.txt"))
+ if not dist_manifests:
+ print("Warning: could not find a file ending in *_dist_manifest.txt" +
+ "in the runfiles directory: %s" % runfiles_directory)
+ files_to_dist = []
+ for dist_manifest in dist_manifests:
+ with open(dist_manifest, "r") as f:
+ files_to_dist += [line.strip() for line in f]
+ return files_to_dist
+
+def copy_files_to_dist_dir(files, dist_dir):
+ for src in files:
+ if not os.path.isfile(src):
+ continue
+
+ src_relpath = src
+ src_abspath = os.path.abspath(src)
+
+ dst = os.path.join(dist_dir, src_relpath)
+ dst_dirname = os.path.dirname(dst)
+ print("[dist] Copying file: %s" % dst)
+ if not os.path.exists(dst_dirname):
+ os.makedirs(dst_dirname)
+
+ shutil.copyfile(src_abspath, dst, follow_symlinks=True)
+
+def main():
+ parser = argparse.ArgumentParser(description="Dist Bazel output files into a custom directory.")
+ parser.add_argument("--dist_dir", required = True, help = "absolute path to the dist dir")
+ args = parser.parse_args()
+ dist_dir = args.dist_dir
+
+ if not os.path.isabs(dist_dir):
+ # BUILD_WORKSPACE_DIRECTORY is the root of the Bazel workspace containing this binary target.
+ # https://docs.bazel.build/versions/main/user-manual.html#run
+ dist_dir = os.path.join(os.environ.get("BUILD_WORKSPACE_DIRECTORY"), dist_dir)
+
+ copy_files_to_dist_dir(files_to_dist(), dist_dir)
+
+if __name__ == "__main__":
+ main()