summaryrefslogtreecommitdiff
path: root/xmake/actions/require/package.lua
diff options
context:
space:
mode:
authorruki <[email protected]>2017-08-14 11:27:19 +0800
committerruki <[email protected]>2017-08-14 11:29:30 +0800
commitdba45d143d8aff047da82cbe1a881056e6e4bc00 (patch)
tree7b80d5de264c17b538a7c9d0fbb234ded39ac683 /xmake/actions/require/package.lua
parent34706ba053dda9dd86902cacf42a6c1878c85234 (diff)
merge repo and require codes
Diffstat (limited to 'xmake/actions/require/package.lua')
-rw-r--r--xmake/actions/require/package.lua360
1 files changed, 360 insertions, 0 deletions
diff --git a/xmake/actions/require/package.lua b/xmake/actions/require/package.lua
new file mode 100644
index 000000000..6b401ab73
--- /dev/null
+++ b/xmake/actions/require/package.lua
@@ -0,0 +1,360 @@
+--!The Make-like 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 - 2017, 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("core.project.project")
+import("core.package.package", {alias = "core_package"})
+import("devel.git")
+import("net.fasturl")
+import("repository")
+
+--
+-- parse require string
+--
+-- add_requires("tboox.tbox >=1.5.1", "zlib >=1.2.11")
+-- add_requires("zlib master")
+-- add_requires("[email protected] >=1.5.1")
+-- add_requires("https://github.com/tboox/[email protected] >=1.5.1")
+-- add_requires("tboox.tbox >=1.5.1 <1.6.0 optional")
+--
+function _parse_require(require_str)
+
+ -- 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 mode at last position
+ --
+ -- .e.g
+ --
+ -- must
+ -- optional
+ --
+ local mode = "must"
+ if #splitinfo > 1 then
+
+ -- get mode
+ local modes = {must = true, optional = true}
+ local value = splitinfo[#splitinfo]:lower()
+ if modes[value] then
+ mode = value
+ table.remove(splitinfo)
+ end
+ end
+
+ -- get version
+ --
+ -- .e.g
+ --
+ -- >=1.5.1 <1.6.0
+ -- master || >1.4
+ -- ~1.2.3
+ -- ^1.1
+ --
+ local version = "master"
+ 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 packageurl = nil
+ local packagename = nil
+ local pos = packageinfo:find_last('@', true)
+ if pos then
+
+ -- get package name
+ packagename = packageinfo:sub(pos + 1)
+
+ -- get reponame or packageurl
+ local repo_or_pkgurl = packageinfo:sub(1, pos - 1)
+
+ -- is package url?
+ if repo_or_pkgurl:find('[/\\]') then
+ packageurl = repo_or_pkgurl
+ else
+ reponame = repo_or_pkgurl
+ end
+ else
+ packagename = packageinfo
+ end
+
+ -- check package name
+ assert(packagename, "require(\"%s\"): the package name not found!", require_str)
+
+ -- init required item
+ local required = {}
+ required.packagename = packagename
+ required.requireinfo = {reponame = reponame, packageurl = packageurl, version = version, mode = mode}
+
+ -- save this required item to cache
+ requires[require_str] = required
+ _g._REQUIRES = requires
+
+ -- ok
+ return required.packagename, required.requireinfo
+end
+
+-- load package instance from the given package url
+function _load_package_from_url(packagename, packageurl)
+
+ -- load it
+ return core_package.load_from_url(packagename, packageurl)
+end
+
+-- load package instance from project
+function _load_package_from_project(packagename)
+
+ -- load it
+ return core_package.load_from_project(packagename)
+end
+
+-- load package instance from repositories
+function _load_package_from_repository(packagename, reponame)
+
+ -- get package directory from the given package name
+ local packagedir, is_global = repository.packagedir(packagename, reponame)
+ if packagedir then
+ -- load it
+ return core_package.load_from_repository(packagename, is_global, packagedir)
+ end
+end
+
+-- load required packages
+function _load_package(packagename, requireinfo)
+
+ -- attempt to get it from cache first
+ local packages = _g._PACKAGES or {}
+ local instance = packages[packagename]
+ if instance then
+
+ -- satisfy required version?
+ if not semver.satisfies(instance:version(), requireinfo.version) then
+ raise("package(%s): version conflict, '%s' does not satisfy '%s'!", packagename, instance:version(), requireinfo.version)
+ end
+
+ -- ok
+ return instance
+ end
+
+ -- load package instance
+ instance = nil
+ if requireinfo.packageurl then
+ -- load package from the given package url
+ instance = _load_package_from_url(packagename, requireinfo.packageurl)
+ else
+ -- load package from project first
+ instance = _load_package_from_project(packagename)
+ if not instance then
+ -- load package from repositories
+ instance = _load_package_from_repository(packagename, requireinfo.reponame)
+ end
+ end
+
+ -- check
+ assert(instance, "package(%s) not found!", packagename)
+
+ -- save require info to package
+ instance:requireinfo_set(requireinfo)
+
+ -- save this package instance to cache
+ packages[packagename] = instance
+ _g._PACKAGES = packages
+
+ -- ok
+ return instance
+end
+
+-- load all required packages
+function _load_packages(requires)
+
+ -- no requires?
+ if not requires or #requires == 0 then
+ return {}
+ end
+
+ -- load packages
+ local packages = {}
+ for packagename, requireinfo in pairs(load_requires(requires)) do
+
+ -- load package instance
+ local package = _load_package(packagename, requireinfo)
+
+ -- load required packages and save them first of this package
+ requires = package:get("requires")
+ if requires then
+ table.join2(packages, _load_packages(requires))
+ end
+
+ -- save this package instance
+ table.insert(packages, package)
+ end
+
+ -- ok?
+ return packages
+end
+
+-- load all git refs from packages
+function _load_packages_gitrefs(packages)
+
+ -- enter cache scope
+ cache.enter("local.require")
+
+ -- load cache
+ local gitrefs = nil
+ if option.get("force") then
+ gitrefs = {}
+ else
+ gitrefs = cache.get("gitrefs") or {}
+ end
+
+ -- run tasks
+ local results = {}
+ process.runjobs(function (index)
+ local package = packages[index]
+ if package then
+
+ -- attempt to get refs from cache first
+ local refs = gitrefs[package:name()]
+ if refs then
+ results[package:name()] = {tags = refs.tags, branches = refs.branches}
+ else
+ -- attempt to get refs from the git url
+ local tags = {}
+ local branches = {}
+ for _, url in ipairs(package:urls()) do
+ if git.checkurl(url) then
+
+ -- fetch refs
+ tags, branches = git.refs(url)
+
+ -- save result
+ results[package:name()] = {tags = tags, branches = branches}
+
+ -- cache result
+ gitrefs[package:name()] = {tags = tags, branches = branches}
+ break
+ end
+ end
+ end
+ end
+ end, #packages)
+
+ -- save cache
+ cache.set("gitrefs", gitrefs)
+ cache.flush()
+
+ -- ok?
+ return results
+end
+
+-- select package version
+function _select_package_version(package, required_ver, gitrefs)
+
+ -- get versions
+ local versions = package:get("versions")
+
+ -- attempt to get tags and branches from the git url
+ local refs = {}
+ if gitrefs then
+ refs = gitrefs[package:name()] or {}
+ end
+
+ -- select required version
+ return semver.select(required_ver, versions, refs.tags, refs.branches)
+end
+
+-- the cache directory
+function cachedir()
+ return path.join(global.directory(), "cache", "packages")
+end
+
+-- load requires
+function load_requires(requires)
+
+ -- parse requires
+ local requireinfos = {}
+ for _, require_str in ipairs(requires) do
+
+ -- parse require info
+ local packagename, requireinfo = _parse_require(require_str)
+
+ -- save this required package
+ requireinfos[packagename] = requireinfo
+ end
+
+ -- ok
+ return requireinfos
+end
+
+-- load all required packages
+function load_packages(requires)
+
+ -- laod all required packages recursively
+ local packages = _load_packages(requires)
+
+ -- add all urls to fasturl and prepare to sort them together
+ for _, package in ipairs(packages) do
+ fasturl.add(package:urls())
+ end
+
+ -- load git refs from packages
+ local gitrefs = _load_packages_gitrefs(packages)
+
+ -- sort and update urls
+ for _, package in ipairs(packages) do
+
+ -- sort package urls
+ package:urls_set(fasturl.sort(package:urls()))
+
+ -- exists urls? otherwise be phony package (only as package group)
+ if #package:urls() > 0 then
+
+ -- select package version
+ local version, source = _select_package_version(package, package:requireinfo().version, gitrefs)
+
+ -- save version to package
+ package:version_set(version, source)
+ end
+ end
+
+ -- ok
+ return packages
+end
+