diff options
| author | ruki <[email protected]> | 2018-09-30 11:32:17 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2018-09-30 11:32:17 +0800 |
| commit | d003fe0a57e65fe71a2853bd6a33aa9d1c42636c (patch) | |
| tree | 72da8d1420f02ed84b67e7cb6dfe1fd16e4b3c36 /xmake/actions/require/impl | |
| parent | aa8567aac171d30261ecd0ec925773d7c170d86f (diff) | |
modify require impl modules
Diffstat (limited to 'xmake/actions/require/impl')
| -rw-r--r-- | xmake/actions/require/impl/action/download.lua | 224 | ||||
| -rw-r--r-- | xmake/actions/require/impl/action/install.lua | 244 | ||||
| -rw-r--r-- | xmake/actions/require/impl/action/test.lua | 63 | ||||
| -rw-r--r-- | xmake/actions/require/impl/environment.lua | 53 | ||||
| -rw-r--r-- | xmake/actions/require/impl/package.lua | 544 | ||||
| -rw-r--r-- | xmake/actions/require/impl/repository.lua | 108 | ||||
| -rw-r--r-- | xmake/actions/require/impl/utils/filter.lua | 144 |
7 files changed, 1380 insertions, 0 deletions
diff --git a/xmake/actions/require/impl/action/download.lua b/xmake/actions/require/impl/action/download.lua new file mode 100644 index 000000000..6fc0d5f3c --- /dev/null +++ b/xmake/actions/require/impl/action/download.lua @@ -0,0 +1,224 @@ +--!The Make-like download Utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file download.lua +-- + +-- imports +import("core.base.option") +import(".utils.filter") +import("net.http") +import("devel.git") +import("utils.archive") + +-- empty chars +function _emptychars() + + -- get left width + local width = os.getwinsize()["width"] or 64 + + -- make empty chars + local emptychars = "" + for i = 1, width do + emptychars = emptychars .. " " + end + return emptychars +end + +-- checkout codes from git +function _checkout(package, url, sourcedir) + + -- use previous source directory if exists + local packagedir = path.join(sourcedir, package:name()) + if os.isdir(packagedir) and not option.get("force") then + + -- clean the previous build files + git.clean({repodir = packagedir, force = true}) + return + end + + -- remove temporary directory + os.rm(sourcedir .. ".tmp") + + -- download package from branches? + packagedir = path.join(sourcedir .. ".tmp", package:name()) + if package:version_from("branches") then + + -- only shadow clone this branch + git.clone(url, {depth = 1, branch = package:version_str(), outputdir = packagedir}) + + -- download package from tags or versions? + else + + -- clone whole history and tags + git.clone(url, {outputdir = packagedir}) + + -- attempt to checkout the given version + git.checkout(package:version_str(), {repodir = packagedir}) + end + + -- move to source directory + os.rm(sourcedir) + os.mv(sourcedir .. ".tmp", sourcedir) + + -- trace + printf("\r" .. _emptychars()) + cprint("\r${yellow} => ${clear}clone %s %s .. ${green}ok", url, package:version_str()) +end + +-- download codes from ftp/http/https +function _download(package, url, sourcedir, url_alias, url_excludes) + + -- get package file + local packagefile = path.filename(url) + + -- get sha256 + local sha256 = package:sha256(url_alias) + assert(sha256, "cannot get sha256 of %s in package(%s)", url, package:name()) + + -- the package file have been downloaded? + if option.get("force") or not os.isfile(packagefile) or sha256 ~= hash.sha256(packagefile) then + + -- attempt to remove package file first + os.rm(packagefile) + + -- download package file + http.download(url, packagefile) + + -- check hash + if sha256 and sha256 ~= hash.sha256(packagefile) then + raise("unmatched checksum!") + end + end + + -- extract package file + os.rm(sourcedir .. ".tmp") + if archive.extract(packagefile, sourcedir .. ".tmp", {excludes = url_excludes}) then + -- move to source directory + os.rm(sourcedir) + os.mv(sourcedir .. ".tmp", sourcedir) + end + + -- save original file path + package:originfile_set(path.absolute(packagefile)) + + -- trace + printf("\r" .. _emptychars()) + cprint("\r${yellow} => ${clear}download %s .. ${green}ok", url) +end + +-- get sorted urls +function _urls(package) + + -- sort urls from the version source + local urls = {{}, {}} + for _, url in ipairs(package:urls()) do + if git.checkurl(url) then + table.insert(urls[1], url) + elseif package:sha256(package:url_alias(url)) then + table.insert(urls[2], url) + end + end + if package:version_from("tags", "branches") then + return table.join(urls[1], urls[2]) + else + return table.join(urls[2], urls[1]) + end +end + +-- download the given package +function main(package) + + -- get working directory of this package + local workdir = package:cachedir() + + -- ensure the working directory first + os.mkdir(workdir) + + -- enter the working directory + local oldir = os.cd(workdir) + + -- get urls + local urls = _urls(package) + assert(#urls > 0, "cannot get url of package(%s)", package:name()) + + -- download package from urls + for idx, url in ipairs(urls) do + + -- get url alias + local url_alias = package:url_alias(url) + + -- get url excludes + local url_excludes = package:url_excludes(url) + + -- filter url + url = filter.handle(url, package) + + -- download url + local ok = try + { + function () + + -- download package + local sourcedir = "source" + if git.checkurl(url) then + _checkout(package, url, sourcedir) + else + _download(package, url, sourcedir, url_alias, url_excludes) + end + + -- ok + return true + end, + catch + { + function (errors) + + -- verbose? + if option.get("verbose") and errors then + cprint("${bright red}error: ${clear}%s", errors) + end + + -- trace + printf("\r" .. _emptychars()) + if git.checkurl(url) then + cprint("\r${yellow} => ${clear}clone %s %s .. ${red}failed", url, package:version_str()) + else + cprint("\r${yellow} => ${clear}download %s .. ${red}failed", url) + end + + -- failed? break it + if idx == #urls and not package:requireinfo().optional then + raise("download failed!") + end + end + } + } + + -- ok? break it + if ok then break end + end + + -- leave working directory + os.cd(oldir) +end + + diff --git a/xmake/actions/require/impl/action/install.lua b/xmake/actions/require/impl/action/install.lua new file mode 100644 index 000000000..5beb0e8f1 --- /dev/null +++ b/xmake/actions/require/impl/action/install.lua @@ -0,0 +1,244 @@ +--!The Make-like install Utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file install.lua +-- + +-- imports +import("core.base.option") +import("core.project.target") +import("test") +import(".utils.filter") + +-- uninstall package from the prefix directory +function uninstall_prefix(package) + + -- remove the previous installed files + local prefixdir = package:prefixdir() + for _, relativefile in ipairs(package:prefixinfo().installed) do + + -- trace + vprint("removing %s ..", relativefile) + + -- remove file + local prefixfile = path.absolute(relativefile, prefixdir) + os.tryrm(prefixfile) + + -- remove it if the parent directory is empty + local parentdir = path.directory(prefixfile) + while parentdir and os.isdir(parentdir) and os.emptydir(parentdir) do + os.tryrm(parentdir) + parentdir = path.directory(parentdir) + end + end + + -- unregister this package + package:unregister() + + -- remove the prefix file + os.tryrm(package:prefixfile()) +end + +-- install package to the prefix directory +function install_prefix(package) + + -- uninstall the prefix package files first + uninstall_prefix(package) + + -- get prefix and install directory + local prefixdir = package:prefixdir() + local installdir = package:installdir() + + -- scan all installed files + local installfiles = {} + if is_host("windows") then + if package:kind() == "binary" then + table.join2(installfiles, (os.files(path.join(installdir, "**")))) + else + table.join2(installfiles, (os.files(path.join(package:installdir("lib"), "**")))) + table.join2(installfiles, (os.files(path.join(package:installdir("include"), "**")))) + end + else + if package:kind() == "binary" then + table.join2(installfiles, (os.files(path.join(package:installdir("bin"), "*")))) + else + table.join2(installfiles, (os.files(path.join(package:installdir("lib"), "**.a")))) + table.join2(installfiles, (os.files(path.join(package:installdir("lib"), is_plat("macosx") and "**.dylib" or "**.so")))) + table.join2(installfiles, (os.files(path.join(package:installdir("lib", "pkgconfig"), "**.pc")))) + table.join2(installfiles, (os.filedirs(path.join(package:installdir("include"), "*")))) + end + end + + -- trace + vprint("installing %s to %s ..", installdir, prefixdir) + + -- install to the prefix directory + local relativefiles = {} + try + { + function () + for _, installfile in ipairs(installfiles) do + + -- get relative file + local relativefile = path.relative(installfile, installdir) + + -- trace + vprint("installing %s ..", relativefile) + + -- install file + if is_host("windows") then + -- copy the whole file to the prefix directory + os.cp(installfile, path.absolute(relativefile, prefixdir)) + else + -- only link file to the prefix directory + os.ln(installfile, path.absolute(relativefile, prefixdir)) + end + + -- save this relative file + table.insert(relativefiles, relativefile) + end + end, + catch + { + function (errors) + raise(errors) + end + }, + finally + { + function () + -- save the prefix info to file + local prefixinfo = package:prefixinfo() + prefixinfo.installed = relativefiles + io.save(package:prefixfile(), prefixinfo) + + -- register this package + package:register() + end + } + } +end + +-- install the given package +function main(package) + + -- get working directory of this package + local workdir = package:cachedir() + + -- enter the working directory + local oldir = nil + if #package:urls() > 0 then + -- only one root directory? skip it + local filedirs = os.filedirs(path.join(workdir, "source", "*")) + if #filedirs == 1 and os.isdir(filedirs[1]) then + oldir = os.cd(filedirs[1]) + else + oldir = os.cd(path.join(workdir, "source")) + end + end + if not oldir then + os.mkdir(workdir) + oldir = os.cd(workdir) + end + + -- init tipname + local tipname = package:name() + if package:version_str() then + tipname = tipname .. "-" .. package:version_str() + end + + -- trace + cprintf("${yellow} => ${clear}installing %s .. ", tipname) + if option.get("verbose") then + print("") + end + + -- install it + try + { + function () + + -- the package scripts + local scripts = + { + package:script("install_before") + , package:script("install") + , package:script("install_after") + } + + -- create the install task + local installtask = function () + + -- clean the install directory first + os.tryrm(package:installdir()) + + -- install it + for i = 1, 3 do + local script = scripts[i] + if script ~= nil then + filter.call(script, package) + end + end + + -- install to the prefix directory + install_prefix(package) + + -- test it + test(package) + end + + -- install package + if option.get("verbose") then + installtask() + else + process.asyncrun(installtask) + end + + -- fetch package and force to flush the cache + assert(package:fetch({force = true}), "fetch %s failed!", tipname) + + -- trace + cprint("${green}ok") + end, + + catch + { + function (errors) + + -- verbose? + if option.get("verbose") and errors then + cprint("${bright red}error: ${clear}%s", errors) + end + + -- trace + cprint("${red}failed") + + -- failed + if not package:requireinfo().optional then + raise("install failed!") + end + end + } + } + + -- leave source codes directory + os.cd(oldir) +end diff --git a/xmake/actions/require/impl/action/test.lua b/xmake/actions/require/impl/action/test.lua new file mode 100644 index 000000000..8fa9caa55 --- /dev/null +++ b/xmake/actions/require/impl/action/test.lua @@ -0,0 +1,63 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file test.lua +-- + +-- imports +import("core.base.option") +import(".utils.filter") + +-- test the given package +function main(package) + + -- the package scripts + local scripts = + { + package:script("test_before") + , package:script("test") + , package:script("test_after") + } + + -- enter the test directory + local testdir = path.join(os.tmpdir(), "pkgtest", package:name(), package:version_str() or "lastest") + if os.isdir(testdir) then + os.tryrm(testdir) + end + if not os.isdir(testdir) then + os.mkdir(testdir) + end + local oldir = os.cd(testdir) + + -- test it + for i = 1, 3 do + local script = scripts[i] + if script ~= nil then + filter.call(script, package) + end + end + + -- restore the current directory + os.cd(oldir) + + -- remove the test directory + os.tryrm(testdir) +end diff --git a/xmake/actions/require/impl/environment.lua b/xmake/actions/require/impl/environment.lua new file mode 100644 index 000000000..f340ab7eb --- /dev/null +++ b/xmake/actions/require/impl/environment.lua @@ -0,0 +1,53 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file environment.lua +-- + +-- imports +import("core.project.config") +import("core.platform.environment") +import("lib.detect.find_tool") +import("package") + +-- enter environment +-- +-- ensure that we can find some basic tools: git, make/nmake/cmake, msbuild ... +-- +-- If these tools not exist, we will install it first. +-- +function enter() + + -- set search pathes of toolchains + environment.enter("toolchains") + + -- git not found? install it first + if not find_tool("git") then + package.install_packages("git") + end +end + +-- leave environment +function leave() + + -- restore search pathes of toolchains + environment.leave("toolchains") +end diff --git a/xmake/actions/require/impl/package.lua b/xmake/actions/require/impl/package.lua new file mode 100644 index 000000000..fc67ea232 --- /dev/null +++ b/xmake/actions/require/impl/package.lua @@ -0,0 +1,544 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file package.lua +-- + +-- imports +import("core.base.semver") +import("core.base.option") +import("core.base.global") +import("core.project.cache") +import("lib.detect.cache", {alias = "detectcache"}) +import("core.project.project") +import("core.package.package", {alias = "core_package"}) +import("action") +import("devel.git") +import("net.fasturl") +import("repository") + +-- +-- parse require string +-- +-- add_requires("zlib") +-- add_requires("tbox >=1.5.1", "zlib >=1.2.11") +-- add_requires("zlib master") +-- add_requires("xmake-repo@tbox >=1.5.1") +-- 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"}}) +-- +-- {system = nil/true/false}: +-- nil: get local or system packages +-- true: only get system package +-- false: only get local packages +-- +function _parse_require(require_str, requires_extra, parentinfo) + + -- get it from cache first + local requires = _g._REQUIRES or {} + local required = requires[require_str] + if required then + return required.packagename, required.requireinfo + end + + -- 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 + -- + -- lastest + -- >=1.5.1 <1.6.0 + -- master || >1.4 + -- ~1.2.3 + -- ^1.1 + -- + local version = "lastest" + if #splitinfo > 1 then + version = table.concat(table.slice(splitinfo, 2), " ") + end + assert(version, "require(\"%s\"): unknown version!", require_str) + + -- get repository name, package name and package url + local reponame = nil + local packagename = nil + local pos = packageinfo:find_last('@', true) + if pos then + + -- get package name + packagename = packageinfo:sub(pos + 1) + + -- get reponame + reponame = packageinfo:sub(1, pos - 1) + else + packagename = packageinfo + end + + -- check package name + assert(packagename, "require(\"%s\"): the package name not found!", require_str) + + -- get require extra + local require_extra = {} + if requires_extra then + require_extra = requires_extra[require_str] or {} + end + + -- init required item + local required = {} + parentinfo = parentinfo or {} + required.packagename = packagename + required.requireinfo = + { + originstr = require_str, + reponame = reponame, + version = version, + alias = require_extra.alias, -- set package alias name + debug = require_extra.debug, -- uses the debug package, default: false + system = require_extra.system, -- default: true, we can set it to disable system package manually + option = require_extra.option, -- set and attach option + config = require_extra.config, -- the build configuration of package + 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 + } + + -- save this required item to cache + requires[require_str] = required + _g._REQUIRES = requires + + -- ok + 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) + + -- get package directory from the given package name + local packagedir, repo = repository.packagedir(packagename, reponame) + if packagedir then + -- load it + return core_package.load_from_repository(packagename, repo, packagedir) + end +end + +-- load required packages +function _load_package(packagename, requireinfo) + + -- attempt to get it from cache first + local packages = _g._PACKAGES or {} + local package = packages[packagename] + if package then + + -- satisfy required version? + local version_str = package:version_str() + if version_str and not semver.satisfies(version_str, requireinfo.version) then + raise("package(%s): version conflict, '%s' does not satisfy '%s'!", packagename, version_str, requireinfo.version) + end + + -- ok + return package + end + + -- load package from project first + package = _load_package_from_project(packagename) + + -- load package from repositories + if not package then + package = _load_package_from_repository(packagename, requireinfo.reponame) + end + + -- load package from system + if not package then + package = _load_package_from_system(packagename) + end + + -- check + assert(package, "package(%s) not found!", packagename) + + -- save require info to package + package:requireinfo_set(requireinfo) + + -- save this package package to cache + packages[packagename] = package + _g._PACKAGES = packages + + -- ok + return package +end + +-- search packages from repositories +function _search_packages(name) + + local packages = {} + for _, packageinfo in ipairs(repository.searchdirs(name)) do + local package = core_package.load_from_repository(packageinfo.name, packageinfo.repo, packageinfo.packagedir) + if package then + table.insert(packages, package) + end + end + return packages +end + +-- sort package deps +-- +-- .e.g +-- +-- a.deps = b +-- b.deps = c +-- +-- orderdeps: c -> b -> a +-- +function _sort_packagedeps(package) + local orderdeps = {} + for _, dep in pairs(package:deps()) do + table.join2(orderdeps, _sort_packagedeps(dep)) + table.insert(orderdeps, dep) + end + return orderdeps +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 = {} + for packagename, requireinfo in pairs(load_requires(requires, opt.requires_extra, opt.parentinfo)) do + + -- attempt to get project option about this package + local packageopt = project.option(packagename) + if packageopt == nil or packageopt:enabled() then -- this package is enabled? + + -- load package package + local package = _load_package(packagename, requireinfo) + + -- maybe package not found and optional + if package then + + -- load dependent packages and save them first of this package + local deps = package:get("deps") + if deps and opt.nodeps ~= true then + local packagedeps = {} + for _, dep in ipairs(_load_packages(deps, {requires_extra = package:get("__extra_deps"), parentinfo = requireinfo, nodeps = opt.nodeps})) do + table.insert(packages, dep) + packagedeps[dep:name()] = dep + end + package._DEPS = packagedeps + package._ORDERDEPS = table.unique(_sort_packagedeps(package)) + end + + -- save this package package + table.insert(packages, package) + end + end + end + + -- ok? + return packages +end + +-- sort packages urls +function _sort_packages_urls(packages) + + -- add all urls to fasturl and prepare to sort them together + for _, package in ipairs(packages) do + fasturl.add(package:urls()) + end + + -- sort and update urls + for _, package in ipairs(packages) do + package:urls_set(fasturl.sort(package:urls())) + end +end + +-- select packages version +function _select_packages_version(packages) + + -- sort and update urls + for _, package in ipairs(packages) do + + -- 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 = package:requireinfo().version + if #package:versions() > 0 and (require_version == "lastest" or require_version:find('.', 1, true)) then -- select version? + version, source = semver.select(require_version, package:versions()) + elseif has_giturl then -- select branch? + version, source = require_version ~= "lastest" and require_version or "master", "branches" + else + raise("package(%s %s): not found!", package:name(), require_version) + end + + -- save version to package + package:version_set(version, source) + end + end +end + +-- get user confirm +function _get_confirm(packages) + + -- no confirmed packages? + if #packages == 0 then + return true + end + + -- get confirm + local confirm = option.get("yes") + if confirm == nil then + + -- show tips + cprint("${bright yellow}note: ${default yellow}try installing these packages (pass -y to skip confirm)?") + for _, package in ipairs(packages) do + print(" -> %s %s", package:name(), package:version_str() or "") + end + cprint("please input: y (y/n)") + + -- get answer + io.flush() + local answer = io.read() + if answer == 'y' or answer == '' then + confirm = true + end + end + + -- ok? + return confirm +end + +-- the cache directory +function cachedir() + return path.join(global.directory(), "cache", "packages") +end + +-- load requires +function load_requires(requires, requires_extra, parentinfo) + + -- parse requires + local requireinfos = {} + for _, require_str in ipairs(requires) do + + -- parse require info + local packagename, requireinfo = _parse_require(require_str, requires_extra, parentinfo) + + -- save this required package + requireinfos[packagename] = requireinfo + end + + -- ok + return requireinfos +end + +-- load all required packages +function load_packages(requires, opt) + + -- init options + opt = opt or {} + + -- laod all required packages recursively + local packages = _load_packages(requires, opt) + + -- select packages version + _select_packages_version(packages) + + -- remove repeat packages with same the package name and version + local unique = {} + local results = {} + for _, package in ipairs(packages) do + local key = package:name() .. (package:version_str() or "") + if not unique[key] then + table.insert(results, package) + unique[key] = true + end + end + return results +end + +-- install packages +function install_packages(requires, opt) + + -- init options + opt = opt or {} + + -- load packages + local packages = load_packages(requires, opt) + + -- fetch packages (with system) from local first + if not option.get("force") then + process.runjobs(function (index) + local package = packages[index] + if package then + package:fetch() + end + end, #packages) + end + + -- filter packages + local packages_install = {} + local packages_download = {} + local packages_unsupported = {} + for _, package in ipairs(packages) do + if (option.get("force") or not package:exists()) and (#package:urls() > 0 or package:script("install")) then + if package:supported() then + if #package:urls() > 0 then + table.insert(packages_download, package) + end + table.insert(packages_install, package) + elseif not package:optional() then + table.insert(packages_unsupported, package) + end + end + end + + -- exists unsupported packages? + if #packages_unsupported > 0 then + -- show tips + cprint("${bright red}note: ${default red}the following packages are unsupported for $(plat)/$(arch)!") + for _, package in ipairs(packages_unsupported) do + print(" -> %s %s", package:name(), package:version_str() or "") + end + raise() + end + + -- get user confirm + if not _get_confirm(packages_install) then + return + end + + -- sort package urls + _sort_packages_urls(packages_download) + + -- download remote packages + local waitindex = 0 + local waitchars = {'\\', '|', '/', '-'} + process.runjobs(function (index) + + local package = packages_download[index] + if package then + action.download(package) + end + + end, #packages_download, ifelse(option.get("verbose"), 1, 4), 300, function (indices) + + -- do not print progress info if be verbose + if option.get("verbose") then + return + end + + -- update waitchar index + waitindex = ((waitindex + 1) % #waitchars) + + -- make downloading packages list + local downloading = {} + for _, index in ipairs(indices) do + local package = packages_download[index] + if package then + table.insert(downloading, package:name()) + end + end + + -- trace + cprintf("\r${yellow} => ${clear}downloading %s .. %s", table.concat(downloading, ", "), waitchars[waitindex + 1]) + io.flush() + end) + + -- install all required packages from repositories + for _, package in ipairs(packages_install) do + action.install(package) + end + + -- ok + return packages +end + +-- remove packages +function remove_packages(requires, opt) + + -- init options + opt = opt or {} + + -- do not remove dependent packages + opt.nodeps = true + + -- clear the detect cache + detectcache.clear() + + -- remove all packages + local packages = {} + for _, instance in ipairs(load_packages(requires, opt)) do + if os.isfile(instance:prefixfile()) then + + -- uninstall package from the prefix directory + action.install.uninstall_prefix(instance) + + -- remove the install files + os.tryrm(instance:installdir()) + + -- remove ok + table.insert(packages, instance) + end + end + return packages +end + +-- search packages +function search_packages(names) + + -- search all names + local results = {} + for _, name in ipairs(names) do + local packages = _search_packages(name) + if packages then + results[name] = packages + end + end + return results +end diff --git a/xmake/actions/require/impl/repository.lua b/xmake/actions/require/impl/repository.lua new file mode 100644 index 000000000..f9fa4d3cb --- /dev/null +++ b/xmake/actions/require/impl/repository.lua @@ -0,0 +1,108 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file repository.lua +-- + +-- imports +import("core.package.repository") + +-- get all repositories +function repositories() + + -- get it from cache it + if _g._REPOSITORIES then + return _g._REPOSITORIES + end + + -- get all repositories (local first) + local repos = table.join(repository.repositories(false), repository.repositories(true)) + + -- save repositories + _g._REPOSITORIES = repos + + -- ok + return repos +end + +-- the remote repositories have been pulled? +function pulled() + for _, repo in ipairs(repositories()) do + if not os.isdir(repo:directory()) then + return false + end + end + return true +end + +-- get package directory from repositories +function packagedir(packagename, reponame) + + -- get it from cache it + local packagedirs = _g._PACKAGEDIRS or {} + local foundir = packagedirs[packagename] + if foundir then + return foundir[1], foundir[2] + end + + -- find the package directory from repositories + for _, repo in ipairs(repositories()) do + local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1):lower(), packagename) + if os.isdir(dir) and (not reponame or reponame == repo:name()) then + foundir = {dir, repo} + break + end + end + + -- found? + if foundir then + + -- save package directory + packagedirs[packagename] = foundir + + -- update cache + _g._PACKAGEDIRS = packagedirs + + -- ok + return foundir[1], foundir[2] + end +end + +-- search package directories from repositories +function searchdirs(name) + + -- find the package directories from all repositories + local unique = {} + local packageinfos = {} + for _, repo in ipairs(repositories()) do + for _, file in ipairs(os.files(path.join(repo:directory(), "packages", "*", string.ipattern("*" .. name .. "*"), "xmake.lua"))) do + local packagename = path.basename(path.directory(file)) + if not unique[packagename] then + table.insert(packageinfos, {name = packagename, repo = repo, packagedir = path.directory(file)}) + unique[packagename] = true + end + end + end + + -- ok? + return packageinfos +end + diff --git a/xmake/actions/require/impl/utils/filter.lua b/xmake/actions/require/impl/utils/filter.lua new file mode 100644 index 000000000..209c703ba --- /dev/null +++ b/xmake/actions/require/impl/utils/filter.lua @@ -0,0 +1,144 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2018, TBOOX Open Source Group. +-- +-- @author ruki +-- @file filter.lua +-- + +-- imports +import("core.base.filter") +import("core.base.option") +import("core.base.global") +import("core.project.config") +import("core.project.project") +import("core.sandbox.sandbox") + +-- get filter +function _filter() + + -- init filter + if _g.filter == nil then + _g.filter = filter.new() + _g.filter:register("common", function (variable) + + -- attempt to get it directly from the configure + local result = config.get(variable) + if result == nil then + + -- init maps + _g.common_maps = _g.common_maps or + { + host = os.host() + , tmpdir = function () return os.tmpdir() end + , curdir = function () return os.curdir() end + , scriptdir = function () return os.scriptdir() end + , globaldir = global.directory() + , configdir = config.directory() + , projectdir = project.directory() + , programdir = os.programdir() + } + + -- map it + result = _g.common_maps[variable] + end + + -- is script? call it + if type(result) == "function" then + result = result() + end + + -- ok? + return result + end) + end + + -- ok + return _g.filter +end + +-- the package handler +function _handler(package, strval) + + -- @note cannot cache it, because the package instance will be changed + return function (variable) + + -- init maps + local maps = + { + version = function () + if strval then + -- set_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz", + -- {version = function (version) return version:gsub("%.", "") end}) + local version_filter = package:url_version(strval) + if version_filter then + return version_filter(package:version_str()) + end + end + return package:version_str() + end + } + + -- get value + local result = maps[variable] + if type(result) == "function" then + result = result() + end + + -- ok? + return result + end +end + +-- attach filter to the given script and call it +function call(script, package) + + -- get sandbox filter and handlers of the given script + local sandbox_filter = sandbox.filter(script) + local sandbox_handlers = sandbox_filter:handlers() + + -- switch to the handlers of the current filter + sandbox_filter:set_handlers(_filter():handlers()) + + -- register package handler + sandbox_filter:register("package", _handler(package)) + + -- call it + script(package) + + -- restore handlers + sandbox_filter:set_handlers(sandbox_handlers) +end + +-- handle the string value of package +function handle(strval, package) + + -- register filter handler + _filter():register("package", _handler(package, strval)) + + -- handle string value + strval = _filter():handle(strval) + + -- register filter handler + _filter():register("package", nil) + + -- ok + return strval +end + |
