diff options
author | Jakob Stendahl <jakob.stendahl@outlook.com> | 2018-01-09 15:01:04 +0100 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2018-01-09 15:01:04 +0100 |
commit | bb6e704ab011a497ac8c735afc0fd4c52a1425ce (patch) | |
tree | bed462d18c4f976f30c690b529d6de8b2a6ba2ff /love2dToAPK/tools/tools/zbstudio-win/lualibs/dist | |
parent | d2c375c015fe3e216234a887ab75e08d40f5f5db (diff) | |
download | Love2dToAPK-bb6e704ab011a497ac8c735afc0fd4c52a1425ce.tar.gz Love2dToAPK-bb6e704ab011a497ac8c735afc0fd4c52a1425ce.zip |
Added old project
Diffstat (limited to 'love2dToAPK/tools/tools/zbstudio-win/lualibs/dist')
10 files changed, 3253 insertions, 0 deletions
diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/config.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/config.lua new file mode 100644 index 0000000..3139750 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/config.lua @@ -0,0 +1,112 @@ +-- Luadist configuration + +module ("dist.config", package.seeall) + +local sys = require "dist.sys" +local utils = require "dist.utils" +local win = (os.getenv('WINDIR') or (os.getenv('OS') or ''):match('[Ww]indows')) + and not (os.getenv('OSTYPE') or ''):match('cygwin') -- exclude cygwin + +-- System information ------------------------------------------------ +version = "0.2.7" -- Current LuaDist version +-- set initial architecture as it's important for path separators +arch = win and "Windows" or "Linux" -- Host architecture +type = "x86" -- Host type + +-- Directories ------------------------------------------------------- +root_dir = os.getenv("DIST_ROOT") or utils.get_luadist_location() or sys.path_separator() +temp_dir = "tmp" +cache_dir = sys.make_path(temp_dir, "cache") +distinfos_dir = sys.make_path("share", "luadist-git", "dists") +test_dir = sys.make_path("share", "luadist-git", "test") + +-- Files ------------------------------------------------------------- +manifest_file = sys.make_path(cache_dir, ".gitmodules") +dep_cache_file = sys.make_path(cache_dir, ".depcache") +log_file = sys.make_path(temp_dir, "luadist.log") +cache_file = "" + +-- Repositories ------------------------------------------------------ +repos = { + "git://github.com/LuaDist/Repository.git", +} + +upload_url = "git@github.com:LuaDist" -- must not contain trailing '/' + +-- Settings ---------------------------------------------------------- +debug = false -- Use debug mode. +verbose = false -- Print verbose output. +simulate = false -- Only simulate installation of packages. +binary = true -- Use binary version of modules. +source = true -- Use source version of modules. +test = false -- Run CTest before install. + +cache = true -- Use cache. +cache_timeout = 3 * 60 * 60 -- Cache timeout in seconds. + +dep_cache = true -- Use cache for dependency information (tree functionality). + +-- Components (of modules) that will be installed. +components = { + "Runtime", "Library", "Header", "Data", "Documentation", "Example", "Test", "Other", "Unspecified" +} + +-- Available log levels are: DEBUG, INFO, WARN, ERROR, FATAL (see dist.logger for more information). +print_log_level = "WARN" -- Minimum level for log messages to be printed (nil to disable). +write_log_level = "INFO" -- Minimum level for log messages to be logged (nil to disable). + + +-- CMake variables --------------------------------------------------- +variables = { + --- Install defaults + INSTALL_BIN = "bin", + INSTALL_LIB = "lib", + INSTALL_INC = "include", + INSTALL_ETC = "etc", + INSTALL_LMOD = "lib/lua", + INSTALL_CMOD = "lib/lua", + + --- LuaDist specific variables + DIST_VERSION = version, + DIST_ARCH = arch, + DIST_TYPE = type, + + -- CMake specific setup + CMAKE_GENERATOR = win and "MinGW Makefiles" or "Unix Makefiles", + CMAKE_BUILD_TYPE = "MinSizeRel", + + -- RPath functionality + CMAKE_SKIP_BUILD_RPATH = "FALSE", + CMAKE_BUILD_WITH_INSTALL_RPATH = "FALSE", + CMAKE_INSTALL_RPATH = "$ORIGIN/../lib", + CMAKE_INSTALL_RPATH_USE_LINK_PATH = "TRUE", + CMAKE_INSTALL_NAME_DIR = "@executable_path/../lib", + + -- OSX specific + CMAKE_OSX_ARCHITECTURES = "", +} + +-- Building ---------------------------------------------------------- +cmake = "cmake" +ctest = "ctest" + +cache_command = cmake .. " -C cache.cmake" +build_command = cmake .. " --build . --clean-first" + +install_component_command = " -DCOMPONENT=#COMPONENT# -P cmake_install.cmake" + +test_command = ctest .. " -V ." + +strip_option = " -DCMAKE_INSTALL_DO_STRIP=true" +cache_debug_options = "-DCMAKE_VERBOSE_MAKEFILE=true -DCMAKE_BUILD_TYPE=Debug" +build_debug_options = "" + +-- Add -j option to make in case of unix makefiles to speed up builds +if (variables.CMAKE_GENERATOR == "Unix Makefiles") then + build_command = build_command .. " -- -j6" +end + +-- Add -j option to make in case of MinGW makefiles to speed up builds +if (variables.CMAKE_GENERATOR == "MinGW Makefiles") then + build_command = "set SHELL=cmd.exe && " .. build_command .. " -- -j" +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/constraints.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/constraints.lua new file mode 100644 index 0000000..1b5cfec --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/constraints.lua @@ -0,0 +1,271 @@ +-- Note: the code of this module is borrowed from the original LuaDist project + + + +--- LuaDist version constraints functions +-- Peter Drahoš, LuaDist Project, 2010 +-- Original Code borrowed from LuaRocks Project + +--- Version constraints handling functions. +-- Dependencies are represented in LuaDist through strings with +-- a dist name followed by a comma-separated list of constraints. +-- Each constraint consists of an operator and a version number. +-- In this string format, version numbers are represented as +-- naturally as possible, like they are used by upstream projects +-- (e.g. "2.0beta3"). Internally, LuaDist converts them to a purely +-- numeric representation, allowing comparison following some +-- "common sense" heuristics. The precise specification of the +-- comparison criteria is the source code of this module, but the +-- test/test_deps.lua file included with LuaDist provides some +-- insights on what these criteria are. + +module ("dist.constraints", package.seeall) + + +local operators = { + ["=="] = "==", + ["~="] = "~=", + [">"] = ">", + ["<"] = "<", + [">="] = ">=", + ["<="] = "<=", + ["~>"] = "~>", + -- plus some convenience translations + [""] = "==", + ["-"] = "==", + ["="] = "==", + ["!="] = "~=" +} + +local deltas = { + scm = -100, + rc = -1000, + pre = -10000, + beta = -100000, + alpha = -1000000, + work = -10000000, +} + +local version_mt = { + --- Equality comparison for versions. + -- All version numbers must be equal. + -- If both versions have revision numbers, they must be equal; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if they are considered equivalent. + __eq = function(v1, v2) + if #v1 ~= #v2 then + return false + end + for i = 1, #v1 do + if v1[i] ~= v2[i] then + return false + end + end + if v1.revision and v2.revision then + return (v1.revision == v2.revision) + end + return true + end, + --- Size comparison for versions. + -- All version numbers are compared. + -- If both versions have revision numbers, they are compared; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if v1 is considered lower than v2. + __lt = function(v1, v2) + for i = 1, math.max(#v1, #v2) do + local v1i, v2i = v1[i] or 0, v2[i] or 0 + if v1i ~= v2i then + return (v1i < v2i) + end + end + if v1.revision and v2.revision then + return (v1.revision < v2.revision) + end + return false + end +} + +local version_cache = {} +setmetatable(version_cache, { + __mode = "kv" +}) + +--- Parse a version string, converting to table format. +-- A version table contains all components of the version string +-- converted to numeric format, stored in the array part of the table. +-- If the version contains a revision, it is stored numerically +-- in the 'revision' field. The original string representation of +-- the string is preserved in the 'string' field. +-- Returned version tables use a metatable +-- allowing later comparison through relational operators. +-- @param vstring string: A version number in string format. +-- @return table or nil: A version table or nil +-- if the input string contains invalid characters. +function parseVersion(vstring) + if not vstring then return nil end + assert(type(vstring) == "string") + + local cached = version_cache[vstring] + if cached then + return cached + end + + local version = {} + local i = 1 + + local function add_token(number) + version[i] = version[i] and version[i] + number/100000 or number + i = i + 1 + end + + -- trim leading and trailing spaces + vstring = vstring:match("^%s*(.*)%s*$") + version.string = vstring + -- store revision separately if any + local main, revision = vstring:match("(.*)%-(%d+)$") + if revision then + vstring = main + version.revision = tonumber(revision) + end + while #vstring > 0 do + -- extract a number + local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") + if token then + add_token(tonumber(token)) + else + -- extract a word + token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") + if not token then + return nil + end + local last = #version + version[i] = deltas[token] or (token:byte() / 1000) + end + vstring = rest + end + setmetatable(version, version_mt) + version_cache[vstring] = version + return version +end + +--- Utility function to compare version numbers given as strings. +-- @param a string: one version. +-- @param b string: another version. +-- @return boolean: True if a > b. +function compareVersions(a, b) + return parseVersion(a) > parseVersion(b) +end + +--- Consumes a constraint from a string, converting it to table format. +-- For example, a string ">= 1.0, > 2.0" is converted to a table in the +-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned +-- back to the caller. +-- @param input string: A list of constraints in string format. +-- @return (table, string) or nil: A table representing the same +-- constraints and the string with the unused input, or nil if the +-- input string is invalid. +local function parseConstraint(input) + assert(type(input) == "string") + + local op, version, rest = input:match("^([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") + op = operators[op] + version = parseVersion(version) + if not op or not version then return nil end + return { op = op, version = version }, rest +end + +--- Convert a list of constraints from string to table format. +-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format +-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}. +-- Version tables use a metatable allowing later comparison through +-- relational operators. +-- @param input string: A list of constraints in string format. +-- @return table or nil: A table representing the same constraints, +-- or nil if the input string is invalid. +function parseConstraints(input) + assert(type(input) == "string") + + local constraints, constraint = {}, nil + while #input > 0 do + constraint, input = parseConstraint(input) + if constraint then + table.insert(constraints, constraint) + else + return nil + end + end + return constraints +end + +--- A more lenient check for equivalence between versions. +-- This returns true if the requested components of a version +-- match and ignore the ones that were not given. For example, +-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. +-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" +-- doesn't. +-- @param version string or table: Version to be tested; may be +-- in string format or already parsed into a table. +-- @param requested string or table: Version requested; may be +-- in string format or already parsed into a table. +-- @return boolean: True if the tested version matches the requested +-- version, false otherwise. +local function partialMatch(version, requested) + assert(type(version) == "string" or type(version) == "table") + assert(type(requested) == "string" or type(version) == "table") + + if type(version) ~= "table" then version = parseVersion(version) end + if type(requested) ~= "table" then requested = parseVersion(requested) end + if not version or not requested then return false end + + for i = 1, #requested do + if requested[i] ~= version[i] then return false end + end + if requested.revision then + return requested.revision == version.revision + end + return true +end + +--- Check if a version satisfies a set of constraints. +-- @param version table: A version in table format +-- @param constraints table: An array of constraints in table format. +-- @return boolean: True if version satisfies all constraints, +-- false otherwise. +function matchConstraints(version, constraints) + assert(type(version) == "table") + assert(type(constraints) == "table") + local ok = true + setmetatable(version, version_mt) + for _, constr in pairs(constraints) do + local constr_version = constr.version + setmetatable(constr.version, version_mt) + if constr.op == "==" then ok = version == constr_version + elseif constr.op == "~=" then ok = version ~= constr_version + elseif constr.op == ">" then ok = version > constr_version + elseif constr.op == "<" then ok = version < constr_version + elseif constr.op == ">=" then ok = version >= constr_version + elseif constr.op == "<=" then ok = version <= constr_version + elseif constr.op == "~>" then ok = partialMatch(version, constr_version) + end + if not ok then break end + end + return ok +end + +--- Check if a version string is satisfied by a constraint string. +-- @param version string: A version in string format +-- @param constraints string: Constraints in string format. +-- @return boolean: True if version satisfies all constraints, +-- false otherwise. +function constraint_satisfied(version, constraints) + local const = parseConstraints(constraints) + local ver = parseVersion(version) + if const and ver then + return matchConstraints(ver, const) + end + return nil, "Error parsing versions." +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/depends.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/depends.lua new file mode 100644 index 0000000..2a6b7f0 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/depends.lua @@ -0,0 +1,770 @@ +-- Utility functions for dependencies + +module ("dist.depends", package.seeall) + +local cfg = require "dist.config" +local mf = require "dist.manifest" +local sys = require "dist.sys" +local const = require "dist.constraints" +local utils = require "dist.utils" +local package = require "dist.package" + +-- Return all packages with specified names from manifest. +-- Names can also contain version constraint (e.g. 'copas>=1.2.3', 'saci-1.0' etc.). +function find_packages(package_names, manifest) + if type(package_names) == "string" then package_names = {package_names} end + manifest = manifest or mf.get_manifest() + assert(type(package_names) == "table", "depends.find_packages: Argument 'package_names' is not a table or string.") + assert(type(manifest) == "table", "depends.find_packages: Argument 'manifest' is not a table.") + + local packages_found = {} + -- find matching packages in manifest + for _, pkg_to_find in pairs(package_names) do + local pkg_name, pkg_constraint = split_name_constraint(pkg_to_find) + pkg_name = utils.escape_magic(pkg_name):gsub("%%%*",".*") + for _, repo_pkg in pairs(manifest) do + if string.match(repo_pkg.name, "^" .. pkg_name .. "$") and (not pkg_constraint or satisfies_constraint(repo_pkg.version, pkg_constraint)) then + table.insert(packages_found, repo_pkg) + end + end + end + return packages_found +end + +-- Return manifest consisting of packages installed in specified deploy_dir directory +function get_installed(deploy_dir) + deploy_dir = deploy_dir or cfg.root_dir + assert(type(deploy_dir) == "string", "depends.get_installed: Argument 'deploy_dir' is not a string.") + deploy_dir = sys.abs_path(deploy_dir) + + local distinfos_path = sys.make_path(deploy_dir, cfg.distinfos_dir) + local manifest = {} + + if not sys.is_dir(distinfos_path) then return {} end + + -- from all directories of packages installed in deploy_dir + for dir in sys.get_directory(distinfos_path) do + + if dir ~= "." and dir ~= ".." and sys.is_dir(sys.make_path(distinfos_path, dir)) then + local pkg_dist_dir = sys.make_path(distinfos_path, dir) + + -- load the dist.info file + for file in sys.get_directory(pkg_dist_dir) do + local pkg_dist_file = sys.make_path(pkg_dist_dir, file) + + if sys.is_file(pkg_dist_file) then + table.insert(manifest, mf.load_distinfo(pkg_dist_file)) + end + end + + end + + end + return manifest +end + +-- If 'pkg.selected' == true then returns 'selected' else 'installed'. +-- Used in error messages. +local function selected_or_installed(pkg) + assert(type(pkg) == "table", "depends.selected_or_installed: Argument 'pkg' is not a table.") + if pkg.selected == true then + return "selected" + else + return "installed" + end +end + +-- Return whether the 'package_name' is installed according to the the manifest 'installed_pkgs' +-- If optional 'version_wanted' constraint is specified, then installed packages must +-- also satisfy specified version constraint. +-- If package is installed but doesn't satisfy version constraint, error message +-- is returned as the second value. +function is_installed(package_name, installed_pkgs, version_wanted) + assert(type(package_name) == "string", "depends.is_installed: Argument 'package_name' is not a string.") + assert(type(installed_pkgs) == "table", "depends.is_installed: Argument 'installed_pkgs' is not a table.") + assert(type(version_wanted) == "string" or type(version_wanted) == "nil", "depends.is_installed: Argument 'version_wanted' is not a string or nil.") + + local pkg_is_installed, err = false, nil + + for _, installed_pkg in pairs(installed_pkgs) do + + -- check if package_name is in installed + if package_name == installed_pkg.name then + + -- check if package is installed in satisfying version + if not version_wanted or satisfies_constraint(installed_pkg.version, version_wanted) then + pkg_is_installed = true + break + else + err = "Package '" .. package_name .. (version_wanted and " " .. version_wanted or "") .. "' needed, but " .. selected_or_installed(installed_pkg) .. " at version '" .. installed_pkg.version .. "'." + break + end + end + + end + return pkg_is_installed, err +end + +-- Check whether the package 'pkg' conflicts with 'installed_pkg' and return +-- false or error message. +local function packages_conflicts(pkg, installed_pkg) + assert(type(pkg) == "table", "depends.packages_conflicts: Argument 'pkg' is not a table.") + assert(type(installed_pkg) == "table", "depends.packages_conflicts: Argument 'installed_pkg' is not a table.") + + -- check if pkg doesn't provide an already installed_pkg + if pkg.provides then + -- for all of pkg's provides + for _, provided_pkg in pairs(get_provides(pkg)) do + if provided_pkg.name == installed_pkg.name then + return "Package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "' provides '" .. pkg_full_name(provided_pkg.name, provided_pkg.version) .. "' but package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' is already " .. selected_or_installed(installed_pkg) .. "." + end + end + end + + -- check for conflicts of package to install with installed package + if pkg.conflicts then + for _, conflict in pairs (pkg.conflicts) do + if conflict == installed_pkg.name then + return "Package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "' conflicts with already " .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "'." + end + end + end + + -- check for conflicts of installed package with package to install + if installed_pkg.conflicts then + + -- direct conflicts with 'pkg' + for _, conflict in pairs (installed_pkg.conflicts) do + if conflict == pkg.name then + return "Already " .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' conflicts with package '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "'." + end + end + + -- conflicts with 'provides' of 'pkg' (packages provided by package to install) + if pkg.provides then + for _, conflict in pairs (installed_pkg.conflicts) do + -- for all of pkg's provides + for _, provided_pkg in pairs(get_provides(pkg)) do + if conflict == provided_pkg.name then + return "Already '" .. selected_or_installed(installed_pkg) .. " package '" .. pkg_full_name(installed_pkg.name, installed_pkg.version) .. "' conflicts with package '" .. pkg_full_name(provided_pkg.name, provided_pkg.version) .. "' provided by '" .. pkg_full_name(pkg.name, pkg.version, pkg.was_scm_version) .. "'." + end + end + end + end + end + + -- no conflicts found + return false +end + +-- Return table of package dependencies 'depends' with OS specific dependencies extracted. +-- +-- OS specific dependencies are stored in a subtable with 'arch' as a key. +-- E.g. this table containing OS specific dependencies: +-- depends = { +-- "lua~>5.1", +-- "luadist-git>=0.1", +-- Linux = { +-- "iup>=3.6", +-- "wxlua>=2.8.10.0", +-- }, +-- Windows = { +-- "luagd>=2.0.33r2", +-- "luacom>=1.4.1", +-- }, +-- } +-- +-- ...will be on the 'Linux' architecture (determined by cfg.arch) converted into: +-- depends = { +-- "lua~>5.1", +-- "luadist-git>=0.1", +-- "iup>=3.6", +-- "wxlua>=2.8.10.0", +-- } +function extract_os_specific_depends(depends) + assert(type(depends) == "table", "depends.extract_os_specific_depends: Argument 'depends' is not a table.") + local extracted = {} + for k, depend in pairs(depends) do + -- if 'depend' is a table, then it must be a table of OS specific + -- dependencies, so extract it if it's for this architecture + if type(depend) == "table" then + if k == cfg.arch then + for _, os_specific_depend in pairs(depend) do + table.insert(extracted, os_specific_depend) + end + end + else + table.insert(extracted, depend) + end + end + return extracted +end + +-- Return all packages needed in order to install package 'pkg' +-- and with specified 'installed' packages in the system using 'manifest'. +-- 'pkg' can also contain version constraint (e.g. 'copas>=1.2.3', 'saci-1.0' etc.). +-- +-- This function also downloads packages to get information about their dependencies. +-- Directory where the package was downloaded is stored in 'download_dir' attribute +-- of that package in the table of packages returned by this function. +-- +-- Optional argument 'dependency_manifest' is a table of dependencies examined +-- from previous installations etc. It can be used to speed-up the dependency +-- resolving procedure for example. +-- +-- When optional 'force_no_download' parameter is set to true, then information +-- about packages won't be downloaded during dependency resolving, assuming that +-- entries in the provided manifest are already complete. +-- +-- When optional 'suppress_printing' parameter is set to true, then messages +-- for the user won't be printed during dependency resolving. +-- +-- Optional argument 'deploy_dir' is used just as a temporary place to place +-- the downloaded packages into. +-- +-- 'dependency_parents' is table of all packages encountered so far when resolving dependencies +-- and is used to detect and deal with circular dependencies. Leave it 'nil' +-- and it will do its job just fine :-). +-- +-- 'tmp_installed' is internal table used in recursion and should be left 'nil' when +-- calling this function from other context. It is used for passing the changes +-- in installed packages between the recursive calls of this function. +-- +-- TODO: refactor this spaghetti code! +local function get_packages_to_install(pkg, installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir, dependency_parents, tmp_installed) + manifest = manifest or mf.get_manifest() + dependency_manifest = dependency_manifest or {} + force_no_download = force_no_download or false + suppress_printing = suppress_printing or false + deploy_dir = deploy_dir or cfg.root_dir + dependency_parents = dependency_parents or {} + + -- set helper table 'tmp_installed' + tmp_installed = tmp_installed or utils.deepcopy(installed) + + assert(type(pkg) == "string", "depends.get_packages_to_install: Argument 'pkg' is not a string.") + assert(type(installed) == "table", "depends.get_packages_to_install: Argument 'installed' is not a table.") + assert(type(manifest) == "table", "depends.get_packages_to_install: Argument 'manifest' is not a table.") + assert(type(dependency_manifest) == "table", "depends.get_packages_to_install: Argument 'dependency_manifest' is not a table.") + assert(type(force_no_download) == "boolean", "depends.get_packages_to_install: Argument 'force_no_download' is not a boolean.") + assert(type(suppress_printing) == "boolean", "depends.get_packages_to_install: Argument 'suppress_printing' is not a boolean.") + assert(type(deploy_dir) == "string", "depends.get_packages_to_install: Argument 'deploy_dir' is not a string.") + assert(type(dependency_parents) == "table", "depends.get_packages_to_install: Argument 'dependency_parents' is not a table.") + assert(type(tmp_installed) == "table", "depends.get_packages_to_install: Argument 'tmp_installed' is not a table.") + deploy_dir = sys.abs_path(deploy_dir) + + --[[ for future debugging: + print('resolving: '.. pkg) + print(' installed: ', utils.table_tostring(installed)) + print(' tmp_installed: ', utils.table_tostring(tmp_installed)) + --]] + + -- check if package is already installed + local pkg_name, pkg_constraint = split_name_constraint(pkg) + local pkg_is_installed, err = is_installed(pkg_name, tmp_installed, pkg_constraint) + if pkg_is_installed then return {} end + if err then return nil, err end + + -- table of packages needed to be installed (will be returned) + local to_install = {} + + -- find out available versions of 'pkg' and insert them into manifest + if not force_no_download then + local versions, err = package.retrieve_versions(pkg, manifest, suppress_printing) + if not versions then return nil, err end + for _, version in pairs(versions) do + table.insert(manifest, version) + end + end + + -- find candidates & sort them + local candidates_to_install = find_packages(pkg, manifest) + if #candidates_to_install == 0 then + return nil, "No suitable candidate for '" .. pkg .. "' found." + end + candidates_to_install = sort_by_versions(candidates_to_install) + + for _, pkg in pairs(candidates_to_install) do + + --[[ for future debugging: + print(' candidate: '.. pkg.name..'-'..pkg.version) + print(' installed: ', utils.table_tostring(installed)) + print(' tmp_installed: ', utils.table_tostring(tmp_installed)) + print(' to_install: ', utils.table_tostring(to_install)) + print(' -is installed: ', is_installed(pkg.name, tmp_installed, pkg_constraint)) + --]] + + -- if there's an error from the previous candidate, print the reason for trying another one + if not suppress_printing and err then print(" - trying another candidate due to: " .. err) end + + -- clear the state from the previous candidate + pkg_is_installed, err = false, nil + + -- check whether this package has already been added to 'tmp_installed' by another of its candidates + pkg_is_installed, err = is_installed(pkg.name, tmp_installed, pkg_constraint) + if pkg_is_installed then break end + + -- preserve information about the 'scm' version, because pkg.version + -- will be rewritten by information taken from pkg's dist.info file + local was_scm_version = (pkg.version == "scm") + + -- Try to obtain cached dependency information from the dependency manifest + if dependency_manifest[pkg.name .. "-" .. pkg.version] and cfg.dep_cache then + pkg = dependency_manifest[pkg.name .. "-" .. pkg.version] + else + -- download info about the package if not already downloaded and downloading not prohibited + if not (pkg.download_dir or force_no_download) then + local path_or_err + pkg, path_or_err = package.retrieve_pkg_info(pkg, deploy_dir, suppress_printing) + if not pkg then + err = "Error when resolving dependencies: " .. path_or_err + else + -- set path to downloaded package - used to indicate that the + -- package was already downloaded, to delete unused but downloaded + -- packages and also to install choosen packages + pkg.download_dir = path_or_err + end + end + end + + if pkg and was_scm_version then pkg.was_scm_version = true end + + -- check arch & type + if not err then + if not (pkg.arch == "Universal" or pkg.arch == cfg.arch) or + not (pkg.type == "all" or pkg.type == "source" or pkg.type == cfg.type) then + err = "Package '" .. pkg_full_name(pkg.name, pkg.version) .. "' doesn't have required arch and type." + end + end + + -- checks for conflicts with other installed (or previously selected) packages + if not err then + for _, installed_pkg in pairs(tmp_installed) do + err = packages_conflicts(pkg, installed_pkg) + if err then break end + end + end + + -- if pkg passed all of the above tests + if not err then + + -- check if pkg's dependencies are satisfied + if pkg.depends then + + -- insert pkg into the stack of circular dependencies detection + table.insert(dependency_parents, pkg.name) + + -- extract all OS specific dependencies of pkg + pkg.depends = extract_os_specific_depends(pkg.depends) + + -- for all dependencies of pkg + for _, depend in pairs(pkg.depends) do + local dep_name = split_name_constraint(depend) + + -- detect circular dependencies using 'dependency_parents' + local is_circular_dependency = false + for _, parent in pairs(dependency_parents) do + if dep_name == parent then + is_circular_dependency = true + break + end + end + + -- if circular dependencies not detected + if not is_circular_dependency then + + -- recursively call this function on the candidates of this pkg's dependency + local depends_to_install, dep_err = get_packages_to_install(depend, installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir, dependency_parents, tmp_installed) + + -- if any suitable dependency packages were found, insert them to the 'to_install' table + if depends_to_install then + for _, depend_to_install in pairs(depends_to_install) do + + -- add some meta information + if not depend_to_install.selected_by then + depend_to_install.selected_by = pkg.name .. "-" .. pkg.version + end + + table.insert(to_install, depend_to_install) + table.insert(tmp_installed, depend_to_install) + table.insert(installed, depend_to_install) + end + else + err = "Error getting dependency of '" .. pkg_full_name(pkg.name, pkg.version) .. "': " .. dep_err + break + end + + -- if circular dependencies detected + else + err = "Error getting dependency of '" .. pkg_full_name(pkg.name, pkg.version) .. "': '" .. dep_name .. "' is a circular dependency." + break + end + end + + -- remove last package from the stack of circular dependencies detection + table.remove(dependency_parents) + end + + -- if no error occured + if not err then + -- add pkg and it's provides to the fake table of installed packages, with + -- property 'selected' set, indicating that the package isn't + -- really installed in the system, just selected to be installed (this is used e.g. in error messages) + pkg.selected = true + table.insert(tmp_installed, pkg) + if pkg.provides then + for _, provided_pkg in pairs(get_provides(pkg)) do + provided_pkg.selected = true + table.insert(tmp_installed, provided_pkg) + end + end + -- add pkg to the table of packages to install + table.insert(to_install, pkg) + + -- if some error occured + else + -- delete the downloaded package + if pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end + + -- set tables of 'packages to install' and 'installed packages' to their original state + + to_install = {} + tmp_installed = utils.deepcopy(installed) + -- add provided packages to installed ones + for _, installed_pkg in pairs(tmp_installed) do + for _, pkg in pairs(get_provides(installed_pkg)) do + table.insert(tmp_installed, pkg) + end + end + end + + -- if error occured + else + -- delete the downloaded package + if pkg and pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end + + -- if pkg is already installed, skip checking its other candidates + if pkg_is_installed then break end + end + end + + -- if package is not installed and no suitable candidates were found, return the last error + if #to_install == 0 and not pkg_is_installed then + return nil, err + else + return to_install + end +end + +-- Resolve dependencies and return all packages needed in order to install +-- 'packages' into the system with already 'installed' packages, using 'manifest'. +-- Also return the table of the dependencies determined during the process +-- as the second return value. +-- +-- Optional argument 'dependency_manifest' is a table of dependencies examined +-- from previous installations etc. It can be used to speed-up the dependency +-- resolving procedure for example. +-- +-- Optional argument 'deploy_dir' is used as a temporary place to place the +-- downloaded packages into. +-- +-- When optional 'force_no_download' parameter is set to true, then information +-- about packages won't be downloaded during dependency resolving, assuming that +-- entries in manifest are complete. +-- +-- When optional 'suppress_printing' parameter is set to true, then messages +-- for the user won't be printed during dependency resolving. +function get_depends(packages, installed, manifest, dependency_manifest, deploy_dir, force_no_download, suppress_printing) + if not packages then return {} end + manifest = manifest or mf.get_manifest() + dependency_manifest = dependency_manifest or {} + deploy_dir = deploy_dir or cfg.root_dir + force_no_download = force_no_download or false + suppress_printing = suppress_printing or false + if type(packages) == "string" then packages = {packages} end + + assert(type(packages) == "table", "depends.get_depends: Argument 'packages' is not a table or string.") + assert(type(installed) == "table", "depends.get_depends: Argument 'installed' is not a table.") + assert(type(manifest) == "table", "depends.get_depends: Argument 'manifest' is not a table.") + assert(type(dependency_manifest) == "table", "depends.get_depends: Argument 'dependency_manifest' is not a table.") + assert(type(deploy_dir) == "string", "depends.get_depends: Argument 'deploy_dir' is not a string.") + assert(type(force_no_download) == "boolean", "depends.get_depends: Argument 'force_no_download' is not a boolean.") + assert(type(suppress_printing) == "boolean", "depends.get_depends: Argument 'suppress_printing' is not a boolean.") + deploy_dir = sys.abs_path(deploy_dir) + + local tmp_installed = utils.deepcopy(installed) + + -- add provided packages to installed ones + for _, installed_pkg in pairs(tmp_installed) do + for _, pkg in pairs(get_provides(installed_pkg)) do + table.insert(tmp_installed, pkg) + end + end + + -- If 'pkg' contains valid (architecture specific) path separator, + -- it is treated like a path to already downloaded package and + -- we assume that user wants to use this specific version of the + -- module to be installed. Hence, we will add information about + -- this version into the manifest and also remove references to + -- any other versions of this module from the manifest. This will + -- enforce the version of the module required by the user. + for k, pkg in pairs(packages) do + if pkg:find(sys.path_separator()) then + local pkg_dir = sys.abs_path(pkg) + local pkg_info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info")) + if not pkg_info then return nil, err end + + -- add information about location of the package, also to prevent downloading it again + pkg_info.download_dir = pkg_dir + -- mark package to skip deleting its directory after installation + pkg_info.preserve_pkg_dir = true + + -- set default arch/type if not explicitly stated and package is of source type + if package.is_source_type(pkg_dir) then + pkg_info = package.ensure_source_arch_and_type(pkg_info) + elseif not (pkg_info.arch and pkg_info.type) then + return nil, pkg_dir .. ": binary package missing arch or type in 'dist.info'." + end + + -- update manifest + manifest = utils.filter(manifest, function(p) return p.name ~= pkg_info.name and true end) + table.insert(manifest, pkg_info) + + -- update packages to install + pkg = pkg_info.name .. "-" .. pkg_info.version + packages[k] = pkg + end + end + + local to_install = {} + + -- get packages needed to satisfy the dependencies + for _, pkg in pairs(packages) do + + local needed_to_install, err = get_packages_to_install(pkg, tmp_installed, manifest, dependency_manifest, force_no_download, suppress_printing, deploy_dir) + + -- if everything's fine + if needed_to_install then + + for _, needed_pkg in pairs(needed_to_install) do + + -- TODO: why not to use 'installed' instead of 'tmp_installed'? + -- It's because provides aren't searched for by find() + -- function inside the update_dependency_manifest(). + dependency_manifest = update_dependency_manifest(needed_pkg, tmp_installed, needed_to_install, dependency_manifest) + + table.insert(to_install, needed_pkg) + table.insert(tmp_installed, needed_pkg) + -- add provides of needed_pkg to installed ones + for _, provided_pkg in pairs(get_provides(needed_pkg)) do + -- copy 'selected' property + provided_pkg.selected = needed_pkg.selected + table.insert(tmp_installed, provided_pkg) + end + end + -- if error occured + else + -- delete already downloaded packages + for _, pkg in pairs(to_install) do + if pkg.download_dir and not cfg.debug then sys.delete(pkg.download_dir) end + end + return nil, "Cannot resolve dependencies for '" .. pkg .. "': ".. err + end + end + + return to_install, dependency_manifest +end + +-- Return table of packages provided by specified package (from it's 'provides' field) +function get_provides(package) + assert(type(package) == "table", "depends.get_provides: Argument 'package' is not a table.") + if not package.provides then return {} end + + local provided = {} + for _, provided_name in pairs(package.provides) do + local pkg = {} + pkg.name, pkg.version = split_name_constraint(provided_name) + pkg.type = package.type + pkg.arch = package.arch + pkg.provided = package.name .. "-" .. package.version + table.insert(provided, pkg) + end + return provided +end + +-- Return package name and version constraint from full package version constraint specification +-- E. g.: +-- for 'luaexpat-1.2.3' return: 'luaexpat' , '1.2.3' +-- for 'luajit >= 1.2' return: 'luajit' , '>=1.2' +function split_name_constraint(version_constraint) + assert(type(version_constraint) == "string", "depends.split_name_constraint: Argument 'version_constraint' is not a string.") + + local split = version_constraint:find("[%s=~<>-]+%d") or version_constraint:find("[%s=~<>-]+scm") + + if split then + return version_constraint:sub(1, split - 1), version_constraint:sub(split):gsub("[%s-]", "") + else + return version_constraint, nil + end +end + +-- Return only packages that can be installed on the specified architecture and type +function filter_packages_by_arch_and_type(packages, req_arch, req_type) + assert(type(packages) == "table", "depends.filter_packages_by_arch_and_type: Argument 'packages' is not a table.") + assert(type(req_arch) == "string", "depends.filter_packages_by_arch_and_type: Argument 'req_arch' is not a string.") + assert(type(req_type) == "string", "depends.filter_packages_by_arch_and_type: Argument 'pkg_type' is not a string.") + + return utils.filter(packages, + function (pkg) + return (pkg.arch == "Universal" or pkg.arch == req_arch) and + (pkg.type == "all" or pkg.type == "source" or pkg.type == req_type) + end) +end + +-- Return only packages that contain one of the specified strings in their 'name-version'. +-- Case is ignored. If no strings are specified, return all the packages. +-- Argument 'search_in_desc' specifies if search also in description of packages. +function filter_packages_by_strings(packages, strings, search_in_desc) + if type(strings) == "string" then strings = {strings} end + assert(type(packages) == "table", "depends.filter_packages_by_strings: Argument 'packages' is not a table.") + assert(type(strings) == "table", "depends.filter_packages_by_strings: Argument 'strings' is not a string or table.") + + if #strings ~= 0 then + return utils.filter(packages, + function (pkg) + for _,str in pairs(strings) do + local name = pkg.name .. "-" .. pkg.version + if search_in_desc then + name = name .. " " .. (pkg.desc or "") + end + if string.find(string.lower(name), string.lower(str), 1 ,true) ~= nil then return true end + end + end) + else + return packages + end +end + + +-- Return full package name and version string (e.g. 'luajit-2.0'). When version +-- is nil or '' then return only name (e.g. 'luajit') and when name is nil or '' +-- then return '<unknown>'. Optional 'was_scm_version' argument is a boolean, +-- stating whether the package was originally selected for installation as a 'scm' version. +function pkg_full_name(name, version, was_scm_version) + name = name or "" + version = version or "" + was_scm_version = was_scm_version or false + if type(version) == "number" then version = tostring(version) end + + assert(type(name) == "string", "depends.pkg_full_name: Argument 'name' is not a string.") + assert(type(version) == "string", "depends.pkg_full_name: Argument 'version' is not a string.") + + if was_scm_version then version = version .. " [scm version]" end + + if name == "" then + return "<unknown>" + else + return name .. ((version ~= "") and "-" .. version or "") + end +end + +-- Return table of packages, sorted descendingly by versions (newer ones are moved to the top). +function sort_by_versions(packages) + assert(type(packages) == "table", "depends.sort_by_versions: Argument 'packages' is not a table.") + return utils.sort(packages, function (a, b) return compare_versions(a.version, b.version) end) +end + +-- Return table of packages, sorted alphabetically by name and then descendingly by version. +function sort_by_names(packages) + assert(type(packages) == "table", "depends.sort_by_names: Argument 'packages' is not a table.") + return utils.sort(packages, function (a, b) + if a.name == b.name then + return compare_versions(a.version, b.version) + else + return a.name < b.name + end + end) +end + +-- Return if version satisfies the specified constraint +function satisfies_constraint(version, constraint) + assert(type(version) == "string", "depends.satisfies_constraint: Argument 'version' is not a string.") + assert(type(constraint) == "string", "depends.satisfies_constraint: Argument 'constraint' is not a string.") + return const.constraint_satisfied(version, constraint) +end + +-- For package versions, return whether: 'version_a' > 'version_b' +function compare_versions(version_a, version_b) + assert(type(version_a) == "string", "depends.compare_versions: Argument 'version_a' is not a string.") + assert(type(version_b) == "string", "depends.compare_versions: Argument 'version_b' is not a string.") + return const.compareVersions(version_a, version_b) +end + +-- Returns 'dep_manifest' updated with information about the 'pkg'. +-- 'installed' is table with installed packages +-- 'to_install' is table with packages that are selected for installation +-- Packages satisfying the dependencies will be searched for in these two tables. +function update_dependency_manifest(pkg, installed, to_install, dep_manifest) + dep_manifest = dep_manifest or {} + assert(type(pkg) == "table", "depends.update_dependency_manifest: Argument 'pkg' is not a table.") + assert(type(installed) == "table", "depends.update_dependency_manifest: Argument 'installed' is not a table.") + assert(type(to_install) == "table", "depends.update_dependency_manifest: Argument 'to_install' is not a table.") + assert(type(dep_manifest) == "table", "depends.update_dependency_manifest: Argument 'dep_manifest' is not a table.") + + local name_ver = pkg.name .. "-" .. (pkg.was_scm_version and "scm" or pkg.version) + + -- add to manifest + if not dep_manifest[name_ver] then + dep_manifest[name_ver] = {} + dep_manifest[name_ver].name = pkg.name + dep_manifest[name_ver].version = pkg.version + dep_manifest[name_ver].was_scm_version = pkg.was_scm_version + dep_manifest[name_ver].arch = pkg.arch + dep_manifest[name_ver].type = pkg.type + dep_manifest[name_ver].path = pkg.path + dep_manifest[name_ver].depends = pkg.depends + dep_manifest[name_ver].conflicts = pkg.conflicts + dep_manifest[name_ver].provides = pkg.provides + dep_manifest[name_ver].license = pkg.license + dep_manifest[name_ver].desc = pkg.desc + dep_manifest[name_ver].url = pkg.url + dep_manifest[name_ver].author = pkg.author + dep_manifest[name_ver].maintainer = pkg.maintainer + + -- add information which dependency is satisfied by which package + if pkg.depends then + + -- TODO: Won't it be better to add OS-specific 'satisfied_by' metadata in a format like OS-specific 'depends' ? + local all_deps = extract_os_specific_depends(pkg.depends) + + dep_manifest[name_ver].satisfied_by = {} + for _, depend in pairs(all_deps) do + + -- find package satisfying the dependency + local satisfying = find_packages(depend, installed)[1] or find_packages(depend, to_install)[1] + satisfying = satisfying.name .. "-" .. satisfying.version + dep_manifest[name_ver].satisfied_by[depend] = satisfying + + -- check whether the satisfying package isn't provided by other one + local provided_by = utils.filter(installed, function(pkg) + return pkg.provides and utils.contains(pkg.provides, satisfying) + end) + if #provided_by == 0 then + provided_by = utils.filter(to_install, function(pkg) + return pkg.provides and utils.contains(pkg.provides, satisfying) + end) + end + + if #provided_by ~= 0 then + if not dep_manifest[name_ver].satisfying_provided_by then + dep_manifest[name_ver].satisfying_provided_by = {} + end + dep_manifest[name_ver].satisfying_provided_by[satisfying] = provided_by[1].name .. "-" .. provided_by[1].version + end + end + + end + end + + return dep_manifest +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/git.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/git.lua new file mode 100644 index 0000000..808f74d --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/git.lua @@ -0,0 +1,306 @@ +-- Encapsulated Git functionality + +module ("dist.git", package.seeall) + +require "git" +local sys = require "dist.sys" +local cfg = require "dist.config" + + +-- Clone the repository from url to dest_dir +function clone(repository_url, dest_dir, depth, branch) + assert(type(repository_url) == "string", "git.clone: Argument 'repository_url' is not a string.") + assert(type(dest_dir) == "string", "git.clone: Argument 'dest_dir' is not a string.") + dest_dir = sys.abs_path(dest_dir) + + local command = "git clone " .. repository_url + + if depth then + assert(type(depth) == "number", "git.clone: Argument 'depth' is not a number.") + command = command .. " --depth " .. depth + end + + if branch then + assert(type(branch) == "string", "git.clone: Argument 'branch' is not a string.") + command = command .. " -b " .. branch + end + + command = command .. " " .. sys.quote(dest_dir) + if sys.exists(dest_dir) then sys.delete(dest_dir) end + sys.make_dir(dest_dir) + + -- change the current working directory to dest_dir + local prev_current_dir = sys.current_dir() + sys.change_dir(dest_dir) + + -- execute git clone + if not cfg.debug then command = command .. " -q " end + local ok, err = sys.exec(command) + + -- change the current working directory back + sys.change_dir(prev_current_dir) + + return ok, err +end + +-- Return table of all refs of the remote repository at the 'git_url'. Ref_type can be "tags" or "heads". +local function get_remote_refs(git_url, ref_type) + assert(type(git_url) == "string", "git.get_remote_refs: Argument 'git_url' is not a string.") + assert(type(ref_type) == "string", "git.get_remote_refs: Argument 'ref_type' is not a string.") + assert(ref_type == "tags" or ref_type == "heads", "git.get_remote_refs: Argument 'ref_type' is not \"tags\" or \"heads\".") + + local refs = {} + + local ok, refs_or_err = pcall(git.protocol.remotes, git_url) + if not ok then return nil, "Error getting refs of the remote repository '" .. git_url .. "': " .. refs_or_err end + + for ref, sha in pairs(refs_or_err) do + if ref:match("%S+/" .. ref_type .. "/%S+") and not ref:match("%^{}") then + table.insert(refs, ref:match("%S+/" .. ref_type .. "/(%S+)")) + end + end + + return refs +end + +-- Return table of all tags of the repository at the 'git_url' +function get_remote_tags(git_url) + return get_remote_refs(git_url, "tags") +end + +-- Return table of all branches of the repository at the 'git_url' +function get_remote_branches(git_url) + return get_remote_refs(git_url, "heads") +end + +-- Checkout specified ref in specified git_repo_dir +function checkout_ref(ref, git_repo_dir, orphaned) + git_repo_dir = git_repo_dir or sys.current_dir() + orphaned = orphaned or false + assert(type(ref) == "string", "git.checkout_ref: Argument 'ref' is not a string.") + assert(type(git_repo_dir) == "string", "git.checkout_ref: Argument 'git_repo_dir' is not a string.") + assert(type(orphaned) == "boolean", "git.checkout_ref: Argument 'orphaned' is not a boolean.") + git_repo_dir = sys.abs_path(git_repo_dir) + + local command = "git checkout " + if orphaned then command = command .. " --orphan " end + command = command .. " " .. ref .. " -f" + if not cfg.debug then command = command .. " -q " end + + local ok, err + if git_repo_dir ~= sys.current_dir() then + local prev_current_dir = sys.current_dir() + sys.change_dir(git_repo_dir) + ok, err = sys.exec(command) + sys.change_dir(prev_current_dir) + else + ok, err = sys.exec(command) + end + + return ok, err +end + +-- Checkout specified sha in specified git_repo_dir +function checkout_sha(sha, git_repo_dir) + git_repo_dir = git_repo_dir or sys.current_dir() + assert(type(sha) == "string", "git.checkout_sha: Argument 'sha' is not a string.") + assert(type(git_repo_dir) == "string", "git.checkout_sha: Argument 'git_repo_dir' is not a string.") + git_repo_dir = sys.abs_path(git_repo_dir) + + local dir_changed, prev_current_dir + + if git_repo_dir ~= sys.current_dir() then + prev_current_dir = sys.current_dir() + sys.change_dir(git_repo_dir) + dir_changed = true + end + + local ok, repo_or_err = pcall(git.repo.open, git_repo_dir) + if not ok then return nil, "Error when opening the git repository '" .. git_repo_dir .. "': " .. repo_or_err end + + local err + ok, err = pcall(repo_or_err.checkout, repo_or_err, sha, git_repo_dir) + if not ok then return nil, "Error when checking out the sha '" .. sha .. "' in the git repository '" .. git_repo_dir .. "': " .. err end + + repo_or_err:close() + if dir_changed then sys.change_dir(prev_current_dir) end + + return true +end + +-- Create an empty git repository in given directory. +function init(dir) + dir = dir or sys.current_dir() + assert(type(dir) == "string", "git.init: Argument 'dir' is not a string.") + dir = sys.abs_path(dir) + + -- create the 'dir' first, since it causes 'git init' to fail on Windows + -- when the parent directory of 'dir' doesn't exist + local ok, err = sys.make_dir(dir) + if not ok then return nil, err end + + local command = "git init " .. sys.quote(dir) + if not cfg.debug then command = command .. " -q " end + return sys.exec(command) +end + +-- Add all files in the 'repo_dir' to the git index. The 'repo_dir' must be +-- in the initialized git repository. +function add_all(repo_dir) + repo_dir = repo_dir or sys.current_dir() + assert(type(repo_dir) == "string", "git.add_all: Argument 'repo_dir' is not a string.") + repo_dir = sys.abs_path(repo_dir) + + local ok, prev_dir, msg + ok, prev_dir = sys.change_dir(repo_dir); + if not ok then return nil, err end + + ok, msg = sys.exec("git add -A -f " .. sys.quote(repo_dir)) + sys.change_dir(prev_dir) + + return ok, msg +end + +-- Commit all indexed files in 'repo_dir' with the given commit 'message'. +-- The 'repo_dir' must be in the initialized git repository. +function commit(message, repo_dir) + repo_dir = repo_dir or sys.current_dir() + message = message or "commit by luadist-git" + assert(type(message) == "string", "git.commit: Argument 'message' is not a string.") + assert(type(repo_dir) == "string", "git.commit: Argument 'repo_dir' is not a string.") + repo_dir = sys.abs_path(repo_dir) + + local ok, prev_dir, msg + ok, prev_dir = sys.change_dir(repo_dir); + if not ok then return nil, err end + + local command = "git commit -m " .. sys.quote(message) + if not cfg.debug then command = command .. " -q " end + ok, msg = sys.exec(command) + sys.change_dir(prev_dir) + + return ok, msg +end + + +-- Rename branch 'old_name' to 'new_name'. -- The 'repo_dir' must be +-- in the initialized git repository and the branch 'new_name' must +-- not already exist in that repository. +function rename_branch(old_name, new_name, repo_dir) + repo_dir = repo_dir or sys.current_dir() + assert(type(old_name) == "string", "git.rename_branch: Argument 'old_name' is not a string.") + assert(type(new_name) == "string", "git.rename_branch: Argument 'new_name' is not a string.") + assert(type(repo_dir) == "string", "git.rename_branch: Argument 'repo_dir' is not a string.") + repo_dir = sys.abs_path(repo_dir) + + local ok, prev_dir, msg + ok, prev_dir = sys.change_dir(repo_dir); + if not ok then return nil, err end + + ok, msg = sys.exec("git branch -m " .. old_name .. " " .. new_name) + sys.change_dir(prev_dir) + + return ok, msg +end + +-- Push the ref 'ref_name' from the 'repo_dir' to the remote git +-- repository 'git_repo_url'. If 'all_tags' is set to true, all tags +-- will be pushed, in addition to the explicitly given ref. +-- If 'delete' is set to 'true' then the explicitly given remote ref +-- will be deleted, not pushed. +function push_ref(repo_dir, ref_name, git_repo_url, all_tags, delete) + repo_dir = repo_dir or sys.current_dir() + all_tags = all_tags or false + delete = delete or false + assert(type(repo_dir) == "string", "git.push_ref: Argument 'repo_dir' is not a string.") + assert(type(git_repo_url) == "string", "git.push_ref: Argument 'git_repo_url' is not a string.") + assert(type(ref_name) == "string", "git.push_ref: Argument 'ref_name' is not a string.") + assert(type(all_tags) == "boolean", "git.push_ref: Argument 'all_tags' is not a boolean.") + assert(type(delete) == "boolean", "git.push_ref: Argument 'delete' is not a boolean.") + repo_dir = sys.abs_path(repo_dir) + + local ok, prev_dir, msg + ok, prev_dir = sys.change_dir(repo_dir); + if not ok then return nil, err end + + local command = "git push " .. git_repo_url + if all_tags then command = command .. " --tags " end + if delete then command = command .. " --delete " end + command = command .. " " .. ref_name .. " -f " + if not cfg.debug then command = command .. " -q " end + + ok, msg = sys.exec(command) + sys.change_dir(prev_dir) + + return ok, msg +end + +-- Creates the tag 'tag_name' in given 'repo_dir', which must be +-- in the initialized git repository +function create_tag(repo_dir, tag_name) + repo_dir = repo_dir or sys.current_dir() + assert(type(repo_dir) == "string", "git.create_tag: Argument 'repo_dir' is not a string.") + assert(type(tag_name) == "string", "git.create_tag: Argument 'tag_name' is not a string.") + repo_dir = sys.abs_path(repo_dir) + + local ok, prev_dir, msg + ok, prev_dir = sys.change_dir(repo_dir); + if not ok then return nil, err end + + ok, msg = sys.exec("git tag " .. tag_name .. " -f ") + sys.change_dir(prev_dir) + + return ok, msg +end + +-- Fetch given 'ref_name' from the remote 'git_repo_url' to the local repository +-- 'repo_dir' and return its sha. 'ref_type' can be "tag" or "head". +local function fetch_ref(repo_dir, git_repo_url, ref_name, ref_type) + repo_dir = repo_dir or sys.current_dir() + assert(type(repo_dir) == "string", "git.fetch_ref: Argument 'repo_dir' is not a string.") + assert(type(git_repo_url) == "string", "git.fetch_ref: Argument 'git_repo_url' is not a string.") + assert(type(ref_name) == "string", "git.fetch_ref: Argument 'ref_name' is not a string.") + assert(type(ref_type) == "string", "git.fetch_ref: Argument 'ref_type' is not a string.") + assert(ref_type == "tag" or ref_type == "head", "git.get_remote_refs: Argument 'ref_type' is not \"tag\" or \"head\".") + repo_dir = sys.abs_path(repo_dir) + + local refstring = "refs/" .. ref_type .. "s/" .. ref_name + + local suppress_fetch_progress = not cfg.debug + local ok, repo_or_err = pcall(git.repo.open, repo_dir) + if not ok then return nil, "Error when opening the git repository '" .. repo_dir .. "': " .. repo_or_err end + + local ok, pack_or_err, sha = pcall(git.protocol.fetch, git_repo_url, repo_or_err, refstring, suppress_fetch_progress) + if not ok then return nil, "Error when fetching ref '" .. refstring .. "' from git repository '" .. git_repo_url .. "': " .. pack_or_err end + + repo_or_err:close() + pack_or_err:close() + + return sha +end + +-- Fetch given 'tag_name' from the remote 'git_repo_url' to the local repository +-- 'repo_dir' and save it as a tag with the same 'tag_name'. +function fetch_tag(repo_dir, git_repo_url, tag_name) + return fetch_ref(repo_dir, git_repo_url, tag_name, "tag") +end + +-- Fetch given 'branch_name' from the remote 'git_repo_url' to the local repository +-- 'repo_dir' and save it as a branch with the same 'branch_name'. +function fetch_branch(repo_dir, git_repo_url, branch_name) + return fetch_ref(repo_dir, git_repo_url, branch_name, "head") +end + +-- Create the git repository and return the repo object (which can be used in checkout_sha etc.) +-- If the 'dir' exists, it's deleted prior to creating the git repository. +function create_repo(dir) + assert(type(dir) == "string", "git.create_repo: Argument 'dir' is not a string.") + + if sys.exists(dir) then sys.delete(dir) end + + local ok, repo_or_err = pcall(git.repo.create, dir) + if not ok then return nil, "Error when creating the git repository '" .. dir .. "': " .. repo_or_err end + + repo_or_err:close() + return true +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/init.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/init.lua new file mode 100644 index 0000000..50c4b7e --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/init.lua @@ -0,0 +1,349 @@ +-- 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 diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/logger.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/logger.lua new file mode 100644 index 0000000..7843223 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/logger.lua @@ -0,0 +1,64 @@ +-- Simple logger for LuaDist. + +module ("dist.logger", package.seeall) + +local cfg = require "dist.config" +local sys = require "dist.sys" + +-- Open 'log_file' and return a log, or nil and error msg on error. +local function get_log(log_file) + log_file = log_file or cfg.log_file + assert(type(log_file) == "string", "log.get_log: Argument 'log_file' is not a string.") + log_file = sys.abs_path(log_file) + + sys.make_dir(sys.parent_dir(log_file)) + local log, err = io.open(log_file, "a") + if not log then + return nil, "Error: can't open a logfile '" .. log_file .. "': " .. err + else + return log + end +end + +-- Set the default log. +local log_file = get_log(cfg.log_file) + +-- Log levels used. +local log_levels = { + DEBUG = 0, -- Fine-grained informational events that are most useful to debug an application. + INFO = 1, -- Informational messages that highlight the progress of the application at coarse-grained level. + WARN = 2, -- Potentially harmful situations. + ERROR = 3, -- Error events that might still allow the application to continue running. + FATAL = 4, -- Very severe error events that would presumably lead the application to abort. +} + +-- Write 'message' with 'level' to 'log'. +local function write(level, ...) + assert(type(level) == "string", "log.write: Argument 'level' is not a string.") + assert(#arg > 0, "log.write: No message arguments provided.") + assert(type(log_levels[level]) == "number", "log.write: Unknown log level used: '" .. level .. "'.") + + level = level:upper() + local message = table.concat(arg, " ") + + -- Check if writing for this log level is enabled. + if cfg.write_log_level and log_levels[level] >= log_levels[cfg.write_log_level] then + log_file:write(os.date("%Y-%m-%d %H:%M:%S") .. " [" .. level .. "]\t" .. message .. "\n") + log_file:flush() + end + + -- Check if printing for this log level is enabled. + if cfg.print_log_level and log_levels[level] >= log_levels[cfg.print_log_level] then + print(message) + end +end + +-- Functions with defined log levels for simple use. +function debug(...) return write("DEBUG", ...) end +function info(...) return write("INFO", ...) end +function warn(...) return write("WARN", ...) end +function error(...) return write("ERROR", ...) end +function fatal(...) return write("FATAL", ...) end + +-- Function with explicitly specified log level. +function log(level, ...) return write(level, ...) end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/manifest.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/manifest.lua new file mode 100644 index 0000000..ccaad13 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/manifest.lua @@ -0,0 +1,248 @@ +-- Working with manifest and dist.info files + +module ("dist.manifest", package.seeall) + +local cfg = require "dist.config" +local git = require "dist.git" +local sys = require "dist.sys" +local utils = require "dist.utils" + +-- Return the manifest table from 'manifest_file'. If the manifest is in cache, +-- then the cached version is used. You can set the cache timeout value in +-- 'config.cache_timeout' variable. +-- If optional 'force_no_cache' parameter is true, then the cache is not used. +function get_manifest(manifest_file, force_no_cache) + manifest_file = manifest_file or sys.make_path(cfg.root_dir, cfg.manifest_file) + force_no_cache = force_no_cache or false + + assert(type(manifest_file) == "string", "manifest.get_manifest: Argument 'manifest_file' is not a string.") + assert(type(force_no_cache) == "boolean", "manifest.get_manifest: Argument 'force_no_cache' is not a boolean.") + manifest_file = sys.abs_path(manifest_file) + + -- download new manifest to the cache if not present or cache not used or cache expired + if not sys.exists(manifest_file) or force_no_cache or not cfg.cache or utils.cache_timeout_expired(cfg.cache_timeout, manifest_file) then + local manifest_dest = sys.parent_dir(manifest_file) or sys.current_dir() + local ok, err = download_manifest(manifest_dest, cfg.repos) + if not ok then return nil, "Error when downloading manifest: " .. err end + end + + -- load manifest from cache + local status, ret = load_manifest(manifest_file) + if not status then return nil, "Error when loading manifest: " .. ret end + + return ret +end + +-- Download manifest from the table of git 'repository_urls' to 'dest_dir' and return true on success +-- and nil and error message on error. +function download_manifest(dest_dir, repository_urls) + dest_dir = dest_dir or sys.make_path(cfg.root_dir, cfg.cache_dir) + repository_urls = repository_urls or cfg.repos + if type(repository_urls) == "string" then repository_urls = {repository_urls} end + + assert(type(dest_dir) == "string", "manifest.download_manifest: Argument 'dest_dir' is not a string.") + assert(type(repository_urls) == "table", "manifest.download_manifest: Argument 'repository_urls' is not a table or string.") + dest_dir = sys.abs_path(dest_dir) + + -- define used files and directories + local manifest_filename = sys.extract_name(cfg.manifest_file) + local manifest_file = sys.make_path(dest_dir, manifest_filename) + local temp_dir = sys.make_path(cfg.root_dir, cfg.temp_dir) + + -- ensure that destination directory exists + local ok, err = sys.make_dir(dest_dir) + if not ok then return nil, err end + + -- retrieve manifests from repositories and collect them into one manifest table + local manifest = {} + + if #repository_urls == 0 then return nil, "No repository url specified." end + + print("Downloading repository information...") + for k, repo in pairs(repository_urls) do + local clone_dir = sys.make_path(temp_dir, "repository_" .. tostring(k)) + + -- clone the repo and add its '.gitmodules' file to the manifest table + + ok, err = git.create_repo(clone_dir) + + local sha + if ok then sha, err = git.fetch_branch(clone_dir, repo, "master") end + if sha then ok, err = git.checkout_sha(sha, clone_dir) end + + if not (ok and sha) then + if not cfg.debug then sys.delete(clone_dir) end + return nil, "Error when downloading the manifest from repository with url: '" .. repo .. "': " .. err + else + for _, pkg in pairs(load_gitmodules(sys.make_path(clone_dir, ".gitmodules"))) do + table.insert(manifest, pkg) + end + end + if not cfg.debug then sys.delete(clone_dir) end + end + + -- save the new manifest table to the file + ok, err = save_manifest(manifest, manifest_file) + if not ok then return nil, err end + + return true +end + +-- A secure loadfile function +-- If file code chunk has upvalues, the first upvalue is set to the given +-- environement, if that parameter is given, or to the value of the global environment. +local function secure_loadfile(file, env) + assert(type(file) == "string", "secure_loadfile: Argument 'file' is not a string.") + + -- use the given (or create a new) restricted environment + local env = env or {} + + -- load the file and run in a protected call with the restricted env + -- setfenv is deprecated in lua 5.2 in favor of giving env in arguments + -- the additional loadfile arguments are simply ignored for previous lua versions + local f, err = loadfile(file, 'bt', env) + if f then + if setfenv ~= nil then + setfenv(f, env) + end + return pcall(f) + else + return nil, err + end +end + +-- Load and return manifest table from the manifest file. +-- If manifest file not present, return nil. +function load_manifest(manifest_file) + manifest_file = manifest_file or sys.make_path(cfg.root_dir, cfg.manifest_file) + + return secure_loadfile(sys.abs_path(manifest_file)) +end + +-- Load '.gitmodules' file and returns manifest table. +-- If the file is not present, return nil. +function load_gitmodules(gitmodules_file) + gitmodules_file = gitmodules_file or sys.make_path(cfg.root_dir, cfg.manifest_file) + assert(type(gitmodules_file) == "string", "manifest.load_gitmodules: Argument 'gitmodules_file' is not a string.") + gitmodules_file = sys.abs_path(gitmodules_file) + + if sys.exists(gitmodules_file) then + -- load the .gitmodules file + local file, err = io.open(gitmodules_file, "r") + if not file then return nil, "Error when opening the .gitmodules file '" .. gitmodules_file .. "':" .. err end + + local mf_text = file:read("*a") + file:close() + if not mf_text then return nil, "Error when reading the .gitmodules file '" .. gitmodules_file .. "':" .. err end + + manifest = {} + for url in mf_text:gmatch("git://%S+/%S+") do + pkg = {name = url:match("git://%S+/(%S+)%.git") or url:match("git://%S+/(%S+)"), version = "scm", path = url} + table.insert(manifest, pkg) + end + + return manifest + else + return nil, "Error when loading the .gitmodules: file '" .. gitmodules_file .. "' doesn't exist." + end +end + +-- Save manifest table to the 'file' +function save_manifest(manifest_table, file) + assert(type(manifest_table) == "table", "manifest.save_distinfo: Argument 'manifest_table' is not a table.") + assert(type(file) == "string", "manifest.save_distinfo: Argument 'file' is not a string.") + file = sys.abs_path(file) + + -- Print table 'tbl' to io stream 'file'. + local function print_table(file, tbl, in_nested_table) + for k, v in pairs(tbl) do + -- print key + if in_nested_table then file:write("\t\t") end + if type(k) ~= "number" then + file:write("['" .. k .. "']" .. " = ") + end + -- print value + if type(v) == "table" then + file:write("{\n") + print_table(file, v, true) + if in_nested_table then file:write("\t") end + file:write("\t}") + else + if in_nested_table then file:write("\t") end + if type(v) == "string" then + file:write('[[' .. v .. ']]') + else + file:write(tostring(v)) + end + end + file:write(",\n") + end + end + + local manifest_file = io.open(file, "w") + if not manifest_file then return nil, "Error when saving manifest: cannot open the file '" .. file .. "'." end + + manifest_file:write('return {\n') + print_table(manifest_file, manifest_table) + manifest_file:write('},\ntrue') + manifest_file:close() + + return true +end + +-- Load and return package info table from the distinfo_file file. +-- If file not present, return nil. +function load_distinfo(distinfo_file) + assert(type(distinfo_file) == "string", "manifest.load_distinfo: Argument 'distinfo_file' is not a string.") + distinfo_file = sys.abs_path(distinfo_file) + + -- load the distinfo file + local distinfo_env = {} + local status, ret = secure_loadfile(distinfo_file, distinfo_env) + if not status then return nil, "Error when loading package info: " .. ret end + + return distinfo_env +end + +-- Save distinfo table to the 'file' +function save_distinfo(distinfo_table, file) + assert(type(distinfo_table) == "table", "manifest.save_distinfo: Argument 'distinfo_table' is not a table.") + assert(type(file) == "string", "manifest.save_distinfo: Argument 'file' is not a string.") + file = sys.abs_path(file) + + -- Print table 'tbl' to io stream 'file'. + local function print_table(file, tbl, in_nested_table) + for k, v in pairs(tbl) do + -- print key + if type(k) ~= "number" then + file:write(k .. " = ") + end + -- print value + if type(v) == "table" then + file:write("{\n") + print_table(file, v, true) + file:write("}\n") + elseif type(v) == "string" then + if in_nested_table then + file:write('[[' .. v .. ']]') + else + file:write('"' .. v .. '"') + end + else + file:write(v) + end + if in_nested_table then + file:write(",") + end + file:write("\n") + end + end + + local distinfo_file = io.open(file, "w") + if not distinfo_file then return nil, "Error when saving dist-info table: cannot open the file '" .. file .. "'." end + + print_table(distinfo_file, distinfo_table) + distinfo_file:close() + + return true +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/package.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/package.lua new file mode 100644 index 0000000..da399c1 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/package.lua @@ -0,0 +1,596 @@ +-- Package functions + +module ("dist.package", package.seeall) + +local cfg = require "dist.config" +local git = require "dist.git" +local sys = require "dist.sys" +local mf = require "dist.manifest" +local utils = require "dist.utils" +local depends = require "dist.depends" + +-- Return whether the package in given 'pkg_dir' is of a source type. +function is_source_type(pkg_dir) + assert(type(pkg_dir) == "string", "package.is_source_type: Argument 'pkg_dir' is not a string.") + pkg_dir = sys.abs_path(pkg_dir) + return utils.to_boolean(sys.exists(sys.make_path(pkg_dir, "CMakeLists.txt"))) +end + +-- Ensure proper arch and type for the given source 'dist_info' table and return it. +-- WARNING: this function should be used only for 'dist_info' tables of modules that are of a source type! +function ensure_source_arch_and_type(dist_info) + assert(type(dist_info) == "table", "package.ensure_source_arch_and_type: Argument 'dist_info' is not a table.") + dist_info.arch = dist_info.arch or "Universal" + dist_info.type = dist_info.type or "source" + return dist_info +end + +-- Remove package from 'pkg_distinfo_dir' of 'deploy_dir'. +function remove_pkg(pkg_distinfo_dir, deploy_dir) + deploy_dir = deploy_dir or cfg.root_dir + assert(type(pkg_distinfo_dir) == "string", "package.remove_pkg: Argument 'pkg_distinfo_dir' is not a string.") + assert(type(deploy_dir) == "string", "package.remove_pkg: Argument 'deploy_dir' is not a string.") + deploy_dir = sys.abs_path(deploy_dir) + + local abs_pkg_distinfo_dir = sys.make_path(deploy_dir, pkg_distinfo_dir) + + -- check for 'dist.info' + local info, err = mf.load_distinfo(sys.make_path(abs_pkg_distinfo_dir, "dist.info")) + if not info then return nil, "Error removing package from '" .. pkg_distinfo_dir .. "' - it doesn't contain valid 'dist.info' file." end + if not info.files then return nil, "File '" .. sys.make_path(pkg_distinfo_dir, "dist.info") .."' doesn't contain list of installed files." end + + -- remove files installed as components of this package + for _, component in ipairs(cfg.components) do + if info.files[component] then + for i = #info.files[component], 1, -1 do + local f = info.files[component][i] + f = sys.make_path(deploy_dir,f) + if sys.is_file(f) then + sys.delete(f) + elseif sys.is_dir(f) then + local dir_files, err = sys.get_file_list(f) + if not dir_files then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "': " .. err end + if #dir_files == 0 then sys.delete(f) end + end + -- delete also all parent directories if empty + local parents = sys.parents_up_to(f, deploy_dir) + for _, parent in ipairs(parents) do + if sys.is_dir(parent) then + local dir_files, err = sys.get_file_list(parent) + if not dir_files then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "': " .. err end + if #dir_files == 0 then + sys.delete(parent) + end + end + end + end + end + end + + -- remove removed components also from 'dist.info' + for _, component in ipairs(cfg.components) do + info.files[component] = nil + end + + -- delete the package information from deploy_dir + local ok = sys.delete(abs_pkg_distinfo_dir) + if not ok then return nil, "Error removing package in '" .. abs_pkg_distinfo_dir .. "'." end + + -- if the package was not completely removed (e.g. some components remain), + -- save the new version of its 'dist.info' + local comp_num = 0 + for _, _ in pairs(info.files) do comp_num = comp_num + 1 end + if comp_num ~= 0 then + sys.make_dir(abs_pkg_distinfo_dir) + local ok, err = mf.save_distinfo(info, sys.make_path(abs_pkg_distinfo_dir, "dist.info")) + if not ok then return nil, "Error resaving the 'dist.info': " .. err end + end + + return ok +end + +-- Install package from 'pkg_dir' to 'deploy_dir', using optional CMake 'variables'. +-- Optional 'preserve_pkg_dir' argument specified whether to preserve the 'pkg_dir'. +function install_pkg(pkg_dir, deploy_dir, variables, preserve_pkg_dir) + deploy_dir = deploy_dir or cfg.root_dir + variables = variables or {} + preserve_pkg_dir = preserve_pkg_dir or false + + assert(type(pkg_dir) == "string", "package.install_pkg: Argument 'pkg_dir' is not a string.") + assert(type(deploy_dir) == "string", "package.install_pkg: Argument 'deploy_dir' is not a string.") + assert(type(variables) == "table", "package.install_pkg: Argument 'variables' is not a table.") + assert(type(preserve_pkg_dir) == "boolean", "package.install_pkg: Argument 'preserve_pkg_dir' is not a boolean.") + + pkg_dir = sys.abs_path(pkg_dir) + deploy_dir = sys.abs_path(deploy_dir) + + -- check for dist.info + local info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info")) + if not info then return nil, "Error installing: the directory '" .. pkg_dir .. "' doesn't exist or doesn't contain valid 'dist.info' file." end + + -- check if the package is source + if is_source_type(pkg_dir) then info = ensure_source_arch_and_type(info) end + + -- check package's architecture + if not (info.arch == "Universal" or info.arch == cfg.arch) then + return nil, "Error installing '" .. info.name .. "-" .. info.version .. "': architecture '" .. info.arch .. "' is not suitable for this machine." + end + + -- check package's type + if not (info.type == "all" or info.type == "source" or info.type == cfg.type) then + return nil, "Error installing '" .. info.name .. "-" .. info.version .. "': architecture type '" .. info.type .. "' is not suitable for this machine." + end + + local ok, err + + -- if package is of binary type, just deploy it + if info.type ~= "source" then + ok, err = deploy_binary_pkg(pkg_dir, deploy_dir) + + -- else build and then deploy + else + + -- check if we have cmake + ok = utils.system_dependency_available("cmake", "cmake --version") + if not ok then return nil, "Error when installing: Command 'cmake' not available on the system." end + + -- set cmake variables + local cmake_variables = {} + + -- set variables from config file + for k, v in pairs(cfg.variables) do + cmake_variables[k] = v + end + + -- set variables specified as argument + for k, v in pairs(variables) do + cmake_variables[k] = v + end + + cmake_variables.CMAKE_INCLUDE_PATH = table.concat({cmake_variables.CMAKE_INCLUDE_PATH or "", sys.make_path(deploy_dir, "include")}, ";") + cmake_variables.CMAKE_LIBRARY_PATH = table.concat({cmake_variables.CMAKE_LIBRARY_PATH or "", sys.make_path(deploy_dir, "lib"), sys.make_path(deploy_dir, "bin")}, ";") + cmake_variables.CMAKE_PROGRAM_PATH = table.concat({cmake_variables.CMAKE_PROGRAM_PATH or "", sys.make_path(deploy_dir, "bin")}, ";") + + -- build the package and deploy it + ok, err = build_pkg(pkg_dir, deploy_dir, cmake_variables) + if not ok then return nil, err end + + end + + -- delete directory of fetched package + if not (cfg.debug or preserve_pkg_dir) then sys.delete(pkg_dir) end + + return ok, err +end + +-- Build and deploy package from 'src_dir' to 'deploy_dir' using 'variables'. +-- Return directory to which the package was built or nil on error. +-- 'variables' is table of optional CMake variables. +function build_pkg(src_dir, deploy_dir, variables) + deploy_dir = deploy_dir or cfg.root_dir + variables = variables or {} + + assert(type(src_dir) == "string", "package.build_pkg: Argument 'src_dir' is not a string.") + assert(type(deploy_dir) == "string", "package.build_pkg: Argument 'deploy_dir' is not a string.") + assert(type(variables) == "table", "package.build_pkg: Argument 'variables' is not a table.") + + src_dir = sys.abs_path(src_dir) + deploy_dir = sys.abs_path(deploy_dir) + + -- check for dist.info + local info, err = mf.load_distinfo(sys.make_path(src_dir, "dist.info")) + if not info then return nil, "Error building package from '" .. src_dir .. "': it doesn't contain valid 'dist.info' file." end + local pkg_name = info.name .. "-" .. info.version + + -- set machine information + info.arch = cfg.arch + info.type = cfg.type + + -- create CMake build dir + local cmake_build_dir = sys.abs_path(sys.make_path(deploy_dir, cfg.temp_dir, pkg_name .. "-CMake-build")) + sys.make_dir(cmake_build_dir) + + -- create cmake cache + variables["CMAKE_INSTALL_PREFIX"] = deploy_dir + local cache_file = io.open(sys.make_path(cmake_build_dir, "cache.cmake"), "w") + if not cache_file then return nil, "Error creating CMake cache file in '" .. cmake_build_dir .. "'" end + + -- Fill in cache variables + for k,v in pairs(variables) do + cache_file:write("SET(" .. k .. " " .. sys.quote(v):gsub("\\+", "/") .. " CACHE STRING \"\" FORCE)\n") + end + + -- If user cache file is provided then append it + if cfg.cache_file ~= "" then + local user_cache = io.open(sys.abs_path(cfg.cache_file), "r") + if user_cache then + cache_file:write(user_cache:read("*all").."\n") + user_cache:close() + end + end + cache_file:close() + + src_dir = sys.abs_path(src_dir) + print("Building " .. sys.extract_name(src_dir) .. "...") + + -- set cmake cache command + local cache_command = cfg.cache_command + if cfg.debug then cache_command = cache_command .. " " .. cfg.cache_debug_options end + + -- set cmake build command + local build_command = cfg.build_command + if cfg.debug then build_command = build_command .. " " .. cfg.build_debug_options end + + -- set the cmake cache + local ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. cache_command .. " " .. sys.quote(src_dir)) + if not ok then return nil, "Error preloading the CMake cache script '" .. sys.make_path(cmake_build_dir, "cache.cmake") .. "'" end + + -- build with cmake + ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. build_command) + if not ok then return nil, "Error building with CMake in directory '" .. cmake_build_dir .. "'" end + + -- if this is only simulation, exit sucessfully, skipping the next actions + if cfg.simulate then + return true, "Simulated build and deployment of package '" .. pkg_name .. "' sucessfull." + end + + -- table to collect files installed in the components + info.files = {} + + -- install the components + for _, component in ipairs(cfg.components) do + local strip_option = "" + if not cfg.debug and component ~= "Library" then strip_option = cfg.strip_option end + + local ok = sys.exec("cd " .. sys.quote(cmake_build_dir) .. " && " .. cfg.cmake .. " " .. strip_option .. " " ..cfg.install_component_command:gsub("#COMPONENT#", component)) + + if not ok then return nil, "Error when installing the component '" .. component .. "' with CMake in directory '" .. cmake_build_dir .. "'" end + + local install_mf = sys.make_path(cmake_build_dir, "install_manifest_" .. component .. ".txt") + local mf, err + local component_files = {} + + -- collect files installed in this component + if sys.exists(install_mf) then + mf, err = io.open(install_mf, "r") + if not mf then return nil, "Error when opening the CMake installation manifest '" .. install_mf .. "': " .. err end + for line in mf:lines() do + line = sys.check_separators(line) + local file = line:gsub(utils.escape_magic(deploy_dir .. sys.path_separator()), "") + table.insert(component_files, file) + end + mf:close() + + -- add list of component files to the 'dist.info' + if #component_files > 0 then info.files[component] = component_files end + end + end +-- if bookmark == 0 then return nil, "Package did not install any files!" end + + -- test with ctest + if cfg.test then + print("Testing " .. sys.extract_name(src_dir) .. " ...") + ok = sys.exec("cd " .. sys.quote(deploy_dir) .. " && " .. cfg.test_command) + if not ok then return nil, "Error when testing the module '" .. pkg_name .. "' with CTest." end + end + + -- save modified 'dist.info' file + local pkg_distinfo_dir = sys.make_path(deploy_dir, cfg.distinfos_dir, pkg_name) + sys.make_dir(pkg_distinfo_dir) + ok, err = mf.save_distinfo(info, sys.make_path(pkg_distinfo_dir, "dist.info")) + if not ok then return nil, err end + + -- clean up + if not cfg.debug then sys.delete(cmake_build_dir) end + + return true, "Package '" .. pkg_name .. "' successfully builded and deployed to '" .. deploy_dir .. "'." +end + +-- Deploy binary package from 'pkg_dir' to 'deploy_dir' by copying. +function deploy_binary_pkg(pkg_dir, deploy_dir) + deploy_dir = deploy_dir or cfg.root_dir + + assert(type(pkg_dir) == "string", "package.deploy_binary_pkg: Argument 'pkg_dir' is not a string.") + assert(type(deploy_dir) == "string", "package.deploy_binary_pkg: Argument 'deploy_dir' is not a string.") + + pkg_dir = sys.abs_path(pkg_dir) + deploy_dir = sys.abs_path(deploy_dir) + + -- check for dist.info + local info, err = mf.load_distinfo(sys.make_path(pkg_dir, "dist.info")) + if not info then return nil, "Error deploying package from '" .. pkg_dir .. "': it doesn't contain valid 'dist.info' file." end + local pkg_name = info.name .. "-" .. info.version + + -- if this is only simulation, exit sucessfully, skipping the next actions + if cfg.simulate then + return true, "Simulated deployment of package '" .. pkg_name .. "' sucessfull." + end + + -- copy all components of the module to the deploy_dir + for _, component in ipairs(cfg.components) do + if info.files[component] then + for _, file in ipairs(info.files[component]) do + local dest_dir = sys.make_path(deploy_dir, sys.parent_dir(file)) + + local ok, err = sys.make_dir(dest_dir) + if not ok then return nil, "Error when deploying package '" .. pkg_name .. "': cannot create directory '" .. dest_dir .. "': " .. err end + + ok, err = sys.copy(sys.make_path(pkg_dir, file), dest_dir) + if not ok then return nil, "Error when deploying package '" .. pkg_name .. "': cannot copy file '" .. file .. "' to the directory '" .. dest_dir .. "': " .. err end + end + end + end + + -- copy dist.info to register the module as installed + local pkg_distinfo_dir = sys.make_path(deploy_dir, cfg.distinfos_dir, pkg_name) + sys.make_dir(pkg_distinfo_dir) + ok, err = mf.save_distinfo(info, sys.make_path(pkg_distinfo_dir, "dist.info")) + if not ok then return nil, err end + + return true, "Package '" .. pkg_name .. "' successfully deployed to '" .. deploy_dir .. "'." +end + +-- Fetch package (table 'pkg') to download_dir. Return the original 'pkg' table +-- with 'pkg.download_dir' containing path to the directory of the +-- downloaded package. +-- +-- When optional 'suppress_printing' parameter is set to true, then messages +-- for the user won't be printed during run of this function. +-- +-- If the 'pkg' already contains the information about download directory (pkg.download_dir), +-- we assume the package was already downloaded there and won't download it again. +function fetch_pkg(pkg, download_dir, suppress_printing) + download_dir = download_dir or sys.current_dir() + suppress_printing = suppress_printing or false + assert(type(pkg) == "table", "package.fetch_pkg: Argument 'pkg' is not a table.") + assert(type(download_dir) == "string", "package.fetch_pkg: Argument 'download_dir' is not a string.") + assert(type(suppress_printing) == "boolean", "package.fetch_pkg: Argument 'suppress_printing' is not a boolean.") + assert(type(pkg.name) == "string", "package.fetch_pkg: Argument 'pkg.name' is not a string.") + assert(type(pkg.version) == "string", "package.fetch_pkg: Argument 'pkg.version' is not a string.") + + -- if the package is already downloaded don't download it again + if pkg.download_dir then return pkg end + + assert(type(pkg.path) == "string", "package.fetch_pkg: Argument 'pkg.path' is not a string.") + download_dir = sys.abs_path(download_dir) + + local pkg_full_name = pkg.name .. "-" .. pkg.version + local repo_url = pkg.path + local clone_dir = sys.abs_path(sys.make_path(download_dir, pkg_full_name)) + pkg.download_dir = clone_dir + + -- check if download_dir already exists, assuming the package was already downloaded + if sys.exists(sys.make_path(clone_dir, "dist.info")) then + if cfg.cache and not utils.cache_timeout_expired(cfg.cache_timeout, clone_dir) then + if not suppress_printing then print("'" .. pkg_full_name .. "' already in cache, skipping downloading (use '-cache=false' to force download).") end + return pkg + else + sys.delete(sys.make_path(clone_dir)) + end + end + + local bin_tag = pkg.version .. "-" .. cfg.arch .. "-" .. cfg.type + local use_binary = false + + if cfg.binary then + -- check if binary version of the module for this arch & type available + local avail_tags, err = git.get_remote_tags(repo_url) + if not avail_tags then return nil, err end + + if utils.contains(avail_tags, bin_tag) then + use_binary = true + end + end + + -- init the git repository + local ok, err = git.create_repo(clone_dir) + if not ok then return nil, err end + + -- Fetch the desired ref (from the pkg's remote repo) and checkout into it. + + if use_binary then + + if not suppress_printing then print("Getting " .. pkg_full_name .. " (binary)...") end + + -- We fetch the binary tag. + local sha + if ok then sha, err = git.fetch_tag(clone_dir, repo_url, bin_tag) end + if sha then ok, err = git.checkout_sha(sha, clone_dir) end + + elseif cfg.source then + + if not suppress_printing then print("Getting " .. pkg_full_name .. " (source)...") end + + -- If we want the 'scm' version, we fetch the 'master' branch, otherwise + -- we fetch the tag, matching the desired package version. + if ok and pkg.version ~= "scm" then + local sha + sha, err = git.fetch_tag(clone_dir, repo_url, pkg.version) + if sha then ok, err = git.checkout_sha(sha, clone_dir) end + elseif ok then + local sha + sha, err = git.fetch_branch(clone_dir, repo_url, "master") + if sha then ok, err = git.checkout_sha(sha, clone_dir) end + end + + else + ok = false + if cfg.binary then + err = "Binary version of module not available and using source modules disabled." + else + err = "Using both binary and source modules disabled." + end + end + + if not ok then + -- clean up + if not cfg.debug then sys.delete(clone_dir) end + return nil, "Error fetching package '" .. pkg_full_name .. "' from '" .. pkg.path .. "' to '" .. download_dir .. "': " .. err + end + + -- delete '.git' directory + if not cfg.debug then sys.delete(sys.make_path(clone_dir, ".git")) end + + return pkg +end + +-- Return table with information about available versions of 'package'. +-- +-- When optional 'suppress_printing' parameter is set to true, then messages +-- for the user won't be printed during run of this function. +function retrieve_versions(package, manifest, suppress_printing) + suppress_printing = suppress_printing or false + assert(type(package) == "string", "package.retrieve_versions: Argument 'string' is not a string.") + assert(type(manifest) == "table", "package.retrieve_versions: Argument 'manifest' is not a table.") + assert(type(suppress_printing) == "boolean", "package.retrieve_versions: Argument 'suppress_printing' is not a boolean.") + + -- get package table + local pkg_name = depends.split_name_constraint(package) + local tmp_packages = depends.find_packages(pkg_name, manifest) + + if #tmp_packages == 0 then + return nil, "No suitable candidate for package '" .. package .. "' found." + else + package = tmp_packages[1] + end + + -- if the package's already downloaded, we assume it's desired to install the downloaded version + if package.download_dir then + local pkg_type = "binary" + if is_source_type(package.download_dir) then pkg_type = "source" end + if not suppress_printing then print("Using " .. package.name .. "-" .. package.version .. " (" .. pkg_type .. ") provided by " .. package.download_dir) end + return {package} + end + + if not suppress_printing then print("Finding out available versions of " .. package.name .. "...") end + + -- get available versions + local tags, err = git.get_remote_tags(package.path) + if not tags then return nil, "Error when retrieving versions of package '" .. package.name .. "': " .. err end + + -- filter out tags of binary packages + local versions = utils.filter(tags, function (tag) return tag:match("^[^%-]+%-?[^%-]*$") and true end) + + packages = {} + + -- create package information + for _, version in pairs(versions) do + pkg = {} + pkg.name = package.name + pkg.version = version + pkg.path = package.path + table.insert(packages, pkg) + end + + return packages +end + +-- Return table with information from package's dist.info and path to downloaded +-- package. Optional argument 'deploy_dir' is used just as a temporary +-- place to place the downloaded packages into. +-- +-- When optional 'suppress_printing' parameter is set to true, then messages +-- for the user won't be printed during the execution of this function. +function retrieve_pkg_info(package, deploy_dir, suppress_printing) + deploy_dir = deploy_dir or cfg.root_dir + assert(type(package) == "table", "package.retrieve_pkg_info: Argument 'package' is not a table.") + assert(type(deploy_dir) == "string", "package.retrieve_pkg_info: Argument 'deploy_dir' is not a string.") + deploy_dir = sys.abs_path(deploy_dir) + + local tmp_dir = sys.abs_path(sys.make_path(deploy_dir, cfg.temp_dir)) + + -- download the package + local fetched_pkg, err = fetch_pkg(package, tmp_dir, suppress_printing) + if not fetched_pkg then return nil, "Error when retrieving the info about '" .. package.name .. "': " .. err end + + -- load information from 'dist.info' + local info, err = mf.load_distinfo(sys.make_path(fetched_pkg.download_dir, "dist.info")) + if not info then return nil, err end + + -- add other attributes + if package.path then info.path = package.path end + if package.was_scm_version then info.was_scm_version = package.was_scm_version end + + -- set default arch/type if not explicitly stated and package is of source type + if is_source_type(fetched_pkg.download_dir) then + info = ensure_source_arch_and_type(info) + elseif not (info.arch and info.type) then + return nil, fetched_pkg.download_dir .. ": binary package missing arch or type in 'dist.info'." + end + + return info, fetched_pkg.download_dir +end + +-- Return manifest, augmented with info about all available versions +-- of package 'pkg'. Optional argument 'deploy_dir' is used just as a temporary +-- place to place the downloaded packages into. +-- Optional argument 'installed' is manifest of all installed packages. When +-- specified, info from installed packages won't be downloaded from repo, +-- but the dist.info from installed package will be used. +function get_versions_info(pkg, manifest, deploy_dir, installed) + deploy_dir = deploy_dir or cfg.root_dir + assert(type(pkg) == "string", "package.get_versions_info: Argument 'pkg' is not a string.") + assert(type(manifest) == "table", "package.get_versions_info: Argument 'manifest' is not a table.") + assert(type(deploy_dir) == "string", "package.get_versions_info: Argument 'deploy_dir' is not a string.") + deploy_dir = sys.abs_path(deploy_dir) + + -- find all available versions of package + local versions, err = retrieve_versions(pkg, manifest) + if not versions then return nil, err end + + -- collect info about all retrieved versions + local infos = {} + for _, version in pairs(versions) do + + local info, path_or_err + local installed_version = {} + + -- find out whether this 'version' is installed so we can use it's dist.info + if type(installed) == "table" then installed_version = depends.find_packages(version.name .. "-" .. version.version, installed) end + + -- get info + if #installed_version > 0 then + print("Using dist.info from installed " .. version.name .. "-" .. version.version) + info = installed_version[1] + info.path = version.path + info.from_installed = true -- flag that dist.info of installed package was used + else + info, path_or_err = retrieve_pkg_info(version, deploy_dir) + if not info then return nil, path_or_err end + sys.delete(path_or_err) + end + table.insert(infos, info) + end + + -- found and add an implicit 'scm' version + local pkg_name = depends.split_name_constraint(pkg) + local found = depends.find_packages(pkg_name, manifest) + if #found == 0 then return nil, "No suitable candidate for package '" .. pkg .. "' found." end + local scm_info, path_or_err = retrieve_pkg_info({name = pkg_name, version = "scm", path = found[1].path}) + if not scm_info then return nil, path_or_err end + sys.delete(path_or_err) + scm_info.version = "scm" + table.insert(infos, scm_info) + + local tmp_manifest = utils.deepcopy(manifest) + + -- add collected info to the temp. manifest, replacing existing tables + for _, info in pairs(infos) do + local already_in_manifest = false + -- find if this version is already in manifest + for idx, pkg in ipairs(tmp_manifest) do + -- if yes, replace it + if pkg.name == info.name and pkg.version == info.version then + tmp_manifest[idx] = info + already_in_manifest = true + break + end + end + -- if not, just normally add to the manifest + if not already_in_manifest then + table.insert(tmp_manifest, info) + end + end + + return tmp_manifest +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/sys.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/sys.lua new file mode 100644 index 0000000..803df12 --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/sys.lua @@ -0,0 +1,386 @@ +-- System functions + +module ("dist.sys", package.seeall) + +local cfg = require "dist.config" +local utils = require "dist.utils" +local lfs = require "lfs" + +-- Return the path separator according to the platform. +function path_separator() + if cfg.arch == "Windows" then + return "\\" + else + return "/" + end +end + +-- Return path with wrong separators replaced with the right ones. +function check_separators(path) + assert(type(path) == "string", "sys.check_separators: Argument 'path' is not a string.") + if cfg.arch == "Windows" then + return path:gsub("/", "\\") + else + return path + end +end + +-- Return the path with the unnecessary trailing separator removed. +function remove_trailing(path) + assert(type(path) == "string", "sys.remove_trailing: Argument 'path' is not a string.") + if path:sub(-1) == path_separator() and not is_root(path) then path = path:sub(1,-2) end + return path +end + +-- Return the path with the all occurences of '/.' or '\.' (representing +-- the current directory) removed. +function remove_curr_dir_dots(path) + assert(type(path) == "string", "sys.remove_curr_dir_dots: Argument 'path' is not a string.") + while path:match(path_separator() .. "%." .. path_separator()) do -- match("/%./") + path = path:gsub(path_separator() .. "%." .. path_separator(), path_separator()) -- gsub("/%./", "/") + end + return path:gsub(path_separator() .. "%.$", "") -- gsub("/%.$", "") +end + +-- Return string argument quoted for a command line usage. +function quote(argument) + assert(type(argument) == "string", "sys.quote: Argument 'argument' is not a string.") + + -- TODO: This seems like a not very nice hack. Why is it needed? + -- Wouldn't it be better to fix the problem where it originates? + -- replace '/' path separators for '\' on Windows + if cfg.arch == "Windows" and argument:match("^[%u%U.]?:?[/\\].*") then + argument = argument:gsub("//","\\"):gsub("/","\\") + end + + -- Windows doesn't recognize paths starting with two slashes or backslashes + -- so we double every backslash except for the first one + if cfg.arch == "Windows" and argument:match("^[/\\].*") then + local prefix = argument:sub(1,1) + argument = argument:sub(2):gsub("\\", "\\\\") + argument = prefix .. argument + else + argument = argument:gsub("\\", "\\\\") + end + argument = argument:gsub('"', '\\"') + + return '"' .. argument .. '"' +end + +-- Run the system command (in current directory). +-- Return true on success, nil on fail and log string. +-- When optional 'force_verbose' parameter is true, then the output will be shown +-- even when not in debug or verbose mode. +function exec(command, force_verbose) + force_verbose = force_verbose or false + assert(type(command) == "string", "sys.exec: Argument 'command' is not a string.") + assert(type(force_verbose) == "boolean", "sys.exec: Argument 'force_verbose' is not a boolean.") + + if not (cfg.verbose or cfg.debug or force_verbose) then + if cfg.arch == "Windows" then + command = command .. " > NUL 2>&1" + else + command = command .. " > /dev/null 2>&1" + end + end + + if cfg.debug then print("Executing the command: " .. command) end + local ok, str, status = os.execute(command) + + -- os.execute returned values on failure are: + -- nil or true, "exit", n or true, "signal", n for lua >= 5.2 + -- status ~= 0 for lua 5.x < 5.2 + if ok == nil or (str == "exit" and status ~= 0) or str == "signal" or (ok ~= 0 and ok ~= true) then + return nil, "Error when running the command: " .. command + else + return true, "Sucessfully executed the command: " .. command + end +end + +-- Execute the 'command' and returns its output as a string. +function capture_output(command) + assert(type(command) == "string", "sys.exec: Argument 'command' is not a string.") + + local executed, err = io.popen(command, "r") + if not executed then return nil, "Error running the command '" .. command .. "':" .. err end + + local captured, err = executed:read("*a") + if not captured then return nil, "Error reading the output of command '" .. command .. "':" .. err end + + executed:close() + return captured +end + +-- Return whether the path is a root. +function is_root(path) + assert(type(path) == "string", "sys.is_root: Argument 'path' is not a string.") + return utils.to_boolean(path:find("^[a-zA-Z]:[/\\]$") or path:find("^[/\\]$")) +end + +-- Return whether the path is absolute. +function is_abs(path) + assert(type(path) == "string", "sys.is_abs: Argument 'path' is not a string.") + return utils.to_boolean(path:find("^[a-zA-Z]:[/\\].*$") or path:find("^[/\\].*$")) +end + +-- Return whether the specified file or directory exists. +function exists(path) + assert(type(path) == "string", "sys.exists: Argument 'path' is not a string.") + local attr, err = lfs.attributes(path) + return utils.to_boolean(attr), err +end + +-- Return whether the 'file' exists and is a file. +function is_file(file) + assert(type(file) == "string", "sys.is_file: Argument 'file' is not a string.") + return lfs.attributes(file, "mode") == "file" +end + +-- Return whether the 'dir' exists and is a directory. +function is_dir(dir) + assert(type(dir) == "string", "sys.is_dir: Argument 'dir' is not a string.") + return lfs.attributes(dir, "mode") == "directory" +end + +-- Return the current working directory +function current_dir() + local dir, err = lfs.currentdir() + if not dir then return nil, err end + return dir +end + +-- Return an iterator over the directory 'dir'. +-- If 'dir' doesn't exist or is not a directory, return nil and error message. +function get_directory(dir) + dir = dir or current_dir() + assert(type(dir) == "string", "sys.get_directory: Argument 'dir' is not a string.") + if is_dir(dir) then + return lfs.dir(dir) + else + return nil, "Error: '".. dir .. "' is not a directory." + end +end + +-- Extract file or directory name from its path. +function extract_name(path) + assert(type(path) == "string", "sys.extract_name: Argument 'path' is not a string.") + if is_root(path) then return path end + + path = remove_trailing(path) + path = path:gsub("^.*" .. path_separator(), "") + return path +end + +-- Return parent directory of the 'path' or nil if there's no parent directory. +-- If 'path' is a path to file, return the directory the file is in. +function parent_dir(path) + assert(type(path) == "string", "sys.parent_dir: Argument 'path' is not a string.") + path = remove_curr_dir_dots(path) + path = remove_trailing(path) + + local dir = path:gsub(utils.escape_magic(extract_name(path)) .. "$", "") + if dir == "" then + return nil + else + return make_path(dir) + end +end + +-- Returns the table of all parent directories of 'path' up to the directory +-- specified by 'boundary_path' (exclusive). +function parents_up_to(path, boundary_path) + assert(type(path) == "string", "sys.parents_up_to: Argument 'path' is not a string.") + assert(type(boundary_path) == "string", "sys.parents_up_to: Argument 'boundary_path' is not a string.") + boundary_path = remove_trailing(boundary_path) + + -- helper function to recursively collect the parent directories + local function collect_parents(_path, _parents) + local _parent = parent_dir(_path) + if _parent and _parent ~= boundary_path then + table.insert(_parents, _parent) + return collect_parents(_parent, _parents) + else + return _parents + end + end + + return collect_parents(path, {}) +end + +-- Compose path composed from specified parts or current +-- working directory when no part specified. +function make_path(...) + -- arg is deprecated in lua 5.2 in favor of table.pack we mimic here + local arg = {n=select('#',...),...} + local parts = arg + assert(type(parts) == "table", "sys.make_path: Argument 'parts' is not a table.") + + local path, err + if parts.n == 0 then + path, err = current_dir() + else + path, err = table.concat(parts, path_separator()) + end + if not path then return nil, err end + + -- squeeze repeated occurences of a file separator + path = path:gsub(path_separator() .. "+", path_separator()) + + -- remove unnecessary trailing path separator + path = remove_trailing(path) + + return path +end + +-- Return absolute path from 'path' +function abs_path(path) + assert(type(path) == "string", "sys.get_abs_path: Argument 'path' is not a string.") + if is_abs(path) then return path end + + local cur_dir, err = current_dir() + if not cur_dir then return nil, err end + + return make_path(cur_dir, path) +end + +-- Returns path to the temporary directory of OS. +function tmp_dir() + return os.getenv("TMPDIR") or os.getenv("TEMP") or os.getenv("TMP") or "/tmp" +end + +-- Returns temporary file (or directory) path (with optional prefix). +function tmp_name(prefix) + prefix = prefix or "" + assert(type(prefix) == "string", "sys.tmp_name: Argument 'prefix' is not a string.") + return make_path(tmp_dir(), prefix .. "luadist_" .. utils.rand(10000000000)) +end + +-- Return table of all paths in 'dir' +function get_file_list(dir) + dir = dir or current_dir() + assert(type(dir) == "string", "sys.get_directory: Argument 'dir' is not a string.") + if not exists(dir) then return nil, "Error getting file list of '" .. dir .. "': directory doesn't exist." end + + local function collect(path, all_paths) + for item in get_directory(path) do + + local item_path = make_path(path, item) + local _, last = item_path:find(dir .. path_separator(), 1, true) + local path_to_insert = item_path:sub(last + 1) + + if is_file(item_path) then + table.insert(all_paths, path_to_insert) + elseif is_dir(item_path) and item ~= "." and item ~= ".." then + table.insert(all_paths, path_to_insert) + collect(item_path, all_paths) + end + end + end + + local all_paths = {} + collect(dir, all_paths) + + return all_paths +end + +-- Return time of the last modification of 'file'. +function last_modification_time(file) + assert(type(file) == "string", "sys.last_modification_time: Argument 'file' is not a string.") + return lfs.attributes(file, "modification") +end + +-- Return the current time (in seconds since epoch). +function current_time() + return os.time() +end + +-- Change the current working directory and return 'true' and previous working +-- directory on success and 'nil' and error message on error. +function change_dir(dir_name) + assert(type(dir_name) == "string", "sys.change_dir: Argument 'dir_name' is not a string.") + local prev_dir = current_dir() + local ok, err = lfs.chdir(dir_name) + if ok then + return ok, prev_dir + else + return nil, err + end +end + +-- Make a new directory, making also all of its parent directories that doesn't exist. +function make_dir(dir_name) + assert(type(dir_name) == "string", "sys.make_dir: Argument 'dir_name' is not a string.") + if exists(dir_name) then + return true + else + local par_dir = parent_dir(dir_name) + if par_dir then + local ok, err = make_dir(par_dir) + if not ok then return nil, err end + end + return lfs.mkdir(dir_name) + end +end + +-- Move file (or directory) to the destination directory +function move_to(file_or_dir, dest_dir) + assert(type(file_or_dir) == "string", "sys.move_to: Argument 'file_or_dir' is not a string.") + assert(type(dest_dir) == "string", "sys.move_to: Argument 'dest_dir' is not a string.") + assert(is_dir(dest_dir), "sys.move_to: Destination '" .. dest_dir .."' is not a directory.") + + -- Extract file/dir name from its path + local file_or_dir_name = extract_name(file_or_dir) + + return os.rename(file_or_dir, make_path(dest_dir, file_or_dir_name)) +end + +-- rename file (or directory) to the new name. +function rename(file, new_name) + assert(type(file) == "string", "sys.rename: Argument 'file' is not a string.") + assert(type(new_name) == "string", "sys.rename: Argument 'new_name' is not a string.") + assert(not exists(new_name), "sys.rename: desired filename already exists.") + + return os.rename(file, new_name) +end + +-- Copy 'source' to the destination directory 'dest_dir'. +-- If 'source' is a directory, then recursive copying is used. +-- For non-recursive copying of directories use the make_dir() function. +function copy(source, dest_dir) + assert(type(source) == "string", "sys.copy: Argument 'file_or_dir' is not a string.") + assert(type(dest_dir) == "string", "sys.copy: Argument 'dest_dir' is not a string.") + assert(is_dir(dest_dir), "sys.copy: destination '" .. dest_dir .."' is not a directory.") + + if cfg.arch == "Windows" then + if is_dir(source) then + make_dir(make_path(dest_dir, extract_name(source))) + return exec("xcopy /E /I /Y /Q " .. quote(source) .. " " .. quote(dest_dir .. "\\" .. extract_name(source))) + else + return exec("copy /Y " .. quote(source) .. " " .. quote(dest_dir)) + end + else + if is_dir(source) then + return exec("cp -fRH " .. quote(source) .. " " .. quote(dest_dir)) + else + return exec("cp -fH " .. quote(source) .. " " .. quote(dest_dir)) + end + end +end + +-- Delete the specified file or directory +function delete(path) + assert(type(path) == "string", "sys.delete: Argument 'path' is not a string.") + assert(is_abs(path), "sys.delete: Argument 'path' is not an absolute path.") + + if cfg.arch == "Windows" then + if not exists(path) then + return true + elseif is_file(path) then + return os.remove(path) + else + return exec("rd /S /Q " .. quote(path)) + end + else + return exec("rm -rf " .. quote(path)) + end +end diff --git a/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/utils.lua b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/utils.lua new file mode 100644 index 0000000..392654c --- /dev/null +++ b/love2dToAPK/tools/tools/zbstudio-win/lualibs/dist/utils.lua @@ -0,0 +1,151 @@ +-- System functions + +module ("dist.utils", package.seeall) + +local sys = require "dist.sys" + +-- Returns a deep copy of 'table' with reference to the same metadata table. +-- Source: http://lua-users.org/wiki/CopyTable +function deepcopy(object) + local lookup_table = {} + local function _copy(object) + if type(object) ~= "table" then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for index, value in pairs(object) do + new_table[_copy(index)] = _copy(value) + end + return setmetatable(new_table, getmetatable(object)) + end + return _copy(object) +end + +-- Return deep copy of table 'array', containing only items for which 'predicate_fn' returns true. +function filter(array, predicate_fn) + assert(type(array) == "table", "utils.filter: Argument 'array' is not a table.") + assert(type(predicate_fn) == "function", "utils.filter: Argument 'predicate_fn' is not a function.") + local filtered = {} + for _,v in pairs(array) do + if predicate_fn(v) == true then table.insert(filtered, deepcopy(v)) end + end + return filtered +end + +-- Return deep copy of table 'array', sorted according to the 'compare_fn' function. +function sort(array, compare_fn) + assert(type(array) == "table", "utils.sort: Argument 'array' is not a table.") + assert(type(compare_fn) == "function", "utils.sort: Argument 'compare_fn' is not a function.") + local sorted = deepcopy(array) + table.sort(sorted, compare_fn) + return sorted +end + +-- Return whether the 'value' is in the table 'tbl'. +function contains(tbl, value) + assert(type(tbl) == "table", "utils.contains: Argument 'tbl' is not a table.") + for _,v in pairs(tbl) do + if v == value then return true end + end + return false +end + +-- Return single line string consisting of values in 'tbl' separated by comma. +-- Used for printing the dependencies/provides/conflicts. +function table_tostring(tbl, label) + assert(type(tbl) == "table", "utils.table_tostring: Argument 'tbl' is not a table.") + local str = "" + for k,v in pairs(tbl) do + if type(v) == "table" then + str = str .. table_tostring(v, k) + else + if label ~= nil then + str = str .. tostring(v) .. " [" .. tostring(label) .. "]" .. ", " + else + str = str .. tostring(v) .. ", " + end + end + end + return str +end + +-- Return table made up from values of the string, separated by separator. +function make_table(str, separator) + assert(type(str) == "string", "utils.make_table: Argument 'str' is not a string.") + assert(type(separator) == "string", "utils.make_table: Argument 'separator' is not a string.") + + local tbl = {} + for val in str:gmatch("(.-)" .. separator) do + table.insert(tbl, val) + end + local last_val = str:gsub(".-" .. separator, "") + if last_val and last_val ~= "" then + table.insert(tbl, last_val) + end + return tbl +end + +-- Return whether the 'cache_timeout' for 'file' has expired. +function cache_timeout_expired(cache_timeout, file) + assert(type(cache_timeout) == "number", "utils.cache_timeout_expired: Argument 'cache_timeout' is not a number.") + assert(type(file) == "string", "utils.cache_timeout_expired: Argument 'file' is not a string.") + return sys.last_modification_time(file) + cache_timeout < sys.current_time() +end + +-- Return the string 'str', with all magic (pattern) characters escaped. +function escape_magic(str) + assert(type(str) == "string", "utils.escape: Argument 'str' is not a string.") + local escaped = str:gsub('[%-%.%+%[%]%(%)%^%%%?%*%^%$]','%%%1') + return escaped +end + +-- Return the boolean representation of an 'arg'. +function to_boolean(arg) + return not not arg +end + + +math.randomseed(os.time()) + +-- Return pseudo-random number in range [0, 1], [1, n] or [n, m]. +function rand(...) + return math.random(...) +end + +-- Perform check of system dependency, which isn't provided in the LuaDist +-- installation itself and if it is missing, print instructions how +-- to install it. The 'command' is used for testing, 'name' when printing +-- information to the user. +function system_dependency_available(name, command) + assert(type(name) == "string", "utils.system_dependency_available: Argument 'name' is not a string.") + assert(type(command) == "string", "utils.system_dependency_available: Argument 'command' is not a string.") + + if not sys.exec(command) then + print("Error: command '" .. name .. "' not found on system. See installation instructions at\nhttps://github.com/LuaDist/Repository/wiki/Installation-of-System-Dependencies") + return false + end + + return true +end + +-- Obtain LuaDist location by checking available package locations +function get_luadist_location() + local paths = {} + local path = package.path:gsub("([^;]+)", function(c) table.insert(paths, c) end) + + for _, path in pairs(paths) do + if (sys.is_abs(path) and path:find("[/\\]lib[/\\]lua[/\\]%?.lua$")) then + -- Remove path to lib/lua + path = path:gsub("[/\\]lib[/\\]lua[/\\]%?.lua$", "") + -- Clean the path up a bit + path = path:gsub("[/\\]bin[/\\]%.[/\\]%.%.", "") + path = path:gsub("[/\\]bin[/\\]%.%.", "") + return path + end + end + return nil +end + |