diff options
| author | ruki <[email protected]> | 2023-05-03 00:32:19 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-03 00:32:19 +0800 |
| commit | 3382dcb4994ca70c1b3c5ea7bc220ccac8b736a2 (patch) | |
| tree | 43eb0712243116dc68ce60b3d74d8065de1aec6e | |
| parent | 3ae0c1af04ae968e1cd39a61685c40725264e4ff (diff) | |
| parent | 34e986ea513cfc509dd860b2edab6f867d13a1dd (diff) | |
Merge pull request #3703 from xmake-io/conan
Support conan 2.0
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | tests/projects/package/conan/xmake.lua | 4 | ||||
| -rw-r--r-- | xmake/modules/package/manager/conan/configurations.lua | 6 | ||||
| -rw-r--r-- | xmake/modules/package/manager/conan/install_package.lua | 268 | ||||
| -rw-r--r-- | xmake/modules/package/manager/conan/v1/install_package.lua | 287 | ||||
| -rw-r--r-- | xmake/modules/package/manager/conan/v2/install_package.lua | 336 | ||||
| -rw-r--r-- | xmake/scripts/conan/extensions/generators/xmake_generator.py | 114 |
7 files changed, 752 insertions, 265 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a0300fb..deb5068d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New features * [#3613](https://github.com/xmake-io/xmake/issues/3613): Add `wasm.preloadfiles` configuration for wasm +* [#3703](https://github.com/xmake-io/xmake/pull/3703): Support for conan 2.0 ### Changes @@ -1578,6 +1579,7 @@ ### 新特性 * [#3613](https://github.com/xmake-io/xmake/issues/3613): 添加 `wasm.preloadfiles` 扩展配置 +* [#3703](https://github.com/xmake-io/xmake/pull/3703): 支持 conan 2.0 ### 改进 diff --git a/tests/projects/package/conan/xmake.lua b/tests/projects/package/conan/xmake.lua index 3021fab7c..ecf8c6b60 100644 --- a/tests/projects/package/conan/xmake.lua +++ b/tests/projects/package/conan/xmake.lua @@ -1,6 +1,6 @@ add_requires("conan::zlib 1.2.11", {alias = "zlib", debug = true}) -add_requires("conan::openssl 1.1.1g", {alias = "openssl", - configs = {options = "OpenSSL:shared=True"}}) +add_requires("conan::openssl 1.1.1t", {alias = "openssl", + configs = {options = "shared=True"}}) target("test") set_kind("binary") diff --git a/xmake/modules/package/manager/conan/configurations.lua b/xmake/modules/package/manager/conan/configurations.lua index b4ed38fc3..8e03cf9a3 100644 --- a/xmake/modules/package/manager/conan/configurations.lua +++ b/xmake/modules/package/manager/conan/configurations.lua @@ -24,10 +24,10 @@ function main() { build = {description = "Use it to choose if you want to build from sources.", default = "missing", values = {"all", "never", "missing", "outdated"}}, remote = {description = "Set the conan remote server."}, - options = {description = "Set the options values, e.g. OpenSSL:shared=True"}, - imports = {description = "Set the imports for conan."}, + options = {description = "Set the options values, e.g. shared=True"}, settings = {description = "Set the build settings for conan."}, - build_requires = {description = "Set the build requires for conan.", default = "xmake_generator/0.1.0@bincrafters/testing"} + imports = {description = "Set the imports for conan 1.x, it has been deprecated in conan 2.x."}, + build_requires = {description = "Set the build requires for conan."} } end diff --git a/xmake/modules/package/manager/conan/install_package.lua b/xmake/modules/package/manager/conan/install_package.lua index 2a3f8b38f..48e9f132f 100644 --- a/xmake/modules/package/manager/conan/install_package.lua +++ b/xmake/modules/package/manager/conan/install_package.lua @@ -21,280 +21,28 @@ -- imports import("core.base.option") import("core.base.semver") -import("core.project.config") -import("core.tool.toolchain") -import("core.platform.platform") import("lib.detect.find_tool") -import("devel.git") -import("net.fasturl") - --- get build env -function _conan_get_build_env(name, plat) - local value = config.get(name) - if value == nil then - value = platform.toolconfig(name, plat) - end - if value == nil then - value = platform.tool(name, plat) - end - value = table.unique(table.wrap(value)) - if #value > 0 then - value = table.unwrap(value) - return value - end -end - --- get build directory -function _conan_get_build_directory(name) - return path.absolute(path.join(config.buildir() or os.tmpdir(), ".conan", name)) -end - --- generate conanfile.txt -function _conan_generate_conanfile(name, configs, opt) - - -- trace - dprint("generate %s ..", path.join(_conan_get_build_directory(name), "conanfile.txt")) - - -- get conan options, imports and build_requires - local options = table.wrap(configs.options) - local imports = table.wrap(configs.imports) - local build_requires = table.wrap(configs.build_requires) - - -- @see https://docs.conan.io/en/latest/systems_cross_building/cross_building.html - -- generate it - local conanfile = io.open("conanfile.txt", "w") - if conanfile then - conanfile:print("[generators]") - conanfile:print("xmake") - conanfile:print("[requires]") - local require_version = opt.require_version - if require_version ~= nil and require_version ~= "latest" then - conanfile:print("%s/%s", name, require_version) - else - conanfile:print("%s", name) - end - if #options > 0 then - conanfile:print("[options]") - conanfile:print("%s", table.concat(options, "\n")) - end - if #imports > 0 then - conanfile:print("[imports]") - conanfile:print("%s", table.concat(imports, "\n")) - end - if #build_requires > 0 then - conanfile:print("[build_requires]") - conanfile:print("%s", table.concat(build_requires, "\n")) - end - conanfile:close() - end -end - --- install xmake generator -function _conan_install_xmake_generator(conan) - local xmake_generator_localdir = path.join(config.directory(), "conan", "xmake_generator") - if not os.isdir(xmake_generator_localdir) then - - -- sort main urls - local mainurls = {"https://github.com/xmake-io/conan-xmake_generator.git", - "https://gitlab.com/xmake-io/conan-xmake_generator.git", - "https://gitee.com/xmake-io/conan-xmake_generator.git"} - fasturl.add(mainurls) - mainurls = fasturl.sort(mainurls) - - -- clone xmake generator repository - local ok = false - for _, url in ipairs(mainurls) do - ok = try { function () git.clone(url, {depth = 1, branch = "0.1.0/testing", outputdir = xmake_generator_localdir}); return true end } - if ok then - break - end - end - if ok then - os.vrunv(conan.program, {"export", xmake_generator_localdir, "bincrafters/testing"}) - end - end -end -- install package -- --- @param name the package name, e.g. conan::OpenSSL/1.0.2n@conan/stable +-- @param name the package name, e.g. conan::OpenSSL 1.0.2n -- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , -- configs = { -- remote = "", build = "all", options = {}, imports = {}, build_requires = {}, -- settings = {"compiler=msvc", "compiler.version=10", "compiler.runtime=MD"}}} -- --- @return true or false --- function main(name, opt) - - -- get configs - opt = opt or {} - local configs = opt.configs or {} - - -- find conan local conan = find_tool("conan", {version = true}) if not conan then raise("conan not found!") end - if conan.version then - assert(semver.compare(conan.version, "2.0") < 0, "conan %s is not supported, please use conan 1.x", conan.version) - end - - -- get build directory - local buildir = _conan_get_build_directory(name) - - -- clean the build directory - os.tryrm(buildir) - if not os.isdir(buildir) then - os.mkdir(buildir) - end - - -- enter build directory - local oldir = os.cd(buildir) - - -- install xmake generator - _conan_install_xmake_generator(conan) - - -- generate conanfile.txt - _conan_generate_conanfile(name, configs, opt) - - -- install package - local argv = {"install", "."} - if configs.build then - if configs.build == "all" then - table.insert(argv, "--build") - else - table.insert(argv, "--build=" .. configs.build) - end - end - - -- set platform - local plats = {macosx = "Macos", windows = "Windows", mingw = "Windows", linux = "Linux", cross = "Linux", iphoneos = "iOS", android = "Android"} - table.insert(argv, "-s") - local plat = plats[opt.plat] - if plat then - table.insert(argv, "os=" .. plat) - else - raise("cannot install package(%s) on platform(%s)!", name, opt.plat) - end - - -- set architecture - local archs = {x86_64 = "x86_64", - x64 = "x86_64", - i386 = "x86", - x86 = "x86", - armv7 = "armv7", - ["armv7-a"] = "armv7", -- for android, deprecated - ["armeabi"] = "armv7", -- for android, removed in ndk r17 - ["armeabi-v7a"] = "armv7", -- for android - armv7s = "armv7s", -- for iphoneos - arm64 = "armv8", -- for iphoneos - ["arm64-v8a"] = "armv8", -- for android - mips = "mips", - mips64 = "mips64"} - table.insert(argv, "-s") - local arch = archs[opt.arch] - if arch then - table.insert(argv, "arch=" .. arch) + if conan.version and semver.compare(conan.version, "2.0.4") >= 0 then -- TODO 2.0.5 + -- https://github.com/conan-io/conan/issues/13709 + import("package.manager.conan.v2.install_package")(conan, name, opt) + elseif conan.version and semver.compare(conan.version, "2.0.0") < 0 then + import("package.manager.conan.v1.install_package")(conan, name, opt) else - raise("cannot install package(%s) for arch(%s)!", name, opt.arch) + -- conan 2.0.0-2.0.4 does not supported + raise("conan %s is not supported, please use conan 1.x", conan.version) end - - -- set build mode - table.insert(argv, "-s") - if opt.mode == "debug" then - table.insert(argv, "build_type=Debug") - else - table.insert(argv, "build_type=Release") - end - - -- set compiler settings - if opt.plat == "windows" then - local vsvers = {["2022"] = "17", - ["2019"] = "16", - ["2017"] = "15", - ["2015"] = "14", - ["2013"] = "12", - ["2012"] = "11", - ["2010"] = "10", - ["2008"] = "9", - ["2005"] = "8"} - local vs = assert(config.get("vs"), "vs not found!") - table.insert(argv, "-s") - table.insert(argv, "compiler=Visual Studio") - table.insert(argv, "-s") - table.insert(argv, "compiler.version=" .. assert(vsvers[vs], "unknown msvc version!")) - if configs.vs_runtime then - table.insert(argv, "-s") - table.insert(argv, "compiler.runtime=" .. configs.vs_runtime) - end - elseif opt.plat == "iphoneos" then - local target_minver = nil - local toolchain_xcode = toolchain.load("xcode", {plat = opt.plat, arch = opt.arch}) - if toolchain_xcode then - target_minver = toolchain_xcode:config("target_minver") - end - if target_minver and tonumber(target_minver) > 10 and (arch == "armv7" or arch == "armv7s" or arch == "x86") then - target_minver = "10" -- iOS 10 is the maximum deployment target for 32-bit targets - end - if target_minver then - table.insert(argv, "-s") - table.insert(argv, "os.version=" .. target_minver) - end - elseif opt.plat == "android" then - local ndk_sdkver = config.get("ndk_sdkver") - if ndk_sdkver then - table.insert(argv, "-s") - table.insert(argv, "os.api_level=" .. ndk_sdkver) - end - end - - -- set custom settings - for _, setting in ipairs(configs.settings) do - table.insert(argv, "-s") - table.insert(argv, setting) - end - - -- set remote - if configs.remote then - table.insert(argv, "-r") - table.insert(argv, configs.remote) - end - - -- TODO set environments - if opt.plat == "android" then - local envs = {} - local cflags = table.join(table.wrap(_conan_get_build_env("cxflags", opt.plat)), _conan_get_build_env("cflags", opt.plat)) - local cxxflags = table.join(table.wrap(_conan_get_build_env("cxflags", opt.plat)), _conan_get_build_env("cxxflags", opt.plat)) - envs.CC = _conan_get_build_env("cc", opt.plat) - envs.CXX = _conan_get_build_env("cxx", opt.plat) - envs.AS = _conan_get_build_env("as", opt.plat) - envs.AR = _conan_get_build_env("ar", opt.plat) - envs.LD = _conan_get_build_env("ld", opt.plat) - envs.LDSHARED = _conan_get_build_env("sh", opt.plat) - envs.CPP = _conan_get_build_env("cpp", opt.plat) - envs.RANLIB = _conan_get_build_env("ranlib", opt.plat) - envs.CFLAGS = table.concat(cflags, ' ') - envs.CXXFLAGS = table.concat(cxxflags, ' ') - envs.ASFLAGS = table.concat(table.wrap(_conan_get_build_env("asflags", opt.plat)), ' ') - envs.ARFLAGS = table.concat(table.wrap(_conan_get_build_env("arflags", opt.plat)), ' ') - envs.LDFLAGS = table.concat(table.wrap(_conan_get_build_env("ldflags", opt.plat)), ' ') - envs.SHFLAGS = table.concat(table.wrap(_conan_get_build_env("shflags", opt.plat)), ' ') - local toolchain_ndk = toolchain.load("ndk", {plat = opt.plat, arch = opt.arch}) - local ndk_sysroot = toolchain_ndk:config("ndk_sysroot") - if ndk_sysroot then - table.insert(argv, "-e") - table.insert(argv, "CONAN_CMAKE_FIND_ROOT_PATH=" .. ndk_sysroot) - end - for k, v in pairs(envs) do - table.insert(argv, "-e") - table.insert(argv, k .. "=" .. v) - end - end - - -- do install - os.vrunv(conan.program, argv) - - -- leave build directory - os.cd(oldir) end diff --git a/xmake/modules/package/manager/conan/v1/install_package.lua b/xmake/modules/package/manager/conan/v1/install_package.lua new file mode 100644 index 000000000..e443e585b --- /dev/null +++ b/xmake/modules/package/manager/conan/v1/install_package.lua @@ -0,0 +1,287 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file install_package.lua +-- + +-- imports +import("core.base.option") +import("core.base.semver") +import("core.project.config") +import("core.tool.toolchain") +import("core.platform.platform") +import("lib.detect.find_tool") +import("devel.git") +import("net.fasturl") + +-- get build env +function _conan_get_build_env(name, plat) + local value = config.get(name) + if value == nil then + value = platform.toolconfig(name, plat) + end + if value == nil then + value = platform.tool(name, plat) + end + value = table.unique(table.wrap(value)) + if #value > 0 then + value = table.unwrap(value) + return value + end +end + +-- get build directory +function _conan_get_build_directory(name) + return path.absolute(path.join(config.buildir() or os.tmpdir(), ".conan", name)) +end + +-- generate conanfile.txt +function _conan_generate_conanfile(name, configs, opt) + + -- trace + dprint("generate %s ..", path.join(_conan_get_build_directory(name), "conanfile.txt")) + + -- get conan options, imports and build_requires + local options = table.wrap(configs.options) + local imports = table.wrap(configs.imports) + local build_requires = table.wrap(configs.build_requires or "xmake_generator/0.1.0@bincrafters/testing") + + -- @see https://docs.conan.io/en/latest/systems_cross_building/cross_building.html + -- generate it + local conanfile = io.open("conanfile.txt", "w") + if conanfile then + conanfile:print("[generators]") + conanfile:print("xmake") + conanfile:print("[requires]") + local require_version = opt.require_version + if require_version ~= nil and require_version ~= "latest" then + conanfile:print("%s/%s", name, require_version) + else + conanfile:print("%s", name) + end + if #options > 0 then + conanfile:print("[options]") + for _, item in ipairs(options) do + if not item:find(":", 1, true) then + item = name .. ":" .. item + end + conanfile:print("%s", item) + end + end + if #imports > 0 then + conanfile:print("[imports]") + conanfile:print("%s", table.concat(imports, "\n")) + end + if #build_requires > 0 then + conanfile:print("[build_requires]") + conanfile:print("%s", table.concat(build_requires, "\n")) + end + conanfile:close() + end +end + +-- install xmake generator +function _conan_install_xmake_generator(conan) + local xmake_generator_localdir = path.join(config.directory(), "conan", "xmake_generator") + if not os.isdir(xmake_generator_localdir) then + + -- sort main urls + local mainurls = {"https://github.com/xmake-io/conan-xmake_generator.git", + "https://gitlab.com/xmake-io/conan-xmake_generator.git", + "https://gitee.com/xmake-io/conan-xmake_generator.git"} + fasturl.add(mainurls) + mainurls = fasturl.sort(mainurls) + + -- clone xmake generator repository + local ok = false + for _, url in ipairs(mainurls) do + ok = try { function () git.clone(url, {depth = 1, branch = "0.1.0/testing", outputdir = xmake_generator_localdir}); return true end } + if ok then + break + end + end + if ok then + os.vrunv(conan.program, {"export", xmake_generator_localdir, "bincrafters/testing"}) + end + end +end + +-- install package +function main(conan, name, opt) + + -- get configs + opt = opt or {} + local configs = opt.configs or {} + + -- get build directory + local buildir = _conan_get_build_directory(name) + + -- clean the build directory + os.tryrm(buildir) + if not os.isdir(buildir) then + os.mkdir(buildir) + end + + -- enter build directory + local oldir = os.cd(buildir) + + -- install xmake generator + _conan_install_xmake_generator(conan) + + -- generate conanfile.txt + _conan_generate_conanfile(name, configs, opt) + + -- install package + local argv = {"install", "."} + if configs.build then + if configs.build == "all" then + table.insert(argv, "--build") + else + table.insert(argv, "--build=" .. configs.build) + end + end + + -- set platform + local plats = {macosx = "Macos", windows = "Windows", mingw = "Windows", linux = "Linux", cross = "Linux", iphoneos = "iOS", android = "Android"} + table.insert(argv, "-s") + local plat = plats[opt.plat] + if plat then + table.insert(argv, "os=" .. plat) + else + raise("cannot install package(%s) on platform(%s)!", name, opt.plat) + end + + -- set architecture + local archs = {x86_64 = "x86_64", + x64 = "x86_64", + i386 = "x86", + x86 = "x86", + armv7 = "armv7", + ["armv7-a"] = "armv7", -- for android, deprecated + ["armeabi"] = "armv7", -- for android, removed in ndk r17 + ["armeabi-v7a"] = "armv7", -- for android + armv7s = "armv7s", -- for iphoneos + arm64 = "armv8", -- for iphoneos + ["arm64-v8a"] = "armv8", -- for android + mips = "mips", + mips64 = "mips64"} + table.insert(argv, "-s") + local arch = archs[opt.arch] + if arch then + table.insert(argv, "arch=" .. arch) + else + raise("cannot install package(%s) for arch(%s)!", name, opt.arch) + end + + -- set build mode + table.insert(argv, "-s") + if opt.mode == "debug" then + table.insert(argv, "build_type=Debug") + else + table.insert(argv, "build_type=Release") + end + + -- set compiler settings + if opt.plat == "windows" then + local vsvers = {["2022"] = "17", + ["2019"] = "16", + ["2017"] = "15", + ["2015"] = "14", + ["2013"] = "12", + ["2012"] = "11", + ["2010"] = "10", + ["2008"] = "9", + ["2005"] = "8"} + local vs = assert(config.get("vs"), "vs not found!") + table.insert(argv, "-s") + table.insert(argv, "compiler=Visual Studio") + table.insert(argv, "-s") + table.insert(argv, "compiler.version=" .. assert(vsvers[vs], "unknown msvc version!")) + if configs.vs_runtime then + table.insert(argv, "-s") + table.insert(argv, "compiler.runtime=" .. configs.vs_runtime) + end + elseif opt.plat == "iphoneos" then + local target_minver = nil + local toolchain_xcode = toolchain.load("xcode", {plat = opt.plat, arch = opt.arch}) + if toolchain_xcode then + target_minver = toolchain_xcode:config("target_minver") + end + if target_minver and tonumber(target_minver) > 10 and (arch == "armv7" or arch == "armv7s" or arch == "x86") then + target_minver = "10" -- iOS 10 is the maximum deployment target for 32-bit targets + end + if target_minver then + table.insert(argv, "-s") + table.insert(argv, "os.version=" .. target_minver) + end + elseif opt.plat == "android" then + local ndk_sdkver = config.get("ndk_sdkver") + if ndk_sdkver then + table.insert(argv, "-s") + table.insert(argv, "os.api_level=" .. ndk_sdkver) + end + end + + -- set custom settings + for _, setting in ipairs(configs.settings) do + table.insert(argv, "-s") + table.insert(argv, setting) + end + + -- set remote + if configs.remote then + table.insert(argv, "-r") + table.insert(argv, configs.remote) + end + + -- TODO set environments + if opt.plat == "android" then + local envs = {} + local cflags = table.join(table.wrap(_conan_get_build_env("cxflags", opt.plat)), _conan_get_build_env("cflags", opt.plat)) + local cxxflags = table.join(table.wrap(_conan_get_build_env("cxflags", opt.plat)), _conan_get_build_env("cxxflags", opt.plat)) + envs.CC = _conan_get_build_env("cc", opt.plat) + envs.CXX = _conan_get_build_env("cxx", opt.plat) + envs.AS = _conan_get_build_env("as", opt.plat) + envs.AR = _conan_get_build_env("ar", opt.plat) + envs.LD = _conan_get_build_env("ld", opt.plat) + envs.LDSHARED = _conan_get_build_env("sh", opt.plat) + envs.CPP = _conan_get_build_env("cpp", opt.plat) + envs.RANLIB = _conan_get_build_env("ranlib", opt.plat) + envs.CFLAGS = table.concat(cflags, ' ') + envs.CXXFLAGS = table.concat(cxxflags, ' ') + envs.ASFLAGS = table.concat(table.wrap(_conan_get_build_env("asflags", opt.plat)), ' ') + envs.ARFLAGS = table.concat(table.wrap(_conan_get_build_env("arflags", opt.plat)), ' ') + envs.LDFLAGS = table.concat(table.wrap(_conan_get_build_env("ldflags", opt.plat)), ' ') + envs.SHFLAGS = table.concat(table.wrap(_conan_get_build_env("shflags", opt.plat)), ' ') + local toolchain_ndk = toolchain.load("ndk", {plat = opt.plat, arch = opt.arch}) + local ndk_sysroot = toolchain_ndk:config("ndk_sysroot") + if ndk_sysroot then + table.insert(argv, "-e") + table.insert(argv, "CONAN_CMAKE_FIND_ROOT_PATH=" .. ndk_sysroot) + end + for k, v in pairs(envs) do + table.insert(argv, "-e") + table.insert(argv, k .. "=" .. v) + end + end + + -- do install + os.vrunv(conan.program, argv) + + -- leave build directory + os.cd(oldir) +end diff --git a/xmake/modules/package/manager/conan/v2/install_package.lua b/xmake/modules/package/manager/conan/v2/install_package.lua new file mode 100644 index 000000000..af2ec771c --- /dev/null +++ b/xmake/modules/package/manager/conan/v2/install_package.lua @@ -0,0 +1,336 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file install_package.lua +-- + +-- imports +import("core.base.option") +import("core.base.semver") +import("core.project.config") +import("core.tool.toolchain") +import("core.platform.platform") +import("lib.detect.find_tool") +import("devel.git") +import("net.fasturl") + +-- get build env +function _conan_get_build_env(name, plat) + local value = config.get(name) + if value == nil then + value = platform.toolconfig(name, plat) + end + if value == nil then + value = platform.tool(name, plat) + end + value = table.unique(table.wrap(value)) + if #value > 0 then + value = table.unwrap(value) + return value + end +end + +-- get build directory +function _conan_get_build_directory(name) + return path.absolute(path.join(config.buildir() or os.tmpdir(), ".conan", name)) +end + +-- generate conanfile.txt +function _conan_generate_conanfile(name, configs, opt) + + -- trace + dprint("generate %s ..", path.join(_conan_get_build_directory(name), "conanfile.txt")) + + -- get conan options, imports and build_requires + local options = table.wrap(configs.options) + local build_requires = table.wrap(configs.build_requires) + + -- @see https://docs.conan.io/1/migrating_to_2.0/recipes.html + -- https://docs.conan.io/en/latest/systems_cross_building/cross_building.html + -- generate it + local conanfile = io.open("conanfile.txt", "w") + if conanfile then + conanfile:print("[requires]") + local require_version = opt.require_version + if require_version ~= nil and require_version ~= "latest" then + conanfile:print("%s/%s", name, require_version) + else + conanfile:print("%s", name) + end + if #options > 0 then + conanfile:print("[options]") + for _, item in ipairs(options) do + if not item:find(":", 1, true) then + item = name .. "/*:" .. item + end + conanfile:print("%s", item) + end + end + if #build_requires > 0 then + conanfile:print("[tool_requires]") + conanfile:print("%s", table.concat(build_requires, "\n")) + end + conanfile:close() + end +end + +-- get conan home directory +function _conan_get_homedir(conan) + local homedir = _g.homedir + if homedir == nil then + homedir = try {function () return os.iorunv(conan.program, {"config", "home"}) end} + _g.homedir = homedir + end + return homedir +end + +-- install xmake generator +-- @see https://github.com/conan-io/conan/pull/13718 +-- +function _conan_install_xmake_generator(conan) + local homedir = assert(_conan_get_homedir(conan), "cannot get conan home") + local scriptfile_now = path.join(os.programdir(), "scripts", "conan", "extensions", "generators", "xmake_generator.py") + local scriptfile_installed = path.join(homedir, "extensions", "generators", "xmake_generator.py") + if not os.isfile(scriptfile_installed) or os.mtime(scriptfile_now) > os.mtime(scriptfile_installed) then + os.vrunv(conan.program, {"config", "install", path.join(os.programdir(), "scripts", "conan")}) + end +end + +-- get arch +function _conan_get_arch(arch) + local map = {x86_64 = "x86_64", + x64 = "x86_64", + i386 = "x86", + x86 = "x86", + armv7 = "armv7", + ["armv7-a"] = "armv7", -- for android, deprecated + ["armeabi"] = "armv7", -- for android, removed in ndk r17 + ["armeabi-v7a"] = "armv7", -- for android + armv7s = "armv7s", -- for iphoneos + arm64 = "armv8", -- for iphoneos + ["arm64-v8a"] = "armv8", -- for android + mips = "mips", + mips64 = "mips64"} + return assert(map[arch], "unknown arch(%s)!", arch) +end + +-- get os +function _conan_get_os(plat) + local map = {macosx = "Macos", + windows = "Windows", + mingw = "Windows", + linux = "Linux", + cross = "Linux", + iphoneos = "iOS", + android = "Android"} + return assert(map[plat], "unknown os(%s)!", plat) +end + +-- get build type +function _conan_get_build_type(mode) + if mode == "debug" then + return "Debug" + else + return "Release" + end +end + +-- generate compiler profile +function _conan_generate_compiler_profile(profile, configs, opt) + local conf + local plat = opt.plat + local arch = opt.arch + if plat == "windows" then + -- https://github.com/conan-io/conan/blob/353c63b16c31c90d370305b5cbb5dc175cf8a443/conan/tools/microsoft/visual.py#L13 + local vsvers = {["2022"] = "193", + ["2019"] = "192", + ["2017"] = "191", + ["2015"] = "190", + ["2013"] = "180", + ["2012"] = "170"} + local vs = assert(config.get("vs"), "vs not found!") + profile:print("compiler=msvc") + profile:print("compiler.version=" .. assert(vsvers[vs], "unknown msvc version!")) + if configs.vs_runtime then + profile:print("compiler.runtime=" .. configs.vs_runtime) + end + elseif plat == "iphoneos" then + local target_minver = nil + local xcode = toolchain.load("xcode", {plat = plat, arch = arch}) + if xcode then + target_minver = xcode:config("target_minver") + end + if target_minver and tonumber(target_minver) > 10 and (arch == "armv7" or arch == "armv7s" or arch == "x86") then + target_minver = "10" -- iOS 10 is the maximum deployment target for 32-bit targets + end + if target_minver then + profile:print("os.version=" .. target_minver) + end + local simulator = xcode:config("appledev") == "simulator" + profile:print("os.sdk=" .. (simulator and "iphonesimulator" or "iphoneos")) + profile:print("compiler=clang") + local version + local result = find_tool("clang", {version = true}) + if result and result.version then + local v = semver.try_parse(result.version) + if v then + version = v:major() + end + end + if version then + profile:print("compiler.version=" .. version) + end + elseif plat == "android" then + local ndk = toolchain.load("ndk", {plat = plat, arch = arch}) + local ndk_sdkver = ndk:config("ndk_sdkver") + if ndk_sdkver then + profile:print("os.api_level=" .. ndk_sdkver) + end + local ndk_cxxstl = config.get("ndk_cxxstl") + if ndk_cxxstl then + profile:print("compiler.libcxx=" .. ndk_cxxstl) + end + local version + local program, toolname = ndk:tool("cc") + local result = find_tool(toolname, {program = program, version = true}) + if result and result.version then + local v = semver.try_parse(result.version) + if v then + version = v:major() + end + end + profile:print("compiler=" .. toolname) + if version then + profile:print("compiler.version=" .. version) + end + conf = {} + conf["tools.android:ndk_path"] = ndk:config("ndk") + else + local program, toolname = platform.tool("cc", plat, arch) + if toolname == "gcc" or toolname == "clang" then + local version + local result = find_tool(toolname, {program = program, version = true}) + if result and result.version then + local v = semver.try_parse(result.version) + if v then + version = v:major() + end + end + profile:print("compiler=" .. toolname) + profile:print("compiler.cppstd=gnu17") + if toolname == "clang" then + profile:print("compiler.libcxx=libc++") + else + profile:print("compiler.libcxx=libstdc++11") + end + if version then + profile:print("compiler.version=" .. version) + end + end + end + + if conf then + profile:print("") + profile:print("[conf]") + for k, v in pairs(conf) do + profile:print("%s=%s", k, v) + end + end +end + +-- generate build profile +function _conan_generate_build_profile(configs, opt) + local profile = io.open("profile_build.txt", "w") + profile:print("[settings]") + profile:print("arch=%s", _conan_get_arch(os.arch())) + profile:print("build_type=%s", _conan_get_build_type(opt.mode)) + profile:print("os=%s", _conan_get_os(os.host())) + _conan_generate_compiler_profile(profile, configs, {plat = os.host(), arch = os.arch()}) + profile:close() +end + +-- generate host profile +function _conan_generate_host_profile(configs, opt) + local profile = io.open("profile_host.txt", "w") + profile:print("[settings]") + profile:print("arch=%s", _conan_get_arch(opt.arch)) + profile:print("build_type=%s", _conan_get_build_type(opt.mode)) + profile:print("os=%s", _conan_get_os(opt.plat)) + _conan_generate_compiler_profile(profile, configs, opt) + profile:close() +end + +-- install package +function main(conan, name, opt) + + -- get configs + opt = opt or {} + local configs = opt.configs or {} + + -- get build directory + local buildir = _conan_get_build_directory(name) + + -- clean the build directory + os.tryrm(buildir) + if not os.isdir(buildir) then + os.mkdir(buildir) + end + + -- enter build directory + local oldir = os.cd(buildir) + + -- install xmake generator + _conan_install_xmake_generator(conan) + + -- generate conanfile.txt + _conan_generate_conanfile(name, configs, opt) + + -- generate host profile + _conan_generate_host_profile(configs, opt) + + -- generate build profile + _conan_generate_build_profile(configs, opt) + + -- install package + local argv = {"install", ".", "-g", "XmakeGenerator", + "--profile:build=profile_build.txt", "--profile:host=profile_host.txt"} + if configs.build then + if configs.build == "all" then + table.insert(argv, "--build") + else + table.insert(argv, "--build=" .. configs.build) + end + end + + -- set custom settings + for _, setting in ipairs(configs.settings) do + table.insert(argv, "-s") + table.insert(argv, setting) + end + + -- set remote + if configs.remote then + table.insert(argv, "-r") + table.insert(argv, configs.remote) + end + + -- do install + os.vrunv(conan.program, argv) + + -- leave build directory + os.cd(oldir) +end diff --git a/xmake/scripts/conan/extensions/generators/xmake_generator.py b/xmake/scripts/conan/extensions/generators/xmake_generator.py new file mode 100644 index 000000000..a7298615f --- /dev/null +++ b/xmake/scripts/conan/extensions/generators/xmake_generator.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from conan import ConanFile +from conan.tools.files import save, load +from conan.tools.microsoft import unix_path, VCVars, is_msvc +from conan.errors import ConanInvalidConfiguration +from conan.errors import ConanException +from conans.model.build_info import CppInfo + +class XmakeGenerator: + def __init__(self, conanfile): + self._conanfile = conanfile + + def filename(self): + return "conanbuildinfo.xmake.lua" + + def generate(self): + print("XmakeGenerator: generating build info ..") + + # extract all dependencies + host_req = self._conanfile.dependencies.host + test_req = self._conanfile.dependencies.test + build_req = self._conanfile.dependencies.build + + full_req = list(host_req.items()) \ + + list(test_req.items()) \ + + list(build_req.items()) + + for require, dep in full_req: + #dep_name = require.ref.name + + # get aggregate dependency's cppinfo + dep_cppinfo = dep.cpp_info.copy() + dep_cppinfo.set_relative_base_folder(dep.package_folder) + dep_aggregate = dep_cppinfo.aggregated_components() + + # format deps + deps = XmakeDepsFormatter(dep_aggregate) + + # make template + plat = self._conanfile.settings.os + arch = self._conanfile.settings.arch + mode = self._conanfile.settings.build_type + template = (' {plat}_{arch}_{mode} = \n' + ' {{\n' + ' includedirs = {{{deps.include_paths}}},\n' + ' linkdirs = {{{deps.lib_paths}}},\n' + ' links = {{{deps.libs}}},\n' + ' frameworkdirs = {{{deps.framework_paths}}},\n' + ' frameworks = {{{deps.frameworks}}},\n' + ' syslinks = {{{deps.system_libs}}},\n' + ' defines = {{{deps.defines}}},\n' + ' cxxflags = {{{deps.cppflags}}},\n' + ' cflags = {{{deps.cflags}}},\n' + ' shflags = {{{deps.sharedlinkflags}}},\n' + ' ldflags = {{{deps.exelinkflags}}},\n' + ' __bindirs = {{{deps.bin_paths}}},\n' + ' __resdirs = {{{deps.res_paths}}},\n' + ' __srcdirs = {{{deps.src_paths}}}\n' + ' }}') + + # make sections + sections = [] + sections.append(template.format(plat = plat, arch = arch, mode = mode, deps = deps)) + + # make content + content = "{\n" + ",\n".join(sections) + "\n}" + print(content) + + # save content to file + with open(self.filename(), 'w') as file: + file.write(content) + +class XmakeDepsFormatter(object): + def __prepare_process_escape_character(self, raw_string): + if raw_string.find('\"') != -1: + raw_string = raw_string.replace("\"","\\\"") + return raw_string + + def __filter_char(self, raw_string): + return self.__prepare_process_escape_character(raw_string) + + def __init__(self, deps_cpp_info): + includedirs = deps_cpp_info._includedirs if deps_cpp_info._includedirs else [] + libdirs = deps_cpp_info._libdirs if deps_cpp_info._libdirs else [] + bindirs = deps_cpp_info._bindirs if deps_cpp_info._bindirs else [] + resdirs = deps_cpp_info._resdirs if deps_cpp_info._resdirs else [] + srcdirs = deps_cpp_info._srcdirs if deps_cpp_info._srcdirs else [] + frameworkdirs = deps_cpp_info._frameworkdirs if deps_cpp_info._frameworkdirs else [] + libs = deps_cpp_info._libs if deps_cpp_info._libs else [] + frameworks = deps_cpp_info._frameworks if deps_cpp_info._frameworks else [] + system_libs = deps_cpp_info._system_libs if deps_cpp_info._system_libs else [] + defines = deps_cpp_info._defines if deps_cpp_info._defines else [] + cxxflags = deps_cpp_info._cxxflags if deps_cpp_info._cxxflags else [] + cflags = deps_cpp_info._cflags if deps_cpp_info._cflags else [] + sharedlinkflags = deps_cpp_info._sharedlinkflags if deps_cpp_info._sharedlinkflags else [] + exelinkflags = deps_cpp_info._exelinkflags if deps_cpp_info._exelinkflags else [] + + self.include_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in includedirs) + self.lib_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in libdirs) + self.bin_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in bindirs) + self.res_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in resdirs) + self.src_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in srcdirs) + self.framework_paths = ",\n".join('"%s"' % self.__filter_char(p.replace("\\", "/")) for p in frameworkdirs) + self.libs = ", ".join('"%s"' % p for p in libs) + self.frameworks = ", ".join('"%s"' % p for p in frameworks) + self.system_libs = ", ".join('"%s"' % p for p in system_libs) + self.defines = ", ".join('"%s"' % self.__filter_char(p) for p in defines) + self.cppflags = ", ".join('"%s"' % p for p in cxxflags) + self.cflags = ", ".join('"%s"' % p for p in cflags) + self.sharedlinkflags = ", ".join('"%s"' % p for p in sharedlinkflags) + self.exelinkflags = ", ".join('"%s"' % p for p in exelinkflags) + |
