diff --git a/examples/shared_library_mvp/BUILD b/examples/shared_library_mvp/BUILD new file mode 100644 index 0000000..8184192 --- /dev/null +++ b/examples/shared_library_mvp/BUILD @@ -0,0 +1,80 @@ +load("//:cc_shared_library.bzl", "cc_bin", "cc_shared_library") + +sh_binary( + name = "symbol_grabber", + srcs = ["symbol_grabber.sh"], +) + +cc_bin( + name = "main", + srcs = ["main.cc"], + dynamic_deps = [":libA_shared.so"], +) + +# FIX: add cc_binary to test +cc_shared_library( + name = "libA_shared.so", + of = "A", + dynamic_deps = [":libB_shared.so"], +) + +cc_library( + name = "A", + srcs = ["a.cc"], + hdrs = ["a.h"], + deps = [ + "C", + "D", + ], +) + +cc_shared_library( + name = "libB_shared.so", + of = "B", + #dynamic_deps = [":libE_shared.so"], +) + +cc_library( + name = "B", + srcs = ["b.cc"], + hdrs = ["b.h"], + deps = [ + "E", + "F", + ], +) + +cc_library( + name = "C", + srcs = ["c.cc"], + hdrs = ["c.h"], + deps = [ + "E", + ], +) + +cc_library( + name = "D", + srcs = ["d.cc"], + hdrs = ["d.h"], + deps = [ + "B", + ], +) + +cc_shared_library( + name = "libE_shared.so", + of = "E", +) + +cc_library( + name = "E", + srcs = ["e.cc"], + hdrs = ["e.h"], +) + +cc_library( + name = "F", + srcs = ["f.cc"], + hdrs = ["f.h"], +) diff --git a/examples/shared_library_mvp/WORKSPACE b/examples/shared_library_mvp/WORKSPACE new file mode 100644 index 0000000..e69de29 diff --git a/examples/shared_library_mvp/a.cc b/examples/shared_library_mvp/a.cc new file mode 100644 index 0000000..6e2160f --- /dev/null +++ b/examples/shared_library_mvp/a.cc @@ -0,0 +1,8 @@ +#include "a.h" + +#include + +#include "c.h" +#include "d.h" + +std::string a() { return "-a" + c() + d(); } diff --git a/examples/shared_library_mvp/a.h b/examples/shared_library_mvp/a.h new file mode 100644 index 0000000..08d05d7 --- /dev/null +++ b/examples/shared_library_mvp/a.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_A +#define LIBRARY_A + +#include + +std::string a(); + +#endif diff --git a/examples/shared_library_mvp/b.cc b/examples/shared_library_mvp/b.cc new file mode 100644 index 0000000..502e9d8 --- /dev/null +++ b/examples/shared_library_mvp/b.cc @@ -0,0 +1,8 @@ +#include "b.h" + +#include + +#include "e.h" +#include "f.h" + +std::string b() { return "-b" + e() + f(); } diff --git a/examples/shared_library_mvp/b.h b/examples/shared_library_mvp/b.h new file mode 100644 index 0000000..1bb85c2 --- /dev/null +++ b/examples/shared_library_mvp/b.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_B +#define LIBRARY_B + +#include + +std::string b(); + +#endif diff --git a/examples/shared_library_mvp/bazel-bin b/examples/shared_library_mvp/bazel-bin new file mode 120000 index 0000000..ff30af0 --- /dev/null +++ b/examples/shared_library_mvp/bazel-bin @@ -0,0 +1 @@ +/usr/local/google/home/plf/.cache/bazel/_bazel_plf/e28f6f4251177f0cba97bce09eefc316/execroot/__main__/bazel-out/k8-fastbuild/bin \ No newline at end of file diff --git a/examples/shared_library_mvp/bazel-genfiles b/examples/shared_library_mvp/bazel-genfiles new file mode 120000 index 0000000..ff30af0 --- /dev/null +++ b/examples/shared_library_mvp/bazel-genfiles @@ -0,0 +1 @@ +/usr/local/google/home/plf/.cache/bazel/_bazel_plf/e28f6f4251177f0cba97bce09eefc316/execroot/__main__/bazel-out/k8-fastbuild/bin \ No newline at end of file diff --git a/examples/shared_library_mvp/bazel-out b/examples/shared_library_mvp/bazel-out new file mode 120000 index 0000000..e7ac4ce --- /dev/null +++ b/examples/shared_library_mvp/bazel-out @@ -0,0 +1 @@ +/usr/local/google/home/plf/.cache/bazel/_bazel_plf/e28f6f4251177f0cba97bce09eefc316/execroot/__main__/bazel-out \ No newline at end of file diff --git a/examples/shared_library_mvp/bazel-shared_library_mvp b/examples/shared_library_mvp/bazel-shared_library_mvp new file mode 120000 index 0000000..2349785 --- /dev/null +++ b/examples/shared_library_mvp/bazel-shared_library_mvp @@ -0,0 +1 @@ +/usr/local/google/home/plf/.cache/bazel/_bazel_plf/e28f6f4251177f0cba97bce09eefc316/execroot/__main__ \ No newline at end of file diff --git a/examples/shared_library_mvp/bazel-testlogs b/examples/shared_library_mvp/bazel-testlogs new file mode 120000 index 0000000..918d68b --- /dev/null +++ b/examples/shared_library_mvp/bazel-testlogs @@ -0,0 +1 @@ +/usr/local/google/home/plf/.cache/bazel/_bazel_plf/e28f6f4251177f0cba97bce09eefc316/execroot/__main__/bazel-out/k8-fastbuild/testlogs \ No newline at end of file diff --git a/examples/shared_library_mvp/c.cc b/examples/shared_library_mvp/c.cc new file mode 100644 index 0000000..2a6b6bb --- /dev/null +++ b/examples/shared_library_mvp/c.cc @@ -0,0 +1,7 @@ +#include "c.h" + +#include + +#include "e.h" + +std::string c() { return "-c" + e(); } diff --git a/examples/shared_library_mvp/c.h b/examples/shared_library_mvp/c.h new file mode 100644 index 0000000..09be8da --- /dev/null +++ b/examples/shared_library_mvp/c.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_C +#define LIBRARY_C + +#include + +std::string c(); + +#endif diff --git a/examples/shared_library_mvp/cc_shared_library.bzl b/examples/shared_library_mvp/cc_shared_library.bzl new file mode 100644 index 0000000..b095ec9 --- /dev/null +++ b/examples/shared_library_mvp/cc_shared_library.bzl @@ -0,0 +1,270 @@ +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") + +CcSharedLibraryInfo = provider() + +def _get_all_labels(direct_children): + node = None + all_children = list(direct_children) + all_labels = {} + for i in range(1, 2147483647): + if len(all_children) == 0: + break + node = all_children.pop(0) + + all_labels[node.label] = True + + all_children.extend(node.children) + return all_labels + +def _get_dynamic_libraries(ctx, all_labels): + direct_cc_shared_library_infos = [] + for direct_dynamic_dep in ctx.attr.dynamic_deps: + cc_shared_library_info = direct_dynamic_dep[CcSharedLibraryInfo] + direct_cc_shared_library_infos.append(cc_shared_library_info) + + dynamic_labels = {} + cc_shared_library_infos = list(direct_cc_shared_library_infos) + dynamic_libraries = [] + for i in range(1, 2147483647): + if len(cc_shared_library_infos) == 0: + break + cc_shared_library_info = cc_shared_library_infos.pop(0) + dynamic_labels[cc_shared_library_info.of] = True + dynamic_libraries.append(cc_shared_library_info.transitive_library) + for direct_cc_shared_library_info in cc_shared_library_info.direct_cc_shared_library_infos: + if direct_cc_shared_library_info.of in all_labels: + cc_shared_library_infos.append(direct_cc_shared_library_info) + + return (direct_cc_shared_library_infos, dynamic_labels, dynamic_libraries) + +def _get_whitelisted_labels(direct_children, dynamic_labels): + whitelisted_labels = {} + whitelisted_children = list(direct_children) + for i in range(1, 2147483647): + if len(whitelisted_children) == 0: + break + node = whitelisted_children.pop(0) + + whitelisted_labels[node.label] = True + + for child in node.children: + if child.label not in dynamic_labels: + whitelisted_children.append(child) + + return whitelisted_labels + +def _get_new_linking_context(cc_info, dynamic_libraries, whitelisted_labels): + libraries_to_link = [] + for library_to_link in cc_info.linking_context.libraries_to_link.to_list(): + if library_to_link.label in whitelisted_labels: + libraries_to_link.append(library_to_link) + + libraries_to_link.extend(dynamic_libraries) + + return cc_common.create_linking_context( + libraries_to_link = libraries_to_link, + user_link_flags = cc_info.linking_context.user_link_flags, + additional_inputs = cc_info.linking_context.additional_inputs.to_list(), + ) + +def _get_version_script(ctx, new_linking_context, export_labels): + libraries_to_link = new_linking_context.libraries_to_link + objects = [] + exports = {} + for export_label in export_labels: + exports[export_label.label] = True + + for library_to_link in libraries_to_link.to_list(): + if library_to_link.label in exports: + if library_to_link.objects != None: + objects.extend(library_to_link.objects) + elif library_to_link.pic_objects != None: + objects.extend(library_to_link.pic_objects) + + symbols_file = ctx.actions.declare_file(ctx.label.name + "_symbols.txt") + arguments = [symbols_file.path] + for object_file in objects: + arguments.append(object_file.path) + + ctx.actions.run( + outputs = [symbols_file], + inputs = objects, + executable = ctx.executable._symbol_grabber, + arguments = arguments, + ) + + return symbols_file + +GraphNodeInfo = provider() + +def _graph_structure_aspect_impl(target, ctx): + children = [] + + # TODO: We should actually check every attribute, not just deps or of, with the + # exception of those which don't have CcInfo. + if hasattr(ctx.rule.attr, "deps"): + for dep in ctx.rule.attr.deps: + if GraphNodeInfo in dep: + children.append(dep[GraphNodeInfo]) + if hasattr(ctx.rule.attr, "of"): + children.append(ctx.rule.attr.of[GraphNodeInfo]) + + return [GraphNodeInfo(label = ctx.label, children = children)] + +graph_structure_aspect = aspect( + attr_aspects = ["*"], + implementation = _graph_structure_aspect_impl, +) + +def _cc_shared_library_impl(ctx): + of_graph_node_info = ctx.attr.of[GraphNodeInfo] + all_labels = _get_all_labels([of_graph_node_info]) + + (direct_cc_shared_library_infos, dynamic_labels, dynamic_libraries) = _get_dynamic_libraries(ctx, all_labels) + + whitelisted_labels = _get_whitelisted_labels([of_graph_node_info], dynamic_labels) + + new_linking_context = _get_new_linking_context(ctx.attr.of[CcInfo], dynamic_libraries, whitelisted_labels) + + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + symbols = _get_version_script(ctx, new_linking_context, [ctx.attr.of]) + + linking_outputs = cc_common.link( + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + linking_contexts = [new_linking_context], + user_link_flags = ["-Wl,--version-script=" + symbols.path], + additional_inputs = [symbols], + name = ctx.label.name, + output_type = "dynamic_library", + ) + transitive_library = linking_outputs.library_to_link + + return [ + DefaultInfo(files = depset([transitive_library.resolved_symlink_dynamic_library, symbols])), + CcSharedLibraryInfo( + of = ctx.attr.of[GraphNodeInfo].label, + compilation_context = ctx.attr.of[CcInfo].compilation_context, + transitive_library = transitive_library, + direct_cc_shared_library_infos = direct_cc_shared_library_infos, + ), + ] + +cc_shared_library = rule( + implementation = _cc_shared_library_impl, + attrs = { + "of": attr.label(aspects = [graph_structure_aspect]), + "dynamic_deps": attr.label_list(providers = [CcSharedLibraryInfo]), + "preloaded_deps": attr.label_list(providers = [CcInfo]), + "export_all": attr.bool(), + "export_wrapped_library": attr.bool(), + "additional_symbols_to_export": attr.label(allow_single_file = True), + "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), + "_symbol_grabber": attr.label(default = ":symbol_grabber", executable = True, cfg = "host"), + }, + fragments = ["google_cpp", "cpp"], +) + +def _cc_bin_impl(ctx): + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + + direct_children = [] + cc_infos = [] + compilation_contexts = [] + for dep in ctx.attr.deps: + if GraphNodeInfo in dep: + direct_children.append(dep[GraphNodeInfo]) + if CcInfo in dep: + compilation_contexts.append(dep[CcInfo].compilation_context) + cc_infos.append(dep[CcInfo]) + + for dep in ctx.attr.dynamic_deps: + if GraphNodeInfo in dep: + direct_children.append(dep[GraphNodeInfo]) + if CcInfo in dep: + compilation_contexts.append(dep[CcInfo].compilation_context) + cc_infos.append(dep[CcInfo]) + + all_labels = _get_all_labels(direct_children) + + (direct_cc_shared_library_infos, dynamic_labels, dynamic_libraries) = _get_dynamic_libraries(ctx, all_labels) + + for direct_cc_shared_library_info in direct_cc_shared_library_infos: + compilation_contexts.append(direct_cc_shared_library_info.compilation_context) + + merged_cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos) + + (_compilation_context, compilation_outputs) = cc_common.compile( + name = ctx.label.name, + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + srcs = ctx.files.srcs, + compilation_contexts = compilation_contexts, + ) + + whitelisted_children = [] + for child in direct_children: + if child.label in dynamic_labels: + fail("From cc_binary, do not depend on the same library statically and dynamically") + whitelisted_children.append(child) + + whitelisted_labels = _get_whitelisted_labels(whitelisted_children, dynamic_labels) + + new_linking_context = _get_new_linking_context(merged_cc_info, dynamic_libraries, whitelisted_labels) + + linking_outputs = cc_common.link( + name = ctx.label.name, + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + compilation_outputs = compilation_outputs, + linking_contexts = [new_linking_context], + link_deps_statically = ctx.attr.linkstatic, + ) + files = [linking_outputs.executable] + runfiles = [] + for library in dynamic_libraries: + runfiles.append(library.dynamic_library) + + return [ + DefaultInfo( + files = depset(files), + runfiles = ctx.runfiles(files = runfiles), + ), + ] + +cc_bin = rule( + implementation = _cc_bin_impl, + attrs = { + "srcs": attr.label_list(allow_files = [".cc"]), + "deps": attr.label_list( + allow_empty = True, + providers = [CcInfo], + ), + "data": attr.label_list( + default = [], + allow_files = True, + ), + "dynamic_deps": attr.label_list( + providers = [CcSharedLibraryInfo], + aspects = [graph_structure_aspect], + ), + "linkstatic": attr.bool(default = True), + "_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"), + }, + fragments = ["google_cpp", "cpp"], +) diff --git a/examples/shared_library_mvp/d.cc b/examples/shared_library_mvp/d.cc new file mode 100644 index 0000000..8771db8 --- /dev/null +++ b/examples/shared_library_mvp/d.cc @@ -0,0 +1,7 @@ +#include "d.h" + +#include + +#include "b.h" + +std::string d() { return "-d" + b(); } diff --git a/examples/shared_library_mvp/d.h b/examples/shared_library_mvp/d.h new file mode 100644 index 0000000..21b9c0b --- /dev/null +++ b/examples/shared_library_mvp/d.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_D +#define LIBRARY_D + +#include + +std::string d(); + +#endif diff --git a/examples/shared_library_mvp/e.cc b/examples/shared_library_mvp/e.cc new file mode 100644 index 0000000..53c6115 --- /dev/null +++ b/examples/shared_library_mvp/e.cc @@ -0,0 +1,5 @@ +#include "e.h" + +#include + +std::string e() { return "-e"; } diff --git a/examples/shared_library_mvp/e.h b/examples/shared_library_mvp/e.h new file mode 100644 index 0000000..b690eaa --- /dev/null +++ b/examples/shared_library_mvp/e.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_E +#define LIBRARY_E + +#include + +std::string e(); + +#endif diff --git a/examples/shared_library_mvp/f.cc b/examples/shared_library_mvp/f.cc new file mode 100644 index 0000000..403edce --- /dev/null +++ b/examples/shared_library_mvp/f.cc @@ -0,0 +1,5 @@ +#include "f.h" + +#include + +std::string f() { return "-f"; } diff --git a/examples/shared_library_mvp/f.h b/examples/shared_library_mvp/f.h new file mode 100644 index 0000000..c5ab064 --- /dev/null +++ b/examples/shared_library_mvp/f.h @@ -0,0 +1,8 @@ +#ifndef LIBRARY_F +#define LIBRARY_F + +#include + +std::string f(); + +#endif diff --git a/examples/shared_library_mvp/main.cc b/examples/shared_library_mvp/main.cc new file mode 100644 index 0000000..c98cdaa --- /dev/null +++ b/examples/shared_library_mvp/main.cc @@ -0,0 +1,8 @@ +#include + +#include "a.h" + +int main() { + std::cout << "Hello " + a() << std::endl; + return 0; +} diff --git a/examples/shared_library_mvp/symbol_grabber.sh b/examples/shared_library_mvp/symbol_grabber.sh new file mode 100755 index 0000000..bd4ceea --- /dev/null +++ b/examples/shared_library_mvp/symbol_grabber.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +output_file=$1 +touch $output_file.symbols +shift +for object_file in "$@" ; do + nm $object_file | grep " T " | rev | cut -f1 -d" " | rev >> $output_file.symbols +done + +sort -u $output_file.symbols > "$output_file".symbols.tmp +sed 's/$/\;/' $output_file.symbols.tmp > "$output_file".symbols +rm "$output_file".symbols.tmp + +echo "VERS_1.1 {" > "$output_file" +echo "global:" >> "$output_file" +cat "$output_file".symbols >> "$output_file" +echo "local:" >> "$output_file" +echo "*;" >> "$output_file" +echo "};" >> "$output_file" +