diff --git a/.bazelrc b/.bazelrc index 21d0db6bb1a6c..225246fbea40f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -46,6 +46,10 @@ build:sanitizer --test_tag_filters=-no_san build:clang --action_env=BAZEL_COMPILER=clang build:clang --linkopt=-fuse-ld=lld +# Flags for Clang + PCH +build:clang-pch --spawn_strategy=local +build:clang-pch --define=ENVOY_CLANG_PCH=1 + # Basic ASAN/UBSAN that works for gcc build:asan --action_env=ENVOY_ASAN=1 build:asan --config=sanitizer diff --git a/bazel/BUILD b/bazel/BUILD index 0dc6f348425ac..3d65037c27928 100644 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -169,6 +169,11 @@ config_setting( }, ) +config_setting( + name = "clang_pch_build", + values = {"define": "ENVOY_CLANG_PCH=1"}, +) + config_setting( name = "gcc_build_gcc", flag_values = { diff --git a/bazel/envoy_build_system.bzl b/bazel/envoy_build_system.bzl index c312d1f6f2e5f..a0463ab770d04 100644 --- a/bazel/envoy_build_system.bzl +++ b/bazel/envoy_build_system.bzl @@ -14,6 +14,7 @@ load( _envoy_cc_win32_library = "envoy_cc_win32_library", _envoy_proto_library = "envoy_proto_library", ) +load(":envoy_pch.bzl", _envoy_pch_library = "envoy_pch_library") load( ":envoy_select.bzl", _envoy_select_boringssl = "envoy_select_boringssl", @@ -225,6 +226,7 @@ envoy_cc_posix_library = _envoy_cc_posix_library envoy_cc_posix_without_linux_library = _envoy_cc_posix_without_linux_library envoy_cc_win32_library = _envoy_cc_win32_library envoy_proto_library = _envoy_proto_library +envoy_pch_library = _envoy_pch_library # Test wrappers (from envoy_test.bzl) envoy_cc_fuzz_test = _envoy_cc_fuzz_test diff --git a/bazel/envoy_library.bzl b/bazel/envoy_library.bzl index a6e77598f912d..9b04b7ebe96c9 100644 --- a/bazel/envoy_library.bzl +++ b/bazel/envoy_library.bzl @@ -8,6 +8,7 @@ load( "envoy_external_dep_path", "envoy_linkstatic", ) +load(":envoy_pch.bzl", "envoy_pch_copts") load("@envoy_api//bazel:api_build_system.bzl", "api_cc_py_proto_library") load( "@envoy_build_config//:extensions_build_config.bzl", @@ -92,17 +93,17 @@ def envoy_cc_library( name = name, srcs = srcs, hdrs = hdrs, - copts = envoy_copts(repository) + copts, + copts = envoy_copts(repository) + envoy_pch_copts(repository, "//source/common/common:common_pch") + copts, visibility = visibility, tags = tags, textual_hdrs = textual_hdrs, deps = deps + [envoy_external_dep_path(dep) for dep in external_deps] + [ repository + "//envoy/common:base_includes", repository + "//source/common/common:fmt_lib", + repository + "//source/common/common:common_pch", envoy_external_dep_path("abseil_flat_hash_map"), envoy_external_dep_path("abseil_flat_hash_set"), envoy_external_dep_path("abseil_strings"), - envoy_external_dep_path("spdlog"), envoy_external_dep_path("fmtlib"), ], alwayslink = 1, diff --git a/bazel/envoy_pch.bzl b/bazel/envoy_pch.bzl new file mode 100644 index 0000000000000..8d1418596dd23 --- /dev/null +++ b/bazel/envoy_pch.bzl @@ -0,0 +1,51 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +# DO NOT LOAD THIS FILE. Load envoy_build_system.bzl instead. +# Envoy library targets +load( + ":envoy_internal.bzl", + "envoy_copts", + "envoy_external_dep_path", + "envoy_linkstatic", +) +load(":pch.bzl", "pch") + +def envoy_pch_copts(repository, target): + return select({ + repository + "//bazel:clang_pch_build": [ + "-include-pch", + "$(location {}{})".format(repository, target), + ], + "//conditions:default": [], + }) + +def envoy_pch_library( + name, + includes, + deps, + external_deps, + visibility, + testonly = False, + repository = ""): + cc_library( + name = name + "_libs", + visibility = ["//visibility:private"], + copts = envoy_copts(repository), + deps = deps + [envoy_external_dep_path(dep) for dep in external_deps], + alwayslink = 1, + testonly = testonly, + linkstatic = envoy_linkstatic(), + ) + + pch( + name = name, + deps = [name + "_libs"], + includes = includes, + visibility = visibility, + testonly = testonly, + tags = ["no-remote"], + enabled = select({ + repository + "//bazel:clang_pch_build": True, + "//conditions:default": False, + }), + ) diff --git a/bazel/envoy_test.bzl b/bazel/envoy_test.bzl index 48f11942cf5a7..f74eda3e8393d 100644 --- a/bazel/envoy_test.bzl +++ b/bazel/envoy_test.bzl @@ -5,6 +5,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("@rules_fuzzing//fuzzing:cc_defs.bzl", "fuzzing_decoration") load(":envoy_binary.bzl", "envoy_cc_binary") load(":envoy_library.bzl", "tcmalloc_external_deps") +load(":envoy_pch.bzl", "envoy_pch_copts") load( ":envoy_internal.bzl", "envoy_copts", @@ -29,19 +30,26 @@ def _envoy_cc_test_infrastructure_library( include_prefix = None, copts = [], alwayslink = 1, + disable_pch = False, **kargs): # Add implicit tcmalloc external dependency(if available) in order to enable CPU and heap profiling in tests. deps += tcmalloc_external_deps(repository) + extra_deps = [] + pch_copts = [] + if disable_pch: + extra_deps = [envoy_external_dep_path("googletest")] + else: + extra_deps = [repository + "//test:test_pch"] + pch_copts = envoy_pch_copts(repository, "//test:test_pch") + cc_library( name = name, srcs = srcs, hdrs = hdrs, data = data, - copts = envoy_copts(repository, test = True) + copts, + copts = envoy_copts(repository, test = True) + copts + pch_copts, testonly = 1, - deps = deps + [envoy_external_dep_path(dep) for dep in external_deps] + [ - envoy_external_dep_path("googletest"), - ], + deps = deps + [envoy_external_dep_path(dep) for dep in external_deps] + extra_deps, tags = tags, include_prefix = include_prefix, alwayslink = alwayslink, @@ -159,11 +167,12 @@ def envoy_cc_test( name = name, srcs = srcs, data = data, - copts = envoy_copts(repository, test = True) + copts, + copts = envoy_copts(repository, test = True) + copts + envoy_pch_copts(repository, "//test:test_pch"), linkopts = _envoy_test_linkopts(), linkstatic = envoy_linkstatic(), malloc = tcmalloc_external_dep(repository), deps = envoy_stdlib_deps() + deps + [envoy_external_dep_path(dep) for dep in external_deps + ["googletest"]] + [ + repository + "//test:test_pch", repository + "//test:main", repository + "//test/test_common:test_version_linkstamp", ], @@ -192,6 +201,7 @@ def envoy_cc_test_library( copts = [], alwayslink = 1, **kargs): + disable_pch = kargs.pop("disable_pch", True) _envoy_cc_test_infrastructure_library( name, srcs, @@ -205,6 +215,7 @@ def envoy_cc_test_library( copts, visibility = ["//visibility:public"], alwayslink = alwayslink, + disable_pch = disable_pch, **kargs ) @@ -283,7 +294,7 @@ def envoy_py_test( # Envoy C++ mock targets should be specified with this function. def envoy_cc_mock(name, **kargs): - envoy_cc_test_library(name = name, **kargs) + envoy_cc_test_library(name = name, disable_pch = True, **kargs) # Envoy shell tests that need to be included in coverage run should be specified with this function. def envoy_sh_test( diff --git a/bazel/pch.bzl b/bazel/pch.bzl new file mode 100644 index 0000000000000..da5fce9144084 --- /dev/null +++ b/bazel/pch.bzl @@ -0,0 +1,114 @@ +load( + "@bazel_tools//tools/build_defs/cc:action_names.bzl", + "CPP_COMPILE_ACTION_NAME", +) + +def _pch(ctx): + deps_cc_info = cc_common.merge_cc_infos( + cc_infos = [dep[CcInfo] for dep in ctx.attr.deps], + ) + + if not ctx.attr.enabled: + return [deps_cc_info] + + cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + + cc_compiler_path = cc_common.get_tool_for_action( + feature_configuration = feature_configuration, + action_name = CPP_COMPILE_ACTION_NAME, + ) + + if "clang" not in cc_compiler_path: + fail("error: attempting to use clang PCH without clang: {}".format(cc_compiler_path)) + + generated_header_file = ctx.actions.declare_file(ctx.label.name + ".h") + ctx.actions.write( + generated_header_file, + "\n".join(["#include \"{}\"".format(include) for include in ctx.attr.includes]) + "\n", + ) + + # TODO: -fno-pch-timestamp / invalidation in that case doesn't work + pch_flags = ["-x", "c++-header"] + pch_file = ctx.actions.declare_file(ctx.label.name + ".pch") + + deps_ctx = deps_cc_info.compilation_context + cc_compile_variables = cc_common.create_compile_variables( + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts + pch_flags, + source_file = generated_header_file.path, + output_file = pch_file.path, + preprocessor_defines = depset(deps_ctx.defines.to_list() + deps_ctx.local_defines.to_list()), + include_directories = deps_ctx.includes, + quote_include_directories = deps_ctx.quote_includes, + system_include_directories = deps_ctx.system_includes, + framework_include_directories = deps_ctx.framework_includes, + ) + + env = cc_common.get_environment_variables( + feature_configuration = feature_configuration, + action_name = CPP_COMPILE_ACTION_NAME, + variables = cc_compile_variables, + ) + + command_line = cc_common.get_memory_inefficient_command_line( + feature_configuration = feature_configuration, + action_name = CPP_COMPILE_ACTION_NAME, + variables = cc_compile_variables, + ) + + transitive_headers = [] + for dep in ctx.attr.deps: + transitive_headers.append(dep[CcInfo].compilation_context.headers) + ctx.actions.run( + executable = cc_compiler_path, + arguments = command_line, + env = env, + inputs = depset( + items = [generated_header_file], + transitive = [cc_toolchain.all_files] + transitive_headers, + ), + outputs = [pch_file], + ) + + return [ + DefaultInfo(files = depset(items = [pch_file])), + cc_common.merge_cc_infos( + direct_cc_infos = [ + CcInfo( + compilation_context = cc_common.create_compilation_context( + headers = depset([pch_file, generated_header_file]), + ), + ), + ], + cc_infos = [deps_cc_info], + ), + ] + +pch = rule( + attrs = dict( + includes = attr.string_list( + mandatory = True, + allow_empty = False, + ), + deps = attr.label_list( + mandatory = True, + allow_empty = False, + providers = [CcInfo], + ), + enabled = attr.bool( + mandatory = True, + ), + _cc_toolchain = attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), + ), + fragments = ["cpp"], + provides = [CcInfo], + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + implementation = _pch, +) diff --git a/source/common/common/BUILD b/source/common/common/BUILD index 15067b74e8e02..6fd4fd29cce63 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -6,6 +6,7 @@ load( "envoy_cc_posix_library", "envoy_cc_win32_library", "envoy_package", + "envoy_pch_library", ) licenses(["notice"]) # Apache 2 @@ -469,3 +470,30 @@ envoy_cc_library( "@com_google_absl//absl/status:statusor", ], ) + +envoy_pch_library( + name = "common_pch", + external_deps = [ + "spdlog", + ], + includes = [ + "envoy/config/bootstrap/v3/bootstrap.pb.h", + "envoy/config/cluster/v3/cluster.pb.h", + "envoy/config/core/v3/base.pb.h", + "envoy/config/core/v3/config_source.pb.h", + "envoy/config/route/v3/route_components.pb.h", + "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h", + "envoy/service/discovery/v3/discovery.pb.h", + "spdlog/sinks/android_sink.h", + "spdlog/spdlog.h", + ], + visibility = ["//visibility:public"], + deps = [ + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/config/route/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) diff --git a/test/BUILD b/test/BUILD index ab4a56a7d42e0..572e27eb653f9 100644 --- a/test/BUILD +++ b/test/BUILD @@ -2,6 +2,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_test_library", "envoy_package", + "envoy_pch_library", ) licenses(["notice"]) # Apache 2 @@ -36,3 +37,52 @@ envoy_cc_test_library( "//test/test_common:printers_lib", ], ) + +envoy_pch_library( + name = "test_pch", + testonly = True, + external_deps = [ + "googletest", + "spdlog", + ], + includes = [ + "envoy/config/bootstrap/v3/bootstrap.pb.h", + "envoy/config/cluster/v3/cluster.pb.h", + "envoy/config/core/v3/base.pb.h", + "envoy/config/core/v3/config_source.pb.h", + "envoy/config/route/v3/route_components.pb.h", + "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h", + "envoy/service/discovery/v3/discovery.pb.h", + "gmock/gmock.h", + "gtest/gtest.h", + "spdlog/sinks/android_sink.h", + "spdlog/spdlog.h", + "test/mocks/access_log/mocks.h", + "test/mocks/common.h", + "test/mocks/event/mocks.h", + "test/mocks/http/mocks.h", + "test/mocks/network/mocks.h", + "test/mocks/runtime/mocks.h", + "test/mocks/server/factory_context.h", + "test/mocks/server/instance.h", + "test/mocks/stats/mocks.h", + ], + visibility = ["//visibility:public"], + deps = [ + "//test/mocks:common_lib", + "//test/mocks/access_log:access_log_mocks", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:factory_context_mocks", + "//test/mocks/server:instance_mocks", + "//test/mocks/stats:stats_mocks", + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", + "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + "@envoy_api//envoy/config/route/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + ], +) diff --git a/tools/code_format/envoy_build_fixer.py b/tools/code_format/envoy_build_fixer.py index 378a1af63fdde..cfdbf59d24a90 100755 --- a/tools/code_format/envoy_build_fixer.py +++ b/tools/code_format/envoy_build_fixer.py @@ -150,6 +150,8 @@ def fix_api_deps(path, contents): kind, name, srcs, hdrs, deps = match.groups() if not name: continue + if kind == "envoy_pch_library": + continue source_paths = [] if srcs != 'missing': source_paths.extend(