def load_rules( flags: Dict[str, object], *, skip_version_check: bool, workspace: bool = False, ): flags = Struct(flags, default=True) repo_root = find_root() src_files = get_repo_files() build_files = (["WORKSPACE"] if workspace else [ file for file in src_files if file.split("/")[-1] == "BUILD" ]) target_rule_lookup = TargetLookup() sys.path.insert(0, repo_root) callback, find, resolve = make_callback(repo_root, None, set(src_files), target_rule_lookup) config.skip_version_check = skip_version_check for build_file in build_files: make_callback.build_root = os.path.dirname(build_file) with open(build_file) as f: frame = {} load = make_load_rules(repo_root, build_file) __builtins__["callback"] = callback __builtins__["find"] = find __builtins__["resolve"] = resolve __builtins__["load"] = load __builtins__["flags"] = flags frame = { **frame, "__builtins__": __builtins__, } reset_mock_imports( frame, ["callback", "find", "load", "flags", "resolve"]) if workspace: __builtins__["config"] = config config.active = True reset_mock_imports(frame, ["config"]) start_time_stack.append(time.time()) try: exec(f.read(), frame) config.active = False except Exception: raise BuildException( f"Error while processing BUILD file {build_file}:\n" + f"\n{Style.RESET_ALL}" + traceback.format_exc()) TIMINGS[build_file] = time.time() - start_time_stack.pop() make_callback.build_root = None return target_rule_lookup
def load_rules( flags: Dict[str, object], *, skip_version_check: bool, workspace: bool = False, ): flags = Struct(flags, default=True) src_files = get_repo_files() build_files = ( ["WORKSPACE"] if workspace else [file for file in src_files if file.split("/")[-1] == "BUILD"] ) target_rule_lookup = TargetLookup() macros = {} callback, find, resolve, macro, make_depset = make_callback( None, set(src_files), target_rule_lookup, macros, ) config.skip_version_check = skip_version_check for build_file in build_files: make_callback.build_root = os.path.dirname(build_file) with open(build_file) as f: frame = {} load = make_load_rules(build_file) __builtins__["callback"] = __builtins__["rule"] = callback __builtins__["find"] = find __builtins__["resolve"] = resolve __builtins__["load"] = load __builtins__["flags"] = flags __builtins__["macro"] = macro __builtins__["provider"] = provider __builtins__["depset"] = make_depset __builtins__["DepsProvider"] = DepsProvider __builtins__["OutputProvider"] = OutputProvider __builtins__["TransitiveDepsProvider"] = TransitiveDepsProvider __builtins__["TransitiveOutputProvider"] = TransitiveOutputProvider frame = { **frame, "__builtins__": __builtins__, } if workspace: __builtins__["config"] = config config.active = True start_time_stack.append(time.time()) try: exec(compile(f.read(), build_file, "exec"), frame) config.active = False except Exception: raise BuildException( f"Error while processing BUILD file {build_file}:\n" + f"\n{Style.RESET_ALL}" + traceback.format_exc() ) TIMINGS[build_file] = time.time() - start_time_stack.pop() make_callback.build_root = None return target_rule_lookup, macros
def cli( targets: Tuple[str], profile: bool, shell_log: bool, locate: bool, verbose: bool, quiet: bool, skip_version_check: bool, skip_setup: bool, skip_build: bool, clean: bool, num_threads: int, state_directory: str, cache_directory: str, flags: List[str], ): """ This is a `make` alternative with a simpler syntax and some useful features. """ try: repo_root = find_root() os.chdir(repo_root) if verbose: enable_logging() if profile or shell_log: enable_profiling() flags = [flag.split("=", 1) + ["true"] for flag in flags] flags = {flag[0].lower(): loads(flag[1]) for flag in flags} if not skip_setup: setup_rule_lookup, macros = load_rules( flags, workspace=True, skip_version_check=skip_version_check) if macros: raise BuildException( "Macros are not supported in setup rules.") setup_targets = [ target[5:] for target in targets if target.startswith("setup:") ] if locate and setup_targets: raise BuildException( "--locate cannot be used with setup rules - they are declared in WORKSPACE" ) setup_targets = setup_targets or ([ config.default_setup_rule ] if config.default_setup_rule else []) initialize_workspace( setup_rule_lookup, setup_targets, state_directory, quiet, ) target_rule_lookup, macros = load_rules( flags, skip_version_check=skip_version_check) target_rule_lookup.verify() all_files = get_repo_files() source_files = target_rule_lookup.find_source_files(all_files) if profile: print("Slow Build / Rules Files (Loading Phase):") slowest = sorted(LOAD_TIMINGS, key=lambda x: LOAD_TIMINGS[x], reverse=True)[:20] for key in slowest: print(key, LOAD_TIMINGS[key]) print() need_target = locate or not skip_build if not targets and need_target: if config.default_build_rule is None: raise BuildException( "No target provided, and no default target set.") targets = [config.default_build_rule] if locate: for target in targets: if target in source_files: raise BuildException( f"Target {target} is a source file, not a build target." ) rule = target_rule_lookup.try_lookup(target) if rule is None and not target.startswith(":"): rule = target_rule_lookup.try_lookup(f":{target}") if rule is None: raise BuildException(f"Target {target} was not found.") print( f"Target {target} is declared by {rule} in {rule.location}/BUILD." ) exit(0) if not skip_build: for _ in range(2 if clean else 1): if clean: for out_dir in config.output_directories: rmtree(out_dir, ignore_errors=True) for rule in set( target_rule_lookup.direct_lookup.values()) | set( target_rule_lookup.direct_lookup.values()): rule.pending_rule_dependencies = set() rule.runtime_dependents = set() run_build( BuildState( target_rule_lookup=target_rule_lookup, source_files=source_files, cache_directory=cache_directory, macros=macros, ), [ target for target in targets if not target.startswith("setup:") ], num_threads, quiet, ) if profile: print("Slow Rules (Execution Phase):") slowest = sorted(BUILD_TIMINGS, key=lambda x: BUILD_TIMINGS[x], reverse=True)[:20] for key in slowest: print(key, BUILD_TIMINGS[key]) print("Cache Statistics") print( f"{STATS['hits']} cache hits, {STATS['misses']} cache misses, {STATS['inserts']} cache inserts (approx)" ) except BuildException as e: display_error(e) exit(1) except Exception as e: display_error(BuildException("Internal error: " + repr(e))) print(f"\n{Style.RESET_ALL}" + traceback.format_exc()) exit(1)