--!A cross-platform build utility based on Lua -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Copyright (C) 2015-present, Xmake Open Source Community. -- -- @author ruki -- @file cmakelists.lua -- -- imports import("core.base.colors") import("core.project.project") import("core.project.config") import("core.tool.compiler") import("core.base.semver") import("core.base.hashset") import("core.project.rule") import("core.platform.platform") import("lib.detect.find_tool") import("private.utils.batchcmds") import("private.utils.target", {alias = "target_utils"}) import("plugins.project.utils.target_cmds", {rootdir = os.programdir()}) import("rules.c++.modules.support", {alias = "module_support", rootdir = os.programdir()}) import("private.utils.executable_path") -- get cmake version function _get_cmake_version() local cmake_version = _g.cmake_version if not cmake_version then local cmake = find_tool("cmake", {version = true}) if cmake and cmake.version then cmake_version = semver.new(cmake.version) end _g.cmake_version = cmake_version end return cmake_version end -- has c++ modules sources function _has_cxxmodules_sources() for _, target in ipairs(project.ordertargets()) do if module_support.contains_modules(target) then return true end end end -- get minimal cmake version function _get_cmake_minver() local cmake_minver = _g.cmake_minver if not cmake_minver then cmake_minver = _get_cmake_version() if cmake_minver then if _has_cxxmodules_sources() and cmake_minver:gt("3.28.0") then cmake_minver = semver.new("3.28.0") elseif cmake_minver:gt("3.15.0") then cmake_minver = semver.new("3.15.0") end end if not cmake_minver then cmake_minver = semver.new("3.15.0") end _g.cmake_minver = cmake_minver end return cmake_minver end -- tranlate path function _translate_path(filepath, outputdir) filepath = path.translate(filepath) if filepath == "" then return "" end if path.is_absolute(filepath) then if filepath:startswith(project.directory()) then return path.relative(filepath, outputdir) end return filepath else return path.relative(path.absolute(filepath), outputdir) end end -- escape path function _escape_path(filepath) if is_host("windows") then filepath = path.unix(filepath) filepath = filepath:gsub(' ', '\\ ') end return filepath end -- escape path in flag -- @see https://github.com/xmake-io/xmake/issues/3161 function _escape_path_in_flag(target, flag) if is_host("windows") then -- e.g. /ManifestInput:..\..\, /def:xxx, -isystem c:\xxx, -Ic:\.. if flag:find("\\", 1, true) then flag = _escape_path(flag) end end return flag end -- get relative unix path function _get_relative_unix_path(filepath, outputdir) filepath = _translate_path(filepath, outputdir) filepath = _escape_path(path.translate(filepath)) return os.args(filepath) end -- get relative unix path to the cmake path -- @see https://github.com/xmake-io/xmake/issues/2026 function _get_relative_unix_path_to_cmake(filepath, outputdir) filepath = _translate_path(filepath, outputdir) filepath = path.unix(path.translate(filepath)) if filepath and not path.is_absolute(filepath) then filepath = "${CMAKE_SOURCE_DIR}/" .. filepath end return os.args(filepath) end -- get enabled languages function _get_project_languages() local languages = {} for _, target in ipairs(project.ordertargets()) do for _, sourcekind in ipairs(target:sourcekinds()) do if sourcekind == "cc" then table.insert(languages, "C") elseif sourcekind == "cxx" then table.insert(languages, "CXX") elseif sourcekind == "as" then table.insert(languages, "ASM") elseif sourcekind == "cu" then table.insert(languages, "CUDA") end end end languages = table.unique(languages) return languages end -- get cmake compiler condition for tool-specific flags function _get_cmake_compiler_condition(toolname) local conditions = { clang = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , clangxx = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , clang_cl = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , gcc = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID STREQUAL "GNU"]] , gxx = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID STREQUAL "GNU"]] , cl = [[CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC"]] } return conditions[toolname] end -- get cmake linker condition for tool-specific flags function _get_cmake_linker_condition(toolname) local conditions = { clang = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , clangxx = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , clang_cl = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "MSVC" AND CURRENT_COMPILER_ID MATCHES "Clang$|^IntelLLVM$"]] , gcc = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID STREQUAL "GNU"]] , gxx = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "GNU" AND CURRENT_COMPILER_ID STREQUAL "GNU"]] , cl = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "MSVC"]] , ld = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "GNU"]] , link = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "MSVC"]] , lld_link = [[CURRENT_LINKER_FRONTEND_VARIANT STREQUAL "MSVC"]] } return conditions[toolname] end -- get configs from target function _get_configs_from_target(target, name) local values = {} if name:find("flags", 1, true) then table.join2(values, target:toolconfig(name)) end for _, value in ipairs((target:get_from(name, "*"))) do table.join2(values, value) end if not name:find("flags", 1, true) then -- for includedirs, links .. table.join2(values, target:toolconfig(name)) end return table.unique(values) end -- Did the current cmake native support for c++ modules? function _can_native_support_for_cxxmodules() local cmake_minver = _get_cmake_minver() if cmake_minver and cmake_minver:ge("3.28") then return true end end -- this sourcebatch is built? function _sourcebatch_is_built(sourcebatch) -- we can only use rulename to filter them because sourcekind may be bound to multiple rules local rulename = sourcebatch.rulename if rulename == "c.build" or rulename == "c++.build" or rulename == "asm.build" or rulename == "cuda.build" or rulename == "objc.build" or rulename == "objc++.build" or rulename == "win.sdk.resource" then return true end if _can_native_support_for_cxxmodules() then if rulename == "c++.build.modules" then return true end end end -- get c++ modules rules function _get_cxxmodules_rules() return {"c++.build.modules", "c++.build.modules.builder"} end -- translate flag function _translate_flag(flag, outputdir) if flag then if path.instance_of(flag) then flag = flag:clone():set(_get_relative_unix_path_to_cmake(flag:rawstr(), outputdir)):str() -- it may be table, https://github.com/xmake-io/xmake/issues/4816 elseif type(flag) == "string" then if path.is_absolute(flag) then flag = _get_relative_unix_path_to_cmake(flag, outputdir) elseif flag:startswith("-fmodule-file=") then flag = "-fmodule-file=" .. _get_relative_unix_path_to_cmake(flag:sub(15), outputdir) elseif flag:startswith("-fmodule-mapper=") then flag = "-fmodule-mapper=" .. _get_relative_unix_path_to_cmake(flag:sub(17), outputdir) elseif flag:match("(.+)=(.+)") then local k, v = flag:match("(.+)=(.+)") if v and (v:endswith(".ifc") or v:endswith(".map")) then -- e.g. hello=xxx/hello.ifc flag = k .. "=" .. _get_relative_unix_path_to_cmake(v, outputdir) end end end end return flag end -- translate flags function _translate_flags(flags, outputdir) if not flags then return end local result = {} for _, flag in ipairs(flags) do if type(flag) == "table" and not path.instance_of(flag) then for _, v in ipairs(flag) do table.insert(result, _translate_flag(v, outputdir)) end else table.insert(result, _translate_flag(flag, outputdir)) end end return result end -- map compiler flags function _map_compflags(toolname, langkind, name, values) local fake_target = { is_shared = function() return false end, tool = function() local program if toolname == "cl" then program = "cl.exe" elseif toolname == "gcc" then program = "gcc" elseif toolname == "clang" then program = "clang" end return program, toolname end, sourcekinds = function() return langkind == "c" and "cc" or "cxx" end } return compiler.map_flags(langkind, name, values, {target = fake_target}) end -- split flag with tool prefix, e.g. clang::-Dclang function _split_flag_with_tool_prefix(flag) local prefix local splitinfo = flag:split("::") if #splitinfo == 2 then prefix = splitinfo[1] flag = splitinfo[2] end return prefix, flag end -- get flags from fileconfig function _get_flags_from_fileconfig(fileconfig, outputdir, name) local flags = {} table.join2(flags, fileconfig[name]) if fileconfig.force then table.join2(flags, fileconfig.force[name]) end flags = _translate_flags(flags, outputdir) if #flags > 0 then return flags end end -- get flags from target -- @see https://github.com/xmake-io/xmake/issues/3594 function _get_flags_from_target(target, flagkind) local results = {} local flags = _get_configs_from_target(target, flagkind) for _, flag in ipairs(flags) do local tools = target:extraconf(flagkind, flag, "tools") if not flag:find("::", 1, true) and tools then for _, toolname in ipairs(tools) do table.insert(results, toolname .. "::" .. flag) end else table.insert(results, flag) end end return results end -- set compiler function _set_compiler(cmakelists) if config.get("toolchain") then local cc = platform.tool("cc") if cc then cc = path.unix(executable_path(cc)) cmakelists:print("set(CMAKE_C_COMPILER \"%s\")", cc) end local cxx, cxx_name = platform.tool("cxx") if cxx then if cxx_name == "clang" or cxx_name == "gcc" then local dir = path.directory(cxx) local name = path.filename(cxx) name = name:gsub("clang$", "clang++") name = name:gsub("clang%-", "clang++-") name = name:gsub("gcc$", "g++") name = name:gsub("gcc%-", "g++-") if dir ~= '.' then cxx = path.join(dir, name) else cxx = name end end cxx = path.unix(executable_path(cxx)) cmakelists:print("set(CMAKE_CXX_COMPILER \"%s\")", cxx) end end end -- add project info function _add_project(cmakelists, outputdir) local cmake_minver = _get_cmake_minver() cmakelists:print([[# this is the build file for project %s # it is autogenerated by the xmake build system. # do not edit by hand. ]], project.name() or "") cmakelists:print("# project") cmakelists:print("cmake_minimum_required(VERSION %s)", cmake_minver) if cmake_minver:ge("3.15.0") then -- for MSVC_RUNTIME_LIBRARY cmakelists:print("cmake_policy(SET CMP0091 NEW)") end -- set compilers, we need set it before project( LANGUAGES..) -- @see https://github.com/xmake-io/xmake/issues/5448 _set_compiler(cmakelists) -- set project name local project_name = project.name() if not project_name then local project_targets = target_utils.get_project_targets() for _, target in table.orderpairs(project_targets) do project_name = target:name() end end if _can_native_support_for_cxxmodules() then cmakelists:print("set(CMAKE_CXX_SCAN_FOR_MODULES ON)") end local languages = _get_project_languages() if project_name then local project_info = "" local project_version = project.version() if project_version then project_info = project_info .. " VERSION " .. project_version end if languages and #languages > 0 then cmakelists:print("project(%s%s LANGUAGES %s)", project_name, project_info, table.concat(languages, " ")) else cmakelists:print("project(%s%s)", project_name, project_info) end end -- Define language-independant global compiler/linker variables if not languages or #languages == 0 then languages = {"C"} -- C should be defined by default if nothing specified end local language = languages[1] cmakelists:print("set(CURRENT_COMPILER_ID ${CMAKE_%s_COMPILER_ID})", language) cmakelists:print("set(CURRENT_COMPILER_FRONTEND_VARIANT ${CMAKE_%s_COMPILER_FRONTEND_VARIANT})", language) cmakelists:print("if(NOT CURRENT_COMPILER_FRONTEND_VARIANT)") cmakelists:print(" if(MSVC)") cmakelists:print(" set(CURRENT_COMPILER_FRONTEND_VARIANT \"MSVC\")") cmakelists:print(" else()") cmakelists:print(" set(CURRENT_COMPILER_FRONTEND_VARIANT \"GNU\")") cmakelists:print(" endif()") cmakelists:print("endif()") cmakelists:print("if(DEFINED CMAKE_%s_COMPILER_LINKER_FRONTEND_VARIANT)", language) cmakelists:print(" set(CURRENT_LINKER_FRONTEND_VARIANT ${CMAKE_%s_COMPILER_LINKER_FRONTEND_VARIANT})", language) cmakelists:print("endif()") cmakelists:print("if(NOT CURRENT_LINKER_FRONTEND_VARIANT)") cmakelists:print(" set(CURRENT_LINKER_FRONTEND_VARIANT ${CURRENT_COMPILER_FRONTEND_VARIANT})") cmakelists:print("endif()") cmakelists:print("") end -- add target: phony function _add_target_phony(cmakelists, target) -- https://github.com/xmake-io/xmake/issues/2337 target:data_set("plugin.project.kind", "cmakelist") cmakelists:printf("add_custom_target(%s", target:name()) local deps = target:get("deps") if deps then cmakelists:write(" DEPENDS") for _, dep in ipairs(deps) do cmakelists:write(" " .. dep) end end cmakelists:print(")") cmakelists:print("") end -- add target: object function _add_target_object(cmakelists, target, outputdir) -- Can't change the output directory of object/intermediate files in CMake -- https://stackoverflow.com/questions/46330056/set-output-directory-for-cmake-object-libraries cmakelists:print("add_library(%s OBJECT \"\")", target:name()) cmakelists:print("set_target_properties(%s PROPERTIES LIBRARY_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) end -- add target: binary function _add_target_binary(cmakelists, target, outputdir) cmakelists:print("add_executable(%s \"\")", target:name()) cmakelists:print("set_target_properties(%s PROPERTIES OUTPUT_NAME \"%s\")", target:name(), target:basename()) cmakelists:print("set_target_properties(%s PROPERTIES RUNTIME_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) end -- add target: static function _add_target_static(cmakelists, target, outputdir) cmakelists:print("add_library(%s STATIC \"\")", target:name()) cmakelists:print("set_target_properties(%s PROPERTIES OUTPUT_NAME \"%s\")", target:name(), target:basename()) cmakelists:print("set_target_properties(%s PROPERTIES ARCHIVE_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) end -- add target: shared function _add_target_shared(cmakelists, target, outputdir) cmakelists:print("add_library(%s SHARED \"\")", target:name()) cmakelists:print("set_target_properties(%s PROPERTIES OUTPUT_NAME \"%s\")", target:name(), target:basename()) if target:is_plat("windows") then -- @see https://github.com/xmake-io/xmake/issues/2192 cmakelists:print("set_target_properties(%s PROPERTIES RUNTIME_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) cmakelists:print("set_target_properties(%s PROPERTIES ARCHIVE_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) else cmakelists:print("set_target_properties(%s PROPERTIES LIBRARY_OUTPUT_DIRECTORY \"%s\")", target:name(), _get_relative_unix_path_to_cmake(target:targetdir(), outputdir)) end end -- add target: headeronly function _add_target_headeronly(cmakelists, target) cmakelists:print("add_library(%s INTERFACE)", target:name()) end -- add target: headeronly function _add_target_moduleonly(cmakelists, target) cmakelists:print("add_custom_target(%s)", target:name()) end -- add target dependencies function _add_target_dependencies(cmakelists, target) local deps = {} for _, dep in ipairs(target:orderdeps()) do if not dep:is_object() then table.insert(deps, dep:name()) end end if #deps ~= 0 then cmakelists:printf("add_dependencies(%s", target:name()) for _, dep in ipairs(deps) do cmakelists:write(" " .. dep) end cmakelists:print(")") end end function _print_target_sources(cmakelists, target, files, visibility, opt) opt = opt or {} local cmake_version = _get_cmake_version() local has_fileset_support = cmake_version and cmake_version:ge("3.23") local fileset = "" if has_fileset_support and opt.set then fileset = "FILE_SET " .. opt.set .. " FILES" end cmakelists:print("target_sources(%s %s %s", target:name(), visibility, fileset) for _, file in ipairs(files) do cmakelists:print(" " .. file) end cmakelists:print(")") end -- add target sources function _add_target_sources(cmakelists, target, outputdir) local has_cuda = false local sourcebatches = target:sourcebatches() for name, sourcebatch in table.orderpairs(sourcebatches) do local public_sources local private_sources if _sourcebatch_is_built(sourcebatch) then local module_sourcebatch = false for _, sourcefile in ipairs(sourcebatch.sourcefiles) do if _has_cxxmodules_sources() and name == "c++.build.modules" then module_sourcebatch = true local fileconfig = target:fileconfig(sourcefile) if fileconfig and fileconfig.public then public_sources = public_sources or {} table.insert(public_sources, _get_relative_unix_path(sourcefile, outputdir)) else private_sources = private_sources or {} table.insert(private_sources, _get_relative_unix_path(sourcefile, outputdir)) end else private_sources = private_sources or {} table.insert(private_sources, _get_relative_unix_path(sourcefile, outputdir)) end end if public_sources then cmakelists:print(format("# public sourcefiles from sourcebatch %s for target %s", name, target:fullname())) _print_target_sources(cmakelists, target, public_sources, "PUBLIC", {set = module_sourcebatch and "CXX_MODULES"}) end if private_sources then cmakelists:print(format("# private sourcefiles from sourcebatch %s for target %s", name, target:fullname())) _print_target_sources(cmakelists, target, private_sources, "PRIVATE", {set = module_sourcebatch and "CXX_MODULES"}) end end if sourcebatch.sourcekind == "cu" then has_cuda = true end end if target:headerfiles() then local public_headers local private_headers for _, headerfile in ipairs(target:headerfiles()) do local fileconfig = target:fileconfig(headerfile) if fileconfig and fileconfig.public then public_headers = public_headers or {} table.insert(public_headers, _get_relative_unix_path(headerfile, outputdir)) else private_headers = private_headers or {} table.insert(private_headers, _get_relative_unix_path(headerfile, outputdir)) end end if public_headers then cmakelists:print(format("# public headers for target %s", target:fullname())) _print_target_sources(cmakelists, target, public_headers, "PUBLIC", {set = "HEADERS"}) end if private_headers then cmakelists:print(format("# private headers for target %s", target:fullname())) _print_target_sources(cmakelists, target, private_headers, "PRIVATE", {set = "HEADERS"}) end end if has_cuda then cmakelists:print("set_target_properties(%s PROPERTIES CUDA_SEPARABLE_COMPILATION ON)", target:name()) local devlink = target:policy("build.cuda.devlink") or target:values("cuda.build.devlink") if devlink ~= nil then cmakelists:print("set_target_properties(%s PROPERTIES CUDA_RESOLVE_DEVICE_SYMBOLS %s)", target:name(), devlink and "ON" or "OFF") end end end -- add target source groups -- @see https://github.com/xmake-io/xmake/issues/1149 function _add_target_source_groups(cmakelists, target, outputdir) local filegroups = target:get("filegroups") for _, filegroup in ipairs(filegroups) do local files = target:extraconf("filegroups", filegroup, "files") or "**" local mode = target:extraconf("filegroups", filegroup, "mode") local rootdir = target:extraconf("filegroups", filegroup, "rootdir") assert(rootdir, "please set root directory, e.g. add_filegroups(%s, {rootdir = 'xxx'})", filegroup) local sources = {} local recurse_sources = {} if path.is_absolute(rootdir) then rootdir = _get_relative_unix_path(rootdir, outputdir) else rootdir = string.format("${CMAKE_CURRENT_SOURCE_DIR}/%s", _get_relative_unix_path(rootdir, outputdir)) end for _, filepattern in ipairs(files) do local filepattern = filepattern if filepattern:find("**", 1, true) then filepattern = filepattern:gsub("%*%*", "*") table.insert(recurse_sources, _get_relative_unix_path(path.join(rootdir, filepattern), outputdir)) else table.insert(sources, _get_relative_unix_path(path.join(rootdir, filepattern), outputdir)) end end if #sources > 0 then cmakelists:print("FILE(GLOB %s_GROUP_SOURCE_LIST %s)", target:name(), table.concat(sources, " ")) if mode and mode == "plain" then cmakelists:print("source_group(%s FILES ${%s_GROUP_SOURCE_LIST})", _get_relative_unix_path(filegroup, outputdir), target:name()) else cmakelists:print("source_group(TREE %s PREFIX %s FILES ${%s_GROUP_SOURCE_LIST})", rootdir, _get_relative_unix_path(filegroup, outputdir), target:name()) end end if #recurse_sources > 0 then cmakelists:print("FILE(GLOB_RECURSE %s_GROUP_RECURSE_SOURCE_LIST %s)", target:name(), table.concat(recurse_sources, " ")) if mode and mode == "plain" then cmakelists:print("source_group(%s FILES ${%s_GROUP_RECURSE_SOURCE_LIST})", _get_relative_unix_path(filegroup, outputdir), target:name()) else cmakelists:print("source_group(TREE %s PREFIX %s FILES ${%s_GROUP_RECURSE_SOURCE_LIST})", rootdir, _get_relative_unix_path(filegroup, outputdir), target:name()) end end end end -- add target precompiled header function _add_target_precompiled_header(cmakelists, target, outputdir) local precompiled_header = target:get("pcheader") or target:get("pcxxheader") if precompiled_header then cmakelists:print("target_precompile_headers(%s PRIVATE", target:name()) cmakelists:print(" $<$:${CMAKE_CURRENT_SOURCE_DIR}/%s>", target:get("pcxxheader") and "CXX" or "C", _get_relative_unix_path(precompiled_header, outputdir)) cmakelists:print(")") end end -- add target include directories function _add_target_include_directories(cmakelists, target, outputdir) local includedirs = _get_configs_from_target(target, "includedirs") if #includedirs > 0 then local access_type = target:kind() == "headeronly" and "INTERFACE" or "PRIVATE" cmakelists:print("target_include_directories(%s %s", target:name(), access_type) for _, includedir in ipairs(includedirs) do cmakelists:print(" " .. _get_relative_unix_path(includedir, outputdir)) end cmakelists:print(")") end local includedirs_interface = target:get("includedirs", {interface = true}) if includedirs_interface then cmakelists:print("target_include_directories(%s INTERFACE", target:name()) for _, headerdir in ipairs(includedirs_interface) do cmakelists:print(" " .. _get_relative_unix_path(headerdir, outputdir)) end cmakelists:print(")") end end -- add target system include directories -- we disable system/external includes first, because cmake doesn’t seem to be able to support msvc /external:I -- https://github.com/xmake-io/xmake/issues/1050 function _add_target_sysinclude_directories(cmakelists, target, outputdir) local includedirs = _get_configs_from_target(target, "sysincludedirs") if #includedirs > 0 then cmakelists:print("target_include_directories(%s SYSTEM PRIVATE", target:name()) for _, includedir in ipairs(includedirs) do cmakelists:print(" " .. _get_relative_unix_path(includedir, outputdir)) end cmakelists:print(")") end local includedirs_interface = target:get("sysincludedirs", {interface = true}) if includedirs_interface then cmakelists:print("target_include_directories(%s SYSTEM INTERFACE", target:name()) for _, headerdir in ipairs(includedirs_interface) do cmakelists:print(" " .. _get_relative_unix_path(headerdir, outputdir)) end cmakelists:print(")") end end -- add target framework directories function _add_target_framework_directories(cmakelists, target, outputdir) local frameworkdirs = _get_configs_from_target(target, "frameworkdirs") if #frameworkdirs > 0 then cmakelists:print("target_compile_options(%s PRIVATE", target:name()) for _, frameworkdir in ipairs(frameworkdirs) do cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") end cmakelists:print(")") local cmake_minver = _get_cmake_minver() if cmake_minver:ge("3.13.0") then cmakelists:print("target_link_options(%s PRIVATE", target:name()) else cmakelists:print("target_link_libraries(%s PRIVATE", target:name()) end for _, frameworkdir in ipairs(frameworkdirs) do cmakelists:print(" -F" .. _get_relative_unix_path(frameworkdir, outputdir)) end cmakelists:print(")") end local frameworkdirs_interface = target:get("frameworkdirs", {interface = true}) if frameworkdirs_interface then cmakelists:print("target_compile_options(%s PRIVATE", target:name()) for _, frameworkdir in ipairs(frameworkdirs_interface) do cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") cmakelists:print(" $<$:-F" .. _get_relative_unix_path(frameworkdir, outputdir) .. ">") end cmakelists:print(")") end end -- add target compile definitions function _add_target_compile_definitions(cmakelists, target) local defines = _get_configs_from_target(target, "defines") if #defines > 0 then cmakelists:print("target_compile_definitions(%s PRIVATE", target:name()) for _, define in ipairs(defines) do cmakelists:print(" " .. define) end cmakelists:print(")") end end -- add target source files flags function _add_target_sourcefiles_flags(cmakelists, target, sourcefile, name, outputdir) local fileconfig = target:fileconfig(sourcefile) if fileconfig then local flags = _get_flags_from_fileconfig(fileconfig, outputdir, name) if flags and #flags > 0 then cmakelists:print("set_source_files_properties(" .. _get_relative_unix_path_to_cmake(sourcefile, outputdir) .. " PROPERTIES COMPILE_OPTIONS") local flagstrs = {} for _, flag in ipairs(flags) do if name == "cxxflags" then table.insert(flagstrs, "$<$:" .. flag .. ">") elseif name == "cflags" then table.insert(flagstrs, "$<$:" .. flag .. ">") elseif name == "cxflags" then table.insert(flagstrs, "$<$:" .. flag .. ">") table.insert(flagstrs, "$<$:" .. flag .. ">") elseif name == "cuflags" then table.insert(flagstrs, "$<$:" .. flag .. ">") end end cmakelists:print(" \"%s\"", table.concat(flagstrs, ";")) cmakelists:print(")") end end end -- add target compile options function _add_target_compile_options(cmakelists, target, outputdir) local cflags = _get_flags_from_target(target, "cflags") local cxflags = _get_flags_from_target(target, "cxflags") local cxxflags = _get_flags_from_target(target, "cxxflags") local cuflags = _get_flags_from_target(target, "cuflags") local toolnames = hashset.new() local function _add_target_compile_options_for_compiler(toolname) if #cflags > 0 or #cxflags > 0 or #cxxflags > 0 or #cuflags > 0 then cmakelists:print("target_compile_options(%s PRIVATE", target:name()) for _, flag in ipairs(_translate_flags(cflags, outputdir)) do local flag = flag local prefix prefix, flag = _split_flag_with_tool_prefix(flag) if prefix == toolname then flag = _escape_path_in_flag(target, flag) cmakelists:print(" $<$:" .. flag .. ">") end if prefix then toolnames:insert(prefix) end end for _, flag in ipairs(_translate_flags(cxflags, outputdir)) do local flag = flag local prefix prefix, flag = _split_flag_with_tool_prefix(flag) if prefix == toolname then flag = _escape_path_in_flag(target, flag) cmakelists:print(" $<$:" .. flag .. ">") cmakelists:print(" $<$:" .. flag .. ">") end if prefix then toolnames:insert(prefix) end end for _, flag in ipairs(_translate_flags(cxxflags, outputdir)) do local flag = flag local prefix prefix, flag = _split_flag_with_tool_prefix(flag) if prefix == toolname then flag = _escape_path_in_flag(target, flag) cmakelists:print(" $<$:" .. flag .. ">") end if prefix then toolnames:insert(prefix) end end for _, flag in ipairs(_translate_flags(cuflags, outputdir)) do local flag = flag local prefix prefix, flag = _split_flag_with_tool_prefix(flag) if prefix == toolname then flag = _escape_path_in_flag(target, flag) cmakelists:print(" $<$:" .. flag .. ">") end if prefix then toolnames:insert(prefix) end end cmakelists:print(")") end end _add_target_compile_options_for_compiler() for _, toolname in toolnames:keys() do local condition = _get_cmake_compiler_condition(toolname) if condition then cmakelists:print("if(%s)", condition) _add_target_compile_options_for_compiler(toolname) cmakelists:print("endif()") end end -- add cflags/cxxflags for the specific source files local sourcebatches = target:sourcebatches() for _, sourcebatch in table.orderpairs(sourcebatches) do if _sourcebatch_is_built(sourcebatch) then for _, sourcefile in ipairs(sourcebatch.sourcefiles) do _add_target_sourcefiles_flags(cmakelists, target, sourcefile, "cxxflags", outputdir) _add_target_sourcefiles_flags(cmakelists, target, sourcefile, "cflags", outputdir) _add_target_sourcefiles_flags(cmakelists, target, sourcefile, "cxflags", outputdir) _add_target_sourcefiles_flags(cmakelists, target, sourcefile, "cuflags", outputdir) end end end end -- add target values function _add_target_values(cmakelists, target, name) local values = target:get(name) if values then if name:endswith("s") then name = name:sub(1, #name - 1) end for index, toolname in ipairs({"cl", "clang", "gcc"}) do local condition = _get_cmake_compiler_condition(toolname) cmakelists:print("%s(%s)", index == 1 and "if" or "elseif", condition) local flags = _map_compflags(toolname, "c", name, values) for _, flag in ipairs(flags) do cmakelists:print(" target_compile_options(%s PRIVATE %s)", target:name(), flag) end end cmakelists:print("endif()") end end -- add target warnings function _add_target_warnings(cmakelists, target) _add_target_values(cmakelists, target, "warnings") end -- add target encodings function _add_target_encodings(cmakelists, target) _add_target_values(cmakelists, target, "encodings") end -- add target exceptions function _add_target_exceptions(cmakelists, target) local exceptions = target:get("exceptions") if exceptions then if exceptions == "none" then cmakelists:print("string(REPLACE \"/EHsc\" \"\" CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")") else _add_target_values(cmakelists, target, "exceptions") end end end -- add target languages function _add_target_languages(cmakelists, target) local features = { c89 = "c_std_90" , c99 = "c_std_99" , c11 = "c_std_11" , c17 = "c_std_17" , c23 = "c_std_23" , clatest = "c_std_latest" , cxx98 = "cxx_std_98" , cxx11 = "cxx_std_11" , cxx14 = "cxx_std_14" , cxx17 = "cxx_std_17" , cxx20 = "cxx_std_20" , cxx23 = "cxx_std_23" , cxx26 = "cxx_std_26" , cxxlatest = "cxx_std_latest" } local languages = target:get("languages") if languages then for _, lang in ipairs(languages) do local lang = lang local has_ext = false -- c | c++ | gnu | gnu++ local flag = lang:replace('xx', '++'):replace('latest', ''):gsub('%d', '') if lang:startswith("gnu") then lang = 'c' .. lang:sub(4) has_ext = true end local feature = features[lang] or (features[lang:replace("++", "xx")]) if feature then cmakelists:print("set_target_properties(%s PROPERTIES %s_EXTENSIONS %s)", target:name(), flag:endswith('++') and 'CXX' or 'C', has_ext and "ON" or "OFF") if feature:endswith('_latest') then if flag:endswith('++') then cmakelists:print('foreach(standard 26 23 20 17 14 11 98)') cmakelists:print(' include(CheckCXXCompilerFlag)') cmakelists:print(' if(CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")') cmakelists:print(' check_cxx_compiler_flag("/std:%s${standard}" %s_support_%s_standard_${standard})', flag, target:name(), flag) cmakelists:print(' else()') cmakelists:print(' check_cxx_compiler_flag("-std=%s${standard}" %s_support_%s_standard_${standard})', flag, target:name(), flag) cmakelists:print(' endif()') cmakelists:print(' if(%s_support_%s_standard_${standard})', target:name(), flag) cmakelists:print(' target_compile_features(%s PRIVATE cxx_std_${standard})', target:name()) cmakelists:print(' break()') cmakelists:print(' endif()') cmakelists:print('endforeach()') else cmakelists:print('foreach(standard 23 17 11 99 90)') cmakelists:print(' include(CheckCCompilerFlag)') cmakelists:print(' if(CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")') cmakelists:print(' check_c_compiler_flag("/std:%s${standard}" %s_support_%s_standard_${standard})', flag, target:name(), flag) cmakelists:print(' else()') cmakelists:print(' check_c_compiler_flag("-std=%s${standard}" %s_support_%s_standard_${standard})', flag, target:name(), flag) cmakelists:print(' endif()') cmakelists:print(' if(%s_support_%s_standard_${standard})', target:name(), flag) cmakelists:print(' target_compile_features(%s PRIVATE c_std_${standard})', target:name()) cmakelists:print(' break()') cmakelists:print(' endif()') cmakelists:print('endforeach()') end else cmakelists:print('target_compile_features(%s PRIVATE %s)', target:name(), feature) end end end end end -- add target optimization function _add_target_optimization(cmakelists, target) local flags_gcc = { none = "-O0" , fast = "-O1" , faster = "-O2" , fastest = "-O3" , smallest = "-Os" , aggressive = "-Ofast" } local flags_msvc = { none = "$<$:-Od>" , faster = "$<$:-Ox>" , fastest = "$<$:-O2>" , smallest = "$<$:-O1>" , aggressive = "$<$:-O2>" } local optimization = target:get("optimize") if optimization then cmakelists:print("if(CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL \"MSVC\")") cmakelists:print(" target_compile_options(%s PRIVATE %s)", target:name(), flags_msvc[optimization]) cmakelists:print("else()") cmakelists:print(" target_compile_options(%s PRIVATE %s)", target:name(), flags_gcc[optimization]) cmakelists:print("endif()") end end -- add target symbols function _add_target_symbols(cmakelists, target) local symbols = target:get("symbols") if symbols then local flags_gcc = {} local flags_msvc = {} local levels = hashset.from(table.wrap(symbols)) if levels:has("debug") then table.insert(flags_gcc, "-g") if levels:has("edit") then table.insert(flags_msvc, "-ZI") elseif levels:has("embed") then table.insert(flags_msvc, "-Z7") else table.insert(flags_msvc, "-Zi") end end if levels:has("hidden") then table.insert(flags_gcc, "-fvisibility=hidden") end cmakelists:print("if(CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL \"MSVC\")") if #flags_msvc > 0 then cmakelists:print(" target_compile_options(%s PRIVATE %s)", target:name(), table.concat(flags_msvc, " ")) end cmakelists:print("else()") if #flags_gcc > 0 then cmakelists:print(" target_compile_options(%s PRIVATE %s)", target:name(), table.concat(flags_gcc, " ")) end cmakelists:print("endif()") end end -- add target runtimes -- -- https://github.com/xmake-io/xmake/issues/1661#issuecomment-927979489 -- https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html -- function _add_target_runtimes(cmakelists, target) local cmake_minver = _get_cmake_minver() if cmake_minver:ge("3.15.0") then local runtimes = target:get("runtimes") cmakelists:print("if(CURRENT_COMPILER_FRONTEND_VARIANT STREQUAL \"MSVC\")") if runtimes then if runtimes == "MT" then runtimes = "MultiThreaded" elseif runtimes == "MTd" then runtimes = "MultiThreadedDebug" elseif runtimes == "MD" then runtimes = "MultiThreadedDLL" elseif runtimes == "MDd" then runtimes = "MultiThreadedDebugDLL" end else runtimes = "MultiThreaded$<$:Debug>" end cmakelists:print(' set_property(TARGET %s PROPERTY', target:name()) cmakelists:print(' MSVC_RUNTIME_LIBRARY "%s")', runtimes) cmakelists:print("endif()") end end -- add target link libraries function _add_target_link_libraries(cmakelists, target, outputdir) -- add links local links = _get_configs_from_target(target, "links") local syslinks = _get_configs_from_target(target, "syslinks") local frameworks = _get_configs_from_target(target, "frameworks") if #frameworks > 0 then for _, framework in ipairs(frameworks) do table.insert(links, "\"-framework " .. framework .. "\"") end end table.join2(links, syslinks) if #links > 0 then cmakelists:print("target_link_libraries(%s PRIVATE", target:name()) for _, link in ipairs(links) do cmakelists:print(" " .. link) end cmakelists:print(")") end -- get c++ modules rules local cxxmodules_rules if _can_native_support_for_cxxmodules() then cxxmodules_rules = _get_cxxmodules_rules() end if cxxmodules_rules then cxxmodules_rules = hashset.from(cxxmodules_rules) else cxxmodules_rules = hashset.new() end -- add other object files, maybe from custom rules local objectfiles_set = hashset.new() local sourcebatches = target:sourcebatches() for _, sourcebatch in table.orderpairs(sourcebatches) do if _sourcebatch_is_built(sourcebatch) or cxxmodules_rules:has(sourcebatch.rulename) then for _, objectfile in ipairs(sourcebatch.objectfiles) do objectfiles_set:insert(objectfile) end end end local object_deps = {} for _, dep in ipairs(target:orderdeps()) do if dep:is_object() then table.insert(object_deps, dep:name()) for _, obj in ipairs(dep:objectfiles()) do objectfiles_set:insert(obj) end end end local has_links = #target:objectfiles() > objectfiles_set:size() local key = target:name() .. "_" .. hash.rand64() if has_links then cmakelists:print("add_library(target_objectfiles_%s OBJECT IMPORTED GLOBAL)", key) cmakelists:print("set_property(TARGET target_objectfiles_%s PROPERTY IMPORTED_OBJECTS", key) for _, objectfile in ipairs(target:objectfiles()) do if not objectfiles_set:has(objectfile) then cmakelists:print(" " .. _get_relative_unix_path_to_cmake(objectfile, outputdir)) end end cmakelists:print(")") end if #object_deps ~= 0 then if not has_links then cmakelists:print("add_library(target_objectfiles_%s INTERFACE)", key) has_links = true end cmakelists:print("target_link_libraries(target_objectfiles_%s INTERFACE", key) for _, dep in ipairs(object_deps) do cmakelists:print(" " .. dep) end cmakelists:print(")") end if has_links then cmakelists:print("target_link_libraries(%s PRIVATE target_objectfiles_%s)", target:name(), key) end end -- add target link directories function _add_target_link_directories(cmakelists, target, outputdir) local linkdirs = _get_configs_from_target(target, "linkdirs") if #linkdirs > 0 then local cmake_minver = _get_cmake_minver() if cmake_minver:ge("3.13.0") then cmakelists:print("target_link_directories(%s PRIVATE", target:name()) for _, linkdir in ipairs(linkdirs) do cmakelists:print(" " .. _get_relative_unix_path(linkdir, outputdir)) end cmakelists:print(")") else cmakelists:print("if(CURRENT_LINKER_FRONTEND_VARIANT STREQUAL \"MSVC\")") cmakelists:print(" target_link_libraries(%s PRIVATE", target:name()) for _, linkdir in ipairs(linkdirs) do cmakelists:print(" -libpath:" .. _get_relative_unix_path(linkdir, outputdir)) end cmakelists:print(" )") cmakelists:print("else()") cmakelists:print(" target_link_libraries(%s PRIVATE", target:name()) for _, linkdir in ipairs(linkdirs) do cmakelists:print(" -L" .. _get_relative_unix_path(linkdir, outputdir)) end cmakelists:print(" )") cmakelists:print("endif()") end end end -- add target link options function _add_target_link_options(cmakelists, target, outputdir) local ldflags = _get_flags_from_target(target, "ldflags") local shflags = _get_flags_from_target(target, "shflags") local toolnames = hashset.new() local function _add_target_link_options_for_linker(toolname) if #ldflags > 0 or #shflags > 0 then local flags = {} for _, flag in ipairs(table.unique(table.join(ldflags, shflags))) do table.insert(flags, _translate_flag(flag, outputdir)) end if #flags > 0 then local cmake_minver = _get_cmake_minver() if cmake_minver:ge("3.13.0") then cmakelists:print("target_link_options(%s PRIVATE", target:name()) else cmakelists:print("target_link_libraries(%s PRIVATE", target:name()) end for _, flag in ipairs(flags) do local flag = flag local prefix prefix, flag = _split_flag_with_tool_prefix(flag) if prefix == toolname then flag = _escape_path_in_flag(target, flag) -- @see https://github.com/xmake-io/xmake/issues/4196 if cmake_minver:ge("3.12.0") and #os.argv(flag) > 1 then cmakelists:print(" " .. os.args("SHELL:" .. flag)) else cmakelists:print(" " .. flag) end end if prefix then toolnames:insert(prefix) end end cmakelists:print(")") end end end _add_target_link_options_for_linker() for _, toolname in toolnames:keys() do local condition = _get_cmake_linker_condition(toolname) if condition then cmakelists:print("if(%s)", condition) _add_target_link_options_for_linker(toolname) cmakelists:print("endif()") end end end -- get command string function _get_command_string(cmd, outputdir) local kind = cmd.kind local opt = cmd.opt if cmd.program then -- @see https://github.com/xmake-io/xmake/discussions/2156 local argv = {} for _, v in ipairs(cmd.argv) do table.insert(argv, _translate_flag(v, outputdir)) end local command = _escape_path(cmd.program) .. " " .. os.args(argv) if opt and opt.curdir then command = "${CMAKE_COMMAND} -E chdir " .. _get_relative_unix_path_to_cmake(opt.curdir, outputdir) .. " " .. command end return command elseif kind == "cp" then if os.isdir(cmd.srcpath) then return string.format("${CMAKE_COMMAND} -E copy_directory %s %s", _get_relative_unix_path_to_cmake(cmd.srcpath, outputdir), _get_relative_unix_path_to_cmake(cmd.dstpath, outputdir)) else return string.format("${CMAKE_COMMAND} -E copy %s %s", _get_relative_unix_path_to_cmake(cmd.srcpath, outputdir), _get_relative_unix_path_to_cmake(cmd.dstpath, outputdir)) end elseif kind == "rm" then return string.format("${CMAKE_COMMAND} -E rm -rf %s", _get_relative_unix_path_to_cmake(cmd.filepath, outputdir)) elseif kind == "rmdir" then return string.format("${CMAKE_COMMAND} -E rm -rf %s", _get_relative_unix_path_to_cmake(cmd.dir, outputdir)) elseif kind == "mv" then return string.format("${CMAKE_COMMAND} -E rename %s %s", _get_relative_unix_path_to_cmake(cmd.srcpath, outputdir), _get_relative_unix_path_to_cmake(cmd.dstpath, outputdir)) elseif kind == "cd" then return string.format("cd %s", _get_relative_unix_path_to_cmake(cmd.dir, outputdir)) elseif kind == "mkdir" then return string.format("${CMAKE_COMMAND} -E make_directory %s", _get_relative_unix_path_to_cmake(cmd.dir, outputdir)) elseif kind == "show" or kind == "show_progress" then local text = string.format(cmd.format, table.unpack(cmd.argv)) text = colors.ignore(text) -- we need to translate paths in text -- https://github.com/xmake-io/xmake/issues/6553 if is_host("windows") then text = path.unix(text) end return string.format("echo %s", text) end end -- add target custom commands for batchcmds function _add_target_custom_commands_for_batchcmds(cmakelists, target, outputdir, suffix, cmds) if #cmds == 0 then return end if suffix == "before" then -- ADD_CUSTOM_COMMAND and PRE_BUILD did not work as I expected, -- so we need to use add_dependencies and fake target to support it. -- -- @see https://gitlab.kitware.com/cmake/cmake/-/issues/17802 -- local key = target:name() .. "_" .. hash.rand64() cmakelists:print("add_custom_command(OUTPUT output_%s", key) for _, cmd in ipairs(cmds) do local command = _get_command_string(cmd, outputdir) if command then cmakelists:print(" COMMAND %s", command) end end cmakelists:print(" VERBATIM") cmakelists:print(")") cmakelists:print("add_custom_target(target_%s", key) cmakelists:print(" DEPENDS output_%s", key) cmakelists:print(")") cmakelists:print("add_dependencies(%s target_%s)", target:name(), key) elseif suffix == "after" then cmakelists:print("add_custom_command(TARGET %s", target:name()) cmakelists:print(" POST_BUILD") for _, cmd in ipairs(cmds) do local command = _get_command_string(cmd, outputdir) if command then cmakelists:print(" COMMAND %s", command) end end cmakelists:print(" VERBATIM") cmakelists:print(")") end end -- add target custom commands function _add_target_custom_commands(cmakelists, target, outputdir) -- ignore c++ modules rules local ignored_rules if _can_native_support_for_cxxmodules() then ignored_rules = _get_cxxmodules_rules() end -- add before commands -- we use irpairs(groups), because the last group that should be given the highest priority. -- rule.on_buildcmd_files should also be executed before building the target, as cmake PRE_BUILD does not work. local cmds_before = target_cmds.get_target_buildcmds(target, {ignored_rules = ignored_rules, stages = {"before", "on"}}) _add_target_custom_commands_for_batchcmds(cmakelists, target, outputdir, "before", cmds_before) -- add after commands local cmds_after = target_cmds.get_target_buildcmds(target, {ignored_rules = ignored_rules, stages = {"after"}}) _add_target_custom_commands_for_batchcmds(cmakelists, target, outputdir, "after", cmds_after) end -- add target function _add_target(cmakelists, target, outputdir) -- add comment cmakelists:print("# target") -- is phony target? if target:is_phony() then return _add_target_phony(cmakelists, target) elseif target:is_object() then _add_target_object(cmakelists, target, outputdir) elseif target:is_binary() then _add_target_binary(cmakelists, target, outputdir) elseif target:is_static() then _add_target_static(cmakelists, target, outputdir) elseif target:is_shared() then _add_target_shared(cmakelists, target, outputdir) elseif target:is_headeronly() then _add_target_headeronly(cmakelists, target) _add_target_include_directories(cmakelists, target, outputdir) return elseif target:is_moduleonly() then _add_target_moduleonly(cmakelists, target) return else raise("unknown target kind %s", target:kind()) end -- add target dependencies _add_target_dependencies(cmakelists, target) -- add target custom commands -- we need to call it first for running all rules, these rules will change some flags, e.g. c++modules _add_target_custom_commands(cmakelists, target, outputdir) -- add target precompilied header _add_target_precompiled_header(cmakelists, target, outputdir) -- add target include directories _add_target_include_directories(cmakelists, target, outputdir) -- add target system include directories _add_target_sysinclude_directories(cmakelists, target, outputdir) -- add target framework directories _add_target_framework_directories(cmakelists, target, outputdir) -- add target compile definitions _add_target_compile_definitions(cmakelists, target) -- add target compile options _add_target_compile_options(cmakelists, target, outputdir) -- add target warnings _add_target_warnings(cmakelists, target) -- add target exceptions _add_target_encodings(cmakelists, target) -- add target exceptions _add_target_exceptions(cmakelists, target) -- add target languages _add_target_languages(cmakelists, target) -- add target optimization _add_target_optimization(cmakelists, target) -- add target symbols _add_target_symbols(cmakelists, target) -- add target runtimes _add_target_runtimes(cmakelists, target) -- add target link libraries _add_target_link_libraries(cmakelists, target, outputdir) -- add target link directories _add_target_link_directories(cmakelists, target, outputdir) -- add target link options _add_target_link_options(cmakelists, target, outputdir) -- add target sources _add_target_sources(cmakelists, target, outputdir) -- add target source groups _add_target_source_groups(cmakelists, target, outputdir) -- end cmakelists:print("") end -- generate cmakelists function _generate_cmakelists(cmakelists, outputdir) -- add project info _add_project(cmakelists, outputdir) -- add targets local project_targets = target_utils.get_project_targets() for _, target in table.orderpairs(project_targets) do _add_target(cmakelists, target, outputdir) end end function make(outputdir) local oldir = os.cd(os.projectdir()) local cmakelists = io.open(path.join(outputdir, "CMakeLists.txt"), "w") target_cmds.prepare_targets() _generate_cmakelists(cmakelists, outputdir) cmakelists:close() os.cd(oldir) end