Exemple #1
0
    def generate(self, target):
        if target["type"] != "class":
            return False

        with self.context.open("CMakeLists.txt", "a") as f:
            for name, values in sorted(target["properties"].items(),
                                       key=self.property_sort_key):
                self.handlers[name](f, name, flatten_list(values),
                                    target["conditions"])

            return True
Exemple #2
0
    def _add_implicit_dependencies(
        self,
        compiler,
        dependencies,
        compile_flags,
        include_dirs,
        sources,
        cwd,
        is_clang_cl=False,
    ):
        if not sources:
            return

        _compiler = os.path.join(cwd, compiler)
        if os.path.exists(_compiler):
            compiler = _compiler

        cmd = [compiler, "/Zs", "/showIncludes"]
        cmd += ["-I" + d
                for d in include_dirs] + flatten_list(compile_flags) + sources
        try:
            stdout, stderr = subprocess_ex.check_output(cmd, cwd=cwd)
        except subprocess_ex.CalledProcessError as e:
            logger.error("{}\nstderr:\n{}stdout:\n{}".format(
                e, e.stderr, e.stdout))
            return

        toolchain_include_dirs = self.msvc_include_dirs
        if is_clang_cl and self.clang_cl_include_dirs:
            toolchain_include_dirs = self.clang_cl_include_dirs
        for line in stdout.splitlines():
            m = self.include_note_re.match(line.strip())
            if m:
                try:
                    path = self.context.normalize_path(m.group("path"))
                    is_toolchain_header = False
                    for d in toolchain_include_dirs:
                        if path.startswith(d):
                            is_toolchain_header = True
                            break
                    if not is_toolchain_header:
                        self.context.get_file_arg(path, dependencies)
                except ValueError:
                    # Path is on drive c:, build dir on drive d:
                    pass
