bp2build: check in cc_library rules and unit test.

This change adds the Starlark code used by the bp2build-generated
targets for cc_library related types.

It also adds a sh_test unit test that analyzes the runfiles outputs of a
full cc_library generated by bp2build, starting with ld-android. It
analyzes that the shared and static libraries are of the right
filetypes, and that the shared library exports an expected set of
symbols.

This CL checks in cc_shared_library from rules_cc HEAD, without any
local changes for Roboleaf.

Test: TH
Test: bp2build, bazel test //build/bazel/tests/...

Change-Id: I4deb3fc3160e393b03b4bd550777146d6cad6de8
diff --git a/rules/cc_include_helpers.bzl b/rules/cc_include_helpers.bzl
index 07e1524..5d4f051 100644
--- a/rules/cc_include_helpers.bzl
+++ b/rules/cc_include_helpers.bzl
@@ -32,16 +32,3 @@
             )
 
     return include_deps
-
-def hdr_globs_for_srcs(srcs):
-    """Return globs for headers in the directories matching the given srcs.
-
-    This simulates hdrs_check = 'loose' by allowing src files to reference headers
-    directly in the directories they are in."""
-    globs = {}
-    for src in srcs:
-        dir_name = src.split("/")[:-1]
-        dir_name += ["*.h"]
-        dir_glob = "/".join(dir_name)
-        globs[dir_glob] = True
-    return native.glob(globs.keys())
diff --git a/rules/cc_library_static.bzl b/rules/cc_library_static.bzl
index 8ddd72a..8999863 100644
--- a/rules/cc_library_static.bzl
+++ b/rules/cc_library_static.bzl
@@ -1,10 +1,11 @@
-load("//build/bazel/rules:cc_include_helpers.bzl", "cc_library_header_suite", "hdr_globs_for_srcs")
+load("//build/bazel/rules:cc_include_helpers.bzl", "cc_library_header_suite")
 load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain")
 
 def cc_library_static(
         name,
         srcs = [],
         deps = [],
+        hdrs = [],
         copts = [],
         includes = [],
         local_include_dirs = [],
@@ -16,8 +17,6 @@
     # combine deps and include deps
     all_deps = deps + include_deps
 
-    hdrs = hdr_globs_for_srcs(srcs)
-
     mainlib_name = "%s_mainlib" % name
 
     # Silently drop these attributes for now:
diff --git a/rules/cc_object.bzl b/rules/cc_object.bzl
index 6b7c656..cf60645 100644
--- a/rules/cc_object.bzl
+++ b/rules/cc_object.bzl
@@ -1,4 +1,4 @@
-load("//build/bazel/rules:cc_include_helpers.bzl", "cc_library_header_suite", "hdr_globs_for_srcs")
+load("//build/bazel/rules:cc_include_helpers.bzl", "cc_library_header_suite")
 load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain")
 
 # "cc_object" module copts, taken from build/soong/cc/object.go
@@ -105,6 +105,7 @@
 def cc_object(
         name,
         copts = [],
+        hdrs = [],
         asflags = [],
         local_include_dirs = [],
         srcs = [],
@@ -115,8 +116,6 @@
 
     include_deps = cc_library_header_suite(local_include_dirs)
 
-    hdrs = hdr_globs_for_srcs(srcs)
-
     _cc_object(
         name = name,
         hdrs = hdrs,
diff --git a/rules/full_cc_library.bzl b/rules/full_cc_library.bzl
new file mode 100644
index 0000000..42cefba
--- /dev/null
+++ b/rules/full_cc_library.bzl
@@ -0,0 +1,63 @@
+load(":cc_library_static.bzl", "cc_library_static")
+load("@rules_cc//examples:experimental_cc_shared_library.bzl", "cc_shared_library", "CcSharedLibraryInfo")
+
+def cc_library(
+        name,
+        srcs = [],
+        hdrs = [],
+        deps = [],
+        transitive_export_deps = [],
+        user_link_flags = [],
+        copts = [],
+        linkopts = [],
+        **kwargs):
+    static_name = name + "_static"
+    shared_name = name + "_shared"
+    _cc_library_proxy(name = name,
+                      static = static_name,
+                      shared = shared_name)
+
+    cc_library_static(
+        name = static_name,
+        hdrs = hdrs,
+        srcs = srcs,
+        copts = copts,
+        linkopts = linkopts,
+        deps = deps,
+    )
+
+    cc_shared_library(
+        name = shared_name,
+        user_link_flags = user_link_flags,
+        # b/184806113: Note this is a pretty a workaround so users don't have to
+        # declare all transitive static deps used by this target.  It'd be great
+        # if a shared library could declare a transitive exported static dep
+        # instead of needing to declare each target transitively.
+        static_deps = ["//:__subpackages__"],
+        roots = [static_name + "_mainlib"],
+    )
+
+
+def _cc_library_proxy_impl(ctx):
+    static_files = ctx.attr.static[DefaultInfo].files.to_list()
+    shared_files = ctx.attr.shared[DefaultInfo].files.to_list()
+
+    files = static_files + shared_files
+
+    return [
+        ctx.attr.shared[CcSharedLibraryInfo],
+        ctx.attr.static[CcInfo],
+        DefaultInfo(
+            files = depset(direct = files),
+            runfiles = ctx.runfiles(files = files),
+        )
+    ]
+
+_cc_library_proxy = rule(
+    implementation = _cc_library_proxy_impl,
+    attrs = {
+        "shared": attr.label(mandatory = True, providers = [CcSharedLibraryInfo]),
+        "static": attr.label(mandatory = True, providers = [CcInfo]),
+    },
+)
+
diff --git a/rules_cc/cc/BUILD b/rules_cc/cc/BUILD
index beb0baa..9a4b067 100644
--- a/rules_cc/cc/BUILD
+++ b/rules_cc/cc/BUILD
@@ -1,2 +1,6 @@
 # Divergence from rules_cc: Use a stub BUILD file, as there are reduced
 # dependencies in this fork.
+alias(
+    name = "toolchain_type",
+    actual = "@bazel_tools//tools/cpp:toolchain_type",
+)
diff --git a/rules_cc/examples/BUILD b/rules_cc/examples/BUILD
index c770074..c7da75d 100644
--- a/rules_cc/examples/BUILD
+++ b/rules_cc/examples/BUILD
@@ -25,6 +25,12 @@
 )
 
 bool_flag(
+    name = "enable_permissions_check",
+    build_setting_default = False,
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
     name = "experimental_debug",
     build_setting_default = False,
     visibility = ["//visibility:public"],
diff --git a/rules_cc/examples/experimental_cc_shared_library.bzl b/rules_cc/examples/experimental_cc_shared_library.bzl
index 44d8c01..905bd27 100644
--- a/rules_cc/examples/experimental_cc_shared_library.bzl
+++ b/rules_cc/examples/experimental_cc_shared_library.bzl
@@ -109,7 +109,7 @@
             if export in exports_map:
                 fail("Two shared libraries in dependencies export the same symbols. Both " +
                      exports_map[export].libraries[0].dynamic_library.short_path +
-                     " and " + linker_input.dynamic_library.short_path +
+                     " and " + linker_input.libraries[0].dynamic_library.short_path +
                      " export " + export)
             exports_map[export] = linker_input
     return exports_map
@@ -162,9 +162,20 @@
     return pattern.package == value.package and pattern.name == value.name
 
 def _check_if_target_can_be_exported(target, current_label, permissions):
-    # Divergence from rules_cc: Ignore permissions, as it does not fit the model of AOSP.
-    # TODO(cparsons): Push an upstream change to disable permissions checking with a flag.
-    return True
+    if permissions == None:
+        return True
+
+    if (target.workspace_name != current_label.workspace_name or
+        _same_package_or_above(current_label, target)):
+        return True
+
+    matched_by_target = False
+    for permission in permissions:
+        for permission_target in permission[CcSharedLibraryPermissionsInfo].targets:
+            if _check_if_target_under_path(target, permission_target):
+                return True
+
+    return False
 
 def _check_if_target_should_be_exported_without_filter(target, current_label, permissions):
     return _check_if_target_should_be_exported_with_filter(target, current_label, None, permissions)
@@ -263,7 +274,7 @@
                     linker_input.owner,
                     ctx.label,
                     ctx.attr.exports_filter,
-                    ctx.attr.permissions,
+                    _get_permissions(ctx),
                 ):
                     exports[owner] = True
                     can_be_linked_statically = True
@@ -293,6 +304,11 @@
 
     return True
 
+def _get_permissions(ctx):
+    if ctx.attr._enable_permissions_check[BuildSettingInfo].value:
+        return ctx.attr.permissions
+    return None
+
 def _cc_shared_library_impl(ctx):
     cc_common.check_experimental_cc_shared_library()
     cc_toolchain = find_cc_toolchain(ctx)
@@ -310,7 +326,7 @@
             fail("Trying to export a library already exported by a different shared library: " +
                  str(export.label))
 
-        _check_if_target_should_be_exported_without_filter(export.label, ctx.label, ctx.attr.permissions)
+        _check_if_target_should_be_exported_without_filter(export.label, ctx.label, _get_permissions(ctx))
 
     preloaded_deps_direct_labels = {}
     preloaded_dep_merged_cc_info = None
@@ -449,11 +465,13 @@
         "static_deps": attr.string_list(),
         "user_link_flags": attr.string_list(),
         "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
+        "_enable_permissions_check": attr.label(default = "//examples:enable_permissions_check"),
         "_experimental_debug": attr.label(default = "//examples:experimental_debug"),
         "_incompatible_link_once": attr.label(default = "//examples:incompatible_link_once"),
     },
-    toolchains = ["@rules_cc//cc:toolchain_type"],  # copybara-use-repo-external-label
+    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],  # copybara-use-repo-external-label
     fragments = ["cpp"],
