Mercurial > repo
diff luabuild/luarocks-2.0.12/src/luarocks/build.lua @ 1125:87f6d05d4b4a
<GreyKnight> (cd luabuild; tar xf luarocks-2.0.12.tar.gz)
author | HackBot |
---|---|
date | Fri, 14 Dec 2012 22:07:40 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/luabuild/luarocks-2.0.12/src/luarocks/build.lua Fri Dec 14 22:07:40 2012 +0000 @@ -0,0 +1,335 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +module("luarocks.build", package.seeall) + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local manif = require("luarocks.manif") +local cfg = require("luarocks.cfg") + +help_summary = "Build/compile a rock." +help_arguments = "[--pack-binary-rock] {<rockspec>|<rock>|<name> [<version>]}" +help = [[ +Build and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +If --pack-binary-rock is passed, the rock is not installed; +instead, a .rock file with the contents of compilation is produced +in the current directory. +]] + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + if type(k) == "string" then + if is_module_path then + dest = dir.path(location, path.module_to_path(k)) + fs.make_dir(dest) + else + dest = dir.path(location, dir.dir_name(k)) + fs.make_dir(dest) + dest = dir.path(dest, dir.base_name(k)) + end + else + fs.make_dir(dest) + end + local ok = fs.copy(dir.path(file), dest) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build = rockspec.build + if build.extra_files then + extract_from_rockspec(build.extra_files) + end + if build.patches then + extract_from_rockspec(build.patches) + for patch, patchdata in util.sortedpairs(build.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "none", "one", "order" or "all". +-- @return boolean or (nil, string, [string]): True if succeeded or +-- nil and an error message followed by an error code. +function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + + local name, version = rockspec.name, rockspec.version + if repos.is_installed(name, version) then + repos.delete_version(name, version) + end + + if not minimal_mode then + local _, source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + fs.change_dir(source_dir) + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true }, + lib = { name = path.lib_dir(name, version), is_module_path = true }, + conf = { name = path.conf_dir(name, version), is_module_path = false }, + bin = { name = path.bin_dir(name, version), is_module_path = false }, + } + + for _, d in pairs(dirs) do + fs.make_dir(d.name) + end + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local build = rockspec.build + + if not minimal_mode then + ok, err = apply_patches(rockspec) + if err then + return nil, err + end + end + + if build.type ~= "none" then + + -- Temporary compatibility + if build.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build.type = "builtin" + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build.type) + if not ok or not type(build_type) == "table" then + return nil, "Failed initializing build back-end for build type '"..build.type.."': "..build_type + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build.install then + for id, install_dir in pairs(dirs) do + ok, err = install_files(build.install[id], install_dir.name, install_dir.is_module_path) + if not ok then + return nil, err + end + end + end + + local copy_directories = build.copy_directories or {"doc"} + + for _, copy_dir in pairs(copy_directories) do + if fs.is_dir(copy_dir) then + local dest = dir.path(path.install_dir(name, version), copy_dir) + fs.make_dir(dest) + fs.copy_contents(copy_dir, dest) + else + util.warning("Directory '"..copy_dir.."' not found") + end + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version)) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = manif.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec)) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + ok, err = manif.update_manifest(name, version, nil, deps_mode) + if err then return nil, err end + + local license = "" + if rockspec.description and rockspec.description.license then + license = ("(license: "..rockspec.description.license..")") + end + + local root_dir = path.root_dir(cfg.rocks_dir) + util.printout() + util.printout(name.." "..version.." is now built and installed in "..root_dir.." "..license) + + util.remove_scheduled_function(rollback) + return true +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "none", "one", "order" or "all". +-- @return boolean or (nil, string, [string]): True if build was successful, +-- or false and an error message and an optional error code. +function build_rock(rock_file, need_to_fetch, deps_mode) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + fs.change_dir(unpack_dir) + local ok, err, errcode = build_rockspec(rockspec_file, need_to_fetch, false, deps_mode) + fs.pop_dir() + return ok, err, errcode +end + +local function do_build(name, version, deps_mode) + if name:match("%.rockspec$") then + return build_rockspec(name, true, false, deps_mode) + elseif name:match("%.src%.rock$") then + return build_rock(name, false, deps_mode) + elseif name:match("%.all%.rock$") then + local install = require("luarocks.install") + return install.install_binary_rock(name, deps_mode) + elseif name:match("%.rock$") then + return build_rock(name, true, deps_mode) + elseif not name:match(dir.separator) then + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(run, name:lower(), version, deps.deps_mode_to_flag(deps_mode)) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function run(...) + local flags, name, version = util.parse_flags(...) + if type(name) ~= "string" then + return nil, "Argument missing, see help." + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err end + return do_build(name, version, deps.get_deps_mode(flags)) + end +end