• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3# Copyright 2022 gRPC authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""
17Generate experiment related code artifacts.
18
19Invoke as: tools/codegen/core/gen_experiments.py
20Experiment definitions are in src/core/lib/experiments/experiments.yaml
21"""
22
23from __future__ import print_function
24
25import argparse
26import os
27import sys
28
29import experiments_compiler as exp
30import yaml
31
32REPO_ROOT = os.path.normpath(
33    os.path.join(os.path.dirname(__file__), "../../..")
34)
35print(REPO_ROOT)
36os.chdir(REPO_ROOT)
37
38DEFAULTS = {
39    "broken": "false",
40    False: "false",
41    True: "true",
42    "debug": "kDefaultForDebugOnly",
43}
44
45PLATFORMS_DEFINE = {
46    "windows": "GPR_WINDOWS",
47    "ios": "GRPC_CFSTREAM",
48    "posix": "",
49}
50
51FINAL_RETURN = {
52    "broken": "return false;",
53    False: "return false;",
54    True: "return true;",
55    "debug": "\n#ifdef NDEBUG\nreturn false;\n#else\nreturn true;\n#endif\n",
56}
57
58FINAL_DEFINE = {
59    "broken": None,
60    False: None,
61    True: "#define %s",
62    "debug": "#ifndef NDEBUG\n#define %s\n#endif",
63}
64
65BZL_LIST_FOR_DEFAULTS = {
66    "broken": None,
67    False: "off",
68    True: "on",
69    "debug": "dbg",
70}
71
72
73def ParseCommandLineArguments(args):
74    """Wrapper for argparse command line arguments handling.
75
76    Args:
77    args: List of command line arguments.
78
79    Returns:
80    Command line arguments namespace built by argparse.ArgumentParser().
81    """
82    # formatter_class=argparse.ArgumentDefaultsHelpFormatter is not used here
83    # intentionally, We want more formatting than this class can provide.
84    flag_parser = argparse.ArgumentParser()
85    flag_parser.add_argument(
86        "--check",
87        action="store_false",
88        help="If specified, disables checking experiment expiry dates",
89    )
90    flag_parser.add_argument(
91        "--no_dbg_experiments",
92        action="store_true",
93        help="Prohibit 'debug' configurations",
94        default=False,
95    )
96    return flag_parser.parse_args(args)
97
98
99args = ParseCommandLineArguments(sys.argv[1:])
100
101
102def _InjectGithubPath(path):
103    base, ext = os.path.splitext(path)
104    return base + ".github" + ext
105
106
107def _GenerateExperimentFiles(args, mode):
108    if mode == "test":
109        _EXPERIMENTS_DEFS = (
110            "test/core/experiments/fixtures/test_experiments.yaml"
111        )
112        _EXPERIMENTS_ROLLOUTS = (
113            "test/core/experiments/fixtures/test_experiments_rollout.yaml"
114        )
115        _EXPERIMENTS_HDR_FILE = "test/core/experiments/fixtures/experiments.h"
116        _EXPERIMENTS_SRC_FILE = "test/core/experiments/fixtures/experiments.cc"
117        _EXPERIMENTS_BZL_FILE = "bazel/test_experiments.bzl"
118    else:
119        _EXPERIMENTS_DEFS = "src/core/lib/experiments/experiments.yaml"
120        _EXPERIMENTS_ROLLOUTS = "src/core/lib/experiments/rollouts.yaml"
121        _EXPERIMENTS_HDR_FILE = "src/core/lib/experiments/experiments.h"
122        _EXPERIMENTS_SRC_FILE = "src/core/lib/experiments/experiments.cc"
123        _EXPERIMENTS_BZL_FILE = "bazel/experiments.bzl"
124        if "/google3/" in REPO_ROOT:
125            _EXPERIMENTS_ROLLOUTS = _InjectGithubPath(_EXPERIMENTS_ROLLOUTS)
126            _EXPERIMENTS_HDR_FILE = _InjectGithubPath(_EXPERIMENTS_HDR_FILE)
127            _EXPERIMENTS_SRC_FILE = _InjectGithubPath(_EXPERIMENTS_SRC_FILE)
128            _EXPERIMENTS_BZL_FILE = _InjectGithubPath(_EXPERIMENTS_BZL_FILE)
129
130    with open(_EXPERIMENTS_DEFS) as f:
131        attrs = yaml.safe_load(f.read())
132
133    if not exp.AreExperimentsOrdered(attrs):
134        print("Experiments are not ordered")
135        sys.exit(1)
136
137    with open(_EXPERIMENTS_ROLLOUTS) as f:
138        rollouts = yaml.safe_load(f.read())
139
140    if not exp.AreExperimentsOrdered(rollouts):
141        print("Rollouts are not ordered")
142        sys.exit(1)
143
144    compiler = exp.ExperimentsCompiler(
145        DEFAULTS,
146        FINAL_RETURN,
147        FINAL_DEFINE,
148        PLATFORMS_DEFINE,
149        BZL_LIST_FOR_DEFAULTS,
150    )
151
152    experiment_annotation = "gRPC Experiments: "
153    for attr in attrs:
154        exp_definition = exp.ExperimentDefinition(attr)
155        if not exp_definition.IsValid(args.check):
156            sys.exit(1)
157        experiment_annotation += exp_definition.name + ":0,"
158        if not compiler.AddExperimentDefinition(exp_definition):
159            print("Experiment = %s ERROR adding" % exp_definition.name)
160            sys.exit(1)
161
162    if len(experiment_annotation) > 2000:
163        print("comma-delimited string of experiments is too long")
164        sys.exit(1)
165
166    for rollout_attr in rollouts:
167        if not compiler.AddRolloutSpecification(rollout_attr):
168            print("ERROR adding rollout spec")
169            sys.exit(1)
170
171    if mode != "test" and args.no_dbg_experiments:
172        print("Ensuring no debug experiments are configured")
173        compiler.EnsureNoDebugExperiments()
174
175    print(f"Mode = {mode} Generating experiments headers")
176    compiler.GenerateExperimentsHdr(_EXPERIMENTS_HDR_FILE, mode)
177
178    print(f"Mode = {mode} Generating experiments srcs")
179    compiler.GenerateExperimentsSrc(
180        _EXPERIMENTS_SRC_FILE, _EXPERIMENTS_HDR_FILE, mode
181    )
182
183    print("Generating experiments.bzl")
184    compiler.GenExperimentsBzl(mode, _EXPERIMENTS_BZL_FILE)
185    if mode == "test":
186        print("Generating experiments tests")
187        compiler.GenTest(
188            os.path.join(REPO_ROOT, "test/core/experiments/experiments_test.cc")
189        )
190
191
192_GenerateExperimentFiles(args, "production")
193_GenerateExperimentFiles(args, "test")
194