Exemple #3
0
    def generate(self, target):
        if not target["type"] == "module":
            return False

        with self.context.open("CMakeLists.txt", "a") as f:
            pthread_found = False
            if "-pthread" in target.get("link_flags", []):
                target["link_flags"].remove("-pthread")
                pthread_found = True
            if "-pthread" in target.get("compile_flags", []):
                target["compile_flags"].remove("-pthread")
                pthread_found = True
            if pthread_found:
                if "libs" not in target:
                    target["libs"] = []
                target["libs"].append("Threads::Threads")

            # Additional newline between targets
            f.write("\n")

            cmake_supported_srcs = list(
                filter(lambda t: t["language"] != "YASM", target["sources"]))
            specify_linker_language = False
            if ("ASM_MASM" in self.context.languages
                    and "ASM_NASM" in self.context.languages):
                masm_srcs = [
                    x["path"] for x in cmake_supported_srcs
                    if x["language"] == "MASM"
                ]
                if masm_srcs:
                    # TODO: use set_language()
                    s = self.context.format_call(
                        "set_source_files_properties",
                        [],
                        masm_srcs,
                        ["PROPERTIES", "LANGUAGE", "ASM_MASM"],
                    )
                    f.write(s)
                nasm_srcs = [
                    x["path"] for x in cmake_supported_srcs
                    if x["language"] == "NASM"
                ]
                if nasm_srcs:
                    # TODO: use set_language()
                    s = self.context.format_call(
                        "set_source_files_properties",
                        [],
                        nasm_srcs,
                        ["PROPERTIES", "LANGUAGE", "ASM_NASM"],
                    )
                    f.write(s)
                if len(nasm_srcs) + len(masm_srcs) == len(
                        cmake_supported_srcs):
                    # CMake gets confused if target has only ASM files
                    specify_linker_language = True

            for source in target["sources"]:
                compile_flags = join_nested_lists(source.get("compile_flags"))
                if compile_flags:
                    compile_flags = self.context.process_compile_flags(
                        compile_flags)
                    # TODO: use source_compile_options()
                    path_ = source["path"]
                    value_ = self.context.join_property_values(compile_flags)
                    # If COMPILE_OPTIONS was previosly set for current source file,
                    # now it's going to be overwritted even for previosly created
                    # targets that use this source file.
                    # Make sure that COMPILE_OPTIONS is set only once.
                    validation_dict = self.context.cmake_module_source_compile_flags_
                    if path_ not in validation_dict:
                        validation_dict[path_] = value_
                        s = self.context.format_call(
                            "set_source_files_properties",
                            [path_, "PROPERTIES", "COMPILE_OPTIONS"],
                            [value_],
                        )
                        f.write(s)
                    elif validation_dict[path_] != value_:
                        prev_value = validation_dict[path_]
                        raise ValueError(
                            "{}: incompatible compile flags: {} vs {}.".format(
                                path_, prev_value, value_))
                include_dirs = source.get("include_dirs")
                if include_dirs:
                    # TODO: use source_include_directories()
                    path_ = source["path"]
                    value_ = self.context.join_property_values(include_dirs)
                    # If INCLUDE_DIRECTORIES was previosly set for current source file,
                    # now it's going to be overwritted even for previosly created
                    # targets that use this source file.
                    # Make sure that INCLUDE_DIRECTORIES is set only once.
                    validation_dict = self.context.cmake_module_source_include_dirs_
                    if path_ not in validation_dict:
                        validation_dict[path_] = value_
                        s = self.context.format_call(
                            "set_source_files_properties",
                            [path_, "PROPERTIES", "INCLUDE_DIRECTORIES"],
                            [value_],
                        )
                        f.write(s)
                    elif validation_dict[path_] != value_:
                        prev_value = validation_dict[path_]
                        raise ValueError(
                            "{}: incompatible include dirs: {} vs {}.".format(
                                path_, prev_value, value_))

            sources_arg = list(map(lambda t: t["path"], cmake_supported_srcs))
            system_objects = []
            if target.get("objects"):
                object_targets = []
                external_objects = []
                for obj in target["objects"]:
                    object_target = self.context.target_index.get(obj)
                    if object_target:
                        if object_target["type"] in ("module", "module_copy"):
                            object_targets.append(object_target)
                        else:
                            external_objects.append(obj)
                    elif obj.startswith(self.context.source_dir_placeholder):
                        external_objects.append(obj)
                    else:
                        system_objects.append(obj)
                if (object_targets or external_objects) and not sources_arg:
                    # CMake gets confused if target has only object files
                    specify_linker_language = True
                for t in object_targets:
                    if t.get("post_build_commands"):
                        # OBJECT libraries don't support POST_BUILD commands.
                        # To work around that, we can use CMake knowledge:
                        # * OBJECT libraries don't support setting output location and
                        #   $<TARGET_OBJECTS:target> (CMake build path) != target['output'] (path expected by BOM).
                        # We can add a add_custom_command that processes $<TARGET_OBJECTS:target>
                        # and outputs processed object to target['output'].
                        # So instead of $<TARGET_OBJECTS:>, we should reference target['output'] here.
                        sources_arg.append(t["output"])
                    else:
                        sources_arg.append("$<TARGET_OBJECTS:{}>".format(
                            t["name"]))
                if external_objects:
                    sources_arg += external_objects
                    s = self.context.format_call(
                        "set_source_files_properties",
                        [],
                        external_objects,
                        ["PROPERTIES", "EXTERNAL_OBJECT", "ON"],
                    )
                    f.write(s)

            yasm_srcs = list(
                filter(lambda t: t["language"] == "YASM", target["sources"]))
            # CMake targets cannot have no sources except interface libraries.
            # Sources can be specified during target definition
            # (add_lirary, add_executable), or via target_sources() call.
            if not sources_arg and (target["module_type"] !=
                                    ModuleTypes.object_lib or not yasm_srcs):
                if target["module_type"] != ModuleTypes.interface:
                    sources_arg = self.create_empty_source_arg(f)

            format_target_func = {
                ModuleTypes.object_lib: self.context.format_object_library,
                ModuleTypes.static_lib: self.context.format_static_library,
                ModuleTypes.shared_lib: self.context.format_shared_library,
                ModuleTypes.executable: self.context.format_executable,
                ModuleTypes.interface: self.context.format_interface,
            }
            f.write(format_target_func[target["module_type"]](target["name"],
                                                              sources_arg))

            if specify_linker_language:
                assert (len(self.context.languages) >
                        0), "At least one language must be enabled"
                # Probably any other LINKER_LANGUAGE value will work as well
                s = self.context.format_call(
                    "set_target_properties",
                    [target["name"]],
                    [
                        "PROPERTIES", "LINKER_LANGUAGE",
                        self.context.languages[0]
                    ],
                )
                f.write(s)

            link_flags = system_objects
            if target.get("link_flags"):
                link_flags += target["link_flags"]

            if link_flags:
                f.write(
                    self.context.format_link_flags(target["name"], link_flags))

            link_libraries = []
            if target.get("libs"):
                prev_gcc_whole_archive = False
                for lib in target["libs"]:
                    gcc_whole_archive = False
                    if isinstance(lib, dict):
                        gcc_whole_archive = bool(lib.get("gcc_whole_archive"))
                        value = lib["value"]
                    else:
                        value = lib
                    lib_target = self.context.target_index.get(value)
                    if lib_target:
                        if lib_target["type"] in ["module", "module_copy"]:
                            value = lib_target["name"]
                        elif value.endswith(".lib"):
                            value = value[:-4]  # remove .lib suffix
                    else:
                        if value.endswith(".lib"):
                            value = value[:-4]  # remove .lib suffix
                        if value in system_library_map:
                            value = system_library_map[value]
                    if value:
                        if gcc_whole_archive != prev_gcc_whole_archive:
                            if gcc_whole_archive:
                                link_libraries.append("-Wl,-whole-archive")
                            else:
                                link_libraries.append("-Wl,-no-whole-archive")
                            prev_gcc_whole_archive = gcc_whole_archive
                        link_libraries.append(value)
                if prev_gcc_whole_archive:
                    link_libraries.append("-Wl,-no-whole-archive")

            visibility = "PRIVATE"
            if target["module_type"] == ModuleTypes.interface:
                visibility = "INTERFACE"

            if link_libraries:
                s = self.context.format_call(
                    "target_link_libraries",
                    [target["name"], visibility],
                    link_libraries,
                )
                f.write(s)

            if target["compile_flags"]:
                values = target["compile_flags"]
                values = self.context.process_compile_flags(values)
                s = self.context.format_call(
                    "target_compile_options",
                    [target["name"], visibility],
                    join_nested_lists(values),
                )
                f.write(s)

            if target["include_dirs"]:
                s = self.context.format_call(
                    "target_include_directories",
                    [target["name"], visibility],
                    target["include_dirs"],
                )
                f.write(s)

            language_flag_properties = sorted(
                self.context.language_flag_properties.items(),
                key=operator.itemgetter(1),
            )
            for property, language in language_flag_properties:
                flags = target.get(property)
                if not flags:
                    continue
                flags = self.context.process_compile_flags(flags)
                s = self.context.format_call(
                    "target_language_compile_options",
                    [target["name"], language, "PRIVATE"],
                    flatten_list(flags),
                )
                f.write(s)

            language_include_properties = sorted(
                self.context.language_include_properties.items(),
                key=operator.itemgetter(1),
            )
            for property, language in language_include_properties:
                dirs = target.get(property)
                if not dirs:
                    continue
                s = self.context.format_call(
                    "target_language_include_directories",
                    [target["name"], language, "PRIVATE"],
                    dirs,
                )
                f.write(s)

            if yasm_srcs:
                yasm_sources_arg = list(map(lambda t: t["path"], yasm_srcs))
                s = self.context.format_call("target_yasm_sources",
                                             [target["name"], "PRIVATE"],
                                             yasm_sources_arg)
                f.write(s)

            if target["dependencies"]:
                s = self.context.format_dependencies(target)
                f.write(s)

            if target["module_type"] != ModuleTypes.interface:
                if target["module_name"] and target["module_name"] != target[
                        "name"]:
                    s = self.context.format_call(
                        "set_target_properties",
                        [target["name"], "PROPERTIES", "OUTPUT_NAME"],
                        [target["module_name"]],
                    )
                    f.write(s)

            msvc_import_lib = target.get("msvc_import_lib")
            if msvc_import_lib and self.context.platform == "windows":
                if isinstance(msvc_import_lib, list):
                    msvc_import_lib = msvc_import_lib[0]
                descr = Windows.parse_import_lib(msvc_import_lib)
                if descr["module_name"] != target["module_name"]:
                    # Custom import lib name
                    s = self.context.format_call(
                        "set_target_properties",
                        [target["name"], "PROPERTIES", "ARCHIVE_OUTPUT_NAME"],
                        [descr["module_name"]],
                    )
                    f.write(s)

            if (target["module_type"] != ModuleTypes.object_lib
                    and not self.context.flat_build_dir):
                output_dir = get_target_output_dir(target)
                if output_dir != self.context.build_dir_placeholder:
                    output_dir = output_dir.replace(
                        self.context.build_dir_placeholder + "/", "")
                    requires_archive_output_dir = False
                    requires_runtime_output_dir = False
                    if target["module_type"] == ModuleTypes.shared_lib:
                        if self.context.platform == "windows":
                            requires_runtime_output_dir = True
                            requires_archive_output_dir = True
                        else:
                            s = self.context.format_call(
                                "set_target_output_subdir",
                                [target["name"], "LIBRARY_OUTPUT_DIRECTORY"],
                                [output_dir],
                            )
                            f.write(s)
                    if target["module_type"] == ModuleTypes.static_lib:
                        requires_archive_output_dir = True
                    elif target["module_type"] != ModuleTypes.interface:
                        requires_runtime_output_dir = True
                    if requires_runtime_output_dir:
                        s = self.context.format_call(
                            "set_target_output_subdir",
                            [target["name"], "RUNTIME_OUTPUT_DIRECTORY"],
                            [output_dir],
                        )
                        f.write(s)
                    if requires_archive_output_dir:
                        s = self.context.format_call(
                            "set_target_output_subdir",
                            [target["name"], "ARCHIVE_OUTPUT_DIRECTORY"],
                            [output_dir],
                        )
                        f.write(s)

            version = target.get("version")
            if version is not None:
                s = self.context.format_call(
                    "set_target_properties",
                    [target["name"], "PROPERTIES", "VERSION"],
                    [version],
                )
                f.write(s)

            compatibility_version = target.get("compatibility_version")
            if compatibility_version is not None:
                s = self.context.format_call(
                    "set_target_properties",
                    [target["name"], "PROPERTIES", "SOVERSION"],
                    [compatibility_version],
                )
                f.write(s)

        post_build_commands = target.get("post_build_commands") or []
        for cmd_target in post_build_commands:
            cmd_target = cmd_target.copy()
            cmd_target["type"] = "cmd"
            if target["module_type"] == ModuleTypes.object_lib:
                # CMake doesn't support POST_BUILD commands for OBJECT libraries
                # See further explanation above (search POST_BUILD)
                cmd_target["name"] = target["name"] + "_post_build"
                cmd_target["dependencies"] = [target["output"]]
                cmd_target["output"] = target["output"]
                pass
            else:
                cmd_target["post_build"] = target["name"]
            self.cmd_generator.generate(cmd_target)

        return True
    def generate(self, target):
        if not target["type"] == "cmd":
            return False

        with self.context.open("CMakeLists.txt", "a") as f:
            program = target["program"]
            program_var = self.found_programs.get(program)
            if program_var is None:
                program_var = self.var_prefix + program.upper()
                s = self.context.format(find_program_tmpl,
                                        name=program,
                                        var=program_var)
                f.write(s)
                self.found_programs[program] = program_var

            working_dir = target.get("working_dir")
            if working_dir is None:
                working_dir = ""
            else:
                working_dir = '\n    WORKING_DIRECTORY "{}"'.format(
                    working_dir)

            object_lib_deps = []
            cmd_dependencies_str = ""
            dependencies = target.get("dependencies")
            if dependencies:
                cmd_dependencies_str = "\n    DEPENDS"
                for dep in dependencies:
                    _tgt = self.context.target_index.get(dep)
                    if _tgt:
                        if _tgt.get("type") == "module":
                            if _tgt["module_type"] == "object_lib":
                                object_lib_deps.append(_tgt)
                            dep = _tgt["name"]
                        elif _tgt.get("type") == "directory":
                            # Don't add directory dependencies, it's unnecessary
                            continue
                    cmd_dependencies_str += "\n        {}".format(dep)

            command_args = []
            for arg in flatten_list(target["args"]):
                if arg in self.context.target_index:
                    arg_target = self.context.target_index[arg]
                    if (arg_target["type"] == "module"
                            and arg_target["module_type"] != "object_lib"):
                        arg = "$<TARGET_FILE:{}>".format(arg_target["name"])
                command_args.append(arg)
            command = ["${" + program_var + "}"] + command_args

            params = ""
            if target.get("parameters"):
                params = " " + " ".join(
                    "{}={}".format(k, v)
                    for k, v in target["parameters"].items())

            command_str = self.context.format(
                command_tmpl,
                params=params,
                command=" ".join(command),
            )
            # CMake doesn't provide built-in way to set object file path.
            # Explicitly copy object files to expected locations
            for _tgt in object_lib_deps:
                cmd = '${{CMAKE_COMMAND}} -E copy_if_different "$<TARGET_OBJECTS:{name}>" "{output}"'.format(
                    name=_tgt["name"],
                    output=_tgt["output"],
                )
                command_str = (self.context.format(
                    command_tmpl,
                    params=params,
                    command=cmd,
                ) + command_str)

            post_build_target_name = target.get("post_build")
            if post_build_target_name:
                s = self.context.format(
                    post_build_cmd_tmpl,
                    target_name=post_build_target_name,
                    command=command_str,
                    working_dir=working_dir,
                )
            else:
                output = target["output"]
                for o in target.get("msvc_import_lib") or []:
                    output += "\n    " + o
                name = target.get("name")
                if name is None:
                    name = output.split("@")[-1][1:].replace(".", "_").replace(
                        "/", "_")
                s = self.context.format(
                    cmd_tmpl,
                    name=name,
                    output=output,
                    command=command_str,
                    depends=cmd_dependencies_str,
                    working_dir=working_dir,
                )
            f.write(s)

        return True
