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
Example #2
0
    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,
        )
Example #3
0
    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,
        )
Example #4
0
    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
Example #5
0
    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,
        )
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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,
        )