diff --git a/xmake/modules/package/manager/cmake/find_package.lua b/xmake/modules/package/manager/cmake/find_package.lua index 4c71291d791..18d8598c68b 100644 --- a/xmake/modules/package/manager/cmake/find_package.lua +++ b/xmake/modules/package/manager/cmake/find_package.lua @@ -24,6 +24,98 @@ import("core.base.hashset") import("core.project.target") import("lib.detect.find_tool") +local _cmake_internal_flags_variables = { + -- default libraries + "CMAKE_C_STANDARD_LIBRARIES", + "CMAKE_CXX_STANDARD_LIBRARIES", + + -- implicit directories + "CMAKE_C_IMPLICIT_LINK_LIBRARIES", + "CMAKE_C_IMPLICIT_LINK_DIRECTORIES", + "CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", + "CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES", + "CMAKE_CXX_IMPLICIT_LINK_LIBRARIES", + "CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES", + "CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", + "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES", + + -- c flags + "CMAKE_C_FLAGS", + "CMAKE_C_FLAGS_DEBUG", + "CMAKE_C_FLAGS_RELEASE", + "CMAKE_C_FLAGS_MINSIZEREL", + "CMAKE_C_FLAGS_RELWITHDEBINFO", + "CMAKE_C_FLAGS_INIT", + "CMAKE_C_FLAGS_DEBUG_INIT", + "CMAKE_C_FLAGS_RELEASE_INIT", + "CMAKE_C_FLAGS_MINSIZEREL_INIT", + "CMAKE_C_FLAGS_RELWITHDEBINFO_INIT", + "CMAKE_C_FLAGS", + + -- c++ flags + "CMAKE_CXX_FLAGS", + "CMAKE_CXX_FLAGS_DEBUG", + "CMAKE_CXX_FLAGS_RELEASE", + "CMAKE_CXX_FLAGS_MINSIZEREL", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO", + "CMAKE_CXX_FLAGS_INIT", + "CMAKE_CXX_FLAGS_DEBUG_INIT", + "CMAKE_CXX_FLAGS_RELEASE_INIT", + "CMAKE_CXX_FLAGS_MINSIZEREL_INIT", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT", + + -- linker flags + "CMAKE_EXE_LINKER_FLAGS", + "CMAKE_EXE_LINKER_FLAGS_DEBUG", + "CMAKE_EXE_LINKER_FLAGS_RELEASE", + "CMAKE_EXE_LINKER_FLAGS_MINSIZEREL", + "CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO", + "CMAKE_EXE_LINKER_FLAGS_INIT", + "CMAKE_EXE_LINKER_FLAGS_DEBUG_INIT", + "CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT", + "CMAKE_EXE_LINKER_FLAGS_MINSIZEREL_INIT", + "CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO_INIT", + "CMAKE_SHARED_LINKER_FLAGS", + "CMAKE_SHARED_LINKER_FLAGS_DEBUG", + "CMAKE_SHARED_LINKER_FLAGS_RELEASE", + "CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL", + "CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO", + "CMAKE_SHARED_LINKER_FLAGS_INIT", + "CMAKE_SHARED_LINKER_FLAGS_DEBUG_INIT", + "CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT", + "CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL_INIT", + "CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO_INIT", + "CMAKE_STATIC_LINKER_FLAGS", + "CMAKE_STATIC_LINKER_FLAGS_DEBUG", + "CMAKE_STATIC_LINKER_FLAGS_RELEASE", + "CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL", + "CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO", + "CMAKE_STATIC_LINKER_FLAGS_INIT", + "CMAKE_STATIC_LINKER_FLAGS_DEBUG_INIT", + "CMAKE_STATIC_LINKER_FLAGS_RELEASE_INIT", + "CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL_INIT", + "CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO_INIT", + "CMAKE_MODULE_LINKER_FLAGS", + "CMAKE_MODULE_LINKER_FLAGS_DEBUG", + "CMAKE_MODULE_LINKER_FLAGS_RELEASE", + "CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL", + "CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO", + "CMAKE_MODULE_LINKER_FLAGS_INIT", + "CMAKE_MODULE_LINKER_FLAGS_DEBUG_INIT", + "CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT", + "CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL_INIT", + "CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO_INIT", + + -- msvc + "CMAKE_MSVC_RUNTIME_LIBRARY", + + -- windows + -- https://github.com/Kitware/CMake/blob/e66e0b2cfefaf61fa995a0aa117df31e680b1c7e/Source/cmLocalGenerator.cxx#L1604 + -- https://github.com/Kitware/CMake/blob/e66e0b2cfefaf61fa995a0aa117df31e680b1c7e/Modules/Platform/Windows-MSVC.cmake#L404 + "CMAKE_CXX_CREATE_WIN32_EXE", + "CMAKE_CXX_CREATE_CONSOLE_EXE" +} + -- exclude cmake internal definitions https://github.com/xmake-io/xmake/issues/5217 function _should_exclude(define) local name = define:split("=")[1] @@ -41,12 +133,37 @@ end -- find package function _find_package(cmake, name, opt) + local is_windows = is_host("windows") -- get work directory local workdir = os.tmpfile() .. ".dir" os.tryrm(workdir) os.mkdir(workdir) - io.writefile(path.join(workdir, "test.cpp"), "") + + -- generate fake ninja + local ninja_version = "1.10.2" + local ninjascript + if is_windows then + ninjascript = path.join(workdir, "ninja1.bat") + io.writefile(ninjascript, "@echo off\necho " .. ninja_version) + else + ninjascript = path.join(workdir, "ninja1.sh") + io.writefile(ninjascript, "#!/bin/bash\necho " .. ninja_version) + os.vrunv("chmod", {"+x", ninjascript}, {curdir = workdir, envs = envs}) + end + + -- generate main.cpp + io.writefile(path.join(workdir, "main.cpp"), "") + + -- generate main-info.cmake.in + local info_cmake_in_file = io.open(path.join(workdir, "main-info.cmake.in"), "w") + info_cmake_in_file:print("set(INFO_INCLUDE_DIRS @INFO_INCLUDE_DIRS@)") + info_cmake_in_file:print("set(INFO_LINK_DIRS @INFO_LINK_DIRS@)") + info_cmake_in_file:print("set(INFO_COMPILE_DEFINITIONS @INFO_COMPILE_DEFINITIONS@)") + info_cmake_in_file:print("set(INFO_COMPILE_OPTIONS @INFO_COMPILE_OPTIONS@)") + info_cmake_in_file:print("set(INFO_LINK_OPTIONS @INFO_LINK_OPTIONS@)") + info_cmake_in_file:print("set(INFO_LINK_LIBRARIES @INFO_LINK_LIBRARIES@)") + info_cmake_in_file:close() -- generate CMakeLists.txt local filepath = path.join(workdir, "CMakeLists.txt") @@ -54,7 +171,12 @@ function _find_package(cmake, name, opt) if cmake.version then cmakefile:print("cmake_minimum_required(VERSION %s)", cmake.version) end - cmakefile:print("project(find_package)") + cmakefile:print("project(main)") + + -- unset CMake internal flags variables + for _, var in ipairs(_cmake_internal_flags_variables) do + cmakefile:print("set(%s \"\")", var) + end -- e.g. OpenCV 4.1.1, Boost COMPONENTS regex system local requirestr = name @@ -62,12 +184,14 @@ function _find_package(cmake, name, opt) if opt.require_version and opt.require_version ~= "latest" then requirestr = requirestr .. " " .. opt.require_version end + -- set search mode, e.g. config, module -- it will be both mode if do not set this config. -- e.g. https://cmake.org/cmake/help/latest/command/find_package.html#id4 if configs.search_mode then requirestr = requirestr .. " " .. configs.search_mode:upper() end + -- use opt.components is for backward compatibility local componentstr = "" local components = configs.components or opt.components @@ -102,10 +226,36 @@ function _find_package(cmake, name, opt) end end end - local testname = "test_" .. name - cmakefile:print("find_package(%s REQUIRED %s)", requirestr, componentstr) - cmakefile:print("if(%s_FOUND)", name) - cmakefile:print(" add_executable(%s test.cpp)", testname) + + -- find package manually + local find_script = configs.find_script or opt.find_script + if find_script then + cmakefile:print(find_script) + else + cmakefile:print("find_package(%s REQUIRED %s)", requirestr, componentstr) + end + + -- add target + local target_type = configs.target_type or opt.target_type + local is_executable + if not target_type then + is_executable = true + cmakefile:print("add_executable(${PROJECT_NAME})") + elseif target_type == "shared" then + cmakefile:print("add_library(${PROJECT_NAME} SHARED)") + elseif target_type == "static" then + cmakefile:print("add_library(${PROJECT_NAME} STATIC)") + else + is_executable = true + cmakefile:print("add_executable(${PROJECT_NAME})") + end + cmakefile:print("target_sources(${PROJECT_NAME} PRIVATE main.cpp)") + + -- set target properties + if is_executable and is_windows and (configs.win32 or opt.win32) then + cmakefile:print("set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE TRUE)") + end + -- setup include directories local includedirs = "" if configs.include_directories then @@ -114,10 +264,11 @@ function _find_package(cmake, name, opt) includedirs = ("${%s_INCLUDE_DIR} ${%s_INCLUDE_DIRS}"):format(name, name) includedirs = includedirs .. (" ${%s_INCLUDE_DIR} ${%s_INCLUDE_DIRS}"):format(name:upper(), name:upper()) end - cmakefile:print(" target_include_directories(%s PRIVATE %s)", testname, includedirs) + cmakefile:print("target_include_directories(${PROJECT_NAME} PRIVATE %s)", includedirs) + -- reserved for backword compatibility - cmakefile:print(" target_include_directories(%s PRIVATE ${%s_CXX_INCLUDE_DIRS})", - testname, name) + cmakefile:print("target_include_directories(${PROJECT_NAME} PRIVATE ${%s_CXX_INCLUDE_DIRS})", name) + -- setup link library/target local linklibs = "" if configs.link_libraries then @@ -126,8 +277,24 @@ function _find_package(cmake, name, opt) linklibs = ("${%s_LIBRARY} ${%s_LIBRARIES} ${%s_LIBS}"):format(name, name, name) linklibs = linklibs .. (" ${%s_LIBRARY} ${%s_LIBRARIES} ${%s_LIBS}"):format(name:upper(), name:upper(), name:upper()) end - cmakefile:print(" target_link_libraries(%s PRIVATE %s)", testname, linklibs) - cmakefile:print("endif(%s_FOUND)", name) + cmakefile:print("target_link_libraries(${PROJECT_NAME} PRIVATE %s)", linklibs) + + -- link target manually + local link_script = configs.link_script or opt.link_script + if link_script then + cmakefile:print(link_script) + end + + -- setup generating information + cmakefile:print("set(INFO_INCLUDE_DIRS $)") + cmakefile:print("set(INFO_LINK_DIRS $)") + cmakefile:print("set(INFO_COMPILE_DEFINITIONS $)") + cmakefile:print("set(INFO_COMPILE_OPTIONS $)") + cmakefile:print("set(INFO_LINK_OPTIONS $)") + cmakefile:print("set(INFO_LINK_LIBRARIES $)") + cmakefile:print("configure_file(\"main-info.cmake.in\" \"${CMAKE_BINARY_DIR}/main-info.cmake.tmp\" @ONLY)") + cmakefile:print("file(GENERATE OUTPUT \"${CMAKE_BINARY_DIR}/main-info.cmake\" INPUT \"${CMAKE_BINARY_DIR}/main-info.cmake.tmp\")") + cmakefile:close() if option.get("diagnosis") then local cmakedata = io.readfile(filepath) @@ -137,161 +304,279 @@ function _find_package(cmake, name, opt) -- run cmake local envs = configs.envs or opt.envs or {} envs.CMAKE_BUILD_TYPE = envs.CMAKE_BUILD_TYPE or _cmake_mode(opt.mode or "release") - try {function() return os.vrunv(cmake.program, {workdir}, {curdir = workdir, envs = envs}) end} - - -- pares defines and includedirs for macosx/linux - local links - local linkdirs - local libfiles - local defines - local includedirs - local ldflags - local flagsfile = path.join(workdir, "CMakeFiles", testname .. ".dir", "flags.make") - if os.isfile(flagsfile) then - local flagsdata = io.readfile(flagsfile) - if flagsdata then - if option.get("diagnosis") then - cprint("finding includes from %s\n${dim}%s", flagsfile, flagsdata) - end - for _, line in ipairs(flagsdata:split("\n", {plain = true})) do - if line:find("CXX_INCLUDES =", 1, true) then - local has_include = false - local flags = os.argv(line:split("=", {plain = true})[2]:trim()) - for _, flag in ipairs(flags) do - if has_include or (flag:startswith("-I") and #flag > 2) then - local includedir = has_include and flag or flag:sub(3) - if includedir and os.isdir(includedir) then - includedirs = includedirs or {} - table.insert(includedirs, includedir) - end - has_include = false - elseif flag == "-isystem" or flag == "-I" then - has_include = true - end - end - elseif line:find("CXX_DEFINES =", 1, true) then - defines = defines or {} - local flags = os.argv(line:split("=", {plain = true})[2]:trim()) - for _, flag in ipairs(flags) do - if flag:startswith("-D") and #flag > 2 then - local define = flag:sub(3) - if define and not _should_exclude(define) then - table.insert(defines, define) - end - end - end - end + + local builddir = path.join(workdir, "build") + try { + function() + return os.vrunv(cmake.program, + { + "-S", + workdir, + "-B", + builddir, + "-G", + "Ninja", + "-DCMAKE_MAKE_PROGRAM=" .. ninjascript, + }, + { + curdir = workdir, + envs = envs + } + ) + end + } + + -- generate strip-info.cmake + local stripscriptpath = path.join(workdir, "strip-info.cmake") + local stripscript = io.open(stripscriptpath, "w") + stripscript:print("include(${INPUT_FILE})") + stripscript:print("function(_strip_genex _var)") + stripscript:print(" string(GENEX_STRIP \"${${_var}}\" _var_no_genex)") + stripscript:print(" if(NOT \"${_var_no_genex}\" STREQUAL \"${${_var}}\")") + stripscript:print(" unset(${_var} PARENT_SCOPE)") + stripscript:print(" endif()") + stripscript:print("endfunction()") + + stripscript:print("function(_list_to_table _list _table)") + stripscript:print(" if(_list AND (NOT \"${${_list}}\" STREQUAL \"\"))") + stripscript:print(" string(REPLACE \";\" \"\\\",\\n \\\"\" _result \"${${_list}}\")") + stripscript:print(" set(_result \"{\n \\\"${_result}\\\" \\n }\")") + stripscript:print(" set(${_table} ${_result} PARENT_SCOPE)") + stripscript:print(" return()") + stripscript:print(" endif()") + stripscript:print(" set(${_table} \"nil\" PARENT_SCOPE)") + stripscript:print("endfunction()") + + stripscript:print("_strip_genex(INFO_INCLUDE_DIRS)") + stripscript:print("_strip_genex(INFO_LINK_DIRS)") + stripscript:print("_strip_genex(INFO_COMPILE_DEFINITIONS)") + stripscript:print("_strip_genex(INFO_COMPILE_OPTIONS)") + stripscript:print("_strip_genex(INFO_LINK_OPTIONS)") + stripscript:print("_strip_genex(INFO_LINK_LIBRARIES)") + + stripscript:print("_list_to_table(INFO_INCLUDE_DIRS INFO_INCLUDE_DIRS_TABLE)") + stripscript:print("_list_to_table(INFO_LINK_DIRS INFO_LINK_DIRS_TABLE)") + stripscript:print("_list_to_table(INFO_COMPILE_DEFINITIONS INFO_COMPILE_DEFINITIONS_TABLE)") + stripscript:print("_list_to_table(INFO_COMPILE_OPTIONS INFO_COMPILE_OPTIONS_TABLE)") + stripscript:print("_list_to_table(INFO_LINK_OPTIONS INFO_LINK_OPTIONS_TABLE)") + stripscript:print("_list_to_table(INFO_LINK_LIBRARIES INFO_LINK_LIBRARIES_TABLE)") + + stripscript:print("set(_content \"{") + stripscript:print(" INCLUDE_DIRS = ${INFO_INCLUDE_DIRS_TABLE},") + stripscript:print(" LINK_DIRS = ${INFO_LINK_DIRS_TABLE},") + stripscript:print(" COMPILE_DEFINITIONS = ${INFO_COMPILE_DEFINITIONS_TABLE},") + stripscript:print(" COMPILE_OPTIONS = ${INFO_COMPILE_OPTIONS_TABLE},") + stripscript:print(" LINK_OPTIONS = ${INFO_LINK_OPTIONS_TABLE},") + stripscript:print(" LINK_LIBRARIES = ${INFO_LINK_LIBRARIES_TABLE}") + stripscript:print("}\")") + + stripscript:print("file(WRITE ${OUTPUT_FILE} ${_content})") + + stripscript:close() + + -- run strip-genex script + local infopath = path.join(builddir, "main-info.txt") + try { + function() + return os.vrunv(cmake.program, + { + "-DINPUT_FILE=" .. path.join(builddir, "main-info.cmake"), + "-DOUTPUT_FILE=" .. infopath, + "-P", + stripscriptpath, + }, + { + curdir = workdir + } + ) + end + } + + -- get main-info.txt + local info = io.load(infopath) + local includedirs = info.INCLUDE_DIRS + local linkdirs = info.LINK_DIRS + local defines = info.COMPILE_DEFINITIONS + local flags = info.COMPILE_OPTIONS + local ldflags = info.LINK_OPTIONS + local links = nil + local libfiles = nil + + -- get CMakeCache.txt + local is_msvc = false + for line in io.lines(path.join(builddir, "CMakeCache.txt")) do + if line:startswith("CMAKE_CXX_COMPILER") then + local compiler = line:sub(line:find("=") + 1):trim() + if path.basename(compiler):lower() == "cl" then + is_msvc = true end + break end end - -- parse links and linkdirs for macosx/linux - local linkfile = path.join(workdir, "CMakeFiles", testname .. ".dir", "link.txt") - if os.isfile(linkfile) then - local linkdata = io.readfile(linkfile) - if linkdata then + -- get build.ninja + local ninjafile = path.join(builddir, "build.ninja") + local ninjabuilds = nil + if os.isfile(ninjafile) then + local ninjadata = io.readfile(ninjafile) + if ninjadata then if option.get("diagnosis") then - cprint("finding links from %s\n${dim}%s", linkfile, linkdata) + cprint("finding links from %s\n${dim}%s", ninjafile, ninjadata) end - for _, line in ipairs(os.argv(linkdata)) do - local is_ldflags = false - local is_library = false - for _, suffix in ipairs({".so", ".dylib", ".dylib", ".tbd", ".lib"}) do - if line:startswith("-Wl,") then - is_ldflags = true - break - elseif line:find(suffix, 1, true) then - is_library = true - break - end - end - if is_ldflags then - ldflags = ldflags or {} - table.insert(ldflags, line) - elseif is_library then - -- strip library version suffix, e.g. libxxx.so.1.1 -> libxxx.so - if line:find(".so", 1, true) then - line = line:gsub("lib(.-)%.so%..+$", "lib%1.so") - end - -- get libfiles - if os.isfile(line) then - libfiles = libfiles or {} - table.insert(libfiles, line) + local results = {} + local current_build = nil + local current_vars = nil + for _, line in ipairs(ninjadata:split("\n")) do + -- https://ninja-build.org/manual.html#_build_statements + -- match build statement + -- e.g. + -- build CMakeFiles/main.dir/main.cpp.obj: ... + -- build main.exe: ... + local buildfiles = line:match("^build%s+([^:]+):") + if buildfiles then + -- save previous statement if exists + if current_build and current_vars then + table.insert(results, { build = current_build, vars = current_vars }) end - -- get links and linkdirs - local linkdir = path.directory(line) - if linkdir ~= "." then - linkdirs = linkdirs or {} - table.insert(linkdirs, linkdir) + local buildfile = os.argv(buildfiles)[1] + local basename = path.basename(buildfile) + if basename == "main" or basename == "libmain" or basename == "main.cpp" then + -- save current statement + current_build = buildfile + current_vars = {} + else + -- ignore unrelated build statements + current_build = nil + current_vars = nil end - local link = target.linkname(path.filename(line)) - if link then - links = links or {} - table.insert(links, link) + elseif current_build then + -- match variable assignments + -- e.g. + -- ^ CONFIG = Debug + -- ^ DEFINES = -DXXX=YYY + -- ^ INCLUDES = -isystem /path/to/include + local key, value = line:match("^%s+([%w_]+)%s*=%s*(.+)$") + if key and value then + -- save current variable + current_vars[key] = value + elseif line == "" then + -- encounter empty line, save current statement and start a new one + table.insert(results, { build = current_build, vars = current_vars }) + current_build = nil + current_vars = nil end - -- is link? e.g. -lxxx - elseif line:startswith("-l") then - local link = line:sub(3):trim() - links = links or {} - table.insert(links, link) end end + + -- save the last statement if exists + if current_build and current_vars then + table.insert(results, { build = current_build, vars = current_vars }) + end + + ninjabuilds = results end end - -- pares includedirs and links/linkdirs for windows - local vcprojfile = path.join(workdir, testname .. ".vcxproj") - if os.isfile(vcprojfile) then - local vcprojdata = io.readfile(vcprojfile) - local vs_mode = envs.CMAKE_BUILD_TYPE or _cmake_mode(opt.mode or "release") - vcprojdata = vcprojdata:match("(.-)") - - if vcprojdata then - for _, line in ipairs(vcprojdata:split("\n", {plain = true})) do - local values = line:match("(.+);%%%(AdditionalIncludeDirectories%)") - if values then - includedirs = includedirs or {} - table.join2(includedirs, path.splitenv(values)) - end + if ninjabuilds then + local ninja_defines = {} + local ninja_includes = {} + local ninja_flags = {} + local ninja_links = {} + local ninja_linkdirs = {} + local ninja_linkflags = {} - values = line:match("(.+)") - if values then - for _, library in ipairs(path.splitenv(values)) do - -- get libfiles - if os.isfile(library) then - libfiles = libfiles or {} - table.insert(libfiles, library) + -- strip command line prefix + for _, builditem in pairs(ninjabuilds) do + for key, value in pairs(builditem.vars) do + if key == "DEFINES" then + -- strip -D, /D + for _, flag in ipairs(os.argv(value)) do + local define = flag:match("^-D(.+)$") or (is_msvc and flag:match("^/D(.+)$")) + if define and not _should_exclude(define) then + table.insert(ninja_defines, define) end - - -- get links and linkdirs - local linkdir = path.directory(library) - linkdir = path.translate(linkdir) - if linkdir ~= "." and not linkdir:startswith(workdir) then - linkdirs = linkdirs or {} - table.insert(linkdirs, linkdir) - local link = target.linkname(path.filename(library)) - if link then - links = links or {} - table.insert(links, link) + end + elseif key == "INCLUDES" then + local has_include = false + for _, flag in ipairs(os.argv(value)) do + if has_include then + local includedir = flag + table.insert(ninja_includes, includedir) + has_include = false + elseif flag == "-isystem" or flag == "-idirafter" or flag == "-I" or (is_msvc and flag == "/I") then + has_include = true + else + -- strip -I, /I, -external:I, /external:I + local includedir = flag:match("^-I(.+)$") or ( + is_msvc and ( + flag:match("^/I(.+)$") or + flag:match("^-external:I(.+)$") or + flag:match("^/external:I(.+)$") + ) + ) + if includedir then + table.insert(ninja_includes, includedir) end end end - end - - values = line:match("%%%(PreprocessorDefinitions%);(.+)") - if values then - defines = defines or {} - values = path.splitenv(values) - for _, value in ipairs(values) do - if not _should_exclude(value) then - table.insert(defines, value) + elseif key == "FLAGS" then + ninja_flags = table.join(ninja_flags, os.argv(value)) + elseif key == "LINK_LIBRARIES" then + for _, flag in ipairs(os.argv(value)) do + -- strip -l + if flag:startswith("-l") then + if #flag > 2 then + local link = flag:sub(3):trim() + table.insert(ninja_links, link) + end + else + table.insert(ninja_links, flag) end end + + elseif key == "LINK_PATH" then + for _, flag in ipairs(os.argv(value)) do + -- strip -L, /LIBPATH, /libpath, -LIBPATH, -libpath + local linkdir + if is_msvc then + linkdir = flag:match("^/LIBPATH:(.+)$") or + flag:match("^/libpath:(.+)$") or + flag:match("^-LIBPATH:(.+)$") or + flag:match("^-libpath:(.+)$") + else + linkdir = flag:match("^-L(.+)$") + end + if linkdir then + table.insert(ninja_linkdirs, linkdir) + end + end + elseif key == "LINK_FLAGS" then + ninja_linkflags = table.join(ninja_linkflags, os.argv(value)) end end end + + if #ninja_links > 0 then + links = ninja_links + libfiles = links + end + if #ninja_flags >0 and not ldflags then + ldflags = ninja_linkflags + end + if #ninja_linkdirs >0 and not linkdirs then + linkdirs = ninja_linkdirs + end + if #ninja_defines >0 and not defines then + defines = ninja_defines + end + if #ninja_flags >0 and not flags then + flags = ninja_flags + end + if #ninja_includes >0 and not includedirs then + includedirs = ninja_includes + end end -- remove work directory @@ -305,6 +590,7 @@ function _find_package(cmake, name, opt) results.linkdirs = table.unique(linkdirs) results.defines = table.unique(defines) results.libfiles = table.unique(libfiles) + results.flags = table.unique(flags) results.includedirs = table.unique(includedirs) return results end @@ -331,8 +617,17 @@ end -- configs = { -- components = {"regex", "system"}, -- moduledirs = "xxx", --- presets = {Boost_USE_STATIC_LIB = true}, --- envs = {CMAKE_PREFIX_PATH = "xxx"}}) +-- presets = { +-- Boost_USE_STATIC_LIB = true +-- }, +-- envs = { +-- CMAKE_PREFIX_PATH = "xxx", +-- }, +-- find_script = "find_package(Foo CONFIG REQUIRED)", +-- link_script = "target_link_libraries(${PROJECT_NAME} PRIVATE Foo::Foo)", +-- target_type = "shared", +-- win32 = true +-- }} -- function main(name, opt) opt = opt or {}