1# Copyright 2016 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15#
16# This is for the gRPC build system. This isn't intended to be used outsite of
17# the BUILD file for gRPC. It contains the mapping for the template system we
18# use to generate other platform's build system files.
19#
20# Please consider that there should be a high bar for additions and changes to
21# this file.
22# Each rule listed must be re-written for Google's internal build system, and
23# each change must be ported from one to the other.
24#
25
26"""
27Contains macros used throughout the repo.
28"""
29
30load("//bazel:cc_grpc_library.bzl", "cc_grpc_library")
31load("//bazel:copts.bzl", "GRPC_DEFAULT_COPTS")
32load("//bazel:experiments.bzl", "EXPERIMENTS")
33load("@upb//bazel:upb_proto_library.bzl", "upb_proto_library", "upb_proto_reflection_library")
34load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test")
35load("@build_bazel_rules_apple//apple/testing/default_runner:ios_test_runner.bzl", "ios_test_runner")
36
37# The set of pollers to test against if a test exercises polling
38POLLERS = ["epoll1", "poll"]
39
40# The set of known EventEngines to test
41EVENT_ENGINES = {"default": {"tags": []}}
42
43def if_not_windows(a):
44    return select({
45        "//:windows": [],
46        "//:windows_msvc": [],
47        "//conditions:default": a,
48    })
49
50def if_windows(a):
51    return select({
52        "//:windows": a,
53        "//:windows_msvc": a,
54        "//conditions:default": [],
55    })
56
57def _get_external_deps(external_deps):
58    ret = []
59    for dep in external_deps:
60        if dep == "address_sorting":
61            ret.append("//third_party/address_sorting")
62        elif dep == "xxhash":
63            ret.append("//third_party/xxhash")
64        elif dep == "cares":
65            ret += select({
66                "//:grpc_no_ares": [],
67                "//conditions:default": ["//external:cares"],
68            })
69        elif dep == "cronet_c_for_grpc":
70            ret.append("//third_party/objective_c/Cronet:cronet_c_for_grpc")
71        elif dep.startswith("absl/"):
72            ret.append("@com_google_absl//" + dep)
73        elif dep.startswith("google/"):
74            ret.append("@com_google_googleapis//" + dep)
75        else:
76            ret.append("//external:" + dep)
77    return ret
78
79def _update_visibility(visibility):
80    if visibility == None:
81        return None
82
83    # Visibility rules prefixed with '@grpc:' are used to flag different visibility rule
84    # classes upstream.
85    PUBLIC = ["//visibility:public"]
86    PRIVATE = ["//:__subpackages__"]
87    VISIBILITY_TARGETS = {
88        "alt_grpc++_base_legacy": PRIVATE,
89        "alt_grpc_base_legacy": PRIVATE,
90        "alt_grpc++_base_unsecure_legacy": PRIVATE,
91        "alts_frame_protector": PRIVATE,
92        "channelz": PRIVATE,
93        "client_channel": PRIVATE,
94        "cli": PRIVATE,
95        "debug_location": PRIVATE,
96        "endpoint_tests": PRIVATE,
97        "exec_ctx": PRIVATE,
98        "grpclb": PRIVATE,
99        "grpc_opencensus_plugin": PUBLIC,
100        "grpcpp_gcp_observability": PUBLIC,
101        "grpc_resolver_fake": PRIVATE,
102        "grpc++_test": PRIVATE,
103        "http": PRIVATE,
104        "httpcli": PRIVATE,
105        "iomgr_timer": PRIVATE,
106        "iomgr_internal_errqueue": PRIVATE,
107        "iomgr_buffer_list": PRIVATE,
108        "json_reader_legacy": PRIVATE,
109        "public": PUBLIC,
110        "ref_counted_ptr": PRIVATE,
111        "trace": PRIVATE,
112        "tsi_interface": PRIVATE,
113        "tsi": PRIVATE,
114        "xds": PRIVATE,
115        "xds_client_core": PRIVATE,
116    }
117    final_visibility = []
118    for rule in visibility:
119        if rule.startswith("@grpc:"):
120            for replacement in VISIBILITY_TARGETS[rule[len("@grpc:"):]]:
121                final_visibility.append(replacement)
122        else:
123            final_visibility.append(rule)
124    return [x for x in final_visibility]
125
126def grpc_cc_library(
127        name,
128        srcs = [],
129        public_hdrs = [],
130        hdrs = [],
131        external_deps = [],
132        defines = [],
133        deps = [],
134        select_deps = None,
135        standalone = False,
136        language = "C++",
137        testonly = False,
138        visibility = None,
139        alwayslink = 0,
140        data = [],
141        tags = [],
142        linkopts = [],
143        linkstatic = False):
144    """An internal wrapper around cc_library.
145
146    Args:
147      name: The name of the library.
148      srcs: The source files.
149      public_hdrs: The public headers.
150      hdrs: The headers.
151      external_deps: External depdendencies to be resolved.
152      defines: Build defines to use.
153      deps: cc_library deps.
154      select_deps: deps included conditionally.
155      standalone: Unused.
156      language: The language of the library, e.g. C, C++.
157      testonly: Whether the target is for tests only.
158      visibility: The visibility of the target.
159      alwayslink: Whether to enable alwayslink on the cc_library.
160      data: Data dependencies.
161      tags: Tags to apply to the rule.
162      linkopts: Extra libraries to link.
163      linkstatic: Whether to enable linkstatic on the cc_library.
164    """
165    visibility = _update_visibility(visibility)
166    copts = []
167    if language.upper() == "C":
168        copts = copts + if_not_windows(["-std=c11"])
169    linkopts = linkopts + if_not_windows(["-pthread"]) + if_windows(["-defaultlib:ws2_32.lib"])
170    if select_deps:
171        for select_deps_entry in select_deps:
172            deps += select(select_deps_entry)
173    native.cc_library(
174        name = name,
175        srcs = srcs,
176        defines = defines +
177                  select({
178                      "//:grpc_no_ares": ["GRPC_ARES=0"],
179                      "//conditions:default": [],
180                  }) +
181                  select({
182                      "//:remote_execution": ["GRPC_PORT_ISOLATED_RUNTIME=1"],
183                      "//conditions:default": [],
184                  }) +
185                  select({
186                      "//:grpc_allow_exceptions": ["GRPC_ALLOW_EXCEPTIONS=1"],
187                      "//:grpc_disallow_exceptions": ["GRPC_ALLOW_EXCEPTIONS=0"],
188                      "//conditions:default": [],
189                  }),
190        hdrs = hdrs + public_hdrs,
191        deps = deps + _get_external_deps(external_deps),
192        copts = GRPC_DEFAULT_COPTS + copts,
193        visibility = visibility,
194        testonly = testonly,
195        linkopts = linkopts,
196        includes = [
197            "include",
198            "src/core/ext/upb-generated",  # Once upb code-gen issue is resolved, remove this.
199            "src/core/ext/upbdefs-generated",  # Once upb code-gen issue is resolved, remove this.
200        ],
201        alwayslink = alwayslink,
202        data = data,
203        tags = tags,
204        linkstatic = linkstatic,
205    )
206
207def grpc_proto_plugin(name, srcs = [], deps = []):
208    native.cc_binary(
209        name = name,
210        srcs = srcs,
211        deps = deps,
212    )
213
214def grpc_proto_library(
215        name,
216        srcs = [],
217        deps = [],
218        well_known_protos = False,
219        has_services = True,
220        use_external = False,
221        generate_mocks = False):
222    cc_grpc_library(
223        name = name,
224        srcs = srcs,
225        deps = deps,
226        well_known_protos = well_known_protos,
227        proto_only = not has_services,
228        use_external = use_external,
229        generate_mocks = generate_mocks,
230    )
231
232def ios_cc_test(
233        name,
234        tags = [],
235        **kwargs):
236    """An ios C++ test target.
237
238    Args:
239      name: The name of the test.
240      tags: The tags to apply to the test.
241      **kwargs: All other arguments to apply.
242    """
243    test_lib_ios = name + "_test_lib_ios"
244    ios_tags = tags + ["manual", "ios_cc_test"]
245    test_runner = "ios_x86_64_sim_runner_" + name
246    ios_test_runner(
247        name = test_runner,
248        device_type = "iPhone X",
249    )
250    if not any([t for t in tags if t.startswith("no_test_ios")]):
251        native.objc_library(
252            name = test_lib_ios,
253            srcs = kwargs.get("srcs"),
254            deps = kwargs.get("deps"),
255            copts = kwargs.get("copts"),
256            data = kwargs.get("data"),
257            tags = ios_tags,
258            alwayslink = 1,
259            testonly = 1,
260        )
261        ios_test_deps = [":" + test_lib_ios]
262        ios_unit_test(
263            name = name + "_on_ios",
264            size = kwargs.get("size"),
265            data = kwargs.get("data"),
266            tags = ios_tags,
267            minimum_os_version = "9.0",
268            runner = test_runner,
269            deps = ios_test_deps,
270        )
271
272def expand_tests(name, srcs, deps, tags, args, exclude_pollers, uses_polling, uses_event_engine, flaky):
273    """Common logic used to parameterize tests for every poller and EventEngine and experiment.
274
275    Args:
276        name: base name of the test
277        srcs: source files
278        deps: base deps
279        tags: base tags
280        args: base args
281        flaky: base flaky
282        exclude_pollers: list of poller names to exclude for this set of tests.
283        uses_polling: set to False if the test is not sensitive to polling methodology.
284        uses_event_engine: set to False if the test is not sensitive to
285            EventEngine implementation differences
286
287    Returns:
288        A list of dictionaries containing modified values of name, srcs, deps, tags, and args.
289    """
290    poller_config = []
291
292    if not uses_polling:
293        tags = tags + ["no_uses_polling"]
294
295        poller_config.append({
296            "name": name,
297            "srcs": srcs,
298            "deps": deps,
299            "tags": tags,
300            "args": args,
301            "flaky": flaky,
302            "env": {},
303        })
304    else:
305        # On linux we run the same test with the default EventEngine, once for each
306        # poller
307        for poller in POLLERS:
308            if poller in exclude_pollers:
309                continue
310            poller_config.append({
311                "name": name + "@poller=" + poller,
312                "srcs": srcs,
313                "deps": deps,
314                "tags": (tags + EVENT_ENGINES["default"]["tags"] + [
315                    "no_windows",
316                    "no_mac",
317                    "bazel_only",
318                ]),
319                "args": args,
320                "env": {
321                    "GRPC_POLL_STRATEGY": poller,
322                },
323                "flaky": flaky,
324            })
325
326        # Now generate one test for each subsequent EventEngine, all using the
327        # default poller. These tests will have `@engine=<name>` appended to the
328        # test target name. If a test target name has no `@engine=<name>` component,
329        # that indicates that the default EventEngine is being used.
330        if not uses_event_engine:
331            # The poller tests exercise the default engine on Linux. This test
332            # handles other platforms.
333            poller_config.append({
334                "name": name,
335                "srcs": srcs,
336                "deps": deps,
337                "tags": tags + ["no_linux"],
338                "args": args,
339                "env": {},
340                "flaky": flaky,
341            })
342        else:
343            for engine_name, engine in EVENT_ENGINES.items():
344                test_name = name + "@engine=" + engine_name
345                test_tags = tags + engine["tags"] + ["bazel_only"]
346                test_args = args + ["--engine=" + engine_name]
347                if engine_name == "default":
348                    # The poller tests exercise the default engine on Linux.
349                    # This test handles other platforms.
350                    test_name = name
351                    test_tags = tags + engine["tags"] + ["no_linux"]
352                    test_args = args
353                poller_config.append({
354                    "name": test_name,
355                    "srcs": srcs,
356                    "deps": deps,
357                    "tags": test_tags,
358                    "args": test_args,
359                    "env": {},
360                    "flaky": flaky,
361                })
362
363    experiments = {}
364    for mode, tag_to_experiments in EXPERIMENTS.items():
365        experiments[mode] = {}
366        for tag in tags:
367            if tag not in tag_to_experiments:
368                continue
369            for experiment in tag_to_experiments[tag]:
370                experiments[mode][experiment] = 1
371        experiments[mode] = list(experiments[mode].keys())
372
373    mode_config = {
374        # format: <mode>: (enabled_target_tags, disabled_target_tags)
375        "dbg": (["noopt"], ["nodbg"]),
376        "on": (None, []),
377        "off": ([], None),
378    }
379
380    must_have_tags = [
381        # We don't run experiments on cmake builds
382        "bazel_only",
383        # Nor on windows
384        "no_windows",
385        # Nor on mac
386        "no_mac",
387        # Nor on arm64
388        "no_arm64",
389    ]
390    experiment_config = list(poller_config)
391    for mode, config in mode_config.items():
392        enabled_tags, disabled_tags = config
393        if enabled_tags != None:
394            for experiment in experiments[mode]:
395                for config in poller_config:
396                    config = dict(config)
397                    config["name"] = config["name"] + "@experiment=" + experiment
398                    env = dict(config["env"])
399                    env["GRPC_EXPERIMENTS"] = experiment
400                    config["env"] = env
401                    tags = config["tags"]
402                    for tag in must_have_tags + enabled_tags:
403                        if tag not in tags:
404                            tags = tags + [tag]
405                    config["tags"] = tags
406                    config["flaky"] = True
407                    experiment_config.append(config)
408        if disabled_tags != None:
409            for experiment in experiments[mode]:
410                for config in poller_config:
411                    config = dict(config)
412                    config["name"] = config["name"] + "@experiment=no_" + experiment
413                    env = dict(config["env"])
414                    env["GRPC_EXPERIMENTS"] = "-" + experiment
415                    config["env"] = env
416                    tags = config["tags"]
417                    for tag in must_have_tags + disabled_tags:
418                        if tag not in tags:
419                            tags = tags + [tag]
420                    config["tags"] = tags
421                    experiment_config.append(config)
422    return experiment_config
423
424def grpc_cc_test(name, srcs = [], deps = [], external_deps = [], args = [], data = [], uses_polling = True, language = "C++", size = "medium", timeout = None, tags = [], exec_compatible_with = [], exec_properties = {}, shard_count = None, flaky = None, copts = [], linkstatic = None, exclude_pollers = [], uses_event_engine = True):
425    """A cc_test target for use in the gRPC repo.
426
427    Args:
428        name: The name of the test.
429        srcs: The source files.
430        deps: The target deps.
431        external_deps: The external deps.
432        args: The args to supply to the test binary.
433        data: Data dependencies.
434        uses_polling: Whether the test uses polling.
435        language: The language of the test, e.g C, C++.
436        size: The size of the test.
437        timeout: The test timeout.
438        tags: The tags for the test.
439        exec_compatible_with: A list of constraint values that must be
440            satisifed for the platform.
441        exec_properties: A dictionary of strings that will be added to the
442            exec_properties of a platform selected for this target.
443        shard_count: The number of shards for this test.
444        flaky: Whether this test is flaky.
445        copts: Add these to the compiler invocation.
446        linkstatic: link the binary in static mode
447        exclude_pollers: list of poller names to exclude for this set of tests.
448        uses_event_engine: set to False if the test is not sensitive to
449            EventEngine implementation differences
450    """
451    if language.upper() == "C":
452        copts = copts + if_not_windows(["-std=c11"])
453
454    core_deps = deps + _get_external_deps(external_deps) + ["//test/core/util:grpc_suppressions"]
455
456    # Test args for all tests
457    test_args = {
458        "data": data,
459        "copts": GRPC_DEFAULT_COPTS + copts,
460        "linkopts": if_not_windows(["-pthread"]) + if_windows(["-defaultlib:ws2_32.lib"]),
461        "size": size,
462        "timeout": timeout,
463        "exec_compatible_with": exec_compatible_with,
464        "exec_properties": exec_properties,
465        "shard_count": shard_count,
466        "linkstatic": linkstatic,
467    }
468
469    if "grpc-fuzzer" not in tags and "no_test_ios" not in tags:
470        ios_cc_test(
471            name = name,
472            srcs = srcs,
473            tags = tags,
474            deps = core_deps,
475            args = args,
476            flaky = flaky,
477            **test_args
478        )
479
480    for poller_config in expand_tests(name, srcs, core_deps, tags, args, exclude_pollers, uses_polling, uses_event_engine, flaky):
481        native.cc_test(
482            name = poller_config["name"],
483            srcs = poller_config["srcs"],
484            deps = poller_config["deps"],
485            tags = poller_config["tags"],
486            args = poller_config["args"],
487            env = poller_config["env"],
488            flaky = poller_config["flaky"],
489            **test_args
490        )
491
492def grpc_cc_binary(name, srcs = [], deps = [], external_deps = [], args = [], data = [], language = "C++", testonly = False, linkshared = False, linkopts = [], tags = [], features = [], visibility = None):
493    """Generates a cc_binary for use in the gRPC repo.
494
495    Args:
496      name: The name of the target.
497      srcs: The source files.
498      deps: The dependencies.
499      external_deps: The external dependencies.
500      args: The arguments to supply to the binary.
501      data: The data dependencies.
502      language: The language of the binary, e.g. C, C++.
503      testonly: Whether the binary is for tests only.
504      linkshared: Enables linkshared on the binary.
505      linkopts: linkopts to supply to the cc_binary.
506      tags: Tags to apply to the target.
507      features: features to be supplied to the cc_binary.
508      visibility: The visibility of the target.
509    """
510    visibility = _update_visibility(visibility)
511    copts = []
512    if language.upper() == "C":
513        copts = ["-std=c11"]
514    native.cc_binary(
515        name = name,
516        srcs = srcs,
517        args = args,
518        data = data,
519        testonly = testonly,
520        linkshared = linkshared,
521        deps = deps + _get_external_deps(external_deps) + ["//test/core/util:grpc_suppressions"],
522        copts = GRPC_DEFAULT_COPTS + copts,
523        linkopts = if_not_windows(["-pthread"]) + linkopts,
524        tags = tags,
525        features = features,
526    )
527
528# buildifier: disable=unnamed-macro
529def grpc_generate_one_off_targets():
530    # In open-source, grpc_objc* libraries depend directly on //:grpc
531    native.alias(
532        name = "grpc_objc",
533        actual = "//:grpc",
534    )
535    native.config_setting(
536        name = "windows_other",
537        values = {"define": "GRPC_WINDOWS_OTHER=1"},
538    )
539
540def grpc_generate_objc_one_off_targets():
541    pass
542
543def grpc_sh_test(name, srcs = [], args = [], data = [], uses_polling = True, size = "medium", timeout = None, tags = [], exec_compatible_with = [], exec_properties = {}, shard_count = None, flaky = None, exclude_pollers = [], uses_event_engine = True):
544    """Execute an sh_test for every <poller> x <EventEngine> combination
545
546    Args:
547        name: The name of the test.
548        srcs: The source files.
549        args: The args to supply to the test binary.
550        data: Data dependencies.
551        uses_polling: Whether the test uses polling.
552        size: The size of the test.
553        timeout: The test timeout.
554        tags: The tags for the test.
555        exec_compatible_with: A list of constraint values that must be
556            satisifed for the platform.
557        exec_properties: A dictionary of strings that will be added to the
558            exec_properties of a platform selected for this target.
559        shard_count: The number of shards for this test.
560        flaky: Whether this test is flaky.
561        exclude_pollers: list of poller names to exclude for this set of tests.
562        uses_event_engine: set to False if the test is not sensitive to
563            EventEngine implementation differences
564    """
565    test_args = {
566        "data": data,
567        "size": size,
568        "timeout": timeout,
569        "exec_compatible_with": exec_compatible_with,
570        "exec_properties": exec_properties,
571        "shard_count": shard_count,
572    }
573
574    for poller_config in expand_tests(name, srcs, [], tags, args, exclude_pollers, uses_polling, uses_event_engine, flaky):
575        native.sh_test(
576            name = poller_config["name"],
577            srcs = poller_config["srcs"],
578            deps = poller_config["deps"],
579            tags = poller_config["tags"],
580            args = poller_config["args"],
581            env = poller_config["env"],
582            flaky = poller_config["flaky"],
583            **test_args
584        )
585
586def grpc_sh_binary(name, srcs, data = []):
587    native.sh_binary(
588        name = name,
589        srcs = srcs,
590        data = data,
591    )
592
593def grpc_py_binary(
594        name,
595        srcs,
596        data = [],
597        deps = [],
598        external_deps = [],
599        testonly = False,
600        python_version = "PY2",
601        **kwargs):
602    native.py_binary(
603        name = name,
604        srcs = srcs,
605        testonly = testonly,
606        data = data,
607        deps = deps + _get_external_deps(external_deps),
608        python_version = python_version,
609        **kwargs
610    )
611
612def grpc_package(name, visibility = "private", features = []):
613    """Creates a package.
614
615    Args:
616        name: The name of the target
617        visibility: The visibility of the target.
618        features: The features to enable.
619    """
620    if visibility == "tests":
621        visibility = ["//test:__subpackages__"]
622    elif visibility == "public":
623        visibility = ["//visibility:public"]
624    elif visibility == "private":
625        visibility = []
626    else:
627        fail("Unknown visibility " + visibility)
628
629    if len(visibility) != 0:
630        # buildifier: disable=native-package
631        native.package(
632            default_visibility = visibility,
633            features = features,
634        )
635
636def grpc_objc_library(
637        name,
638        srcs = [],
639        hdrs = [],
640        non_arc_srcs = [],
641        textual_hdrs = [],
642        testonly = False,
643        data = [],
644        deps = [],
645        defines = [],
646        sdk_frameworks = [],
647        includes = [],
648        visibility = ["//visibility:public"]):
649    """The grpc version of objc_library, only used for the Objective-C library compilation
650
651    Args:
652        name: name of target
653        hdrs: public headers
654        srcs: all source files (.m)
655        non_arc_srcs: list of Objective-C files that DO NOT use ARC.
656        textual_hdrs: private headers
657        testonly: Whether the binary is for tests only.
658        data: any other bundle resources
659        defines: preprocessors
660        sdk_frameworks: sdks
661        includes: added to search path, always [the path to objc directory]
662        deps: dependencies
663        visibility: visibility, default to public
664    """
665
666    native.objc_library(
667        name = name,
668        hdrs = hdrs,
669        srcs = srcs,
670        non_arc_srcs = non_arc_srcs,
671        textual_hdrs = textual_hdrs,
672        copts = GRPC_DEFAULT_COPTS + ["-ObjC++", "-std=gnu++14"],
673        testonly = testonly,
674        data = data,
675        deps = deps,
676        defines = defines,
677        includes = includes,
678        sdk_frameworks = sdk_frameworks,
679        visibility = visibility,
680    )
681
682def grpc_upb_proto_library(name, deps):
683    upb_proto_library(name = name, deps = deps)
684
685def grpc_upb_proto_reflection_library(name, deps):
686    upb_proto_reflection_library(name = name, deps = deps)
687
688# buildifier: disable=unnamed-macro
689def python_config_settings():
690    native.config_setting(
691        name = "python3",
692        flag_values = {"@bazel_tools//tools/python:python_version": "PY3"},
693    )
694