diff options
Diffstat (limited to 'src/luarocks/cmd/install.lua')
| -rw-r--r-- | src/luarocks/cmd/install.lua | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua new file mode 100644 index 0000000..05e31fe --- /dev/null +++ b/src/luarocks/cmd/install.lua @@ -0,0 +1,271 @@ +--- Module implementing the LuaRocks "install" command. +-- Installs binary rocks. +local install = {} + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local writer = require("luarocks.manif.writer") +local remove = require("luarocks.remove") +local search = require("luarocks.search") +local queries = require("luarocks.queries") +local cfg = require("luarocks.core.cfg") + +function install.add_to_parser(parser) + local cmd = parser:command("install", "Install a rock.", util.see_also()) -- luacheck: ignore 431 + + cmd:argument("rock", "The name of a rock to be fetched from a repository ".. + "or a filename of a locally available rock.") + :action(util.namespaced_name_action) + cmd:argument("version", "Version of the rock.") + :args("?") + + cmd:flag("--keep", "Do not remove previously installed versions of the ".. + "rock after building a new one. This behavior can be made permanent by ".. + "setting keep_other_versions=true in the configuration file.") + cmd:flag("--force", "If --keep is not specified, force removal of ".. + "previously installed versions if it would break dependencies. ".. + "If rock is already installed, reinstall it anyway.") + cmd:flag("--force-fast", "Like --force, but performs a forced removal ".. + "without reporting dependency issues.") + cmd:flag("--only-deps --deps-only", "Install only the dependencies of the rock.") + cmd:flag("--no-doc", "Install the rock without its documentation.") + cmd:flag("--verify", "Verify signature of the rockspec or src.rock being ".. + "built. If the rockspec or src.rock is being downloaded, LuaRocks will ".. + "attempt to download the signature as well. Otherwise, the signature ".. + "file should be already available locally in the same directory.\n".. + "You need the signer’s public key in your local keyring for this ".. + "option to work properly.") + cmd:flag("--check-lua-versions", "If the rock can't be found, check repository ".. + "and report if it is available for another Lua version.") + util.deps_mode_option(cmd) + cmd:flag("--no-manifest", "Skip creating/updating the manifest") + cmd:flag("--pin", "If the installed rock is a Lua module, create a ".. + "luarocks.lock file listing the exact versions of each dependency found for ".. + "this rock (recursively), and store it in the rock's directory. ".. + "Ignores any existing luarocks.lock file in the rock's sources.") + -- luarocks build options + parser:flag("--pack-binary-rock"):hidden(true) + parser:option("--branch"):hidden(true) + parser:flag("--sign"):hidden(true) +end + +install.opts = util.opts_table("install.opts", { + namespace = "string?", + keep = "boolean", + force = "boolean", + force_fast = "boolean", + no_doc = "boolean", + deps_mode = "string", + verify = "boolean", +}) + +--- Install a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param opts table: installation options +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function install.install_binary_rock(rock_file, opts) + assert(type(rock_file) == "string") + assert(opts:type() == "install.opts") + + local namespace = opts.namespace + local deps_mode = opts.deps_mode + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + if repos.is_installed(name, version) then + if not (opts.force or opts.force_fast) then + util.printout(name .. " " .. version .. " is already installed in " .. path.root_dir(cfg.root_dir)) + util.printout("Use --force to reinstall.") + return name, version + end + repos.delete_version(name, version, opts.deps_mode) + end + + local install_dir = path.install_dir(name, version) + + local rollback = util.schedule_function(function() + fs.delete(install_dir) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify) + if not ok then return nil, err, errcode end + + local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + if opts.deps_mode ~= "none" then + ok, err, errcode = deps.check_external_deps(rockspec, "install") + if err then return nil, err, errcode end + end + + -- For compatibility with .rock files built with LuaRocks 1 + if not fs.exists(path.rock_manifest_file(name, version)) then + ok, err = writer.make_rock_manifest(name, version) + if err then return nil, err end + end + + if namespace then + ok, err = writer.make_namespace_file(name, version, namespace) + if err then return nil, err end + end + + if deps_mode ~= "none" then + local deplock_dir = fs.exists(dir.path(".", "luarocks.lock")) + and "." + or install_dir + ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", deps_mode, opts.verify, deplock_dir) + if err then return nil, err, errcode end + end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Installs the dependencies of a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param opts table: installation options +-- @return (string, string) or (nil, string, [string]): Name and version of +-- the rock whose dependencies were installed if succeeded or nil and an error message +-- followed by an error code. +function install.install_binary_rock_deps(rock_file, opts) + assert(type(rock_file) == "string") + assert(opts:type() == "install.opts") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + + local install_dir = path.install_dir(name, version) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, install_dir, opts.verify) + if not ok then return nil, err, errcode end + + local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + ok, err, errcode = deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, install_dir) + if err then return nil, err, errcode end + + util.printout() + util.printout("Successfully installed dependencies for " ..name.." "..version) + + return name, version +end + +local function install_rock_file_deps(filename, opts) + assert(opts:type() == "install.opts") + + local name, version = install.install_binary_rock_deps(filename, opts) + if not name then return nil, version end + + writer.check_dependencies(nil, opts.deps_mode) + return name, version +end + +local function install_rock_file(filename, opts) + assert(type(filename) == "string") + assert(opts:type() == "install.opts") + + local name, version = install.install_binary_rock(filename, opts) + if not name then return nil, version end + + if opts.no_doc then + util.remove_doc_dir(name, version) + end + + if (not opts.keep) and not cfg.keep_other_versions then + local ok, err, warn = remove.remove_other_versions(name, version, opts.force, opts.force_fast) + if not ok then + return nil, err + elseif warn then + util.printerr(err) + end + end + + writer.check_dependencies(nil, opts.deps_mode) + return name, version +end + +--- Driver function for the "install" command. +-- If an URL or pathname to a binary rock is given, fetches and installs it. +-- If a rockspec or a source rock is given, forwards the request to the "build" +-- command. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @return boolean or (nil, string, exitcode): True if installation was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function install.command(args) + if args.rock:match("%.rockspec$") or args.rock:match("%.src%.rock$") then + local build = require("luarocks.cmd.build") + return build.command(args) + elseif args.rock:match("%.rock$") then + local deps_mode = deps.get_deps_mode(args) + local opts = install.opts({ + namespace = args.namespace, + keep = not not args.keep, + force = not not args.force, + force_fast = not not args.force_fast, + no_doc = not not args.no_doc, + deps_mode = deps_mode, + verify = not not args.verify, + }) + if args.only_deps then + return install_rock_file_deps(args.rock, opts) + else + return install_rock_file(args.rock, opts) + end + else + local url, err = search.find_rock_checking_lua_versions( + queries.new(args.rock, args.namespace, args.version), + args.check_lua_versions) + if not url then + return nil, err + end + util.printout("Installing "..url) + args.rock = url + return install.command(args) + end +end + +install.needs_lock = function(args) + if args.pack_binary_rock then + return false + end + return true +end + +return install |
