From d003fe0a57e65fe71a2853bd6a33aa9d1c42636c Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 30 Sep 2018 11:32:17 +0800 Subject: modify require impl modules --- xmake/actions/require/action/download.lua | 224 --------- xmake/actions/require/action/filter.lua | 144 ------ xmake/actions/require/action/install.lua | 244 --------- xmake/actions/require/action/test.lua | 63 --- xmake/actions/require/environment.lua | 53 -- xmake/actions/require/impl/action/download.lua | 224 +++++++++ xmake/actions/require/impl/action/install.lua | 244 +++++++++ xmake/actions/require/impl/action/test.lua | 63 +++ xmake/actions/require/impl/environment.lua | 53 ++ xmake/actions/require/impl/package.lua | 544 +++++++++++++++++++++ xmake/actions/require/impl/repository.lua | 108 ++++ xmake/actions/require/impl/utils/filter.lua | 144 ++++++ xmake/actions/require/info.lua | 8 +- xmake/actions/require/install.lua | 6 +- xmake/actions/require/list.lua | 6 +- xmake/actions/require/package.lua | 544 --------------------- xmake/actions/require/remove.lua | 6 +- xmake/actions/require/repository.lua | 108 ---- xmake/actions/require/search.lua | 8 +- .../sandbox/modules/import/core/sandbox/module.lua | 6 +- 20 files changed, 1400 insertions(+), 1400 deletions(-) delete mode 100644 xmake/actions/require/action/download.lua delete mode 100644 xmake/actions/require/action/filter.lua delete mode 100644 xmake/actions/require/action/install.lua delete mode 100644 xmake/actions/require/action/test.lua delete mode 100644 xmake/actions/require/environment.lua create mode 100644 xmake/actions/require/impl/action/download.lua create mode 100644 xmake/actions/require/impl/action/install.lua create mode 100644 xmake/actions/require/impl/action/test.lua create mode 100644 xmake/actions/require/impl/environment.lua create mode 100644 xmake/actions/require/impl/package.lua create mode 100644 xmake/actions/require/impl/repository.lua create mode 100644 xmake/actions/require/impl/utils/filter.lua delete mode 100644 xmake/actions/require/package.lua delete mode 100644 xmake/actions/require/repository.lua diff --git a/xmake/actions/require/action/download.lua b/xmake/actions/require/action/download.lua deleted file mode 100644 index bb5017e5f..000000000 --- a/xmake/actions/require/action/download.lua +++ /dev/null @@ -1,224 +0,0 @@ ---!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("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/action/filter.lua b/xmake/actions/require/action/filter.lua deleted file mode 100644 index 209c703ba..000000000 --- a/xmake/actions/require/action/filter.lua +++ /dev/null @@ -1,144 +0,0 @@ ---!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 - diff --git a/xmake/actions/require/action/install.lua b/xmake/actions/require/action/install.lua deleted file mode 100644 index 0af940549..000000000 --- a/xmake/actions/require/action/install.lua +++ /dev/null @@ -1,244 +0,0 @@ ---!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("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/action/test.lua b/xmake/actions/require/action/test.lua deleted file mode 100644 index dba807865..000000000 --- a/xmake/actions/require/action/test.lua +++ /dev/null @@ -1,63 +0,0 @@ ---!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("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/environment.lua b/xmake/actions/require/environment.lua deleted file mode 100644 index f340ab7eb..000000000 --- a/xmake/actions/require/environment.lua +++ /dev/null @@ -1,53 +0,0 @@ ---!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/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 + diff --git a/xmake/actions/require/info.lua b/xmake/actions/require/info.lua index 8fcbe8d9e..19a51139f 100644 --- a/xmake/actions/require/info.lua +++ b/xmake/actions/require/info.lua @@ -26,10 +26,10 @@ import("core.base.task") import("core.base.option") import("core.project.project") -import("action.filter") -import("package") -import("repository") -import("environment") +import("impl.utils.filter") +import("impl.package") +import("impl.repository") +import("impl.environment") -- show the given package info function main(requires) diff --git a/xmake/actions/require/install.lua b/xmake/actions/require/install.lua index e43e99b4d..f42380d2a 100644 --- a/xmake/actions/require/install.lua +++ b/xmake/actions/require/install.lua @@ -27,9 +27,9 @@ import("core.base.option") import("core.base.task") import("core.project.project") import("lib.detect.find_tool") -import("package") -import("repository") -import("environment") +import("impl.package") +import("impl.repository") +import("impl.environment") -- attach package to option function _attach_to_option(instance, opt) diff --git a/xmake/actions/require/list.lua b/xmake/actions/require/list.lua index 54356d14d..066ed2a64 100644 --- a/xmake/actions/require/list.lua +++ b/xmake/actions/require/list.lua @@ -25,9 +25,9 @@ -- imports import("core.project.project") import("core.base.task") -import("package") -import("repository") -import("environment") +import("impl.package") +import("impl.repository") +import("impl.environment") -- from local/global/system/remote? function _from(instance) diff --git a/xmake/actions/require/package.lua b/xmake/actions/require/package.lua deleted file mode 100644 index fc67ea232..000000000 --- a/xmake/actions/require/package.lua +++ /dev/null @@ -1,544 +0,0 @@ ---!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/remove.lua b/xmake/actions/require/remove.lua index 4296ab05c..4ca2dc207 100644 --- a/xmake/actions/require/remove.lua +++ b/xmake/actions/require/remove.lua @@ -25,9 +25,9 @@ -- imports import("core.base.task") import("core.base.option") -import("package") -import("repository") -import("environment") +import("impl.package") +import("impl.repository") +import("impl.environment") -- show the given package info function main(requires) diff --git a/xmake/actions/require/repository.lua b/xmake/actions/require/repository.lua deleted file mode 100644 index f9fa4d3cb..000000000 --- a/xmake/actions/require/repository.lua +++ /dev/null @@ -1,108 +0,0 @@ ---!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/search.lua b/xmake/actions/require/search.lua index c7e341337..f2f11a75c 100644 --- a/xmake/actions/require/search.lua +++ b/xmake/actions/require/search.lua @@ -24,10 +24,10 @@ -- imports import("core.base.task") -import("action.filter") -import("package") -import("repository") -import("environment") +import("impl.utils.filter") +import("impl.package") +import("impl.repository") +import("impl.environment") -- search the given packages function main(names) diff --git a/xmake/core/sandbox/modules/import/core/sandbox/module.lua b/xmake/core/sandbox/modules/import/core/sandbox/module.lua index 8f73bf298..c51c16ef1 100644 --- a/xmake/core/sandbox/modules/import/core/sandbox/module.lua +++ b/xmake/core/sandbox/modules/import/core/sandbox/module.lua @@ -278,7 +278,7 @@ end -- import module -- -- @param name the module name, .e.g core.platform --- @param opt the argument options, .e.g {alias = "", rootdir = "", try = false, inherit = false, anonymous = false, nocache = false} +-- @param opt the argument options, .e.g {alias = "", nolocal = true, rootdir = "", try = false, inherit = false, anonymous = false, nocache = false} -- -- @return the module instance -- @@ -342,8 +342,8 @@ function core_sandbox_module.import(name, opt) local rootdir = opt.rootdir or instance:rootdir() assert(rootdir) - -- init module directories - local modules_directories = table.join(rootdir, core_sandbox_module.directories()) + -- init module directories (disable local packages?) + local modules_directories = opt.nolocal and core_sandbox_module.directories() or table.join(rootdir, core_sandbox_module.directories()) -- load module local found = false -- cgit v1.3.1