def parse(self, target): tokens = target.get("tokens") if not tokens: return target if not self.program_re.match(tokens[0]): return target tokens.pop(0) namespace = self.parser.parse_args(tokens) if not namespace.static: logger.error("libtool: only -static mode is supported") return target output = self.context.normalize_path(namespace.output) dependencies = [] objects = [] for o in namespace.objects: objects.append( self.context.get_file_arg(self.context.normalize_path(o), dependencies) ) descr = self.platform.parse_static_lib(output) output = self.context.get_output(output) return get_module_target( ModuleTypes.static_lib, descr["target_name"], output, objects=objects, dependencies=dependencies, module_name=descr["module_name"], )
def build_object_lib_from_sources(project_name, targets_by_output, object_libs, index): lib_name = "_".join(filter(None, [project_name, "object_lib", str(index)])) output_name = "@build_dir@/" + lib_name + ".o" sources = [] for object_lib_output in object_libs: old_object_lib = targets_by_output[object_lib_output] for source in old_object_lib["sources"]: sources.append( get_source_with_inherited_flags(old_object_lib, source)) target = get_module_target( "object_lib", lib_name, output_name, compile_flags=sources[0]["compile_flags"], include_dirs=sources[0]["include_dirs"], sources=[ get_source_file_reference( source["path"], language=source["language"], dependencies=source.get("dependencies"), ) for source in sources ], ) return target
def parse(self, target): tokens = target.get("tokens") or [] if not tokens: return target if not self.filename_re.match(tokens[0]): return target if len(tokens) < 2 or tokens[1] == ":": # skip warning message return target tokens.pop(0) namespace = self.parser.parse_args(tokens) assert namespace.compile_only dependencies = [] self.process_namespace(namespace) # ml.exe doesn't support /showIncludes or anything similar add_included_dependencies( self.context, self.include_re, dependencies, namespace.include_dirs, namespace.infiles, self.context.working_dir, ) include_dirs = [] for dir in namespace.include_dirs: dir = self.context.normalize_path(dir) arg = self.context.get_dir_arg(dir, dependencies) if arg: include_dirs.append(arg) sources = [] for infile in namespace.infiles: sources.append( get_source_file_reference( self.context.get_file_arg( self.context.normalize_path(infile), dependencies), "MASM", )) output = self.context.get_output( self.context.normalize_path(namespace.output)) return get_module_target( ModuleTypes.object_lib, None, output, compile_flags=namespace.compile_flags, include_dirs=include_dirs, dependencies=dependencies, sources=sources, )
def parse(self, target): tokens = target.get("tokens") if not tokens: return target if not self.program_re.match(tokens[0]): return target tokens.pop(0) # 'ar' location doesn't matter # ArgumentParser doesn't support flags without prefix if not tokens[0].startswith("-"): tokens[0] = "-" + tokens[0] namespace, tokens = self.parser.parse_known_args(tokens) if namespace.extract: # Extract objects from previously built archive return self.extract_targets_from_archive(namespace, tokens) if namespace.table: # skip listing content of archive return [] if namespace.mode != self.Modes.default: tokens.pop(0) # 'relpos' is unused if namespace.multiple: tokens.pop(0) # 'count' is unused output = self.context.normalize_path(tokens.pop(0)) dependencies = [] objects = [] for token in tokens: objects.append( self.context.get_file_arg(self.context.normalize_path(token), dependencies)) descr = self.platform.parse_static_lib(output) output = self.context.get_output(output) return get_module_target( ModuleTypes.static_lib, descr["target_name"], output, objects=objects, dependencies=dependencies, module_name=descr["module_name"], )
def parse(self, target): tokens = target.get("tokens") or [] if not tokens: return target is_lld_link = False if self.link_re.match(tokens[0]) and is_lib_shim(tokens): is_lld_link = bool(self.lld_link_re.match(tokens[0])) tokens = tokens[2:] elif not self.filename_re.match(tokens[0]): return target else: tokens.pop(0) if len(tokens) > 0: if tokens[0] == ":": # skipping parsing output of utilities, like: `lib : warning ...` return target if is_lld_link: namespace = self.lld_link_parser.parse_args(tokens) else: namespace = self.parser.parse_args(tokens) dependencies = [] libs = [] objects = [] for infile in namespace.infiles: if os_ext.is_static_lib(infile): libs.append(self.context.get_lib_arg(infile, dependencies, [])) else: path = next( os_ext.Windows.get_obj_file_locations( infile, [], self.context.working_dir)) if self.context.find_target(self.context.get_file_arg( path)) or os.path.exists(path): objects.append( self.context.get_file_arg(path, dependencies)) else: # System object file, treat it as a linker flag # https://docs.microsoft.com/en-us/cpp/c-runtime-library/link-options objects.append(infile) output = self.context.normalize_path(namespace.output) descr = os_ext.parse_static_lib(output) module_name = descr["module_name"] name = descr["target_name"] version = descr["version"] or self.project_version output = self.context.get_output(output, dependencies) return get_module_target( ModuleTypes.static_lib, name, output, dependencies=dependencies, module_name=module_name, libs=libs, objects=objects, version=version, link_flags=namespace.link_flags, )
def parse(self, target): tokens = target.get("tokens") or [] if not tokens: return target if not self.filename_re.match(tokens[0]): return target compiler = tokens.pop(0) compiler_nrm = self.context.platform.normalize_path(compiler) if compiler_nrm in self.context.path_aliases: compiler = self.context.path_aliases[compiler_nrm] namespace = self.parser.parse_args(tokens) dependencies = [] output = self.context.get_output( self.context.normalize_path(namespace.output)) self.process_namespace(namespace) self._add_implicit_dependencies( compiler, dependencies, namespace.compile_flags, namespace.include_dirs, namespace.preinclude_files, namespace.file, self.context.working_dir, ) compile_flags = namespace.compile_flags for preinclude in namespace.preinclude_files: path = self.context.get_file_arg( self.context.normalize_path(preinclude), dependencies) compile_flags.append("-P" + path) source = get_source_file_reference( self.context.get_file_arg( self.context.normalize_path(namespace.file), dependencies), "YASM", compile_flags=compile_flags, ) self.process_namespace(source) include_dirs = [] for dir in namespace.include_dirs: # include dir may not exist, it's normal dir = self.context.normalize_path(dir) arg = self.context.get_dir_arg(dir, dependencies) if arg: include_dirs.append(arg) return get_module_target( ModuleTypes.object_lib, None, output, dependencies=dependencies, compile_flags=[], sources=[source], include_dirs=include_dirs, )
def optimize(self, targets): source_index = {} pending_sources = set() module_targets = [] for idx, target in enumerate(targets): if target["type"] == "module": module_targets.append((idx, target)) for source in target["sources"]: path = source["path"] if path in source_index: s1 = source s2 = source_index[path] if ( s1["compile_flags"] != s2["compile_flags"] or s1["include_dirs"] != s2["include_dirs"] ): pending_sources.add(path) else: source_index[path] = source if not pending_sources: return targets logger.debug( "Creating object libraries for each source file instance: %r" % pending_sources ) object_targets = [] # source + flags => object_target # this index is needed to avoid making duplicate object targets object_target_index = {} counter = {} for idx, target in module_targets: if len(target["sources"]) == 0: continue sources = [s for s in target["sources"] if s["path"] not in pending_sources] remaining = [s for s in target["sources"] if s["path"] in pending_sources] target["sources"] = sources target_output_dir = os.path.dirname(target["output"]) for source in remaining: source = get_source_with_inherited_flags(target, source) key = source_object_to_string(source) if key in object_target_index: object_target_output = object_target_index[key]["output"] else: path = source["path"] number = counter.get(path, 0) + 1 counter[path] = number object_target_name = path.replace( ParserContext.build_dir_placeholder + "/", "" ) object_target_name = object_target_name.replace( ParserContext.source_dir_placeholder + "/", "" ) object_target_name = ( object_target_name.replace("/", "_").replace(".", "_").lower() ) object_target_name += "_" + str(number) object_target_output = ( target_output_dir + "/" + object_target_name + ".o" ) object_target = get_module_target( "object_lib", object_target_name, object_target_output, compile_flags=source["compile_flags"], include_dirs=source["include_dirs"], sources=[ get_source_file_reference( path, language=source["language"], dependencies=source.get("dependencies"), ) ], ) # current target depends on object_target, which means # object_target must be declared before it. # idx: array index that should accomodate object_target object_targets.append((idx, object_target)) object_target_index[key] = object_target target["objects"].append(object_target_output) target["dependencies"].append(object_target_output) if len(target["sources"]) == 0: # All sources were replaced with object files if self.compile_flags_placeholder in target["compile_flags"]: target["compile_flags"] = [self.compile_flags_placeholder] else: target["compile_flags"] = [] if self.include_dirs_placeholder in target["include_dirs"]: target["include_dirs"] = [self.include_dirs_placeholder] else: target["include_dirs"] = [] for prop in language_specific_properties: if prop in target: target[prop] = [] counter = 0 for idx, target in object_targets: targets.insert(idx + counter, target) counter += 1 return targets
def parse(self, target): tokens = target.get("tokens") or [] if not tokens: return target if not self.filename_re.match(tokens[0]): return target compiler = tokens.pop(0) namespace = self.parser.parse_args(tokens) dependencies = [] infile = namespace.infile if not infile: assert len(namespace.preincludes) > 0 infile = namespace.preincludes.pop() self.process_namespace(namespace) source_compile_flags = copy(namespace.compile_flags) for preinclude in namespace.preincludes: path = self.context.get_file_arg( self.context.normalize_path(preinclude), dependencies) source_compile_flags.append("-P" + path) source = get_source_file_reference( self.context.get_file_arg(self.context.normalize_path(infile), dependencies), "NASM", compile_flags=source_compile_flags, ) self.process_namespace(source) output = self.context.get_output( self.context.normalize_path(namespace.output)) include_dirs = [] for dir in namespace.include_dirs: # include dir may not exist, it's normal dir = self.context.normalize_path(dir) arg = self.context.get_dir_arg(dir, dependencies) if arg: include_dirs.append(arg) self._add_implicit_dependencies( compiler, dependencies, namespace.compile_flags, namespace.include_dirs, namespace.preincludes, infile, self.context.working_dir, ) return get_module_target( ModuleTypes.object_lib, None, output, dependencies=dependencies, sources=[source], include_dirs=include_dirs, )
def parse(self, target): tokens = target.get("tokens") if not tokens: return target if not self.filename_re.match(tokens[0]): return target if len(tokens) < 2 or tokens[1] == ":": # skip warning message return target compiler = tokens.pop(0) compiler_nrm = self.context.platform.normalize_path(compiler) is_clang_cl = bool(self.clang_cl_re.match(compiler)) if compiler_nrm in self.context.path_aliases: compiler = self.context.path_aliases[compiler_nrm] if not is_clang_cl: namespace, _ = self.parser.parse_known_args( tokens, unknown_dest="compile_flags") else: namespace, _ = self.clang_cl_parser.parse_known_args( tokens, unknown_dest="compile_flags") if self.clang_cl_include_dirs is None: self.clang_cl_include_dirs = self._get_clang_cl_toolchain_include_dirs( compiler) logger.debug("clang-cl include dirs: %r" % self.clang_cl_include_dirs) lib_dirs = [] dependencies = [] if namespace.link_flags: link_flags = namespace.link_flags[0] namespace.link_flags = [] namespace, _ = self.link_parser.parse_known_args( link_flags[1:], namespace, unknown_dest=["link_flags"]) # Copied from msvc_link.py # TODO: unify msvc_cl.py, msvc_link.py, msvc_lib.py lib_dirs = list( map( lambda p: p[len("/LIBPATH:"):], filter_flags( self.ignore_link_flags_rxs, ["/LIBPATH:" + d for d in namespace.lib_dirs], ), )) for d in lib_dirs: d = self.context.normalize_path(d) relocatable_path = self.context.get_dir_arg(d) if relocatable_path.startswith("@"): # ignore non-relocatable lib dirs self.context.get_dir_arg(d, dependencies) if namespace.syntax_check_only: return target sources = [] src_info = {} objects = [] libs = [] for infile in namespace.infiles: ext = os.path.splitext(infile)[1].lower() language = None if ext in self.c_exts: language = "C" elif ext in self.cpp_exts: language = "C++" if language: src_deps = [] relocatable_path = self.context.get_file_arg( self.context.normalize_path(infile), src_deps) src_info[relocatable_path] = { "unmodified_path": infile, "dependencies": src_deps, } sources.append( get_source_file_reference(relocatable_path, language)) else: # Copied from msvc_link.py # TODO: unify msvc_cl.py, msvc_link.py, msvc_lib.py if os_ext.Windows.is_static_lib(infile): libs.append( self.context.get_lib_arg(infile, dependencies, lib_dirs)) else: paths = os_ext.Windows.get_obj_file_locations( infile, lib_dirs, self.context.working_dir) found = False for path in paths: if self.context.find_target( self.context.get_file_arg( path)) or os.path.exists(path): objects.append( self.context.get_file_arg(path, dependencies)) found = True break if not found: # System object file, treat it as a linker flag # https://docs.microsoft.com/en-us/cpp/c-runtime-library/link-options objects.append(infile) CompilerParser.process_namespace(self, namespace) include_dirs_not_relocatable = namespace.include_dirs include_dirs = list( map( lambda d: self.context.get_dir_arg( self.context.normalize_path(d), dependencies), include_dirs_not_relocatable, )) namespace.include_dirs = list(filter(lambda d: bool(d), include_dirs)) output = None if namespace.output[-1] in ["/", "\\"]: # output is a directory output_dir = self.context.normalize_path(namespace.output) else: output = self.context.normalize_path(namespace.output) output_dict = {} if output: output_dict[output] = sources else: assert namespace.compile_only # put .obj files in output_dir # separate .obj file for each source for s in sources: basename = os.path.basename(s["path"]) objfile = os.path.splitext(basename)[0] + ".obj" cur_output = os.path.join(output_dir, objfile) output_dict[cur_output] = [s] targets = [] for output, sources in output_dict.items(): deps_local = deepcopy(dependencies) original_sources = [] for s in sources: deps_local.extend(src_info[s["path"]]["dependencies"]) original_sources.append(src_info[s["path"]]["unmodified_path"]) self._add_implicit_dependencies( compiler, deps_local, namespace.compile_flags, include_dirs_not_relocatable, original_sources, self.context.working_dir, is_clang_cl=is_clang_cl, ) import_lib = None descr = {} if namespace.compile_only: module_type = ModuleTypes.object_lib elif namespace.is_dll: assert os_ext.Windows.is_shared_lib(output), output module_type = ModuleTypes.shared_lib descr = os_ext.Windows.parse_shared_lib(output) import_lib = self.context.get_output( os.path.splitext(output)[0] + ".lib", deps_local) else: module_type = ModuleTypes.executable descr = os_ext.Windows.parse_executable(output) output = self.context.get_output(output, deps_local) module_name = descr.get("module_name") name = descr.get("target_name") LinkerParser.process_namespace(self, namespace) targets.append( get_module_target( module_type, name, output, msvc_import_lib=import_lib, module_name=module_name, compile_flags=namespace.compile_flags, dependencies=deps_local, include_dirs=namespace.include_dirs, link_flags=namespace.link_flags, objects=objects, libs=libs, sources=sources, )) return targets
def parse(self, target): tokens = target.get("tokens") if not tokens: return target # .strip('{}$') is a workaround for paths like ${LDCMD:-gcc} # TODO: parameter expansion parser: http://wiki.bash-hackers.org/syntax/pe if not self.program_re.match(tokens[0].strip("{}$")): return target gcc = tokens.pop(0) gcc_nrm = self.context.platform.normalize_path(gcc) if gcc_nrm in self.context.path_aliases: gcc = self.context.path_aliases[gcc_nrm] namespace, _ = self.parser.parse_known_args( tokens, unknown_dest=["compile_flags", "link_flags"]) if namespace.mode not in [self.Mode.link, self.Mode.assemble]: return target if namespace.mode != self.Mode.link: namespace.link_flags = [] namespace.lib_dirs = [] namespace.libs = [] dependencies = [] namespace.lib_dirs = list( map( lambda p: p[2:], filter_flags(self.ignore_link_flags_rxs, ["-L" + d for d in namespace.lib_dirs]), )) LinkerParser.process_namespace(self, namespace) self._filter_lib_args(namespace, dependencies) namespace.link_flags = self._process_link_flags( namespace.link_flags, dependencies) original_srcs = [] objects = [] sources = [] for infile in namespace.infiles: infile = self.context.normalize_path(infile) assert not (self.platform.is_static_lib(infile) or self.platform.is_shared_lib(infile)), infile language = None ext = os.path.splitext(infile)[1] if ext in self.c_exts: language = "C" elif ext in self.cpp_exts: language = "C++" elif ext in self.asm_exts: language = "GASM" if language is None: objects.append(self.context.get_file_arg(infile, dependencies)) else: original_srcs.append(infile) sources.append( get_source_file_reference( self.context.get_file_arg(infile, dependencies), language)) if not sources: # Ignore compiler flags if no source files specified namespace.compile_flags = [] namespace.include_dirs = [] CompilerParser.process_namespace(self, namespace) include_dirs_not_relocatable = namespace.include_dirs namespace.include_dirs = [ self.context.get_dir_arg(self.context.normalize_path(d), dependencies) for d in include_dirs_not_relocatable ] if namespace.mode == self.Mode.assemble: if namespace.output is None: namespace.output = (os.path.basename( os.path.splitext(namespace.infiles[0])[0]) + ".o") module_type = ModuleTypes.object_lib name = None module_name = None version = None elif namespace.mode == self.Mode.link: if namespace.output is None: namespace.output = "a.out" descr = None if namespace.is_shared: descr = self.platform.parse_shared_lib(namespace.output) module_type = ModuleTypes.shared_lib else: descr = self.platform.parse_executable(namespace.output) module_type = ModuleTypes.executable if descr: module_name = descr["module_name"] name = descr["target_name"] version = descr["version"] or self.project_version else: module_name = None name = None version = self.project_version namespace.output = self.context.normalize_path(namespace.output) self._add_implicit_dependencies( gcc, dependencies, namespace.compile_flags, include_dirs_not_relocatable, original_srcs, self.platform_name, self.context.working_dir, ) namespace.output = self.context.get_output(namespace.output, dependencies) target = get_module_target( module_type, name, namespace.output, compile_flags=namespace.compile_flags, dependencies=dependencies, module_name=module_name, include_dirs=namespace.include_dirs, link_flags=namespace.link_flags, libs=namespace.libs, objects=objects, sources=sources, version=version, ) return target
def parse(self, target): tokens = target.get("tokens") or [] if not tokens: return target if not self.filename_re.match(tokens[0]): return target else: if is_lib_shim(tokens): return target if len(tokens) < 2 or tokens[1] == ":": # skip warning message return target is_lld_link = bool(self.lld_link_re.match(tokens[0])) tokens.pop(0) if is_lld_link: namespace, _ = self.lld_link_parser.parse_known_args( tokens, unknown_dest=["compile_flags", "link_flags"] ) else: namespace, _ = self.parser.parse_known_args( tokens, unknown_dest=["compile_flags", "link_flags"] ) dependencies = [] lib_dirs = list( map( lambda p: p[len("/LIBPATH:"):], filter_flags( self.ignore_link_flags_rxs, ["/LIBPATH:" + d for d in namespace.lib_dirs], ), ) ) for d in lib_dirs: d = self.context.normalize_path(d) relocatable_path = self.context.get_dir_arg(d) if relocatable_path.startswith("@"): # ignore non-relocatable lib dirs self.context.get_dir_arg(d, dependencies) objects = [] libs = [] for infile in namespace.infiles: if os_ext.Windows.is_static_lib(infile): libs.append(self.context.get_lib_arg(infile, dependencies, lib_dirs)) else: paths = os_ext.Windows.get_obj_file_locations( infile, lib_dirs, self.context.working_dir ) found = False for path in paths: if self.context.find_target( self.context.get_file_arg(path) ) or os.path.exists(path): objects.append(self.context.get_file_arg(path, dependencies)) found = True break if not found: # System object file, treat it as a linker flag # https://docs.microsoft.com/en-us/cpp/c-runtime-library/link-options objects.append(infile) namespace.link_flags = self._process_link_flags( namespace.link_flags, dependencies ) sources = [] for manifest in vars(namespace).get("manifest_files") or []: sources.append( get_source_file_reference( self.context.get_file_arg( self.context.normalize_path(manifest), dependencies ) ) ) import_lib = None if vars(namespace).get("implib"): import_lib = self.context.get_output(namespace.implib, dependencies) output = self.context.normalize_path(namespace.output) if os_ext.Windows.is_shared_lib(output): # DLL file is created if: # 1. /DLL flag was specified, or # 2. LIBRARY statement is present in provided .def file # TODO: validate these conditions here descr = os_ext.Windows.parse_shared_lib(output) module_type = ModuleTypes.shared_lib if not import_lib: import_lib = self.context.get_output( os.path.splitext(namespace.output)[0] + ".lib", dependencies ) else: descr = os_ext.Windows.parse_executable(output) module_type = ModuleTypes.executable module_name = descr["module_name"] name = descr["target_name"] version = descr["version"] or self.project_version output = self.context.get_output(output, dependencies) self.process_namespace(namespace) return get_module_target( module_type, name, output, msvc_import_lib=import_lib, dependencies=dependencies, module_name=module_name, objects=objects, libs=libs, link_flags=namespace.link_flags, sources=sources, version=version, )