| # Copyright 2019 Google LLC | |
| # | |
| # 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 | |
| # | |
| # https://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. | |
| load("@com_google_api_gax_java_properties//:dependencies.properties.bzl", "PROPERTIES") | |
| def _wrapPropertyNamesInBraces(properties): | |
| wrappedProperties = {} | |
| for k, v in properties.items(): | |
| wrappedProperties["{{%s}}" % k] = v | |
| return wrappedProperties | |
| _PROPERTIES = _wrapPropertyNamesInBraces(PROPERTIES) | |
| # ======================================================================== | |
| # General packaging helpers. | |
| # ======================================================================== | |
| def _construct_package_dir_paths(attr_package_dir, out_pkg, label_name): | |
| if attr_package_dir: | |
| package_dir = attr_package_dir | |
| package_dir_expr = "../{}/".format(package_dir) | |
| tar_cd_suffix = ".." | |
| tar_prefix = attr_package_dir | |
| else: | |
| package_dir = label_name | |
| package_dir_expr = "./" | |
| tar_cd_suffix = "." | |
| tar_prefix = "." | |
| # We need to include label in the path to eliminate possible output files duplicates | |
| # (labels are guaranteed to be unique by bazel itself) | |
| package_dir_path = "%s/%s/%s" % (out_pkg.dirname, label_name, package_dir) | |
| return struct( | |
| package_dir = package_dir, | |
| package_dir_expr = package_dir_expr, | |
| package_dir_path = package_dir_path, | |
| package_dir_sibling_parent = out_pkg, | |
| package_dir_sibling_basename = label_name, | |
| tar_cd_suffix = tar_cd_suffix, | |
| tar_prefix = tar_prefix, | |
| ) | |
| def _put_dep_in_a_bucket(dep, dep_bucket, processed_deps): | |
| if processed_deps.get(dep): | |
| return | |
| dep_bucket.append(dep) | |
| processed_deps[dep] = True | |
| def _gapic_pkg_tar_impl(ctx): | |
| deps = [] | |
| for dep in ctx.attr.deps: | |
| for f in dep.files.to_list(): | |
| deps.append(f) | |
| samples =[] | |
| for s in ctx.attr.samples: | |
| for f in s.files.to_list(): | |
| samples.append(f) | |
| paths = _construct_package_dir_paths( | |
| ctx.attr.package_dir, | |
| ctx.outputs.pkg, | |
| ctx.label.name, | |
| ) | |
| script = """ | |
| for s in {samples}; do | |
| mkdir -p {package_dir_path}/{tar_cd_suffix}/{tar_prefix}/samples/snippets/generated/ | |
| unzip -q ./$s -d {package_dir_path}/{tar_cd_suffix}/{tar_prefix}/samples/snippets/generated/ | |
| done | |
| mkdir -p {package_dir_path} | |
| for dep in {deps}; do | |
| tar -xzpf $dep -C {package_dir_path} | |
| done | |
| cd {package_dir_path}/{tar_cd_suffix} | |
| tar -zchpf {tar_prefix}/{package_dir}.tar.gz {tar_prefix}/* | |
| cd - > /dev/null | |
| mv {package_dir_path}/{package_dir}.tar.gz {pkg} | |
| rm -rf {package_dir_path} | |
| """.format( | |
| deps = " ".join(["'%s'" % d.path for d in deps]), | |
| package_dir_path = paths.package_dir_path, | |
| package_dir = paths.package_dir, | |
| samples = " ".join(["'%s'" % s.path for s in samples]), | |
| pkg = ctx.outputs.pkg.path, | |
| tar_cd_suffix = paths.tar_cd_suffix, | |
| tar_prefix = paths.tar_prefix, | |
| ) | |
| ctx.actions.run_shell( | |
| inputs = deps + samples, | |
| command = script, | |
| outputs = [ctx.outputs.pkg], | |
| ) | |
| # The Bazel's native gapic_pkg_tar rule behaves weirdly when package_dir parameter | |
| # is specified (at least on some Linux machines it does not put all the files | |
| # under the package_dir). As a workaround for that bug we provide the custom | |
| # implementation of the gapic_pkg_tar rule. | |
| gapic_pkg_tar = rule( | |
| attrs = { | |
| "deps": attr.label_list(mandatory = True), | |
| "samples": attr.label_list(mandatory = False), | |
| "package_dir": attr.string(mandatory = False, default = ""), | |
| "extension": attr.string(mandatory = False, default = "tar.gz"), | |
| }, | |
| outputs = {"pkg": "%{name}.%{extension}"}, | |
| implementation = _gapic_pkg_tar_impl, | |
| ) | |
| # ======================================================================== | |
| # Java Gapic package helpers. | |
| # ======================================================================== | |
| def _construct_extra_deps(scope_to_deps, versions_map): | |
| label_name_to_maven_artifact = { | |
| "policy_proto": "maven.com_google_api_grpc_proto_google_iam_v1", | |
| "iam_policy_proto": "maven.com_google_api_grpc_proto_google_iam_v1", | |
| "iam_java_proto": "maven.com_google_api_grpc_proto_google_iam_v1", | |
| "iam_java_grpc": "maven.com_google_api_grpc_grpc_google_iam_v1", | |
| "iam_policy_java_grpc": "maven.com_google_api_grpc_grpc_google_iam_v1", | |
| "location_java_grpc": "maven.com_google_api_grpc_grpc_google_common_protos", | |
| } | |
| extra_deps = {} | |
| for scope, deps in scope_to_deps.items(): | |
| for dep in deps: | |
| pkg_dependency = _get_gapic_pkg_dependency_name(dep) | |
| if pkg_dependency: | |
| key = "{{%s}}" % pkg_dependency | |
| if not extra_deps.get(key): | |
| extra_deps[key] = "%s project(':%s')" % (scope, pkg_dependency) | |
| elif _is_java_dependency(dep): | |
| for f in dep[JavaInfo].transitive_deps.to_list(): | |
| maven_artifact = label_name_to_maven_artifact.get(f.owner.name) | |
| if not maven_artifact: | |
| continue | |
| key = "{{%s}}" % maven_artifact | |
| if not extra_deps.get(key): | |
| extra_deps[key] = "%s '%s'" % (scope, versions_map[key]) | |
| return "\n ".join(extra_deps.values()) | |
| def _is_java_dependency(dep): | |
| return JavaInfo in dep | |
| def _is_source_dependency(dep): | |
| return _is_java_dependency(dep) and hasattr(dep[JavaInfo], "source_jars") and dep.label.package != "jar" | |
| def _is_proto_dependency(dep): | |
| return ProtoInfo in dep | |
| def _get_gapic_pkg_dependency_name(dep): | |
| files_list = dep.files.to_list() | |
| if not files_list or len(files_list) != 1: | |
| return None | |
| for ext in (".tar.gz", ".gz", ".tgz"): | |
| if files_list[0].basename.endswith(ext): | |
| return files_list[0].basename[:-len(ext)] | |
| return None | |
| # ======================================================================== | |
| # Java Gapic package rules. | |
| # ======================================================================== | |
| def _java_gapic_build_configs_pkg_impl(ctx): | |
| expanded_templates = [] | |
| paths = _construct_package_dir_paths(ctx.attr.package_dir, ctx.outputs.pkg, ctx.label.name) | |
| substitutions = dict(ctx.attr.static_substitutions) | |
| substitutions["{{extra_deps}}"] = _construct_extra_deps({ | |
| "api": ctx.attr.deps, | |
| "testImplementation": ctx.attr.test_deps, | |
| }, substitutions) | |
| for template in ctx.attr.templates.items(): | |
| expanded_template = ctx.actions.declare_file( | |
| "%s/%s" % (paths.package_dir_sibling_basename, template[1]), | |
| sibling = paths.package_dir_sibling_parent, | |
| ) | |
| expanded_templates.append(expanded_template) | |
| ctx.actions.expand_template( | |
| template = template[0].files.to_list()[0], | |
| substitutions = substitutions, | |
| output = expanded_template, | |
| ) | |
| # Note the script is more complicated than it intuitively should be because of the limitations | |
| # inherent to bazel execution environment: no absolute paths allowed, the generated artifacts | |
| # must ensure uniqueness within a build. The template output directory manipulations are | |
| # to modify default 555 file permissions on any generated by bazel file (exectuable read-only, | |
| # which is not at all what we need for build files). There is no bazel built-in way to change | |
| # the generated files permissions, also the actual files accessible by the script are symlinks | |
| # and `chmod`, when applied to a directory, does not change the attributes of symlink targets | |
| # inside the directory. Chaning the symlink target's permissions is also not an option, because | |
| # they are on a read-only file system. | |
| script = """ | |
| mkdir -p {package_dir_path} | |
| for templ in {templates}; do | |
| cp $templ {package_dir_path}/ | |
| done | |
| chmod 644 {package_dir_path}/* | |
| cd {package_dir_path}/{tar_cd_suffix} | |
| tar -zchpf {tar_prefix}/{package_dir}.tar.gz {tar_prefix}/* | |
| cd - > /dev/null | |
| mv {package_dir_path}/{package_dir}.tar.gz {pkg} | |
| """.format( | |
| templates = " ".join(["'%s'" % f.path for f in expanded_templates]), | |
| package_dir_path = paths.package_dir_path, | |
| package_dir = paths.package_dir, | |
| pkg = ctx.outputs.pkg.path, | |
| tar_cd_suffix = paths.tar_cd_suffix, | |
| tar_prefix = paths.tar_prefix, | |
| ) | |
| ctx.actions.run_shell( | |
| inputs = expanded_templates, | |
| command = script, | |
| outputs = [ctx.outputs.pkg], | |
| ) | |
| java_gapic_build_configs_pkg = rule( | |
| attrs = { | |
| "deps": attr.label_list(mandatory = True), | |
| "test_deps": attr.label_list(mandatory = False, allow_empty = True), | |
| "package_dir": attr.string(mandatory = False), | |
| "templates": attr.label_keyed_string_dict(mandatory = False, allow_files = True), | |
| "static_substitutions": attr.string_dict(mandatory = False, allow_empty = True, default = {}), | |
| }, | |
| outputs = {"pkg": "%{name}.tar.gz"}, | |
| implementation = _java_gapic_build_configs_pkg_impl, | |
| ) | |
| def _java_gapic_srcs_pkg_impl(ctx): | |
| srcs = [] | |
| proto_srcs = [] | |
| for src_dep in ctx.attr.deps: | |
| if _is_source_dependency(src_dep): | |
| srcs.extend(src_dep[JavaInfo].source_jars) | |
| if _is_proto_dependency(src_dep): | |
| proto_srcs.extend(src_dep[ProtoInfo].check_deps_sources.to_list()) | |
| test_srcs = [] | |
| for test_src_dep in ctx.attr.test_deps: | |
| if _is_source_dependency(test_src_dep): | |
| test_srcs.extend(test_src_dep[JavaInfo].source_jars) | |
| paths = _construct_package_dir_paths(ctx.attr.package_dir, ctx.outputs.pkg, ctx.label.name) | |
| # Note the script is more complicated than it intuitively should be because of limitations | |
| # inherent to bazel execution environment: no absolute paths allowed, the generated artifacts | |
| # must ensure uniqueness within a build. | |
| script = """ | |
| for src in {srcs}; do | |
| mkdir -p {package_dir_path}/src/main/java | |
| unzip -q -o $src -d {package_dir_path}/src/main/java | |
| rm -r -f {package_dir_path}/src/main/java/META-INF | |
| # Remove empty files. If there are no resource names, one such file might have | |
| # been created. See java_gapic.bzl. | |
| find {package_dir_path}/src/main/java -type f -size 0 | while read f; do rm -f $f; done | |
| if [ -d {package_dir_path}/src/main/java/samples ]; then | |
| mv {package_dir_path}/src/main/java/samples {package_dir_path} | |
| fi | |
| done | |
| for proto_src in {proto_srcs}; do | |
| mkdir -p {package_dir_path}/src/main/proto | |
| cp -f --parents $proto_src {package_dir_path}/src/main/proto | |
| done | |
| for test_src in {test_srcs}; do | |
| mkdir -p {package_dir_path}/src/test/java | |
| unzip -q -o $test_src -d {package_dir_path}/src/test/java | |
| rm -r -f {package_dir_path}/src/test/java/META-INF | |
| done | |
| cd {package_dir_path}/{tar_cd_suffix} | |
| tar -zchpf {tar_prefix}/{package_dir}.tar.gz {tar_prefix}/* | |
| cd - > /dev/null | |
| mv {package_dir_path}/{package_dir}.tar.gz {pkg} | |
| """.format( | |
| srcs = " ".join(["'%s'" % f.path for f in srcs]), | |
| proto_srcs = " ".join(["'%s'" % f.path for f in proto_srcs]), | |
| test_srcs = " ".join(["'%s'" % f.path for f in test_srcs]), | |
| package_dir_path = paths.package_dir_path, | |
| package_dir = paths.package_dir, | |
| pkg = ctx.outputs.pkg.path, | |
| tar_cd_suffix = paths.tar_cd_suffix, | |
| tar_prefix = paths.tar_prefix, | |
| ) | |
| ctx.actions.run_shell( | |
| inputs = srcs + proto_srcs + test_srcs, | |
| command = script, | |
| outputs = [ctx.outputs.pkg], | |
| ) | |
| java_gapic_srcs_pkg = rule( | |
| attrs = { | |
| "deps": attr.label_list(mandatory = True), | |
| "test_deps": attr.label_list(mandatory = False, allow_empty = True), | |
| "package_dir": attr.string(mandatory = True), | |
| }, | |
| outputs = {"pkg": "%{name}.tar.gz"}, | |
| implementation = _java_gapic_srcs_pkg_impl, | |
| ) | |
| def java_gapic_assembly_gradle_pkg( | |
| name, | |
| deps, | |
| include_samples = False, | |
| assembly_name = None, | |
| transport = None, | |
| **kwargs): | |
| package_dir = name | |
| if assembly_name: | |
| package_dir = "google-cloud-%s-%s" % (assembly_name, name) | |
| # Rename to avoid target conflicts with the monolith. | |
| proto_target = "proto-%s" % package_dir | |
| proto_target_dep = [] | |
| grpc_target = "grpc-%s" % package_dir | |
| grpc_target_dep = [] | |
| client_target = "gapic-%s" % package_dir | |
| client_target_dep = [] | |
| client_deps = [] | |
| client_test_deps = [] | |
| grpc_deps = [] | |
| proto_deps = [] | |
| samples = [] | |
| processed_deps = {} #there is no proper Set in Starlark | |
| for dep in deps: | |
| # Use contains instead of endswith since microgenerator testing may use differently-named targets. | |
| if "_java_gapic" in dep: | |
| if include_samples: | |
| samples.append(dep + "_samples") | |
| _put_dep_in_a_bucket(dep, client_deps, processed_deps) | |
| _put_dep_in_a_bucket("%s_test" % dep, client_test_deps, processed_deps) | |
| _put_dep_in_a_bucket("%s_resource_name" % dep, proto_deps, processed_deps) | |
| elif dep.endswith("_java_grpc"): | |
| _put_dep_in_a_bucket(dep, grpc_deps, processed_deps) | |
| else: | |
| _put_dep_in_a_bucket(dep, proto_deps, processed_deps) | |
| if proto_deps: | |
| _java_gapic_gradle_pkg( | |
| name = proto_target, | |
| template_label = Label("//rules_java_gapic:resources/gradle/proto.gradle.tmpl"), | |
| deps = proto_deps, | |
| **kwargs | |
| ) | |
| proto_target_dep = [":%s" % proto_target] | |
| if grpc_deps: | |
| _java_gapic_gradle_pkg( | |
| name = grpc_target, | |
| template_label = Label("//rules_java_gapic:resources/gradle/grpc.gradle.tmpl"), | |
| deps = proto_target_dep + grpc_deps, | |
| **kwargs | |
| ) | |
| grpc_target_dep = ["%s" % grpc_target] | |
| if client_deps: | |
| if not transport or transport == "grpc": | |
| template_label = Label("//rules_java_gapic:resources/gradle/client_grpc.gradle.tmpl") | |
| elif transport == "rest": | |
| template_label = Label("//rules_java_gapic:resources/gradle/client_rest.gradle.tmpl") | |
| elif transport == "grpc+rest": | |
| template_label = Label("//rules_java_gapic:resources/gradle/client_grpcrest.gradle.tmpl") | |
| _java_gapic_gradle_pkg( | |
| name = client_target, | |
| template_label = template_label, | |
| deps = proto_target_dep + client_deps, | |
| test_deps = grpc_target_dep + client_test_deps, | |
| **kwargs | |
| ) | |
| client_target_dep = ["%s" % client_target] | |
| _java_gapic_assembly_gradle_pkg( | |
| name = name, | |
| assembly_name = package_dir, | |
| deps = proto_target_dep + grpc_target_dep + client_target_dep, | |
| samples = samples, | |
| ) | |
| def _java_gapic_gradle_pkg( | |
| name, | |
| template_label, | |
| deps, | |
| test_deps = None, | |
| project_deps = None, | |
| test_project_deps = None, | |
| **kwargs): | |
| resource_target_name = "%s-resources" % name | |
| static_substitutions = dict(_PROPERTIES) | |
| static_substitutions["{{name}}"] = name | |
| java_gapic_build_configs_pkg( | |
| name = resource_target_name, | |
| deps = deps, | |
| test_deps = test_deps, | |
| package_dir = name, | |
| templates = { | |
| template_label: "build.gradle", | |
| }, | |
| static_substitutions = static_substitutions, | |
| ) | |
| srcs_gapic_pkg_target_name = "%s-srcs_pkg" % name | |
| java_gapic_srcs_pkg( | |
| name = srcs_gapic_pkg_target_name, | |
| deps = deps, | |
| test_deps = test_deps, | |
| package_dir = name, | |
| **kwargs | |
| ) | |
| gapic_pkg_tar( | |
| name = name, | |
| extension = "tar.gz", | |
| deps = [ | |
| resource_target_name, | |
| srcs_gapic_pkg_target_name, | |
| ], | |
| **kwargs | |
| ) | |
| def _java_gapic_assembly_gradle_pkg(name, assembly_name, deps, samples = None, visibility = None): | |
| resource_target_name = "%s-resources" % assembly_name | |
| java_gapic_build_configs_pkg( | |
| name = resource_target_name, | |
| deps = deps, | |
| templates = { | |
| Label("//rules_java_gapic:resources/gradle/assembly.gradle.tmpl"): "build.gradle", | |
| Label("//rules_java_gapic:resources/gradle/settings.gradle.tmpl"): "settings.gradle", | |
| }, | |
| ) | |
| gapic_pkg_tar( | |
| name = name, | |
| extension = "tar.gz", | |
| deps = [ | |
| Label("//rules_java_gapic:gradlew"), | |
| resource_target_name, | |
| ] + deps, | |
| samples = samples, | |
| package_dir = assembly_name, | |
| visibility = visibility, | |
| ) |