def load(name, *symbols, **aliases): """Load and import symbols into current calling BUILD file Args: name: str, file name to be loaded. symbols: str*, symbol names to be imported. aliases: alias_name='real_name'*, symbol name to be imported as alias. """ src_loc = _current_source_location() if not symbols and not aliases: console.diagnose( src_loc, 'error', 'The symbols to be imported must be explicitly declared') extension_globals = _load_extension(name) def error(symbol): console.diagnose(src_loc, 'error', '"%s" is not defined in "%s"' % (symbol, name)) # Only import declared symbols into current file for symbol in symbols: if symbol not in extension_globals: error(symbol) continue __current_globals[symbol] = extension_globals[symbol] for alias, real_name in aliases.items(): if real_name not in extension_globals: error(real_name) continue __current_globals[alias] = extension_globals[real_name]
def _load_extension(name): """Load symbols from file or obtain from loaded cache.""" full_path = _expand_include_path(name) if full_path in __loaded_extension_info: return __loaded_extension_info[full_path] if not os.path.isfile(full_path): console.diagnose(_current_source_location(), 'error', 'File "%s" does not exist' % name) return {} # The symbols in the current context should be invisible to the extension, # make an isolated symbol set to implement this approach. origin_globals = build_rules.get_all() extension_globals = origin_globals.copy() exec_file(full_path, extension_globals, None) # Extract new symbols result = {} for symbol, value in extension_globals.items(): if symbol.startswith('_'): continue if symbol in origin_globals and value is origin_globals[symbol]: continue if isinstance(value, types.ModuleType): continue result[symbol] = value __loaded_extension_info[full_path] = result return result
def include(name): """Include another file into current BUILD file""" full_path = _expand_include_path(name) if not os.path.isfile(full_path): console.diagnose(_current_source_location(), 'error', 'File "%s" does not exist' % name) return exec_file(full_path, __current_globals, None)
def _check_direct_headers(self, full_src, direct_hdrs, suppressd_hdrs, missing_dep_hdrs, undeclared_hdrs, check_msg): """Verify directly included header files is in deps.""" msg = [] for hdr in direct_hdrs: if hdr in self.declared_hdrs: console.diagnose(self.source_location, 'debug', '"%s" is a declared header' % (hdr)) continue libs = self.find_libs_by_header(hdr) if not libs: libs = self.find_targets_by_private_hdr(hdr) if libs and self.key not in libs: msg.append(' "%s" is a private header file of %s' % ( hdr, self._or_joined_libs(libs))) continue console.diagnose(self.source_location, 'debug', '"%s" is an undeclared header' % hdr) undeclared_hdrs.add(hdr) # We need also check suppressd_hdrs because target maybe not loaded in partial build if hdr not in suppressd_hdrs and not self.is_allowed_undeclared_hdr(hdr): msg.append(' %s' % self._header_undeclared_message(hdr)) continue deps = set(self.deps + [self.key]) # Don't forget target itself if not (libs & deps): # pylint: disable=superfluous-parens # NOTE: # We just don't report a suppressd hdr, but still need to record it as a failure. # Because a passed src will not be verified again, even if we remove it from the # suppress list. # Same reason in the _check_generated_headers. missing_dep_hdrs.add(hdr) if hdr not in suppressd_hdrs: msg.append(' For %s' % self._hdr_declaration_message(hdr, libs)) if msg: check_msg.append(' In file included from "%s",' % full_src) check_msg += msg
def includes_iterator(): results = [] for pattern in include: if not pattern: console.diagnose(source_loc, 'error', '"glob": Empty pattern is not allowed') continue for path in source_dir.glob(pattern): if path.is_file() and not path.name.startswith('.'): results.append(path.relative_to(source_dir)) return results
def wrapper(*args, **kwargs): src_loc = util.calling_source_location( 1) # Skip the `wrapper` function error = '"%s" is forbidden in blade, please use the builtin `blade` module' % name console.diagnose(src_loc, 'error', error)
def wrapper(message): console.diagnose(util.calling_source_location(1), severity, message)
def glob(include, exclude=None, excludes=None, allow_empty=False): """This function can be called in BUILD to specify a set of files using patterns. Args: include:List[str], file patterns to be matched. exclude:Optional[List[str]], file patterns to be removed from the result. allow_empty:bool: Whether a empty result is a error. Patterns may contain shell-like wildcards, such as * , ? , or [charset]. Additionally, the path element '**' matches any subpath. """ from blade import build_manager # pylint: disable=import-outside-toplevel source_dir = Path(build_manager.instance.get_current_source_path()) source_loc = _current_source_location() include = var_to_list(include) severity = config.get_item('global_config', 'glob_error_severity') if excludes: console.diagnose(source_loc, severity, '"excludes" is deprecated, use "exclude" instead') exclude = var_to_list(exclude) + var_to_list(excludes) def includes_iterator(): results = [] for pattern in include: if not pattern: console.diagnose(source_loc, 'error', '"glob": Empty pattern is not allowed') continue for path in source_dir.glob(pattern): if path.is_file() and not path.name.startswith('.'): results.append(path.relative_to(source_dir)) return results def is_special(pattern): return '*' in pattern or '?' in pattern or '[' in pattern non_special_excludes = set() match_excludes = set() for pattern in exclude: if is_special(pattern): match_excludes.add(pattern) else: non_special_excludes.add(pattern) def exclusion(path): if str(path) in non_special_excludes: return True for pattern in match_excludes: ret = path.match(pattern) if ret: return True return False result = sorted({str(p) for p in includes_iterator() if not exclusion(p)}) if not result and not allow_empty: args = repr(include) if exclude: args += ', exclude=%s' % repr(exclude) console.diagnose( source_loc, severity, '"glob(%s)" got an empty result. If it is the expected behavior, ' 'specify "allow_empty=True" to eliminate this message' % args) return result
def error(symbol): console.diagnose(src_loc, 'error', '"%s" is not defined in "%s"' % (symbol, name))
def check(self): """ Check whether included header files is declared in "deps" correctly. Returns: Whether nothing is wrong. """ missing_details = {} # {src: list(hdrs)} undeclared_hdrs = set() all_direct_hdrs = set() all_generated_hdrs = set() direct_check_msg = [] generated_check_msg = [] def check_file(src, full_src, is_header): if util.path_under_dir(full_src, self.build_dir): # Don't check generated files. return path = self._find_inclusion_file(src, is_header) if not path: console.warning('No inclusion file found for %s' % full_src) return direct_hdrs, stacks = _parse_inclusion_stacks(path, self.build_dir) all_direct_hdrs.update(direct_hdrs) missing_dep_hdrs = set() self._check_direct_headers( full_src, direct_hdrs, self.suppress.get(src, []), missing_dep_hdrs, undeclared_hdrs, direct_check_msg) for stack in stacks: all_generated_hdrs.add(stack[-1]) # But direct headers can not cover all, so it is still useful self._check_generated_headers( full_src, stacks, direct_hdrs, self.suppress.get(src, []), missing_dep_hdrs, generated_check_msg) if missing_dep_hdrs: missing_details[src] = list(missing_dep_hdrs) for src, full_src in self.expanded_srcs: check_file(src, full_src, is_header=False) for hdr, full_hdr in self.expanded_hdrs: check_file(hdr, full_hdr, is_header=True) severity = self.severity if direct_check_msg: console.diagnose(self.source_location, severity, '%s: Missing dependency declaration:\n%s' % (self.name, '\n'.join(direct_check_msg))) if generated_check_msg: console.diagnose(self.source_location, severity, '%s: Missing indirect dependency declaration:\n%s' % (self.name, '\n'.join(generated_check_msg))) ok = (severity != 'error' or not direct_check_msg and not generated_check_msg) details = {} if missing_details: details['missing_dep'] = missing_details if undeclared_hdrs: details['undeclared'] = sorted(undeclared_hdrs) details['direct_hdrs'] = all_direct_hdrs details['generated_hdrs'] = all_generated_hdrs return ok, details