Exemple #5
0
 def format_link_flags(self, target_name, flags):
     return self.format_call("target_link_options",
                             [target_name, "PRIVATE"], flatten_list(flags))
Exemple #6
0
    def _add_implicit_dependencies(
        self,
        compiler,
        dependencies,
        compile_flags,
        include_dirs,
        sources,
        target_platform,
        cwd,
    ):
        if not sources:
            return

        include_dir_args = ["-I" + d for d in include_dirs]
        sources = [s for s in sources]

        host_system = platform.system().lower()
        if compiler.find("clang") != -1:
            if host_system == "windows" and target_platform != host_system:
                # to avoid errors like:
                # * clang++.exe: error: unsupported option '-fPIC' for target 'x86_64-pc-windows-msvc'
                if target_platform == "linux":
                    compile_flags = compile_flags + [
                        "--target=i686-pc-linux-gnu"
                    ]
                if target_platform == "darwin":
                    compile_flags = compile_flags + [
                        "--target=i686-apple-darwin10"
                    ]

        implicit_dependencies = []
        implicit_dependencies_set = set()
        for compile_flags in self._split_compile_flags_for_multiarch(
                compile_flags):
            cmd = ([compiler, "-M"] + flatten_list(compile_flags) +
                   include_dir_args + sources)
            p = subprocess.Popen(cmd,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 cwd=cwd)
            stdout, stderr = p.communicate()
            if type(stdout) is not str:
                stdout = stdout.decode("utf-8", "replace")
            if type(stderr) is not str:
                stderr = stderr.decode("utf-8", "replace")
            retcode = p.poll()
            if retcode:
                cmd_str = " ".join(cmd)
                logger.error(
                    "Command '{}' returned non-zero exit code {}\n{}".format(
                        cmd_str, retcode, stderr))

            for line in stdout.splitlines():
                files = line.rstrip("\\").lstrip().split(" ")
                for f in files:
                    if not f or f.endswith(":") or f in sources:
                        continue
                    if f not in implicit_dependencies_set:
                        implicit_dependencies.append(f)
                        implicit_dependencies_set.add(f)

        return [
            self.context.get_file_arg(self.context.normalize_path(dep),
                                      dependencies)
            for dep in implicit_dependencies
        ]