1*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2015 gRPC authors. 2*cc02d7e2SAndroid Build Coastguard Worker# 3*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*cc02d7e2SAndroid Build Coastguard Worker# 7*cc02d7e2SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*cc02d7e2SAndroid Build Coastguard Worker# 9*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License. 14*cc02d7e2SAndroid Build Coastguard Worker 15*cc02d7e2SAndroid Build Coastguard Workerimport argparse 16*cc02d7e2SAndroid Build Coastguard Workerimport glob 17*cc02d7e2SAndroid Build Coastguard Workerimport multiprocessing 18*cc02d7e2SAndroid Build Coastguard Workerimport os 19*cc02d7e2SAndroid Build Coastguard Workerimport pickle 20*cc02d7e2SAndroid Build Coastguard Workerimport shutil 21*cc02d7e2SAndroid Build Coastguard Workerimport sys 22*cc02d7e2SAndroid Build Coastguard Workerimport tempfile 23*cc02d7e2SAndroid Build Coastguard Workerfrom typing import Dict, List, Union 24*cc02d7e2SAndroid Build Coastguard Worker 25*cc02d7e2SAndroid Build Coastguard Workerimport _utils 26*cc02d7e2SAndroid Build Coastguard Workerimport yaml 27*cc02d7e2SAndroid Build Coastguard Worker 28*cc02d7e2SAndroid Build Coastguard WorkerPROJECT_ROOT = os.path.join( 29*cc02d7e2SAndroid Build Coastguard Worker os.path.dirname(os.path.abspath(__file__)), "..", ".." 30*cc02d7e2SAndroid Build Coastguard Worker) 31*cc02d7e2SAndroid Build Coastguard Workeros.chdir(PROJECT_ROOT) 32*cc02d7e2SAndroid Build Coastguard Worker# TODO(lidiz) find a better way for plugins to reference each other 33*cc02d7e2SAndroid Build Coastguard Workersys.path.append(os.path.join(PROJECT_ROOT, "tools", "buildgen", "plugins")) 34*cc02d7e2SAndroid Build Coastguard Worker 35*cc02d7e2SAndroid Build Coastguard Worker# from tools.run_tests.python_utils import jobset 36*cc02d7e2SAndroid Build Coastguard Workerjobset = _utils.import_python_module( 37*cc02d7e2SAndroid Build Coastguard Worker os.path.join( 38*cc02d7e2SAndroid Build Coastguard Worker PROJECT_ROOT, "tools", "run_tests", "python_utils", "jobset.py" 39*cc02d7e2SAndroid Build Coastguard Worker ) 40*cc02d7e2SAndroid Build Coastguard Worker) 41*cc02d7e2SAndroid Build Coastguard Worker 42*cc02d7e2SAndroid Build Coastguard WorkerPREPROCESSED_BUILD = ".preprocessed_build" 43*cc02d7e2SAndroid Build Coastguard Workertest = {} if os.environ.get("TEST", "false") == "true" else None 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard Workerassert sys.argv[1:], "run generate_projects.sh instead of this directly" 46*cc02d7e2SAndroid Build Coastguard Workerparser = argparse.ArgumentParser() 47*cc02d7e2SAndroid Build Coastguard Workerparser.add_argument( 48*cc02d7e2SAndroid Build Coastguard Worker "build_files", 49*cc02d7e2SAndroid Build Coastguard Worker nargs="+", 50*cc02d7e2SAndroid Build Coastguard Worker default=[], 51*cc02d7e2SAndroid Build Coastguard Worker help="build files describing build specs", 52*cc02d7e2SAndroid Build Coastguard Worker) 53*cc02d7e2SAndroid Build Coastguard Workerparser.add_argument( 54*cc02d7e2SAndroid Build Coastguard Worker "--templates", nargs="+", default=[], help="mako template files to render" 55*cc02d7e2SAndroid Build Coastguard Worker) 56*cc02d7e2SAndroid Build Coastguard Workerparser.add_argument( 57*cc02d7e2SAndroid Build Coastguard Worker "--output_merged", 58*cc02d7e2SAndroid Build Coastguard Worker "-m", 59*cc02d7e2SAndroid Build Coastguard Worker default="", 60*cc02d7e2SAndroid Build Coastguard Worker type=str, 61*cc02d7e2SAndroid Build Coastguard Worker help="merge intermediate results to a file", 62*cc02d7e2SAndroid Build Coastguard Worker) 63*cc02d7e2SAndroid Build Coastguard Workerparser.add_argument( 64*cc02d7e2SAndroid Build Coastguard Worker "--jobs", 65*cc02d7e2SAndroid Build Coastguard Worker "-j", 66*cc02d7e2SAndroid Build Coastguard Worker default=multiprocessing.cpu_count(), 67*cc02d7e2SAndroid Build Coastguard Worker type=int, 68*cc02d7e2SAndroid Build Coastguard Worker help="maximum parallel jobs", 69*cc02d7e2SAndroid Build Coastguard Worker) 70*cc02d7e2SAndroid Build Coastguard Workerparser.add_argument( 71*cc02d7e2SAndroid Build Coastguard Worker "--base", default=".", type=str, help="base path for generated files" 72*cc02d7e2SAndroid Build Coastguard Worker) 73*cc02d7e2SAndroid Build Coastguard Workerargs = parser.parse_args() 74*cc02d7e2SAndroid Build Coastguard Worker 75*cc02d7e2SAndroid Build Coastguard Worker 76*cc02d7e2SAndroid Build Coastguard Workerdef preprocess_build_files() -> _utils.Bunch: 77*cc02d7e2SAndroid Build Coastguard Worker """Merges build yaml into a one dictionary then pass it to plugins.""" 78*cc02d7e2SAndroid Build Coastguard Worker build_spec = dict() 79*cc02d7e2SAndroid Build Coastguard Worker for build_file in args.build_files: 80*cc02d7e2SAndroid Build Coastguard Worker with open(build_file, "r") as f: 81*cc02d7e2SAndroid Build Coastguard Worker _utils.merge_json(build_spec, yaml.safe_load(f.read())) 82*cc02d7e2SAndroid Build Coastguard Worker # Executes plugins. Plugins update the build spec in-place. 83*cc02d7e2SAndroid Build Coastguard Worker for py_file in sorted(glob.glob("tools/buildgen/plugins/*.py")): 84*cc02d7e2SAndroid Build Coastguard Worker plugin = _utils.import_python_module(py_file) 85*cc02d7e2SAndroid Build Coastguard Worker plugin.mako_plugin(build_spec) 86*cc02d7e2SAndroid Build Coastguard Worker if args.output_merged: 87*cc02d7e2SAndroid Build Coastguard Worker with open(args.output_merged, "w") as f: 88*cc02d7e2SAndroid Build Coastguard Worker f.write(yaml.dump(build_spec)) 89*cc02d7e2SAndroid Build Coastguard Worker # Makes build_spec sort of immutable and dot-accessible 90*cc02d7e2SAndroid Build Coastguard Worker return _utils.to_bunch(build_spec) 91*cc02d7e2SAndroid Build Coastguard Worker 92*cc02d7e2SAndroid Build Coastguard Worker 93*cc02d7e2SAndroid Build Coastguard Workerdef generate_template_render_jobs(templates: List[str]) -> List[jobset.JobSpec]: 94*cc02d7e2SAndroid Build Coastguard Worker """Generate JobSpecs for each one of the template rendering work.""" 95*cc02d7e2SAndroid Build Coastguard Worker jobs = [] 96*cc02d7e2SAndroid Build Coastguard Worker base_cmd = [sys.executable, "tools/buildgen/_mako_renderer.py"] 97*cc02d7e2SAndroid Build Coastguard Worker for template in sorted(templates, reverse=True): 98*cc02d7e2SAndroid Build Coastguard Worker root, f = os.path.split(template) 99*cc02d7e2SAndroid Build Coastguard Worker if os.path.splitext(f)[1] == ".template": 100*cc02d7e2SAndroid Build Coastguard Worker out_dir = args.base + root[len("templates") :] 101*cc02d7e2SAndroid Build Coastguard Worker out = os.path.join(out_dir, os.path.splitext(f)[0]) 102*cc02d7e2SAndroid Build Coastguard Worker if not os.path.exists(out_dir): 103*cc02d7e2SAndroid Build Coastguard Worker os.makedirs(out_dir) 104*cc02d7e2SAndroid Build Coastguard Worker cmd = base_cmd[:] 105*cc02d7e2SAndroid Build Coastguard Worker cmd.append("-P") 106*cc02d7e2SAndroid Build Coastguard Worker cmd.append(PREPROCESSED_BUILD) 107*cc02d7e2SAndroid Build Coastguard Worker cmd.append("-o") 108*cc02d7e2SAndroid Build Coastguard Worker if test is None: 109*cc02d7e2SAndroid Build Coastguard Worker cmd.append(out) 110*cc02d7e2SAndroid Build Coastguard Worker else: 111*cc02d7e2SAndroid Build Coastguard Worker tf = tempfile.mkstemp() 112*cc02d7e2SAndroid Build Coastguard Worker test[out] = tf[1] 113*cc02d7e2SAndroid Build Coastguard Worker os.close(tf[0]) 114*cc02d7e2SAndroid Build Coastguard Worker cmd.append(test[out]) 115*cc02d7e2SAndroid Build Coastguard Worker cmd.append(args.base + "/" + root + "/" + f) 116*cc02d7e2SAndroid Build Coastguard Worker jobs.append( 117*cc02d7e2SAndroid Build Coastguard Worker jobset.JobSpec(cmd, shortname=out, timeout_seconds=None) 118*cc02d7e2SAndroid Build Coastguard Worker ) 119*cc02d7e2SAndroid Build Coastguard Worker return jobs 120*cc02d7e2SAndroid Build Coastguard Worker 121*cc02d7e2SAndroid Build Coastguard Worker 122*cc02d7e2SAndroid Build Coastguard Workerdef main() -> None: 123*cc02d7e2SAndroid Build Coastguard Worker templates = args.templates 124*cc02d7e2SAndroid Build Coastguard Worker if not templates: 125*cc02d7e2SAndroid Build Coastguard Worker for root, _, files in os.walk("templates"): 126*cc02d7e2SAndroid Build Coastguard Worker for f in files: 127*cc02d7e2SAndroid Build Coastguard Worker templates.append(os.path.join(root, f)) 128*cc02d7e2SAndroid Build Coastguard Worker 129*cc02d7e2SAndroid Build Coastguard Worker build_spec = preprocess_build_files() 130*cc02d7e2SAndroid Build Coastguard Worker with open(PREPROCESSED_BUILD, "wb") as f: 131*cc02d7e2SAndroid Build Coastguard Worker pickle.dump(build_spec, f) 132*cc02d7e2SAndroid Build Coastguard Worker 133*cc02d7e2SAndroid Build Coastguard Worker err_cnt, _ = jobset.run( 134*cc02d7e2SAndroid Build Coastguard Worker generate_template_render_jobs(templates), maxjobs=args.jobs 135*cc02d7e2SAndroid Build Coastguard Worker ) 136*cc02d7e2SAndroid Build Coastguard Worker if err_cnt != 0: 137*cc02d7e2SAndroid Build Coastguard Worker print( 138*cc02d7e2SAndroid Build Coastguard Worker "ERROR: %s error(s) found while generating projects." % err_cnt, 139*cc02d7e2SAndroid Build Coastguard Worker file=sys.stderr, 140*cc02d7e2SAndroid Build Coastguard Worker ) 141*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 142*cc02d7e2SAndroid Build Coastguard Worker 143*cc02d7e2SAndroid Build Coastguard Worker if test is not None: 144*cc02d7e2SAndroid Build Coastguard Worker for s, g in test.items(): 145*cc02d7e2SAndroid Build Coastguard Worker if os.path.isfile(g): 146*cc02d7e2SAndroid Build Coastguard Worker assert 0 == os.system("diff %s %s" % (s, g)), s 147*cc02d7e2SAndroid Build Coastguard Worker os.unlink(g) 148*cc02d7e2SAndroid Build Coastguard Worker else: 149*cc02d7e2SAndroid Build Coastguard Worker assert 0 == os.system("diff -r %s %s" % (s, g)), s 150*cc02d7e2SAndroid Build Coastguard Worker shutil.rmtree(g, ignore_errors=True) 151*cc02d7e2SAndroid Build Coastguard Worker 152*cc02d7e2SAndroid Build Coastguard Worker 153*cc02d7e2SAndroid Build Coastguard Workerif __name__ == "__main__": 154*cc02d7e2SAndroid Build Coastguard Worker main() 155