+    incompatible_use_toolchain_transition = True,
 )
 
 for_testing_dont_use_check_if_target_under_path = _check_if_target_under_path
diff --git a/tests/bionic/BUILD b/tests/bionic/BUILD
new file mode 100644
index 0000000..b2a04fd
--- /dev/null
+++ b/tests/bionic/BUILD
@@ -0,0 +1,10 @@
+# This test requires bp2build to run and the generated BUILD files in the source tree.
+sh_test(
+    name = "verify_bionic_outputs",
+    srcs = ["verify_bionic_outputs.sh"],
+    data = [
+        "//bionic/linker:ld-android",
+        "//bionic/libdl:libdl_android",
+    ],
+    deps = ["@bazel_tools//tools/bash/runfiles"],
+)
diff --git a/tests/bionic/verify_bionic_outputs.sh b/tests/bionic/verify_bionic_outputs.sh
new file mode 100755
index 0000000..df22b47
--- /dev/null
+++ b/tests/bionic/verify_bionic_outputs.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/bash
+#
+# Copyright 2021 Google Inc. 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 -euo pipefail
+
+source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+
+# This should be abstracted to a unit-test library when it has more uses.
+function assert_contains_regex() {
+    local needle="$1"
+    local haystack="$2"
+    local message="${3:-Expected regexp "$needle" not found in "$haystack"}"
+    echo "${haystack}" | grep "${needle}" && return 0
+
+    echo "$message"
+    exit 1
+}
+
+# Test that a library is the expected filetype.
+function test_filetype() {
+    local filepath="$(readlink -f $1)"; shift
+    local regex="$1"; shift
+    local file_output="$(file ${filepath})"
+    assert_contains_regex "${regex}" "${file_output}"
+}
+
+# Test that the shared library contains a symbol
+function test_shared_library_symbols() {
+    local filepath="$(readlink -f $1)"; shift
+    local symbols="$1"; shift
+    local nm_output="$(nm -D "${filepath}")"
+    for symbol in "${symbols[@]}"
+    do
+        assert_contains_regex "${symbol}" "${nm_output}"
+    done
+}
+
+# Test file contents of //bionic/linker:ld-android
+function test_ld-android() {
+    local shared_library="$(rlocation __main__/bionic/linker/libld-android_shared.so)"
+    local static_library="$(rlocation __main__/bionic/linker/libld-android_static_mainlib.a)"
+
+    test_filetype "${shared_library}" "shared object.*dynamically linked"
+    test_filetype "${static_library}" "current ar archive"
+
+    symbols=(
+        __internal_linker_error
+        __loader_add_thread_local_dtor
+        __loader_android_create_namespace
+        __loader_android_dlopen_ext
+        __loader_android_dlwarning
+        __loader_android_get_application_target_sdk_version
+        __loader_android_get_exported_namespace
+        __loader_android_get_LD_LIBRARY_PATH
+        __loader_android_init_anonymous_namespace
+        __loader_android_link_namespaces
+        __loader_android_link_namespaces_all_libs
+        __loader_android_set_application_target_sdk_version
+        __loader_android_update_LD_LIBRARY_PATH
+        __loader_cfi_fail
+        __loader_dladdr
+        __loader_dlclose
+        __loader_dlerror
+        __loader_dl_iterate_phdr
+        __loader_dlopen
+        __loader_dlsym
+        __loader_dlvsym
+        __loader_remove_thread_local_dtor
+        __loader_shared_globals
+        _db_dlactivity
+    )
+
+    test_shared_library_symbols "${shared_library}" "${symbols}"
+}
+
+function test_libdl_android() {
+    local shared_library="$(rlocation __main__/bionic/libdl/liblibdl_android_shared.so)"
+    local static_library="$(rlocation __main__/bionic/libdl/liblibdl_android_static_mainlib.a)"
+
+    test_filetype "${shared_library}" "shared object.*dynamically linked"
+    test_filetype "${static_library}" "current ar archive"
+
+    symbols=(
+        android_create_namespace
+        android_dlwarning
+        android_get_exported_namespace
+        android_get_LD_LIBRARY_PATH
+        android_init_anonymous_namespace
+        android_link_namespaces
+        android_set_application_target_sdk_version
+        android_update_LD_LIBRARY_PATH
+        __loader_android_create_namespace
+        __loader_android_dlwarning
+        __loader_android_get_exported_namespace
+        __loader_android_get_LD_LIBRARY_PATH
+        __loader_android_init_anonymous_namespace
+        __loader_android_link_namespaces
+        __loader_android_set_application_target_sdk_version
+        __loader_android_update_LD_LIBRARY_PATH
+    )
+
+    test_shared_library_symbols "${shared_library}" "${symbols}"
+}
+
+test_ld-android
+test_libdl_android