diff options
Diffstat (limited to 'xmake/core/package/package.lua')
| -rw-r--r-- | xmake/core/package/package.lua | 534 |
1 files changed, 303 insertions, 231 deletions
diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index 87c075439..eeae38819 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -38,6 +38,7 @@ local interpreter = require("base/interpreter") local sandbox = require("sandbox/sandbox") local config = require("project/config") local platform = require("platform/platform") +local language = require("language/language") local sandbox = require("sandbox/sandbox") local sandbox_os = require("sandbox/modules/os") local sandbox_module = require("sandbox/modules/import/core/sandbox/module") @@ -50,6 +51,11 @@ function _instance.new(name, info) return instance end +-- get the package name +function _instance:name() + return self._NAME +end + -- get the package configure function _instance:get(name) @@ -60,9 +66,19 @@ function _instance:get(name) end end --- get the package name -function _instance:name() - return self._NAME +-- set the value to the package info +function _instance:set(name, ...) + self._INFO:apival_set(name, ...) +end + +-- add the value to the package info +function _instance:add(name, ...) + self._INFO:apival_add(name, ...) +end + +-- get the extra configuration +function _instance:extraconf(name, item, key) + return self._INFO:extraconf(name, item, key) end -- get the package description @@ -167,47 +183,30 @@ function _instance:orderdeps() return self._ORDERDEPS end --- get sha256 of the url_alias@version_str -function _instance:sha256(url_alias) +-- get hash of the source package for the url_alias@version_str +function _instance:sourcehash(url_alias) - -- get sha256 + -- get sourcehash local versions = self:get("versions") local version_str = self:version_str() if versions and version_str then - local sha256 = nil + local sourcehash = nil if url_alias then - sha256 = versions[url_alias .. ":" ..version_str] + sourcehash = versions[url_alias .. ":" ..version_str] end - if not sha256 then - sha256 = versions[version_str] + if not sourcehash then + sourcehash = versions[version_str] end -- ok? - return sha256 + return sourcehash end end -- get revision(commit, tag, branch) of the url_alias@version_str, only for git url function _instance:revision(url_alias) - return self:sha256(url_alias) -end - --- this package is from system/local/global? --- --- @param kind the from kind --- --- system: from the system directories (.e.g /usr/local) --- local: from the local project package directories (.e.g projectdir/.xmake/packages) --- global: from the global package directories (.e.g ~/.xmake/packages) --- -function _instance:from(kind) - return self._FROMKIND == kind -end - --- get from kind -function _instance:fromkind() - return self._FROMKIND + return self:sourcehash(url_alias) end -- get the package kind, binary or nil(static, shared) @@ -223,94 +222,164 @@ end -- get the installed directory of this package function _instance:installdir(...) - - -- make the given install directory local name = self:name():lower():gsub("::", "_") - local dir = path.join(package.installdir(table.concat({self:mode(), self:configs_hash()}, '_'), self:plat(), self:arch()), name:sub(1, 1):lower(), name, self:version_str(), ...) - - -- ensure the install directory + local dir = path.join(package.installdir(), name:sub(1, 1):lower(), name, self:version_str(), self:buildhash(), ...) if not os.isdir(dir) then os.mkdir(dir) end return dir end --- get the prefix directory -function _instance:prefixdir(...) - - -- make the given prefix directory - local dir = path.join(package.prefixdir(self:from("global"), table.concat({self:mode(), self:configs_hash()}, '_'), self:plat(), self:arch()), ...) - - -- ensure the prefix directory - if not os.isdir(dir) then - os.mkdir(dir) +-- get the references info of this package +function _instance:references() + local references_file = path.join(self:installdir(), "references.txt") + if os.isfile(references_file) then + local references, errors = io.load(references_file) + if not references then + os.raise(errors) + end + return references end - return dir end --- get the prefix info -function _instance:prefixinfo() - if self._PREFIXINFO == nil then - local prefixfile = self:prefixfile() - self._PREFIXINFO = os.isfile(prefixfile) and io.load(prefixfile) or {} - end - return self._PREFIXINFO +-- get the manifest file of this package +function _instance:manifest_file() + return path.join(self:installdir(), "manifest.txt") end --- get the prefix info file -function _instance:prefixfile() - local name = self:name():lower():gsub("::", "_") - return path.join(package.prefixinfodir(self:from("global"), table.concat({self:mode(), self:configs_hash()}, '_'), self:plat(), self:arch()), name:sub(1, 1):lower(), name, self:version_str(), "info.txt") +-- load the manifest file of this package +function _instance:manifest_load() + local manifest_file = self:manifest_file() + if os.isfile(manifest_file) then + + -- load manifest + local manifest, errors = io.load(manifest_file) + if not manifest then + os.raise(errors) + end + return manifest + end end --- get prefix variables -function _instance:getvar(name) - return self:prefixinfo()[name] +-- save the manifest file of this package +function _instance:manifest_save() + + -- make manifest + local manifest = {} + manifest.name = self:name() + manifest.description = self:description() + manifest.version = self:version_str() + manifest.kind = self:kind() + manifest.plat = self:plat() + manifest.arch = self:arch() + manifest.mode = self:mode() + manifest.configs = self:configs() + manifest.envs = self:envs() + + -- save variables + local vars = {} + local apis = language.apis() + for _, apiname in ipairs(table.join(apis.values, apis.pathes)) do + if apiname:startswith("package.add_") or apiname:startswith("package.set_") then + local name = apiname:sub(13) + local value = self:get(name) + if value ~= nil then + vars[name] = value + end + end + end + manifest.vars = vars + + -- save repository + local repo = self:repo() + if repo then + manifest.repo = {} + manifest.repo.name = repo:name() + manifest.repo.url = repo:url() + manifest.repo.branch = repo:branch() + end + + -- save manifest + local ok, errors = io.save(self:manifest_file(), manifest) + if not ok then + os.raise(errors) + end end --- set prefix variables +-- TODO: set the given variable, deprecated function _instance:setvar(name, ...) - self:prefixinfo()[name] = {...} + self:set(name, ...) end --- add prefix variables +-- TODO add the given variable, deprecated function _instance:addvar(name, ...) - self:prefixinfo()[name] = table.join(self:prefixinfo()[name] or {}, ...) + self:add(name, ...) end --- get environment variables -function _instance:getenv(name) - return self:prefixinfo().envars and self:prefixinfo().envars[name] or nil +-- get the exported environments +function _instance:envs() + local envs = self._ENVS + if not envs then + envs = {} + if self:kind() == "binary" then + envs.PATH = {"bin"} + end + self._ENVS = envs + end + return envs end --- set environment variables -function _instance:setenv(name, ...) - self:prefixinfo().envars = self:prefixinfo().envars or {} - self:prefixinfo().envars[name] = {...} -end +-- enter the package environments +function _instance:envs_enter() --- add values to environment variable -function _instance:addenv(name, ...) - self:prefixinfo().envars = self:prefixinfo().envars or {} - self:prefixinfo().envars[name] = table.join(self:prefixinfo().envars[name] or {}, ...) -end + -- save the old environments + local oldenvs = self._OLDENVS + if not oldenvs then + oldenvs = {} + self._OLDENVS = oldenvs + end --- register package info in the root prefix info -function _instance:register() + -- add the new environments + local installdir = self:installdir() + for name, values in pairs(self:envs()) do + oldenvs[name] = oldenvs[name] or os.getenv(name) + if name == "PATH" then + for _, value in ipairs(values) do + if path.is_absolute(value) then + os.addenv(name, value) + else + os.addenv(name, path.join(installdir, value)) + end + end + else + os.addenv(name, unpack(table.wrap(values))) + end + end +end - -- register the environment variables - for name, values in pairs(table.wrap(self:prefixinfo().envars)) do - package.addenv(self:from("global"), table.concat({self:mode(), self:configs_hash()}, '_'), self:plat(), self:arch(), name, values) +-- leave the package environments +function _instance:envs_leave() + if self._OLDENVS then + for name, values in pairs(self._OLDENVS) do + os.setenv(name, values) + end + self._OLDENVS = nil end end --- unregister package info from the root prefix info -function _instance:unregister() +-- get the given environment variable +function _instance:getenv(name) + return self:envs()[name] +end + +-- set the given environment variable +function _instance:setenv(name, ...) + self:envs()[name] = {...} +end - -- unregister the environment variables - for name, values in pairs(table.wrap(self:prefixinfo().envars)) do - package.delenv(self:from("global"), table.concat({self:mode(), self:configs_hash()}, '_'), self:plat(), self:arch(), name, values) - end +-- add the given environment variable +function _instance:addenv(name, ...) + self:envs()[name] = table.join(self:envs()[name] or {}, ...) end -- get user private data @@ -406,44 +475,52 @@ end -- set the require info function _instance:requireinfo_set(requireinfo) - - -- save require info self._REQUIREINFO = requireinfo +end - -- get version - local version = requireinfo and requireinfo.version or nil - local limitversion = version and version ~= "master" and version ~= "lastest" - if requireinfo and not self:is3rd() then - - -- switch to local package if exists package configuration or debug package or limit version - if requireinfo.config or requireinfo.debug or limitversion then - self._FROMKIND = "local" - end - - -- disable the system package if limit version - if limitversion then - requireinfo.system = false - end +-- get the given configuration value of package +function _instance:config(name) + local configs = self:configs() + if configs then + return configs[name] end end --- get the all configuration values of package +-- get the configurations of package function _instance:configs() - local requireinfo = self:requireinfo() - if requireinfo then - return requireinfo.config + local configs = self._CONFIGS + if configs == nil then + local configs_defined = self:get("configs") + if configs_defined then + configs = {} + local requireinfo = self:requireinfo() + local configs_required = requireinfo and requireinfo.configs or {} + for _, name in ipairs(table.wrap(configs_defined)) do + local value = configs_required[name] + if value == nil then + value = self:extraconf("configs", name, "default") + end + configs[name] = value + end + else + configs = false + end + self._CONFIGS = configs end + return configs and configs or nil end --- get the hash of configs -function _instance:configs_hash() - if self._CONFIGS_HASH == nil then +-- get the build hash +function _instance:buildhash() + if self._BUILDHASH == nil then + local str = self:plat() .. self:arch() local configs = self:configs() if configs then - self._CONFIGS_HASH = hash.uuid(string.serialize(configs, true)):split('-')[1]:lower() + str = str .. string.serialize(configs, true) end + self._BUILDHASH = hash.uuid(str):gsub('-', ''):lower() end - return self._CONFIGS_HASH + return self._BUILDHASH end -- get the group name @@ -454,14 +531,6 @@ function _instance:group() end end --- get the given configuration value of package -function _instance:config(name) - local configs = self:configs() - if configs then - return configs[name] - end -end - -- is optional package? function _instance:optional() local requireinfo = self:requireinfo() @@ -470,12 +539,7 @@ end -- is debug package? function _instance:debug() - -- @note always release for the binary package - if self:kind() == "binary" then - return false - end - local requireinfo = self:requireinfo() - return requireinfo and requireinfo.debug or false + return self:config("debug") end -- is the supported package? @@ -488,7 +552,12 @@ end -- we need install and find package by third-party package manager directly -- function _instance:is3rd() - return self:name():find("::", 1, true) + return self._is3rd +end + +-- is the system package? +function _instance:isSys() + return self._isSys end -- get xxx_script @@ -545,11 +614,11 @@ function _instance:script(name, generic) return result end --- fetch package info from the local packages +-- fetch the local package info -- -- @param opt the fetch option, .e.g {force = true, system = false} -- --- @return {packageinfo}, fetchfrom (.e.g local/global/system) +-- @return {packageinfo}, fetchfrom (.e.g xmake/system) -- function _instance:fetch(opt) @@ -557,10 +626,9 @@ function _instance:fetch(opt) opt = opt or {} -- attempt to get it from cache - local fetchfrom = self._FETCHFROM local fetchinfo = self._FETCHINFO if not opt.force and fetchinfo then - return fetchinfo, fetchfrom + return fetchinfo end -- fetch the require version @@ -569,39 +637,52 @@ function _instance:fetch(opt) require_ver = nil end + -- nil: find xmake or system packages + -- true: only find system package + -- false: only find xmake packages + local system = opt.system or self:requireinfo().system + -- fetch binary tool? fetchinfo = nil - fetchfrom = nil + local isSys = nil if self:kind() == "binary" then -- import find_tool self._find_tool = self._find_tool or sandbox_module.import("lib.detect.find_tool", {anonymous = true}) - -- fetch it from the system directories, TODO find the given version - fetchinfo = self._find_tool(self:name(), {force = opt.force}) - if fetchinfo then - fetchfrom = "system" -- ignore self:requireinfo().system + -- only fetch it from the xmake repository first + if not fetchinfo and system ~= true and not self:is3rd() then + fetchinfo = self._find_tool(self:name(), {version = self:version_str(), + cachekey = "fetch_package_xmake", + buildhash = self:buildhash(), + force = opt.force}) + if fetchinfo then + isSys = self._isSys + end + end + + -- fetch it from the system directories + if not fetchinfo and system ~= false then + fetchinfo = self._find_tool(self:name(), {cachekey = "fetch_package_system", + force = opt.force}) + if fetchinfo then + isSys = true + end end else -- import find_package self._find_package = self._find_package or sandbox_module.import("lib.detect.find_package", {anonymous = true}) - -- nil: find local or system packages - -- true: only find system package - -- false: only find local packages - local system = opt.system or self:requireinfo().system - -- only fetch it from the xmake repository first if not fetchinfo and system ~= true and not self:is3rd() then - fetchinfo = self._find_package("xmake::" .. self:name(), {prefixdirs = self:prefixdir(), - mode = self:mode(), - islocal = self:from("local"), - version = require_ver, + fetchinfo = self._find_package("xmake::" .. self:name(), {version = self:version_str(), cachekey = "fetch_package_xmake", - configs_hash = self:configs_hash(), - force = opt.force or self:from("local")}) - if fetchinfo then fetchfrom = self._FROMKIND end + buildhash = self:buildhash(), + force = opt.force}) + if fetchinfo then + isSys = self._isSys + end end -- fetch it from the system directories @@ -611,21 +692,76 @@ function _instance:fetch(opt) mode = self:mode(), cachekey = "fetch_package_system", system = true}) - if fetchinfo then fetchfrom = "system" end + if fetchinfo then + isSys = true + end end end -- save to cache self._FETCHINFO = fetchinfo - self._FETCHFROM = fetchfrom + + -- mark as system package? + if isSys ~= nil then + self._isSys = isSys + end -- ok - return fetchinfo, fetchfrom + return fetchinfo end --- exists this package in local +-- exists this package? function _instance:exists() - return self._FETCHINFO + return self._FETCHINFO ~= nil +end + +-- fetch all local info with dependencies +function _instance:fetchdeps() + local fetchinfo = self:fetch() + if not fetchinfo then + return + end + local orderdeps = self:orderdeps() + if orderdeps then + local total = #orderdeps + for idx, _ in ipairs(orderdeps) do + local dep = orderdeps[total + 1 - idx] + local depinfo = dep:fetch() + if depinfo then + for name, values in pairs(depinfo) do + fetchinfo[name] = table.wrap(fetchinfo[name]) + table.join2(fetchinfo[name], values) + end + end + end + end + return fetchinfo +end + +-- has the given c funcs? +-- +-- @param funcs the funcs +-- @param opt the argument options, .e.g { includes = ""} +-- +-- @return true or false +-- +function _instance:has_cfuncs(funcs, opt) + opt = opt or {} + opt.configs = self:fetchdeps() + return sandbox_module.import("lib.detect.has_cfuncs", {anonymous = true})(funcs, opt) +end + +-- has the given c++ funcs? +-- +-- @param funcs the funcs +-- @param opt the argument options, .e.g { includes = ""} +-- +-- @return true or false +-- +function _instance:has_cxxfuncs(funcs, opt) + opt = opt or {} + opt.configs = self:fetchdeps() + return sandbox_module.import("lib.detect.has_cxxfuncs", {anonymous = true})(funcs, opt) end -- the current mode is belong to the given modes? @@ -662,6 +798,9 @@ function package._interpreter() -- define apis interp:api_define(package.apis()) + + -- define apis for language + interp:api_define(language.apis()) -- save interpreter package._INTERPRETER = interp @@ -686,6 +825,7 @@ function package.apis() , "package.add_deps" , "package.add_urls" , "package.add_imports" + , "package.add_configs" } , script = { @@ -724,77 +864,8 @@ function package.cachedir() end -- the install directory -function package.installdir(mode, plat, arch) - return path.join(global.directory(), "installed", plat or os.host(), arch or os.arch(), mode or "release") -end - --- get the prefix directory -function package.prefixdir(is_global, mode, plat, arch) - return path.join(is_global and global.directory() or config.directory(), "prefix", plat or os.host(), arch or os.arch(), mode or "release") -end - --- get the prefix info directory -function package.prefixinfodir(is_global, mode, plat, arch) - return path.join(is_global and global.directory() or config.directory(), "prefix", "info", plat or os.host(), arch or os.arch(), mode or "release") -end - --- get the prefix info -function package.prefixinfo(is_global, mode, plat, arch) - local prefixfile = package.prefixfile(is_global, mode, plat, arch) - return os.isfile(prefixfile) and io.load(prefixfile) or {} -end - --- get the prefix info file -function package.prefixfile(is_global, mode, plat, arch) - return path.join(package.prefixinfodir(is_global, mode, plat, arch), "info.txt") -end - --- get environment variables -function package.getenv(is_global, mode, plat, arch, name) - local prefixinfo = package.prefixinfo(is_global, mode, plat, arch) - return prefixinfo.envars and prefixinfo.envars[name] or nil -end - --- add values to environment variable -function package.addenv(is_global, mode, plat, arch, name, values) - - -- add to the root prefix info - local prefixinfo = package.prefixinfo(is_global, mode, plat, arch) - prefixinfo.envars = prefixinfo.envars or {} - prefixinfo.envars[name] = table.join(prefixinfo.envars[name] or {}, values) - io.save(package.prefixfile(is_global, mode, plat, arch), prefixinfo) - - -- add to the current environment - if values then - -- PATH? add the prefix root directory - if name:lower() == "path" then - local prefixdir = package.prefixdir(is_global, mode, plat, arch) - for _, value in ipairs(values) do - os.addenv(name, path.join(prefixdir, value)) - end - else - os.addenv(name, unpack(values)) - end - end -end - --- remove values to environment variable -function package.delenv(is_global, mode, plat, arch, name, values) - local prefixinfo = package.prefixinfo(is_global, mode, plat, arch) - local prefixvalues = prefixinfo.envars and prefixinfo.envars[name] or nil - if prefixvalues then - local exists = {} - for _, value in ipairs(values) do - exists[value:trim()] = true - end - for i = #prefixvalues, 1, -1 do - value = prefixvalues[i]:trim() - if exists[value] then - table.remove(prefixvalues, i) - end - end - io.save(package.prefixfile(is_global, mode, plat, arch), prefixinfo) - end +function package.installdir() + return path.join(global.directory(), "packages") end -- load the package from the system directories @@ -808,6 +879,7 @@ function package.load_from_system(packagename) -- get package info local packageinfo = {} + local is3rd = false if packagename:find("::", 1, true) then -- get interpreter @@ -830,6 +902,11 @@ function package.load_from_system(packagename) -- save the install script packageinfo.install = instance:script() + + -- is third-party package? + if not packagename:startswith("xmake::") then + is3rd = true + end end -- new an instance @@ -838,8 +915,9 @@ function package.load_from_system(packagename) return nil, errors end - -- mark as system package - instance._FROMKIND = "system" + -- mark as system or 3rd package + instance._isSys = true + instance._is3rd = is3rd -- save instance to the cache package._PACKAGES[packagename] = instance @@ -874,9 +952,6 @@ function package.load_from_project(packagename, project) return nil, errors end - -- mark as local package - instance._FROMKIND = "local" - -- save instance to the cache package._PACKAGES[packagename] = instance @@ -942,9 +1017,6 @@ function package.load_from_repository(packagename, repo, packagedir, packagefile -- save repository instance._REPO = repo - -- mark as global/project package? - instance._FROMKIND = repo:is_global() and "global" or "local" - -- save instance to the cache package._PACKAGES[packagename] = instance |
