alunizaje/StartGamedev-160604-osx/tools/zbstudio.app/Contents/ZeroBraneStudio/lualibs/dist/init.lua
2016-11-03 00:05:36 +01:00

350 lines
14 KiB
Lua

-- main API of LuaDist
module ("dist", package.seeall)
local cfg = require "dist.config"
local depends = require "dist.depends"
local git = require "dist.git"
local sys = require "dist.sys"
local package = require "dist.package"
local mf = require "dist.manifest"
local utils = require "dist.utils"
-- Return the deployment directory.
function get_deploy_dir()
return sys.abs_path(cfg.root_dir)
end
-- Return packages deployed in 'deploy_dir' also with their provides.
function get_deployed(deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(deploy_dir) == "string", "dist.get_deployed: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local deployed = depends.get_installed(deploy_dir)
local provided = {}
for _, pkg in pairs(deployed) do
for _, provided_pkg in pairs(depends.get_provides(pkg)) do
provided_pkg.provided_by = pkg.name .. "-" .. pkg.version
table.insert(provided, provided_pkg)
end
end
for _, provided_pkg in pairs(provided) do
table.insert(deployed, provided_pkg)
end
deployed = depends.sort_by_names(deployed)
return deployed
end
-- Download new 'manifest_file' from repository and returns it.
-- Return nil and error message on error.
function update_manifest(deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
assert(type(deploy_dir) == "string", "dist.update_manifest: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- TODO: use 'deploy_dir' argument in manifest functions
-- retrieve the new manifest (forcing no cache use)
local manifest, err = mf.get_manifest(nil, true)
if manifest then
return manifest
else
return nil, err
end
end
-- Install 'package_names' to 'deploy_dir', using optional CMake 'variables'.
function install(package_names, deploy_dir, variables)
if not package_names then return true end
deploy_dir = deploy_dir or cfg.root_dir
if type(package_names) == "string" then package_names = {package_names} end
assert(type(package_names) == "table", "dist.install: Argument 'package_names' is not a table or string.")
assert(type(deploy_dir) == "string", "dist.install: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
-- find installed packages
local installed = depends.get_installed(deploy_dir)
-- get manifest
local manifest, err = mf.get_manifest()
if not manifest then return nil, "Error getting manifest: " .. err end
-- get dependency manifest
-- TODO: Is it good that dep_manifest is deploy_dir-specific?
-- Probably it'd be better not to be specific, but then there're
-- problems with 'provides'. E.g. What to do if there's a module
-- installed, that is provided by two different modules in two deploy_dirs?
local dep_manifest_file = sys.abs_path(sys.make_path(deploy_dir, cfg.dep_cache_file))
local dep_manifest, status = {}
if sys.exists(dep_manifest_file) and not utils.cache_timeout_expired(cfg.cache_timeout, dep_manifest_file) then
status, dep_manifest = mf.load_manifest(dep_manifest_file)
if not dep_manifest then return nil, status end
end
-- resolve dependencies
local dependencies, dep_manifest_or_err = depends.get_depends(package_names, installed, manifest, dep_manifest, deploy_dir, false, false)
if not dependencies then return nil, dep_manifest_or_err end
if #dependencies == 0 then return nil, "No packages to install." end
-- save updated dependency manifest
local ok, err = sys.make_dir(sys.parent_dir(dep_manifest_file))
if not ok then return nil, err end
ok, err = mf.save_manifest(dep_manifest_or_err, dep_manifest_file)
if not ok then return nil, err end
-- fetch the packages from repository
local fetched_pkgs = {}
for _, pkg in pairs(dependencies) do
local fetched_pkg, err = package.fetch_pkg(pkg, sys.make_path(deploy_dir, cfg.temp_dir))
if not fetched_pkg then return nil, err end
table.insert(fetched_pkgs, fetched_pkg)
end
-- install fetched packages
for _, pkg in pairs(fetched_pkgs) do
local ok, err = package.install_pkg(pkg.download_dir, deploy_dir, variables, pkg.preserve_pkg_dir)
if not ok then return nil, err end
end
return true
end
-- Manually deploy packages from 'package_dirs' to 'deploy_dir', using optional
-- CMake 'variables'. The 'package_dirs' are preserved (will not be deleted).
function make(deploy_dir, package_dirs, variables)
deploy_dir = deploy_dir or cfg.root_dir
package_dirs = package_dirs or {}
assert(type(deploy_dir) == "string", "dist.make: Argument 'deploy_dir' is not a string.")
assert(type(package_dirs) == "table", "dist.make: Argument 'package_dirs' is not a table.")
deploy_dir = sys.abs_path(deploy_dir)
for _, dir in pairs(package_dirs) do
local ok, err = package.install_pkg(sys.abs_path(dir), deploy_dir, variables, true)
if not ok then return nil, err end
end
return true
end
-- Remove 'package_names' from 'deploy_dir' and return the number of removed
-- packages.
function remove(package_names, deploy_dir)
deploy_dir = deploy_dir or cfg.root_dir
if type(package_names) == "string" then package_names = {package_names} end
assert(type(package_names) == "table", "dist.remove: Argument 'package_names' is not a string or table.")
assert(type(deploy_dir) == "string", "dist.remove: Argument 'deploy_dir' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local pkgs_to_remove = {}
local installed = depends.get_installed(deploy_dir)
-- find packages to remove
if #package_names == 0 then
pkgs_to_remove = installed
else
pkgs_to_remove = depends.find_packages(package_names, installed)
end
-- remove them
for _, pkg in pairs(pkgs_to_remove) do
local pkg_distinfo_dir = sys.make_path(cfg.distinfos_dir, pkg.name .. "-" .. pkg.version)
local ok, err = package.remove_pkg(pkg_distinfo_dir, deploy_dir)
if not ok then return nil, err end
end
return #pkgs_to_remove
end
-- Download 'pkg_names' to 'fetch_dir' and return the table of their directories.
function fetch(pkg_names, fetch_dir)
fetch_dir = fetch_dir or sys.current_dir()
assert(type(pkg_names) == "table", "dist.fetch: Argument 'pkg_names' is not a string or table.")
assert(type(fetch_dir) == "string", "dist.fetch: Argument 'fetch_dir' is not a string.")
fetch_dir = sys.abs_path(fetch_dir)
local manifest = mf.get_manifest()
local pkgs_to_fetch = {}
for _, pkg_name in pairs(pkg_names) do
-- retrieve available versions
local versions, err = package.retrieve_versions(pkg_name, manifest)
if not versions then return nil, err end
for _, version in pairs(versions) do
table.insert(manifest, version)
end
local packages = depends.find_packages(pkg_name, manifest)
if #packages == 0 then return nil, "No packages found for '" .. pkg_name .. "'." end
packages = depends.sort_by_versions(packages)
table.insert(pkgs_to_fetch, packages[1])
end
local fetched_dirs = {}
for _, pkg in pairs(pkgs_to_fetch) do
local fetched_pkg, err = package.fetch_pkg(pkg, fetch_dir)
if not fetched_pkg then return nil, err end
table.insert(fetched_dirs, fetched_pkg.download_dir)
end
return fetched_dirs
end
-- Upload binary version of given modules installed in the specified
-- 'deploy_dir' to the repository specified by provided base url.
-- Return the number of uploaded packages.
--
-- Organization of uploaded modules and their repositories is subject
-- to the following conventions:
-- - destination repository is: 'DEST_GIT_BASE_URL/MODULE_NAME'
-- - module will be uploaded to the branch: 'ARCH-TYPE' according
-- to the arch and type of the user's machine
-- - the module will be tagged as: 'VERSION-ARCH-TYPE' (if the tag already
-- exists, it will be overwritten)
--
-- E.g. assume that the module 'lua-5.1.4' is installed on the 32bit Linux
-- system (Linux-i686). When this function is called with the module name
-- 'lua' and base url 'git@github.com:LuaDist', then the binary version
-- of the module 'lua', that is installed on the machine, will be uploaded
-- to the branch 'Linux-i686' of the repository 'git@github.com:LuaDist/lua.git'
-- and tagged as '5.1.4-Linux-i686'.
function upload_modules(deploy_dir, module_names, dest_git_base_url)
deploy_dir = deploy_dir or cfg.root_dir
if type(module_names) == "string" then module_names = {module_names} end
assert(type(deploy_dir) == "string", "dist.upload_module: Argument 'deploy_dir' is not a string.")
assert(type(module_names) == "table", "dist.upload_module: Argument 'module_name' is not a string or table.")
assert(type(dest_git_base_url) == "string", "dist.upload_module: Argument 'dest_git_base_url' is not a string.")
deploy_dir = sys.abs_path(deploy_dir)
local modules_to_upload = {}
local installed = depends.get_installed(deploy_dir)
-- find modules to upload
if #module_names == 0 then
modules_to_upload = installed
else
modules_to_upload = depends.find_packages(module_names, installed)
end
for _, installed_module in pairs(modules_to_upload) do
-- set names
local branch_name = cfg.arch .. "-" .. cfg.type
local tag_name = installed_module.version .. "-" .. branch_name
local full_name = installed_module.name .. "-" .. tag_name
local tmp_dir = sys.make_path(deploy_dir, cfg.temp_dir, full_name .. "-to-upload")
local dest_git_url = dest_git_base_url .. "/" .. installed_module.name .. ".git"
local distinfo_file = sys.make_path(deploy_dir, cfg.distinfos_dir, installed_module.name .. "-" .. installed_module.version, "dist.info")
-- create temporary directory (delete previous if already exists)
if sys.exists(tmp_dir) then sys.delete(tmp_dir) end
local ok, err = sys.make_dir(tmp_dir)
if not ok then return nil, err end
-- copy the module files for all enabled components
for _, component in ipairs(cfg.components) do
if installed_module.files[component] then
for _, file in ipairs(installed_module.files[component]) do
local file_path = sys.make_path(deploy_dir, file)
local dest_dir = sys.parent_dir(sys.make_path(tmp_dir, file))
if sys.is_file(file_path) then
sys.make_dir(dest_dir)
sys.copy(file_path, dest_dir)
end
end
end
end
-- add module's dist.info file
sys.copy(distinfo_file, tmp_dir)
-- create git repo
ok, err = git.init(tmp_dir)
if not ok then return nil, "Error initializing empty git repository in '" .. tmp_dir .. "': " .. err end
-- add all files
ok, err = git.add_all(tmp_dir)
if not ok then return nil, "Error adding all files to the git index in '" .. tmp_dir .. "': " .. err end
-- create commit
ok, err = git.commit("[luadist-git] add " .. full_name .. " [ci skip]", tmp_dir)
if not ok then return nil, "Error commiting changes in '" .. tmp_dir .. "': " .. err end
-- rename branch
ok, err = git.rename_branch("master", branch_name, tmp_dir)
if not ok then return nil, "Error renaming branch 'master' to '" .. branch_name .. "' in '" .. tmp_dir .. "': " .. err end
-- create tag
ok, err = git.create_tag(tmp_dir, tag_name)
if not ok then return nil, "Error creating tag '" .. tag_name .. "' in '" .. tmp_dir .. "': " .. err end
print("Uploading " .. full_name .. " to " .. dest_git_url .. "...")
-- push to the repository
ok, err = git.push_ref(tmp_dir, branch_name, dest_git_url, true)
if not ok then return nil, "Error when pushing branch '" .. branch_name .. "' and tag '" .. tag_name .. "' to '" .. dest_git_url .. "': " .. err end
-- delete temporary directory (if not in debug mode)
if not cfg.debug then sys.delete(tmp_dir) end
end
return #modules_to_upload
end
-- Returns table with information about module's dependencies, using the cache.
function dependency_info(module, deploy_dir)
cache_file = cache_file or sys.abs_path(sys.make_path(cfg.root_dir, cfg.dep_cache_file))
assert(type(module) == "string", "dist.dependency_info: Argument 'module' is not a string.")
assert(type(deploy_dir) == "string", "dist.dependency_info: Argument 'deploy_dir' is not a string.")
-- get manifest
local manifest, err = mf.get_manifest()
if not manifest then return nil, "Error getting manifest: " .. err end
-- get dependency manifest
-- TODO: Is it good that dep_manifest is deploy_dir-specific?
-- Probably it'd be better not to be specific, but then there're
-- problems with 'provides'. E.g. What to do if there's a module
-- installed, that is provided by two different modules in two deploy_dirs?
local dep_manifest_file = sys.abs_path(sys.make_path(deploy_dir, cfg.dep_cache_file))
local dep_manifest, status = {}
if sys.exists(dep_manifest_file) and cfg.cache and not utils.cache_timeout_expired(cfg.cache_timeout, dep_manifest_file) then
status, dep_manifest = mf.load_manifest(dep_manifest_file)
if not dep_manifest then return nil, status end
end
-- force getting the dependency information
local installed = {}
-- resolve dependencies
local dependencies, dep_manifest_or_err = depends.get_depends(module, installed, manifest, dep_manifest, deploy_dir, false, true and not cfg.debug)
if not dependencies then return nil, dep_manifest_or_err end
-- save updated dependency manifest
local ok, err = sys.make_dir(sys.parent_dir(dep_manifest_file))
if not ok then return nil, err end
ok, err = mf.save_manifest(dep_manifest_or_err, dep_manifest_file)
if not ok then return nil, err end
-- collect just relevant dependencies from dependency manifest
local relevant_deps = {}
for _, dep in pairs(dependencies) do
local name_ver = dep.name .. "-" .. (dep.was_scm_version and "scm" or dep.version)
if dep_manifest_or_err[name_ver] then
table.insert(relevant_deps, dep_manifest_or_err[name_ver])
else
return nil, "Error: dependency information for '" .. name_ver .. "' not found in dependency manifest."
end
end
return relevant_deps
end