diff options
| author | Ângelo Andrade Cirino <[email protected]> | 2021-07-15 23:23:48 -0300 |
|---|---|---|
| committer | Ângelo Andrade Cirino <[email protected]> | 2021-07-15 23:23:48 -0300 |
| commit | 81ad292ea67f82d88d785a37d23b1f1603996da1 (patch) | |
| tree | 817b50173948547f7e4b491da271918c67b8a5ff /xmake/modules/private/action/require/impl/package.lua | |
| parent | 942c32f448d9b8a850438aa2af1de2415b7ae872 (diff) | |
| parent | 8c1141aba414c07554179cb28687124514f6e770 (diff) | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'xmake/modules/private/action/require/impl/package.lua')
| -rw-r--r-- | xmake/modules/private/action/require/impl/package.lua | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua new file mode 100644 index 000000000..844847558 --- /dev/null +++ b/xmake/modules/private/action/require/impl/package.lua @@ -0,0 +1,866 @@ +--!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 package.lua +-- + +-- imports +import("core.base.semver") +import("core.base.option") +import("core.base.global") +import("core.base.hashset") +import("private.utils.progress") +import("core.cache.memcache") +import("core.project.project") +import("core.package.package", {alias = "core_package"}) +import("devel.git") +import("private.action.require.impl.repository") + +-- get memcache +function _memcache() + return memcache.cache("require.impl.package") +end + +-- +-- parse require string +-- +-- basic +-- - add_requires("zlib") +-- +-- semver +-- - add_requires("tbox >=1.5.1", "zlib >=1.2.11") +-- - add_requires("tbox", {version = ">=1.5.1"}) +-- +-- git branch/tag +-- - add_requires("zlib master") +-- +-- with the given repository +-- - add_requires("xmake-repo@tbox >=1.5.1") +-- +-- with the given configs +-- - add_requires("aaa_bbb_ccc >=1.5.1 <1.6.0", {optional = true, alias = "mypkg", debug = true}) +-- - add_requires("tbox", {config = {coroutine = true, abc = "xxx"}}) +-- +-- with namespace and the 3rd package manager +-- - add_requires("xmake::xmake-repo@tbox >=1.5.1") +-- - add_requires("vcpkg::ffmpeg") +-- - add_requires("conan::OpenSSL/1.0.2n@conan/stable") +-- - add_requires("conan::openssl/1.1.1g") -- new +-- - add_requires("brew::pcre2/libpcre2-8 10.x", {alias = "pcre2"}) +-- +-- clone as a standalone package with the different configs +-- we can install and use these three packages at the same time. +-- - add_requires("zlib") +-- - add_requires("zlib~debug", {debug = true}) +-- - add_requires("zlib~shared", {configs = {shared = true}, alias = "zlib_shared"}) +-- +-- - add_requires("zlib~label1") +-- - add_requires("zlib", {label = "label2"}) +-- +-- private package, only for installation, do not export any links/includes and environments to target +-- - add_requires("zlib", {private = true}) +-- +-- {system = nil/true/false}: +-- nil: get remote or system packages +-- true: only get system package +-- false: only get remote packages +-- +-- {build = true}: always build packages, we do not use the precompiled artifacts +-- +function _parse_require(require_str) + + -- split package and version info + local splitinfo = require_str:split('%s+') + assert(splitinfo and #splitinfo > 0, "require(\"%s\"): invalid!", require_str) + + -- get package info + local packageinfo = splitinfo[1] + + -- get version + -- + -- e.g. + -- + -- latest + -- >=1.5.1 <1.6.0 + -- master || >1.4 + -- ~1.2.3 + -- ^1.1 + -- + local version = "latest" + if #splitinfo > 1 then + version = table.concat(table.slice(splitinfo, 2), " ") + end + assert(version, "require(\"%s\"): unknown version!", require_str) + + -- require third-party packages? e.g. brew::pcre2/libpcre2-8 + local reponame = nil + local packagename = nil + if require_str:find("::", 1, true) then + packagename = packageinfo + else + + -- get repository name, package name and package url + local pos = packageinfo:lastof('@', true) + if pos then + packagename = packageinfo:sub(pos + 1) + reponame = packageinfo:sub(1, pos - 1) + else + packagename = packageinfo + end + end + + -- check package name + assert(packagename, "require(\"%s\"): the package name not found!", require_str) + return packagename, version, reponame +end + +-- load require info +function _load_require(require_str, requires_extra, parentinfo) + + -- parse require + local packagename, version, reponame = _parse_require(require_str) + + -- get require extra + local require_extra = {} + if requires_extra then + require_extra = requires_extra[require_str] or {} + end + + -- get required building configurations + local require_build_configs = require_extra.configs or require_extra.config + if require_extra.debug then + require_build_configs = require_build_configs or {} + require_build_configs.debug = true + end + + -- require packge in the current host platform + if require_extra.host then + require_extra.plat = os.host() + require_extra.arch = os.arch() + end + + -- init required item + local required = {} + parentinfo = parentinfo or {} + required.packagename = packagename + required.requireinfo = + { + originstr = require_str, + reponame = reponame, + version = require_extra.version or version, + plat = require_extra.plat, -- require package in the given platform + arch = require_extra.arch, -- require package in the given architecture + targetos = require_extra.targetos, -- require package in the given target os + kind = require_extra.kind, -- default: library, set package kind, e.g. binary, library, we can set `kind = "binary"` to only detect binary program and ignore library. + alias = require_extra.alias, -- set package alias name + group = require_extra.group, -- only uses the first package in same group + system = require_extra.system, -- default: true, we can set it to disable system package manually + option = require_extra.option, -- set and attach option + configs = require_build_configs, -- the required building configurations + default = require_extra.default, -- default: true, we can set it to disable package manually + optional = parentinfo.optional or require_extra.optional, -- default: false, inherit parentinfo.optional + verify = require_extra.verify, -- default: true, we can set false to ignore sha256sum and select any version + external = require_extra.external, -- default: true, we use sysincludedirs/-isystem instead of -I/xxx + private = require_extra.private, -- default: false, private package, only for installation, do not export any links/includes and environments + build = require_extra.build -- default: false, always build packages, we do not use the precompiled artifacts + } + return required.packagename, required.requireinfo +end + +-- load package package from system +function _load_package_from_system(packagename) + return core_package.load_from_system(packagename) +end + +-- load package package from project +function _load_package_from_project(packagename) + return core_package.load_from_project(packagename) +end + +-- load package package from repositories +function _load_package_from_repository(packagename, reponame) + local packagedir, repo = repository.packagedir(packagename, reponame) + if packagedir then + return core_package.load_from_repository(packagename, repo, packagedir) + end +end + +-- sort package deps +-- +-- e.g. +-- +-- a.deps = b +-- b.deps = c +-- +-- orderdeps: c -> b -> a +-- +function _sort_packagedeps(package, onlylink) + -- we must use native deps list instead of package:deps() to generate correct linkdeps + local orderdeps = {} + for _, dep in ipairs(package:plaindeps()) do + if dep and (onlylink ~= true or (dep:is_library() and not dep:is_private())) then + table.join2(orderdeps, _sort_packagedeps(dep, onlylink)) + table.insert(orderdeps, dep) + end + end + return orderdeps +end + +-- add some builtin configurations to package +function _add_package_configurations(package) + -- we can define configs to override it and it's default value in package() + if package:extraconf("configs", "debug", "default") == nil then + package:add("configs", "debug", {builtin = true, description = "Enable debug symbols.", default = false, type = "boolean"}) + end + if package:extraconf("configs", "shared", "default") == nil then + package:add("configs", "shared", {builtin = true, description = "Build shared library.", default = false, type = "boolean"}) + end + if package:extraconf("configs", "pic", "default") == nil then + package:add("configs", "pic", {builtin = true, description = "Enable the position independent code.", default = true, type = "boolean"}) + end + if package:extraconf("configs", "vs_runtime", "default") == nil then + package:add("configs", "vs_runtime", {builtin = true, description = "Set vs compiler runtime.", values = {"MT", "MTd", "MD", "MDd"}}) + end + if package:extraconf("configs", "toolchains", "default") == nil then + package:add("configs", "toolchains", {builtin = true, description = "Set package toolchains only for cross-compilation."}) + end + package:add("configs", "cflags", {builtin = true, description = "Set the C compiler flags."}) + package:add("configs", "cxflags", {builtin = true, description = "Set the C/C++ compiler flags."}) + package:add("configs", "cxxflags", {builtin = true, description = "Set the C++ compiler flags."}) + package:add("configs", "asflags", {builtin = true, description = "Set the assembler flags."}) +end + +-- select package version +function _select_package_version(package, requireinfo) + + -- exists urls? otherwise be phony package (only as package group) + if #package:urls() > 0 then + + -- has git url? + local has_giturl = false + for _, url in ipairs(package:urls()) do + if git.checkurl(url) then + has_giturl = true + break + end + end + + -- select package version + local source = nil + local version = nil + local require_version = requireinfo.version + local require_verify = requireinfo.verify + if (not package:get("versions") or require_verify == false) and semver.is_valid(require_version) then + -- no version list in package() or need not verify sha256sum? try selecting this version directly + -- @see https://github.com/xmake-io/xmake/issues/930 + -- https://github.com/xmake-io/xmake/issues/1009 + version = require_version + source = "versions" + elseif #package:versions() > 0 then -- select version? + version, source = try { function () return semver.select(require_version, package:versions()) end } + end + if not version and has_giturl and not require_version:find('.', 1, true) then -- select branch? + version, source = require_version ~= "latest" and require_version or "master", "branches" + end + if not version then + raise("package(%s): version(%s) not found!", package:name(), require_version) + end + return version, source + end +end + +-- check the configurations of packages +-- +-- package("pcre2") +-- add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) +-- add_configs("bitwidth", {type = "number", values = {8, 16, 32}}) +-- add_configs("bitwidth", {restrict = function(value) if tonumber(value) < 100 then return true end}) +-- +function _check_package_configurations(package) + local configs_defined = {} + for _, name in ipairs(package:get("configs")) do + configs_defined[name] = package:extraconf("configs", name) or {} + end + for name, value in pairs(package:configs()) do + local conf = configs_defined[name] + if conf then + local config_type = conf.type + if config_type ~= nil and type(value) ~= config_type then + raise("package(%s %s): invalid type(%s) for config(%s), need type(%s)!", package:displayname(), package:version_str(), type(value), name, config_type) + end + if conf.values then + local found = false + for _, config_value in ipairs(conf.values) do + if tostring(value) == tostring(config_value) then + found = true + break + end + end + if not found then + raise("package(%s %s): invalid value(%s) for config(%s), please run `xmake require --info %s` to get all valid values!", package:displayname(), package:version_str(), value, name, package:name()) + end + end + if conf.restrict then + if not conf.restrict(value) then + raise("package(%s %s): invalid value(%s) for config(%s)!", package:displayname(), package:version_str(), value, name) + end + end + else + raise("package(%s %s): invalid config(%s), please run `xmake require --info %s` to get all configurations!", package:displayname(), package:version_str(), name, package:name()) + end + end +end + +-- match require path +function _match_requirepath(requirepath, requireconf) + + -- get pattern + local function _get_pattern(pattern) + pattern = pattern:gsub("([%+%.%-%^%$%(%)%%])", "%%%1") + pattern = pattern:gsub("%*%*", "\001") + pattern = pattern:gsub("%*", "\002") + pattern = pattern:gsub("\001", ".*") + pattern = pattern:gsub("\002", "[^.]*") + pattern = string.ipattern(pattern, true) + return pattern + end + + -- get the excludes + local excludes = requireconf:match("|.*$") + if excludes then excludes = excludes:split("|", {plain = true}) end + + -- do match + local pattern = requireconf:gsub("|.*$", "") + pattern = _get_pattern(pattern) + if (requirepath:match('^' .. pattern .. '$')) then + -- exclude sub-deps, e.g. "libwebp.**|cmake|autoconf" + local splitinfo = requirepath:split(".", {plain = true}) + if #splitinfo > 0 then + local name = splitinfo[#splitinfo] + for _, exclude in ipairs(excludes) do + pattern = _get_pattern(exclude) + if (name:match('^' .. pattern .. '$')) then + return false + end + end + end + return true + end +end + +-- init requireinfo +function _init_requireinfo(requireinfo, package, opt) + -- pass root toolchains to top library package + requireinfo.configs = requireinfo.configs or {} + if opt.is_toplevel then + requireinfo.is_toplevel = true + if not package:is_headeronly() then + if package:is_cross() and package:is_library() then + requireinfo.configs.toolchains = requireinfo.configs.toolchains or project.get("target.toolchains") or get_config("toolchain") + end + requireinfo.configs.vs_runtime = requireinfo.configs.vs_runtime or project.get("target.runtimes") or get_config("vs_runtime") + end + end +end + +-- finish requireinfo +function _finish_requireinfo(requireinfo, package) + requireinfo.configs = requireinfo.configs or {} + if not package:is_headeronly() then + if requireinfo.configs.vs_runtime == nil and package:is_plat("windows") then + requireinfo.configs.vs_runtime = "MT" + end + end + -- we need ensure readonly configs + for _, name in ipairs(table.keys(requireinfo.configs)) do + if package:extraconf("configs", name, "readonly") then + -- package:config() will use default value after loading package + requireinfo.configs[name] = nil + end + end +end + +-- merge requireinfo from `add_requireconfs()` +-- +-- add_requireconfs("*", {system = false, configs = {vs_runtime = "MD"}}) +-- add_requireconfs("lib*", {system = false, configs = {vs_runtime = "MD"}}) +-- add_requireconfs("libwebp", {system = false, configs = {vs_runtime = "MD"}}) +-- add_requireconfs("libpng.zlib", {system = false, override = true, configs = {cxflags = "-DTEST1"}, version = "1.2.10"}) +-- add_requireconfs("libtiff.*", {system = false, configs = {cxflags = "-DTEST2"}}) +-- add_requireconfs("libwebp.**|cmake|autoconf", {system = false, configs = {cxflags = "-DTEST3"}}) -- recursive deps +-- +function _merge_requireinfo(requireinfo, requirepath) + + -- only for project + if not os.isfile(os.projectfile()) then + return + end + + -- find requireconf from the given requirepath + local requireconf_result = {} + local requireconfs, requireconfs_extra = project.requireconfs_str() + if requireconfs then + for _, requireconf in ipairs(requireconfs) do + if _match_requirepath(requirepath, requireconf) then + local requireconf_extra = requireconfs_extra[requireconf] + table.insert(requireconf_result, {requireconf = requireconf, requireconf_extra = requireconf_extra}) + end + end + end + + -- append requireconf_extra into requireinfo + -- and the configs of add_requires have a higher priority than add_requireconfs. + -- + -- e.g. + -- add_requireconfs("*", {configs = {debug = false}}) + -- add_requires("foo", "bar", {configs = {debug = true}}) + -- + -- foo and bar will be debug mode + -- + -- we can also override the configs of add_requires + -- + -- e.g. + -- add_requires("zlib 1.2.11") + -- add_requireconfs("zlib", {override = true, version = "1.2.10"}) + -- + -- we override the version of zlib to 1.2.10 + -- + if #requireconf_result == 1 then + local requireconf_extra = requireconf_result[1].requireconf_extra + if requireconf_extra then + -- preprocess requireconf_extra, (debug, override ..) + local override = requireconf_extra.override + requireconf_extra.override = nil + if requireconf_extra.debug then + requireconf_extra.configs = requireconf_extra.configs or {} + requireconf_extra.configs.debug = true + requireconf_extra.debug = nil + end + -- append or override configs and extra options + for k, v in pairs(requireconf_extra.configs) do + requireinfo.configs = requireinfo.configs or {} + if override or requireinfo.configs[k] == nil then + requireinfo.configs[k] = v + end + end + for k, v in pairs(requireconf_extra) do + if k ~= "configs" then + if override or requireinfo[k] == nil then + requireinfo[k] = v + end + end + end + end + elseif #requireconf_result > 1 then + local confs = {} + for _, item in ipairs(requireconf_result) do + table.insert(confs, item.requireconf) + end + raise("package(%s) will match multiple add_requireconfs(%s)!", requirepath, table.concat(confs, " ")) + end +end + +-- get package key +function _get_packagekey(packagename, requireinfo, version) + local key = packagename .. "/" .. (version or requireinfo.version) + if requireinfo.plat then + key = key .. "/" .. requireinfo.plat + end + if requireinfo.arch then + key = key .. "/" .. requireinfo.arch + end + if requireinfo.label then + key = key .. "/" .. requireinfo.label + end + local configs = requireinfo.configs + if configs then + local configs_order = {} + for k, v in pairs(configs) do + table.insert(configs_order, k .. "=" .. tostring(v)) + end + table.sort(configs_order) + key = key .. ":" .. string.serialize(configs_order, true) + end + return key +end + +-- inherit some builtin configs of parent package if these config values are not default value +-- e.g. add_requires("libpng", {configs = {vs_runtime = "MD", pic = false}}) +-- +function _inherit_parent_configs(requireinfo, package, parentinfo) + if package:is_library() then + local requireinfo_configs = requireinfo.configs or {} + local parentinfo_configs = parentinfo.configs or {} + if not requireinfo_configs.shared then + if requireinfo_configs.vs_runtime == nil then + requireinfo_configs.vs_runtime = parentinfo_configs.vs_runtime + end + if requireinfo_configs.pic == nil then + requireinfo_configs.pic = parentinfo_configs.pic + end + end + if parentinfo.plat then + requireinfo.plat = parentinfo.plat + end + if parentinfo.arch then + requireinfo.arch = parentinfo.arch + end + if parentinfo.private ~= nil then + requireinfo.private = parentinfo.private + end + requireinfo_configs.toolchains = requireinfo_configs.toolchains or parentinfo_configs.toolchains + requireinfo_configs.vs_runtime = requireinfo_configs.vs_runtime or parentinfo_configs.vs_runtime + requireinfo.configs = requireinfo_configs + end +end + +-- load required packages +function _load_package(packagename, requireinfo, opt) + + -- strip trailng ~tag, e.g. zlib~debug + local displayname + if packagename:find('~', 1, true) then + displayname = packagename + local splitinfo = packagename:split('~', {plain = true, limit = 2}) + packagename = splitinfo[1] + requireinfo.alias = requireinfo.alias or displayname + requireinfo.label = splitinfo[2] + end + + -- load package from project first + local package + if os.isfile(os.projectfile()) then + package = _load_package_from_project(packagename) + end + + -- load package from repositories + local from_repo = false + if not package then + package = _load_package_from_repository(packagename, requireinfo.reponame) + if package then + from_repo = true + end + end + + -- load package from system + local system = requireinfo.system + if system == nil then + system = opt.system + end + if not package and (system ~= false or packagename:find("::", 1, true)) then + package = _load_package_from_system(packagename) + end + + -- check + assert(package, "package(%s) not found!", packagename) + + -- init requireinfo + _init_requireinfo(requireinfo, package, {is_toplevel = not opt.parentinfo}) + + -- merge requireinfo from `add_requireconfs()` + _merge_requireinfo(requireinfo, opt.requirepath) + + -- inherit some builtin configs of parent package, e.g. vs_runtime, pic + if opt.parentinfo then + _inherit_parent_configs(requireinfo, package, opt.parentinfo) + end + + -- finish requireinfo + _finish_requireinfo(requireinfo, package) + + -- select package version + local version, source = _select_package_version(package, requireinfo) + if version then + package:version_set(version, source) + end + + -- get package key + local packagekey = _get_packagekey(packagename, requireinfo, version) + + -- get package from cache first + local package_cached = _memcache():get2("packages", packagekey) + if package_cached then + return package_cached + end + + -- save require info + package:requireinfo_set(requireinfo) + + -- save display name + if not displayname then + local packageid = _memcache():get2("packageids", packagename) + displayname = packagename + if packageid then + displayname = displayname .. "#" .. tostring(packageid) + end + _memcache():set2("packageids", packagename, (packageid or 0) + 1) + end + package:displayname_set(displayname) + + -- disable parallelize if the package cache directory conflicts + local cachedirs = _memcache():get2("cachedirs", package:cachedir()) + if cachedirs then + package:set("parallelize", false) + end + _memcache():set2("cachedirs", package:cachedir(), true) + + -- add some builtin configurations to package + _add_package_configurations(package) + + -- check package configurations + _check_package_configurations(package) + + -- save artifacts info, we need add it at last before buildhash need depend on package configurations + if from_repo and not option.get("build") and not requireinfo.build then + local artifacts_manifest = repository.artifacts_manifest(packagename, version) + if artifacts_manifest then + local buildid = package:plat() .. "-" .. package:arch() .. "-" .. package:buildhash() + local artifacts_info = artifacts_manifest[buildid] + if artifacts_info then + package:artifacts_set(artifacts_info) + end + end + end + + -- do load + local on_load = package:script("load") + if on_load then + on_load(package) + end + + -- load environments from the manifest to enable the environments of on_install() + package:envs_load() + + -- save this package package to cache + _memcache():set2("packages", packagekey, package) + return package +end + +-- load all required packages +function _load_packages(requires, opt) + + -- no requires? + if not requires or #requires == 0 then + return {} + end + + -- load packages + local packages = {} + local packages_nodeps = {} + for _, requireitem in ipairs(load_requires(requires, opt.requires_extra, opt)) do + + -- load package + local requireinfo = requireitem.info + local requirepath = opt.requirepath and (opt.requirepath .. "." .. requireitem.name) or requireitem.name + local package = _load_package(requireitem.name, requireinfo, table.join(opt, {requirepath = requirepath})) + + -- maybe package not found and optional + if package then + + -- load dependent packages and save them first of this package + if not package._DEPS then + if package:get("deps") and opt.nodeps ~= true then + + -- load dependent packages and do not load system/3rd packages for package/deps() + local packagedeps = {} + local deps, plaindeps = _load_packages(package:get("deps"), {requirepath = requirepath, + requires_extra = package:extraconf("deps") or {}, + parentinfo = requireinfo, + nodeps = opt.nodeps, + system = false}) + for _, dep in ipairs(deps) do + dep:parents_add(package) + table.insert(packages, dep) + packagedeps[dep:name()] = dep + end + package._DEPS = packagedeps + package._PLAINDEPS = plaindeps + package._ORDERDEPS = table.unique(_sort_packagedeps(package)) + package._LINKDEPS = table.unique(_sort_packagedeps(package, true)) + end + end + + -- save this package + table.insert(packages, package) + table.insert(packages_nodeps, package) + end + end + return packages, packages_nodeps +end + +-- get package parents string +function _get_parents_str(package) + local parents = package:parents() + if parents then + local parentnames = {} + for _, parent in pairs(parents) do + table.insert(parentnames, parent:displayname()) + end + if #parentnames == 0 then + return + end + return table.concat(parentnames, ",") + end +end + +-- check dependences conflicts +-- +-- It exists conflict for dependent packages for each root packages? resolve it first +-- e.g. +-- add_requires("foo") -> bar -> zlib 1.2.10 +-- -> xyz -> zlib 1.2.11 or other configs +-- +-- add_requires("ddd") -> zlib +-- +-- We assume that there is no conflict between `foo` and `ddd`. +-- +-- Of course, conflicts caused by `add_packages("foo", "ddd")` +-- cannot be detected at present and can only be resolved by the user +-- +function _check_package_depconflicts(package) + local packagekeys = {} + for _, dep in ipairs(package:linkdeps()) do + local key = _get_packagekey(dep:name(), dep:requireinfo()) + local prevkey = packagekeys[dep:name()] + if prevkey then + assert(key == prevkey, "package(%s): conflict dependences with package(%s)!", key, prevkey) + else + packagekeys[dep:name()] = key + end + end +end + +-- must depend on the given package? +function _must_depend_on(package, dep) + local manifest = package:manifest_load() + if manifest and manifest.linkdeps then + local linkdeps = hashset.from(manifest.linkdeps) + return linkdeps:has(dep:name()) + end +end + +-- the cache directory +function cachedir() + return path.join(global.directory(), "cache", "packages") +end + +-- this package should be install? +function should_install(package) + if package:exists() then + return false + end + -- we need not install it if this package need only be fetched + if package:is_fetchonly() then + return false + end + -- only get system package? e.g. add_requires("xxx", {system = true}) + local requireinfo = package:requireinfo() + if requireinfo and requireinfo.system then + return false + end + if package:parents() then + -- if all the packages that depend on it already exist, then there is no need to install it + for _, parent in pairs(package:parents()) do + if should_install(parent) and not parent:exists() then + return true + end + + -- if the existing parent package is already using it, + -- then even if it is an optional package, you must make sure to install it + -- + -- @see https://github.com/xmake-io/xmake/issues/1460 + -- + if parent:exists() and not option.get("force") and _must_depend_on(parent, package) then + -- mark this package as non-optional because parent package need it + local requireinfo = package:requireinfo() + if requireinfo.optional then + requireinfo.optional = nil + end + return true + end + end + else + return true + end +end + +-- get package configs string +function get_configs_str(package) + local configs = {} + if package:is_optional() then + table.insert(configs, "optional") + end + if package:is_private() then + table.insert(configs, "private") + end + local requireinfo = package:requireinfo() + if requireinfo then + if requireinfo.plat then + table.insert(configs, requireinfo.plat) + end + if requireinfo.arch then + table.insert(configs, requireinfo.arch) + end + for k, v in pairs(requireinfo.configs) do + if type(v) == "boolean" then + table.insert(configs, k .. ":" .. (v and "y" or "n")) + else + table.insert(configs, k .. ":" .. v) + end + end + end + local parents_str = _get_parents_str(package) + if parents_str then + table.insert(configs, "from:" .. parents_str) + end + local configs_str = #configs > 0 and "[" .. table.concat(configs, ", ") .. "]" or "" + local limitwidth = os.getwinsize().width * 2 / 3 + if #configs_str > limitwidth then + configs_str = configs_str:sub(1, limitwidth) .. " ..)" + end + return configs_str +end + +-- load requires +function load_requires(requires, requires_extra, opt) + opt = opt or {} + local requireitems = {} + for _, require_str in ipairs(requires) do + local packagename, requireinfo = _load_require(require_str, requires_extra, opt.parentinfo) + table.insert(requireitems, {name = packagename, info = requireinfo}) + end + return requireitems +end + +-- load all required packages +function load_packages(requires, opt) + opt = opt or {} + local unique = {} + local packages = {} + for _, package in ipairs((_load_packages(requires, opt))) do + if package:is_toplevel() then + _check_package_depconflicts(package) + end + local key = _get_packagekey(package:name(), package:requireinfo()) + if not unique[key] then + table.insert(packages, package) + unique[key] = true + end + end + return packages +end + |
