diff options
28 files changed, 2189 insertions, 349 deletions
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua index f0f7225d..5d527876 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/build.lua | |||
| @@ -8,6 +8,7 @@ local util = require("luarocks.util") | |||
| 8 | local rep = require("luarocks.rep") | 8 | local rep = require("luarocks.rep") |
| 9 | local fetch = require("luarocks.fetch") | 9 | local fetch = require("luarocks.fetch") |
| 10 | local fs = require("luarocks.fs") | 10 | local fs = require("luarocks.fs") |
| 11 | local dir = require("luarocks.dir") | ||
| 11 | local deps = require("luarocks.deps") | 12 | local deps = require("luarocks.deps") |
| 12 | local manif = require("luarocks.manif") | 13 | local manif = require("luarocks.manif") |
| 13 | 14 | ||
| @@ -38,10 +39,10 @@ local function install_files(files, location) | |||
| 38 | for k, file in pairs(files) do | 39 | for k, file in pairs(files) do |
| 39 | local dest = location | 40 | local dest = location |
| 40 | if type(k) == "string" then | 41 | if type(k) == "string" then |
| 41 | dest = fs.make_path(location, path.module_to_path(k)) | 42 | dest = dir.path(location, path.module_to_path(k)) |
| 42 | end | 43 | end |
| 43 | fs.make_dir(dest) | 44 | fs.make_dir(dest) |
| 44 | local ok = fs.copy(fs.make_path(file), dest) | 45 | local ok = fs.copy(dir.path(file), dest) |
| 45 | if not ok then | 46 | if not ok then |
| 46 | return nil, "Failed copying "..file | 47 | return nil, "Failed copying "..file |
| 47 | end | 48 | end |
| @@ -55,7 +56,7 @@ end | |||
| 55 | -- @param files table: The table of files to be written. | 56 | -- @param files table: The table of files to be written. |
| 56 | local function extract_from_rockspec(files) | 57 | local function extract_from_rockspec(files) |
| 57 | for name, content in pairs(files) do | 58 | for name, content in pairs(files) do |
| 58 | local fd = io.open(fs.make_path(fs.current_dir(), name), "w+") | 59 | local fd = io.open(dir.path(fs.current_dir(), name), "w+") |
| 59 | fd:write(content) | 60 | fd:write(content) |
| 60 | fd:close() | 61 | fd:close() |
| 61 | end | 62 | end |
| @@ -76,9 +77,9 @@ function apply_patches(rockspec) | |||
| 76 | end | 77 | end |
| 77 | if build.patches then | 78 | if build.patches then |
| 78 | extract_from_rockspec(build.patches) | 79 | extract_from_rockspec(build.patches) |
| 79 | for patch, _ in util.sortedpairs(build.patches) do | 80 | for patch, patchdata in util.sortedpairs(build.patches) do |
| 80 | print("Applying patch "..patch.."...") | 81 | print("Applying patch "..patch.."...") |
| 81 | local ok, err = fs.patch(tostring(patch)) | 82 | local ok, err = fs.apply_patch(tostring(patch, patchdata)) |
| 82 | if not ok then | 83 | if not ok then |
| 83 | return nil, "Failed applying patch "..patch | 84 | return nil, "Failed applying patch "..patch |
| 84 | end | 85 | end |
| @@ -91,28 +92,28 @@ end | |||
| 91 | -- @param rockspec_file string: local or remote filename of a rockspec. | 92 | -- @param rockspec_file string: local or remote filename of a rockspec. |
| 92 | -- @param need_to_fetch boolean: true if sources need to be fetched, | 93 | -- @param need_to_fetch boolean: true if sources need to be fetched, |
| 93 | -- false if the rockspec was obtained from inside a source rock. | 94 | -- false if the rockspec was obtained from inside a source rock. |
| 94 | -- @return boolean or (nil, string): True if succeeded or | 95 | -- @return boolean or (nil, string, [string]): True if succeeded or |
| 95 | -- nil and an error message. | 96 | -- nil and an error message followed by an error code. |
| 96 | function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | 97 | function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) |
| 97 | assert(type(rockspec_file) == "string") | 98 | assert(type(rockspec_file) == "string") |
| 98 | assert(type(need_to_fetch) == "boolean") | 99 | assert(type(need_to_fetch) == "boolean") |
| 99 | 100 | ||
| 100 | local rockspec, err = fetch.load_rockspec(rockspec_file) | 101 | local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) |
| 101 | if err then | 102 | if err then |
| 102 | return nil, err | 103 | return nil, err, errcode |
| 103 | elseif not rockspec.build then | 104 | elseif not rockspec.build then |
| 104 | return nil, "Rockspec error: build table not specified" | 105 | return nil, "Rockspec error: build table not specified" |
| 105 | elseif not rockspec.build.type then | 106 | elseif not rockspec.build.type then |
| 106 | return nil, "Rockspec error: build type not specified" | 107 | return nil, "Rockspec error: build type not specified" |
| 107 | end | 108 | end |
| 108 | 109 | ||
| 109 | local ok, err = deps.fulfill_dependencies(rockspec) | 110 | local ok, err, errcode = deps.fulfill_dependencies(rockspec) |
| 110 | if err then | 111 | if err then |
| 111 | return nil, err | 112 | return nil, err, errcode |
| 112 | end | 113 | end |
| 113 | ok, err = deps.check_external_deps(rockspec, "build") | 114 | ok, err, errcode = deps.check_external_deps(rockspec, "build") |
| 114 | if err then | 115 | if err then |
| 115 | return nil, err | 116 | return nil, err, errcode |
| 116 | end | 117 | end |
| 117 | 118 | ||
| 118 | local name, version = rockspec.name, rockspec.version | 119 | local name, version = rockspec.name, rockspec.version |
| @@ -121,13 +122,13 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | |||
| 121 | end | 122 | end |
| 122 | 123 | ||
| 123 | if not minimal_mode then | 124 | if not minimal_mode then |
| 124 | local _, dir | 125 | local _, source_dir |
| 125 | if need_to_fetch then | 126 | if need_to_fetch then |
| 126 | ok, dir = fetch.fetch_sources(rockspec, true) | 127 | ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) |
| 127 | if not ok then | 128 | if not ok then |
| 128 | return nil, dir | 129 | return nil, source_dir, errcode |
| 129 | end | 130 | end |
| 130 | fs.change_dir(dir) | 131 | fs.change_dir(source_dir) |
| 131 | elseif rockspec.source.file then | 132 | elseif rockspec.source.file then |
| 132 | local ok, err = fs.unpack_archive(rockspec.source.file) | 133 | local ok, err = fs.unpack_archive(rockspec.source.file) |
| 133 | if not ok then | 134 | if not ok then |
| @@ -144,8 +145,8 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | |||
| 144 | bin = path.bin_dir(name, version), | 145 | bin = path.bin_dir(name, version), |
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | for _, dir in pairs(dirs) do | 148 | for _, d in pairs(dirs) do |
| 148 | fs.make_dir(dir) | 149 | fs.make_dir(d) |
| 149 | end | 150 | end |
| 150 | local rollback = util.schedule_function(function() | 151 | local rollback = util.schedule_function(function() |
| 151 | fs.delete(path.install_dir(name, version)) | 152 | fs.delete(path.install_dir(name, version)) |
| @@ -178,8 +179,8 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | |||
| 178 | end | 179 | end |
| 179 | 180 | ||
| 180 | if build.install then | 181 | if build.install then |
| 181 | for id, dir in pairs(dirs) do | 182 | for id, install_dir in pairs(dirs) do |
| 182 | ok, err = install_files(build.install[id], dir) | 183 | ok, err = install_files(build.install[id], install_dir) |
| 183 | if not ok then | 184 | if not ok then |
| 184 | return nil, err | 185 | return nil, err |
| 185 | end | 186 | end |
| @@ -188,16 +189,16 @@ function build_rockspec(rockspec_file, need_to_fetch, minimal_mode) | |||
| 188 | 189 | ||
| 189 | local copy_directories = build.copy_directories or {"doc"} | 190 | local copy_directories = build.copy_directories or {"doc"} |
| 190 | 191 | ||
| 191 | for _, dir in pairs(copy_directories) do | 192 | for _, copy_dir in pairs(copy_directories) do |
| 192 | if fs.is_dir(dir) then | 193 | if fs.is_dir(copy_dir) then |
| 193 | local dest = fs.make_path(path.install_dir(name, version), dir) | 194 | local dest = dir.path(path.install_dir(name, version), copy_dir) |
| 194 | fs.make_dir(dest) | 195 | fs.make_dir(dest) |
| 195 | fs.copy_contents(dir, dest) | 196 | fs.copy_contents(copy_dir, dest) |
| 196 | end | 197 | end |
| 197 | end | 198 | end |
| 198 | 199 | ||
| 199 | for _, dir in pairs(dirs) do | 200 | for _, d in pairs(dirs) do |
| 200 | fs.remove_dir_if_empty(dir) | 201 | fs.remove_dir_if_empty(d) |
| 201 | end | 202 | end |
| 202 | 203 | ||
| 203 | fs.pop_dir() | 204 | fs.pop_dir() |
| @@ -227,21 +228,21 @@ end | |||
| 227 | -- @param rock_file string: local or remote filename of a rock. | 228 | -- @param rock_file string: local or remote filename of a rock. |
| 228 | -- @param need_to_fetch boolean: true if sources need to be fetched, | 229 | -- @param need_to_fetch boolean: true if sources need to be fetched, |
| 229 | -- false if the rockspec was obtained from inside a source rock. | 230 | -- false if the rockspec was obtained from inside a source rock. |
| 230 | -- @return boolean or (nil, string): True if build was successful, | 231 | -- @return boolean or (nil, string, [string]): True if build was successful, |
| 231 | -- or false and an error message. | 232 | -- or false and an error message and an optional error code. |
| 232 | local function build_rock(rock_file, need_to_fetch) | 233 | function build_rock(rock_file, need_to_fetch) |
| 233 | assert(type(rock_file) == "string") | 234 | assert(type(rock_file) == "string") |
| 234 | assert(type(need_to_fetch) == "boolean") | 235 | assert(type(need_to_fetch) == "boolean") |
| 235 | 236 | ||
| 236 | local dir, err = fetch.fetch_and_unpack_rock(rock_file) | 237 | local unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) |
| 237 | if not dir then | 238 | if not unpack_dir then |
| 238 | return nil, err | 239 | return nil, err, errcode |
| 239 | end | 240 | end |
| 240 | local rockspec_file = path.rockspec_name_from_rock(rock_file) | 241 | local rockspec_file = path.rockspec_name_from_rock(rock_file) |
| 241 | fs.change_dir(dir) | 242 | fs.change_dir(unpack_dir) |
| 242 | local ok, err = build_rockspec(rockspec_file, need_to_fetch) | 243 | local ok, err, errcode = build_rockspec(rockspec_file, need_to_fetch) |
| 243 | fs.pop_dir() | 244 | fs.pop_dir() |
| 244 | return ok, err | 245 | return ok, err, errcode |
| 245 | end | 246 | end |
| 246 | 247 | ||
| 247 | --- Driver function for "build" command. | 248 | --- Driver function for "build" command. |
| @@ -268,7 +269,7 @@ function run(...) | |||
| 268 | return install.install_binary_rock(name) | 269 | return install.install_binary_rock(name) |
| 269 | elseif name:match("%.rock$") then | 270 | elseif name:match("%.rock$") then |
| 270 | return build_rock(name, true) | 271 | return build_rock(name, true) |
| 271 | elseif not name:match(fs.dir_separator) then | 272 | elseif not name:match(dir.separator) then |
| 272 | local search = require("luarocks.search") | 273 | local search = require("luarocks.search") |
| 273 | return search.act_on_src_or_rockspec(run, name, version) | 274 | return search.act_on_src_or_rockspec(run, name, version) |
| 274 | end | 275 | end |
diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua index c93edaa2..cfd71858 100644 --- a/src/luarocks/build/builtin.lua +++ b/src/luarocks/build/builtin.lua | |||
| @@ -6,6 +6,7 @@ local fs = require("luarocks.fs") | |||
| 6 | local path = require("luarocks.path") | 6 | local path = require("luarocks.path") |
| 7 | local util = require("luarocks.util") | 7 | local util = require("luarocks.util") |
| 8 | local cfg = require("luarocks.cfg") | 8 | local cfg = require("luarocks.cfg") |
| 9 | local dir = require("luarocks.dir") | ||
| 9 | 10 | ||
| 10 | --- Check if platform was detected | 11 | --- Check if platform was detected |
| 11 | -- @param query string: The platform name to check. | 12 | -- @param query string: The platform name to check. |
| @@ -62,13 +63,13 @@ function run(rockspec) | |||
| 62 | local extras = { unpack(objects) } | 63 | local extras = { unpack(objects) } |
| 63 | add_flags(extras, "-libpath:%s", libdirs) | 64 | add_flags(extras, "-libpath:%s", libdirs) |
| 64 | add_flags(extras, "%s.lib", libraries) | 65 | add_flags(extras, "%s.lib", libraries) |
| 65 | local basename = fs.base_name(library):gsub(".[^.]*$", "") | 66 | local basename = dir.base_name(library):gsub(".[^.]*$", "") |
| 66 | local deffile = basename .. ".def" | 67 | local deffile = basename .. ".def" |
| 67 | local def = io.open(fs.make_path(fs.current_dir(), deffile), "w+") | 68 | local def = io.open(dir.path(fs.current_dir(), deffile), "w+") |
| 68 | def:write("EXPORTS\n") | 69 | def:write("EXPORTS\n") |
| 69 | def:write("luaopen_"..name:gsub("%.", "_").."\n") | 70 | def:write("luaopen_"..name:gsub("%.", "_").."\n") |
| 70 | def:close() | 71 | def:close() |
| 71 | local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, fs.make_path(variables.LUA_LIBDIR, "lua5.1.lib"), unpack(extras)) | 72 | local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, dir.path(variables.LUA_LIBDIR, "lua5.1.lib"), unpack(extras)) |
| 72 | local manifestfile = basename..".dll.manifest" | 73 | local manifestfile = basename..".dll.manifest" |
| 73 | if ok and fs.exists(manifestfile) then | 74 | if ok and fs.exists(manifestfile) then |
| 74 | ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") | 75 | ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") |
| @@ -103,7 +104,7 @@ function run(rockspec) | |||
| 103 | if type(info) == "string" then | 104 | if type(info) == "string" then |
| 104 | local ext = info:match(".([^.]+)$") | 105 | local ext = info:match(".([^.]+)$") |
| 105 | if ext == "lua" then | 106 | if ext == "lua" then |
| 106 | local dest = fs.make_path(luadir, moddir) | 107 | local dest = dir.path(luadir, moddir) |
| 107 | built_modules[info] = dest | 108 | built_modules[info] = dest |
| 108 | else | 109 | else |
| 109 | info = {info} | 110 | info = {info} |
| @@ -124,11 +125,11 @@ function run(rockspec) | |||
| 124 | table.insert(objects, object) | 125 | table.insert(objects, object) |
| 125 | end | 126 | end |
| 126 | if not ok then break end | 127 | if not ok then break end |
| 127 | local module_name = fs.make_path(moddir, name:match("([^.]*)$").."."..cfg.lib_extension):gsub("//", "/") | 128 | local module_name = dir.path(moddir, name:match("([^.]*)$").."."..cfg.lib_extension):gsub("//", "/") |
| 128 | if moddir ~= "" then | 129 | if moddir ~= "" then |
| 129 | fs.make_dir(moddir) | 130 | fs.make_dir(moddir) |
| 130 | end | 131 | end |
| 131 | local dest = fs.make_path(libdir, moddir) | 132 | local dest = dir.path(libdir, moddir) |
| 132 | built_modules[module_name] = dest | 133 | built_modules[module_name] = dest |
| 133 | ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) | 134 | ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) |
| 134 | if not ok then break end | 135 | if not ok then break end |
| @@ -141,7 +142,7 @@ function run(rockspec) | |||
| 141 | end | 142 | end |
| 142 | if ok then | 143 | if ok then |
| 143 | if fs.is_dir("lua") then | 144 | if fs.is_dir("lua") then |
| 144 | fs.copy_contents("lua", luadir) | 145 | ok = fs.copy_contents("lua", luadir) |
| 145 | end | 146 | end |
| 146 | end | 147 | end |
| 147 | if ok then | 148 | if ok then |
diff --git a/src/luarocks/command_line.lua b/src/luarocks/command_line.lua index 9389d6ef..e220bb34 100644 --- a/src/luarocks/command_line.lua +++ b/src/luarocks/command_line.lua | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | ||
| 2 | program_version = "1.0.1" | 2 | program_version = "1.1" |
| 3 | 3 | ||
| 4 | --- Functions for command-line scripts. | 4 | --- Functions for command-line scripts. |
| 5 | module("luarocks.command_line", package.seeall) | 5 | module("luarocks.command_line", package.seeall) |
diff --git a/src/luarocks/deps.lua b/src/luarocks/deps.lua index d5a64e52..6f986575 100644 --- a/src/luarocks/deps.lua +++ b/src/luarocks/deps.lua | |||
| @@ -17,10 +17,11 @@ local rep = require("luarocks.rep") | |||
| 17 | local search = require("luarocks.search") | 17 | local search = require("luarocks.search") |
| 18 | local install = require("luarocks.install") | 18 | local install = require("luarocks.install") |
| 19 | local cfg = require("luarocks.cfg") | 19 | local cfg = require("luarocks.cfg") |
| 20 | local manif = require("luarocks.manif") | 20 | local manif_core = require("luarocks.manif_core") |
| 21 | local fs = require("luarocks.fs") | 21 | local fs = require("luarocks.fs") |
| 22 | local fetch = require("luarocks.fetch") | 22 | local fetch = require("luarocks.fetch") |
| 23 | local path = require("luarocks.path") | 23 | local path = require("luarocks.path") |
| 24 | local dir = require("luarocks.dir") | ||
| 24 | 25 | ||
| 25 | local operators = { | 26 | local operators = { |
| 26 | ["=="] = "==", | 27 | ["=="] = "==", |
| @@ -320,7 +321,7 @@ local function match_dep(dep, blacklist) | |||
| 320 | if dep.name == "lua" then | 321 | if dep.name == "lua" then |
| 321 | versions = { "5.1" } | 322 | versions = { "5.1" } |
| 322 | else | 323 | else |
| 323 | versions = manif.get_versions(dep.name) | 324 | versions = manif_core.get_versions(dep.name) |
| 324 | end | 325 | end |
| 325 | if not versions then | 326 | if not versions then |
| 326 | return nil | 327 | return nil |
| @@ -399,8 +400,9 @@ end | |||
| 399 | -- Packages are installed using the LuaRocks "install" command. | 400 | -- Packages are installed using the LuaRocks "install" command. |
| 400 | -- Aborts the program if a dependency could not be fulfilled. | 401 | -- Aborts the program if a dependency could not be fulfilled. |
| 401 | -- @param rockspec table: A rockspec in table format. | 402 | -- @param rockspec table: A rockspec in table format. |
| 402 | -- @return boolean or (nil, string): True if no errors occurred, or | 403 | -- @return boolean or (nil, string, [string]): True if no errors occurred, or |
| 403 | -- nil and an error message if any test failed. | 404 | -- nil and an error message if any test failed, followed by an optional |
| 405 | -- error code. | ||
| 404 | function fulfill_dependencies(rockspec) | 406 | function fulfill_dependencies(rockspec) |
| 405 | 407 | ||
| 406 | if rockspec.supported_platforms then | 408 | if rockspec.supported_platforms then |
| @@ -469,9 +471,9 @@ function fulfill_dependencies(rockspec) | |||
| 469 | if not rock then | 471 | if not rock then |
| 470 | return nil, "Could not find a rock to satisfy dependency: "..show_dep(dep) | 472 | return nil, "Could not find a rock to satisfy dependency: "..show_dep(dep) |
| 471 | end | 473 | end |
| 472 | local ok, err = install.run(rock) | 474 | local ok, err, errcode = install.run(rock) |
| 473 | if not ok then | 475 | if not ok then |
| 474 | return nil, "Failed installing dependency: "..rock.." - "..err | 476 | return nil, "Failed installing dependency: "..rock.." - "..err, errcode |
| 475 | end | 477 | end |
| 476 | end | 478 | end |
| 477 | end | 479 | end |
| @@ -521,7 +523,7 @@ function check_external_deps(rockspec, mode) | |||
| 521 | prefix = extdir | 523 | prefix = extdir |
| 522 | end | 524 | end |
| 523 | for dirname, dirdata in pairs(dirs) do | 525 | for dirname, dirdata in pairs(dirs) do |
| 524 | dirdata.dir = vars[name.."_"..dirname] or fs.make_path(prefix, dirdata.subdir) | 526 | dirdata.dir = vars[name.."_"..dirname] or dir.path(prefix, dirdata.subdir) |
| 525 | local file = files[dirdata.testfile] | 527 | local file = files[dirdata.testfile] |
| 526 | if file then | 528 | if file then |
| 527 | local files = {} | 529 | local files = {} |
| @@ -535,10 +537,11 @@ function check_external_deps(rockspec, mode) | |||
| 535 | local found = false | 537 | local found = false |
| 536 | failed_file = nil | 538 | failed_file = nil |
| 537 | for _, f in pairs(files) do | 539 | for _, f in pairs(files) do |
| 540 | -- small convenience hack | ||
| 538 | if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then | 541 | if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then |
| 539 | f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) | 542 | f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) |
| 540 | end | 543 | end |
| 541 | local testfile = fs.make_path(dirdata.dir, f) | 544 | local testfile = dir.path(dirdata.dir, f) |
| 542 | if fs.exists(testfile) then | 545 | if fs.exists(testfile) then |
| 543 | found = true | 546 | found = true |
| 544 | break | 547 | break |
| @@ -565,7 +568,7 @@ function check_external_deps(rockspec, mode) | |||
| 565 | end | 568 | end |
| 566 | end | 569 | end |
| 567 | if not ok then | 570 | if not ok then |
| 568 | return nil, "Could not find expected file "..failed_file.." for "..name.." -- you may have to install "..name.." in your system and/or set the "..name.."_DIR variable" | 571 | return nil, "Could not find expected file "..failed_file.." for "..name.." -- you may have to install "..name.." in your system and/or set the "..name.."_DIR variable", "dependency" |
| 569 | end | 572 | end |
| 570 | end | 573 | end |
| 571 | end | 574 | end |
diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua new file mode 100644 index 00000000..5cb3b846 --- /dev/null +++ b/src/luarocks/dir.lua | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | |||
| 2 | module("luarocks.dir", package.seeall) | ||
| 3 | |||
| 4 | separator = "/" | ||
| 5 | |||
| 6 | --- Strip the path off a path+filename. | ||
| 7 | -- @param pathname string: A path+name, such as "/a/b/c" | ||
| 8 | -- or "\a\b\c". | ||
| 9 | -- @return string: The filename without its path, such as "c". | ||
| 10 | function base_name(pathname) | ||
| 11 | assert(type(pathname) == "string") | ||
| 12 | |||
| 13 | local base = pathname:match(".*[/\\]([^/\\]*)") | ||
| 14 | return base or pathname | ||
| 15 | end | ||
| 16 | |||
| 17 | --- Strip the name off a path+filename. | ||
| 18 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 19 | -- @return string: The filename without its path, such as "/a/b/". | ||
| 20 | -- For entries such as "/a/b/", "/a/" is returned. If there are | ||
| 21 | -- no directory separators in input, "" is returned. | ||
| 22 | function dir_name(pathname) | ||
| 23 | assert(type(pathname) == "string") | ||
| 24 | |||
| 25 | return (pathname:gsub("/*$", ""):match("(.*/)[^/]*")) or "" | ||
| 26 | end | ||
| 27 | |||
| 28 | --- Describe a path in a cross-platform way. | ||
| 29 | -- Use this function to avoid platform-specific directory | ||
| 30 | -- separators in other modules. If the first item contains a | ||
| 31 | -- protocol descriptor (e.g. "http:"), paths are always constituted | ||
| 32 | -- with forward slashes. | ||
| 33 | -- @param ... strings representing directories | ||
| 34 | -- @return string: a string with a platform-specific representation | ||
| 35 | -- of the path. | ||
| 36 | function path(...) | ||
| 37 | local items = {...} | ||
| 38 | local i = 1 | ||
| 39 | while items[i] do | ||
| 40 | if items[i] == "" then | ||
| 41 | table.remove(items, i) | ||
| 42 | else | ||
| 43 | i = i + 1 | ||
| 44 | end | ||
| 45 | end | ||
| 46 | return table.concat(items, "/") | ||
| 47 | end | ||
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua index b61fab28..5b2d20cb 100644 --- a/src/luarocks/fetch.lua +++ b/src/luarocks/fetch.lua | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | module("luarocks.fetch", package.seeall) | 3 | module("luarocks.fetch", package.seeall) |
| 4 | 4 | ||
| 5 | local fs = require("luarocks.fs") | 5 | local fs = require("luarocks.fs") |
| 6 | local dir = require("luarocks.dir") | ||
| 6 | local type_check = require("luarocks.type_check") | 7 | local type_check = require("luarocks.type_check") |
| 7 | local path = require("luarocks.path") | 8 | local path = require("luarocks.path") |
| 8 | local deps = require("luarocks.deps") | 9 | local deps = require("luarocks.deps") |
| @@ -18,8 +19,9 @@ local util = require("luarocks.util") | |||
| 18 | -- resulting local filename of the remote file as the basename of the URL; | 19 | -- resulting local filename of the remote file as the basename of the URL; |
| 19 | -- if that is not correct (due to a redirection, for example), the local | 20 | -- if that is not correct (due to a redirection, for example), the local |
| 20 | -- filename can be given explicitly as this second argument. | 21 | -- filename can be given explicitly as this second argument. |
| 21 | -- @return string or (nil, string): the absolute local pathname for the | 22 | -- @return string or (nil, string, [string]): the absolute local pathname for the |
| 22 | -- fetched file, or nil and a message in case of errors. | 23 | -- fetched file, or nil and a message in case of errors, followed by |
| 24 | -- an optional error code. | ||
| 23 | function fetch_url(url, filename) | 25 | function fetch_url(url, filename) |
| 24 | assert(type(url) == "string") | 26 | assert(type(url) == "string") |
| 25 | assert(type(filename) == "string" or not filename) | 27 | assert(type(filename) == "string" or not filename) |
| @@ -30,9 +32,9 @@ function fetch_url(url, filename) | |||
| 30 | elseif protocol == "http" or protocol == "ftp" or protocol == "https" then | 32 | elseif protocol == "http" or protocol == "ftp" or protocol == "https" then |
| 31 | local ok = fs.download(url) | 33 | local ok = fs.download(url) |
| 32 | if not ok then | 34 | if not ok then |
| 33 | return nil, "Failed downloading "..url | 35 | return nil, "Failed downloading "..url, "network" |
| 34 | end | 36 | end |
| 35 | return fs.make_path(fs.current_dir(), filename or fs.base_name(url)) | 37 | return dir.path(fs.current_dir(), filename or dir.base_name(url)) |
| 36 | else | 38 | else |
| 37 | return nil, "Unsupported protocol "..protocol | 39 | return nil, "Unsupported protocol "..protocol |
| 38 | end | 40 | end |
| @@ -46,30 +48,31 @@ end | |||
| 46 | -- when creating temporary directory. | 48 | -- when creating temporary directory. |
| 47 | -- @param filename string or nil: local filename of URL to be downloaded, | 49 | -- @param filename string or nil: local filename of URL to be downloaded, |
| 48 | -- in case it can't be inferred from the URL. | 50 | -- in case it can't be inferred from the URL. |
| 49 | -- @return (string, string) or (nil, string): absolute local pathname of | 51 | -- @return (string, string) or (nil, string, [string]): absolute local pathname of |
| 50 | -- the fetched file and temporary directory name; or nil and an error message. | 52 | -- the fetched file and temporary directory name; or nil and an error message |
| 53 | -- followed by an optional error code | ||
| 51 | function fetch_url_at_temp_dir(url, tmpname, filename) | 54 | function fetch_url_at_temp_dir(url, tmpname, filename) |
| 52 | assert(type(url) == "string") | 55 | assert(type(url) == "string") |
| 53 | assert(type(tmpname) == "string") | 56 | assert(type(tmpname) == "string") |
| 54 | assert(type(filename) == "string" or not filename) | 57 | assert(type(filename) == "string" or not filename) |
| 55 | filename = filename or fs.base_name(url) | 58 | filename = filename or dir.base_name(url) |
| 56 | 59 | ||
| 57 | local protocol, pathname = fs.split_url(url) | 60 | local protocol, pathname = fs.split_url(url) |
| 58 | if protocol == "file" then | 61 | if protocol == "file" then |
| 59 | return pathname, fs.dir_name(pathname) | 62 | return pathname, dir.dir_name(pathname) |
| 60 | else | 63 | else |
| 61 | local dir = fs.make_temp_dir(tmpname) | 64 | local temp_dir = fs.make_temp_dir(tmpname) |
| 62 | if not dir then | 65 | if not temp_dir then |
| 63 | return nil, "Failed creating temporary directory." | 66 | return nil, "Failed creating temporary directory." |
| 64 | end | 67 | end |
| 65 | util.schedule_function(fs.delete, dir) | 68 | util.schedule_function(fs.delete, temp_dir) |
| 66 | fs.change_dir(dir) | 69 | fs.change_dir(temp_dir) |
| 67 | local file, err = fetch_url(url, filename) | 70 | local file, err, errcode = fetch_url(url, filename) |
| 71 | fs.pop_dir() | ||
| 68 | if not file then | 72 | if not file then |
| 69 | return nil, "Error fetching file: "..err | 73 | return nil, "Error fetching file: "..err, errcode |
| 70 | end | 74 | end |
| 71 | fs.pop_dir() | 75 | return file, temp_dir |
| 72 | return file, dir | ||
| 73 | end | 76 | end |
| 74 | end | 77 | end |
| 75 | 78 | ||
| @@ -79,37 +82,37 @@ end | |||
| 79 | -- @param rock_file string: URL or filename of the rock. | 82 | -- @param rock_file string: URL or filename of the rock. |
| 80 | -- @param dest string or nil: if given, directory will be used as | 83 | -- @param dest string or nil: if given, directory will be used as |
| 81 | -- a permanent destination. | 84 | -- a permanent destination. |
| 82 | -- @return string or (nil, string): the directory containing the contents | 85 | -- @return string or (nil, string, [string]): the directory containing the contents |
| 83 | -- of the unpacked rock. | 86 | -- of the unpacked rock. |
| 84 | function fetch_and_unpack_rock(rock_file, dest) | 87 | function fetch_and_unpack_rock(rock_file, dest) |
| 85 | assert(type(rock_file) == "string") | 88 | assert(type(rock_file) == "string") |
| 86 | assert(type(dest) == "string" or not dest) | 89 | assert(type(dest) == "string" or not dest) |
| 87 | 90 | ||
| 88 | local name = fs.base_name(rock_file):match("(.*)%.[^.]*%.rock") | 91 | local name = dir.base_name(rock_file):match("(.*)%.[^.]*%.rock") |
| 89 | 92 | ||
| 90 | local rock_file, err = fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) | 93 | local rock_file, err, errcode = fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) |
| 91 | if not rock_file then | 94 | if not rock_file then |
| 92 | return nil, "Could not fetch rock file: " .. err | 95 | return nil, "Could not fetch rock file: " .. err, errcode |
| 93 | end | 96 | end |
| 94 | 97 | ||
| 95 | rock_file = fs.absolute_name(rock_file) | 98 | rock_file = fs.absolute_name(rock_file) |
| 96 | local dir | 99 | local unpack_dir |
| 97 | if dest then | 100 | if dest then |
| 98 | dir = dest | 101 | unpack_dir = dest |
| 99 | fs.make_dir(dir) | 102 | fs.make_dir(unpack_dir) |
| 100 | else | 103 | else |
| 101 | dir = fs.make_temp_dir(name) | 104 | unpack_dir = fs.make_temp_dir(name) |
| 102 | end | 105 | end |
| 103 | if not dest then | 106 | if not dest then |
| 104 | util.schedule_function(fs.delete, dir) | 107 | util.schedule_function(fs.delete, unpack_dir) |
| 105 | end | 108 | end |
| 106 | fs.change_dir(dir) | 109 | fs.change_dir(unpack_dir) |
| 107 | local ok = fs.unzip(rock_file) | 110 | local ok = fs.unzip(rock_file) |
| 108 | if not ok then | 111 | if not ok then |
| 109 | return nil, "Failed unpacking rock file: " .. rock_file | 112 | return nil, "Failed unpacking rock file: " .. rock_file |
| 110 | end | 113 | end |
| 111 | fs.pop_dir() | 114 | fs.pop_dir() |
| 112 | return dir | 115 | return unpack_dir |
| 113 | end | 116 | end |
| 114 | 117 | ||
| 115 | --- Back-end function that actually loads the local rockspec. | 118 | --- Back-end function that actually loads the local rockspec. |
| @@ -142,7 +145,7 @@ function load_local_rockspec(filename) | |||
| 142 | util.platform_overrides(rockspec.source) | 145 | util.platform_overrides(rockspec.source) |
| 143 | util.platform_overrides(rockspec.hooks) | 146 | util.platform_overrides(rockspec.hooks) |
| 144 | 147 | ||
| 145 | local basename = fs.base_name(filename) | 148 | local basename = dir.base_name(filename) |
| 146 | rockspec.name = basename:match("(.*)-[^-]*-[0-9]*") | 149 | rockspec.name = basename:match("(.*)-[^-]*-[0-9]*") |
| 147 | if not rockspec.name then | 150 | if not rockspec.name then |
| 148 | return nil, "Expected filename in format 'name-version-revision.rockspec'." | 151 | return nil, "Expected filename in format 'name-version-revision.rockspec'." |
| @@ -150,7 +153,7 @@ function load_local_rockspec(filename) | |||
| 150 | 153 | ||
| 151 | local protocol, pathname = fs.split_url(rockspec.source.url) | 154 | local protocol, pathname = fs.split_url(rockspec.source.url) |
| 152 | if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then | 155 | if protocol == "http" or protocol == "https" or protocol == "ftp" or protocol == "file" then |
| 153 | rockspec.source.file = rockspec.source.file or fs.base_name(rockspec.source.url) | 156 | rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url) |
| 154 | end | 157 | end |
| 155 | rockspec.source.protocol, rockspec.source.pathname = protocol, pathname | 158 | rockspec.source.protocol, rockspec.source.pathname = protocol, pathname |
| 156 | 159 | ||
| @@ -167,7 +170,7 @@ function load_local_rockspec(filename) | |||
| 167 | 170 | ||
| 168 | rockspec.local_filename = filename | 171 | rockspec.local_filename = filename |
| 169 | local filebase = rockspec.source.file or rockspec.source.url | 172 | local filebase = rockspec.source.file or rockspec.source.url |
| 170 | local base = fs.base_name(filebase) | 173 | local base = dir.base_name(filebase) |
| 171 | base = base:gsub("%.[^.]*$", ""):gsub("%.tar$", "") | 174 | base = base:gsub("%.[^.]*$", ""):gsub("%.tar$", "") |
| 172 | rockspec.source.dir = rockspec.source.dir | 175 | rockspec.source.dir = rockspec.source.dir |
| 173 | or rockspec.source.module | 176 | or rockspec.source.module |
| @@ -197,19 +200,19 @@ end | |||
| 197 | -- Only the LuaRocks runtime loader should use | 200 | -- Only the LuaRocks runtime loader should use |
| 198 | -- load_local_rockspec directly. | 201 | -- load_local_rockspec directly. |
| 199 | -- @param filename string: Local or remote filename of a rockspec. | 202 | -- @param filename string: Local or remote filename of a rockspec. |
| 200 | -- @return table or (nil, string): A table representing the rockspec | 203 | -- @return table or (nil, string, [string]): A table representing the rockspec |
| 201 | -- or nil followed by an error message. | 204 | -- or nil followed by an error message and optional error code. |
| 202 | function load_rockspec(filename) | 205 | function load_rockspec(filename) |
| 203 | assert(type(filename) == "string") | 206 | assert(type(filename) == "string") |
| 204 | 207 | ||
| 205 | local name = fs.base_name(filename):match("(.*)%.rockspec") | 208 | local name = dir.base_name(filename):match("(.*)%.rockspec") |
| 206 | if not name then | 209 | if not name then |
| 207 | return nil, "Filename '"..filename.."' does not look like a rockspec." | 210 | return nil, "Filename '"..filename.."' does not look like a rockspec." |
| 208 | end | 211 | end |
| 209 | 212 | ||
| 210 | local filename, err = fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) | 213 | local filename, err, errcode = fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) |
| 211 | if not filename then | 214 | if not filename then |
| 212 | return nil, err | 215 | return nil, err, errcode |
| 213 | end | 216 | end |
| 214 | 217 | ||
| 215 | return load_local_rockspec(filename) | 218 | return load_local_rockspec(filename) |
| @@ -220,9 +223,9 @@ end | |||
| 220 | -- @param extract boolean: Whether to extract the sources from | 223 | -- @param extract boolean: Whether to extract the sources from |
| 221 | -- the fetched source tarball or not. | 224 | -- the fetched source tarball or not. |
| 222 | -- @param dest_dir string or nil: If set, will extract to the given directory. | 225 | -- @param dest_dir string or nil: If set, will extract to the given directory. |
| 223 | -- @return (string, string) or (nil, string): The absolute pathname of | 226 | -- @return (string, string) or (nil, string, [string]): The absolute pathname of |
| 224 | -- the fetched source tarball and the temporary directory created to | 227 | -- the fetched source tarball and the temporary directory created to |
| 225 | -- store it; or nil and an error message. | 228 | -- store it; or nil and an error message and optional error code. |
| 226 | function get_sources(rockspec, extract, dest_dir) | 229 | function get_sources(rockspec, extract, dest_dir) |
| 227 | assert(type(rockspec) == "table") | 230 | assert(type(rockspec) == "table") |
| 228 | assert(type(extract) == "boolean") | 231 | assert(type(extract) == "boolean") |
| @@ -231,17 +234,17 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 231 | local url = rockspec.source.url | 234 | local url = rockspec.source.url |
| 232 | local name = rockspec.name.."-"..rockspec.version | 235 | local name = rockspec.name.."-"..rockspec.version |
| 233 | local filename = rockspec.source.file | 236 | local filename = rockspec.source.file |
| 234 | local source_file, dir, err | 237 | local source_file, store_dir, err, errcode |
| 235 | if dest_dir then | 238 | if dest_dir then |
| 236 | fs.change_dir(dest_dir) | 239 | fs.change_dir(dest_dir) |
| 237 | source_file, err = fetch_url(url, filename) | 240 | source_file, err, errcode = fetch_url(url, filename) |
| 238 | fs.pop_dir() | 241 | fs.pop_dir() |
| 239 | dir = dest_dir | 242 | store_dir = dest_dir |
| 240 | else | 243 | else |
| 241 | source_file, dir = fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) | 244 | source_file, store_dir, errcode = fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) |
| 242 | end | 245 | end |
| 243 | if not source_file then | 246 | if not source_file then |
| 244 | return nil, err or dir | 247 | return nil, err or store_dir, errcode |
| 245 | end | 248 | end |
| 246 | if rockspec.source.md5 then | 249 | if rockspec.source.md5 then |
| 247 | if not fs.check_md5(source_file, rockspec.source.md5) then | 250 | if not fs.check_md5(source_file, rockspec.source.md5) then |
| @@ -249,11 +252,11 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 249 | end | 252 | end |
| 250 | end | 253 | end |
| 251 | if extract then | 254 | if extract then |
| 252 | fs.change_dir(dir) | 255 | fs.change_dir(store_dir) |
| 253 | fs.unpack_archive(rockspec.source.file) | 256 | fs.unpack_archive(rockspec.source.file) |
| 254 | fs.pop_dir() | 257 | fs.pop_dir() |
| 255 | end | 258 | end |
| 256 | return source_file, dir | 259 | return source_file, store_dir |
| 257 | end | 260 | end |
| 258 | 261 | ||
| 259 | --- Download sources for building a rock, calling the appropriate protocol method. | 262 | --- Download sources for building a rock, calling the appropriate protocol method. |
diff --git a/src/luarocks/fetch/cvs.lua b/src/luarocks/fetch/cvs.lua index 291388cb..c2957c09 100644 --- a/src/luarocks/fetch/cvs.lua +++ b/src/luarocks/fetch/cvs.lua | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | module("luarocks.fetch.cvs", package.seeall) | 3 | module("luarocks.fetch.cvs", package.seeall) |
| 4 | 4 | ||
| 5 | local fs = require("luarocks.fs") | 5 | local fs = require("luarocks.fs") |
| 6 | local dir = require("luarocks.dir") | ||
| 6 | local util = require("luarocks.util") | 7 | local util = require("luarocks.util") |
| 7 | 8 | ||
| 8 | --- Download sources for building a rock, using CVS. | 9 | --- Download sources for building a rock, using CVS. |
| @@ -17,27 +18,27 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 17 | assert(type(dest_dir) == "string" or not dest_dir) | 18 | assert(type(dest_dir) == "string" or not dest_dir) |
| 18 | 19 | ||
| 19 | local name_version = rockspec.name .. "-" .. rockspec.version | 20 | local name_version = rockspec.name .. "-" .. rockspec.version |
| 20 | local module = rockspec.source.module or fs.base_name(rockspec.source.url) | 21 | local module = rockspec.source.module or dir.base_name(rockspec.source.url) |
| 21 | local command = {"cvs", "-d"..rockspec.source.pathname, "export", module} | 22 | local command = {"cvs", "-d"..rockspec.source.pathname, "export", module} |
| 22 | if rockspec.source.tag then | 23 | if rockspec.source.tag then |
| 23 | table.insert(command, 4, "-r") | 24 | table.insert(command, 4, "-r") |
| 24 | table.insert(command, 5, rockspec.source.tag) | 25 | table.insert(command, 5, rockspec.source.tag) |
| 25 | end | 26 | end |
| 26 | local dir | 27 | local store_dir |
| 27 | if not dest_dir then | 28 | if not dest_dir then |
| 28 | dir = fs.make_temp_dir(name_version) | 29 | store_dir = fs.make_temp_dir(name_version) |
| 29 | if not dir then | 30 | if not store_dir then |
| 30 | return nil, "Failed creating temporary directory." | 31 | return nil, "Failed creating temporary directory." |
| 31 | end | 32 | end |
| 32 | util.schedule_function(fs.delete, dir) | 33 | util.schedule_function(fs.delete, store_dir) |
| 33 | else | 34 | else |
| 34 | dir = dest_dir | 35 | store_dir = dest_dir |
| 35 | end | 36 | end |
| 36 | fs.change_dir(dir) | 37 | fs.change_dir(store_dir) |
| 37 | if not fs.execute(unpack(command)) then | 38 | if not fs.execute(unpack(command)) then |
| 38 | return nil, "Failed fetching files from CVS." | 39 | return nil, "Failed fetching files from CVS." |
| 39 | end | 40 | end |
| 40 | fs.pop_dir() | 41 | fs.pop_dir() |
| 41 | return module, dir | 42 | return module, store_dir |
| 42 | end | 43 | end |
| 43 | 44 | ||
diff --git a/src/luarocks/fetch/git.lua b/src/luarocks/fetch/git.lua index d0d204a9..d2420ef8 100644 --- a/src/luarocks/fetch/git.lua +++ b/src/luarocks/fetch/git.lua | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | module("luarocks.fetch.git", package.seeall) | 3 | module("luarocks.fetch.git", package.seeall) |
| 4 | 4 | ||
| 5 | local fs = require("luarocks.fs") | 5 | local fs = require("luarocks.fs") |
| 6 | local dir = require("luarocks.dir") | ||
| 6 | local util = require("luarocks.util") | 7 | local util = require("luarocks.util") |
| 7 | 8 | ||
| 8 | --- Download sources for building a rock, using git. | 9 | --- Download sources for building a rock, using git. |
| @@ -17,7 +18,7 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 17 | assert(type(dest_dir) == "string" or not dest_dir) | 18 | assert(type(dest_dir) == "string" or not dest_dir) |
| 18 | 19 | ||
| 19 | local name_version = rockspec.name .. "-" .. rockspec.version | 20 | local name_version = rockspec.name .. "-" .. rockspec.version |
| 20 | local module = fs.base_name(rockspec.source.url) | 21 | local module = dir.base_name(rockspec.source.url) |
| 21 | -- Strip off .git from base name if present | 22 | -- Strip off .git from base name if present |
| 22 | module = module:gsub("%.git$", "") | 23 | module = module:gsub("%.git$", "") |
| 23 | local command = {"git", "clone", rockspec.source.url, module} | 24 | local command = {"git", "clone", rockspec.source.url, module} |
| @@ -26,17 +27,17 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 26 | if tag_or_branch then | 27 | if tag_or_branch then |
| 27 | checkout_command = {"git", "checkout", tag_or_branch} | 28 | checkout_command = {"git", "checkout", tag_or_branch} |
| 28 | end | 29 | end |
| 29 | local dir | 30 | local store_dir |
| 30 | if not dest_dir then | 31 | if not dest_dir then |
| 31 | dir = fs.make_temp_dir(name_version) | 32 | store_dir = fs.make_temp_dir(name_version) |
| 32 | if not dir then | 33 | if not store_dir then |
| 33 | return nil, "Failed creating temporary directory." | 34 | return nil, "Failed creating temporary directory." |
| 34 | end | 35 | end |
| 35 | util.schedule_function(fs.delete, dir) | 36 | util.schedule_function(fs.delete, store_dir) |
| 36 | else | 37 | else |
| 37 | dir = dest_dir | 38 | store_dir = dest_dir |
| 38 | end | 39 | end |
| 39 | fs.change_dir(dir) | 40 | fs.change_dir(store_dir) |
| 40 | if not fs.execute(unpack(command)) then | 41 | if not fs.execute(unpack(command)) then |
| 41 | return nil, "Failed fetching files from GIT while cloning." | 42 | return nil, "Failed fetching files from GIT while cloning." |
| 42 | end | 43 | end |
| @@ -48,6 +49,6 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 48 | fs.pop_dir() | 49 | fs.pop_dir() |
| 49 | end | 50 | end |
| 50 | fs.pop_dir() | 51 | fs.pop_dir() |
| 51 | return module, dir | 52 | return module, store_dir |
| 52 | end | 53 | end |
| 53 | 54 | ||
diff --git a/src/luarocks/fetch/sscm.lua b/src/luarocks/fetch/sscm.lua index a2c70b96..539014c2 100644 --- a/src/luarocks/fetch/sscm.lua +++ b/src/luarocks/fetch/sscm.lua | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | module("luarocks.fetch.sscm", package.seeall) | 3 | module("luarocks.fetch.sscm", package.seeall) |
| 4 | 4 | ||
| 5 | local fs = require("luarocks.fs") | 5 | local fs = require("luarocks.fs") |
| 6 | local dir = require("luarocks.dir") | ||
| 6 | 7 | ||
| 7 | --- Download sources via Surround SCM Server for building a rock. | 8 | --- Download sources via Surround SCM Server for building a rock. |
| 8 | -- @param rockspec table: The rockspec table | 9 | -- @param rockspec table: The rockspec table |
| @@ -15,7 +16,7 @@ function get_sources(rockspec, extract, dest_dir) | |||
| 15 | assert(type(rockspec) == "table") | 16 | assert(type(rockspec) == "table") |
| 16 | assert(type(dest_dir) == "string" or not dest_dir) | 17 | assert(type(dest_dir) == "string" or not dest_dir) |
| 17 | 18 | ||
| 18 | local module = rockspec.source.module or fs.base_name(rockspec.source.url) | 19 | local module = rockspec.source.module or dir.base_name(rockspec.source.url) |
| 19 | local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") | 20 | local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") |
| 20 | if not branch or not repository then | 21 | if not branch or not repository then |
| 21 | return nil, "Error retrieving branch and repository from rockspec." | 22 | return nil, "Error retrieving branch and repository from rockspec." |
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua index 7027167a..a905755d 100644 --- a/src/luarocks/fs.lua +++ b/src/luarocks/fs.lua | |||
| @@ -21,14 +21,12 @@ for _, platform in ipairs(cfg.platforms) do | |||
| 21 | end | 21 | end |
| 22 | end | 22 | end |
| 23 | 23 | ||
| 24 | local fs_lua = require("luarocks.fs.lua") | ||
| 24 | local fs_unix = require("luarocks.fs.unix") | 25 | local fs_unix = require("luarocks.fs.unix") |
| 25 | 26 | ||
| 26 | local fs_mt = { | 27 | local fs_mt = { |
| 27 | __index = function(t, k) | 28 | __index = function(t, k) |
| 28 | local impl = fs_impl and fs_impl[k] | 29 | local impl = fs_lua[k] or fs_impl[k] or fs_unix[k] |
| 29 | if not impl then | ||
| 30 | impl = fs_unix[k] | ||
| 31 | end | ||
| 32 | rawset(t, k, impl) | 30 | rawset(t, k, impl) |
| 33 | return impl | 31 | return impl |
| 34 | end | 32 | end |
| @@ -36,6 +34,7 @@ local fs_mt = { | |||
| 36 | 34 | ||
| 37 | setmetatable(luarocks.fs, fs_mt) | 35 | setmetatable(luarocks.fs, fs_mt) |
| 38 | 36 | ||
| 37 | fs_lua.init_fs_functions(luarocks.fs) | ||
| 39 | fs_unix.init_fs_functions(luarocks.fs) | 38 | fs_unix.init_fs_functions(luarocks.fs) |
| 40 | if fs_impl then | 39 | if fs_impl then |
| 41 | fs_impl.init_fs_functions(luarocks.fs) | 40 | fs_impl.init_fs_functions(luarocks.fs) |
diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua new file mode 100644 index 00000000..00efd4f4 --- /dev/null +++ b/src/luarocks/fs/lua.lua | |||
| @@ -0,0 +1,565 @@ | |||
| 1 | |||
| 2 | local assert, type, table, io, package, math, os, ipairs = | ||
| 3 | assert, type, table, io, package, math, os, ipairs | ||
| 4 | |||
| 5 | --- Native Lua implementation of filesystem and platform abstractions, | ||
| 6 | -- using LuaFileSystem, LZLib, MD5 and LuaCurl. | ||
| 7 | module("luarocks.fs.lua", package.seeall) | ||
| 8 | |||
| 9 | local cfg = require("luarocks.cfg") | ||
| 10 | local dir = require("luarocks.dir") | ||
| 11 | |||
| 12 | local zip_ok, zip = pcall(require, "luarocks.tools.zip") | ||
| 13 | local lfs_ok, lfs = pcall(require, "lfs") | ||
| 14 | local curl_ok, curl = pcall(require, "luacurl") | ||
| 15 | local md5_ok, md5 = pcall(require, "md5") | ||
| 16 | |||
| 17 | local tar = require("luarocks.tools.tar") | ||
| 18 | local patch = require("luarocks.tools.patch") | ||
| 19 | |||
| 20 | local dir_stack = {} | ||
| 21 | |||
| 22 | math.randomseed(os.time()) | ||
| 23 | |||
| 24 | dir_separator = "/" | ||
| 25 | |||
| 26 | local fs_absolute_name, | ||
| 27 | fs_copy, | ||
| 28 | fs_current_dir, | ||
| 29 | fs_dir_stack, | ||
| 30 | fs_execute, | ||
| 31 | fs_execute_string, | ||
| 32 | fs_exists, | ||
| 33 | fs_find, | ||
| 34 | fs_is_dir, | ||
| 35 | fs_is_file, | ||
| 36 | fs_make_dir, | ||
| 37 | fs_set_time, | ||
| 38 | fs_Q | ||
| 39 | |||
| 40 | function init_fs_functions(impl) | ||
| 41 | fs_absolute_name = impl.absolute_name | ||
| 42 | fs_copy = impl.copy | ||
| 43 | fs_current_dir = impl.current_dir | ||
| 44 | fs_dir_stack = impl.dir_stack | ||
| 45 | fs_execute = impl.execute | ||
| 46 | fs_execute_string = impl.execute_string | ||
| 47 | fs_exists = impl.exists | ||
| 48 | fs_find = impl.find | ||
| 49 | fs_is_dir = impl.is_dir | ||
| 50 | fs_is_file = impl.is_file | ||
| 51 | fs_make_dir = impl.make_dir | ||
| 52 | fs_set_time = impl.set_time | ||
| 53 | fs_Q = impl.Q | ||
| 54 | end | ||
| 55 | |||
| 56 | --- Quote argument for shell processing. | ||
| 57 | -- Adds single quotes and escapes. | ||
| 58 | -- @param arg string: Unquoted argument. | ||
| 59 | -- @return string: Quoted argument. | ||
| 60 | function Q(arg) | ||
| 61 | assert(type(arg) == "string") | ||
| 62 | |||
| 63 | -- FIXME Unix-specific | ||
| 64 | return "'" .. arg:gsub("\\", "\\\\"):gsub("'", "'\\''") .. "'" | ||
| 65 | end | ||
| 66 | |||
| 67 | --- Run the given command. | ||
| 68 | -- The command is executed in the current directory in the dir stack. | ||
| 69 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 70 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 71 | -- otherwise. | ||
| 72 | function execute_string(cmd) | ||
| 73 | if os.execute(cmd) == 0 then | ||
| 74 | return true | ||
| 75 | else | ||
| 76 | return false | ||
| 77 | end | ||
| 78 | end | ||
| 79 | |||
| 80 | --- Run the given command, quoting its arguments. | ||
| 81 | -- The command is executed in the current directory in the dir stack. | ||
| 82 | -- @param command string: The command to be executed. No quoting/escaping | ||
| 83 | -- is applied. | ||
| 84 | -- @param ... Strings containing additional arguments, which are quoted. | ||
| 85 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 86 | -- otherwise. | ||
| 87 | function execute(command, ...) | ||
| 88 | assert(type(command) == "string") | ||
| 89 | |||
| 90 | for _, arg in ipairs({...}) do | ||
| 91 | assert(type(arg) == "string") | ||
| 92 | command = command .. " " .. fs_Q(arg) | ||
| 93 | end | ||
| 94 | return fs_execute_string(command) | ||
| 95 | end | ||
| 96 | |||
| 97 | --- Test is file/dir is writable. | ||
| 98 | -- Warning: testing if a file/dir is writable does not guarantee | ||
| 99 | -- that it will remain writable and therefore it is no replacement | ||
| 100 | -- for checking the result of subsequent operations. | ||
| 101 | -- @param file string: filename to test | ||
| 102 | -- @return boolean: true if file exists, false otherwise. | ||
| 103 | function is_writable(file) | ||
| 104 | assert(file) | ||
| 105 | local result | ||
| 106 | if fs_is_dir(file) then | ||
| 107 | local file2 = file .. '/.tmpluarockstestwritable' | ||
| 108 | local fh = io.open(file2, 'w') | ||
| 109 | result = fh ~= nil | ||
| 110 | if fh then fh:close() end | ||
| 111 | os.remove(file2) | ||
| 112 | else | ||
| 113 | local fh = io.open(file, 'r+') | ||
| 114 | result = fh ~= nil | ||
| 115 | if fh then fh:close() end | ||
| 116 | end | ||
| 117 | return result | ||
| 118 | end | ||
| 119 | |||
| 120 | --- Create a temporary directory. | ||
| 121 | -- @param name string: name pattern to use for avoiding conflicts | ||
| 122 | -- when creating temporary directory. | ||
| 123 | -- @return string or nil: name of temporary directory or nil on failure. | ||
| 124 | function make_temp_dir(name) | ||
| 125 | assert(type(name) == "string") | ||
| 126 | |||
| 127 | local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) | ||
| 128 | if fs_make_dir(temp_dir) then | ||
| 129 | return temp_dir | ||
| 130 | else | ||
| 131 | return nil | ||
| 132 | end | ||
| 133 | end | ||
| 134 | |||
| 135 | --- Return an absolute pathname from a potentially relative one. | ||
| 136 | -- @param pathname string: pathname to convert. | ||
| 137 | -- @param relative_to string or nil: path to prepend when making | ||
| 138 | -- pathname absolute, or the current dir in the dir stack if | ||
| 139 | -- not given. | ||
| 140 | -- @return string: The pathname converted to absolute. | ||
| 141 | function absolute_name(pathname, relative_to) | ||
| 142 | assert(type(pathname) == "string") | ||
| 143 | assert(type(relative_to) == "string" or not relative_to) | ||
| 144 | |||
| 145 | relative_to = relative_to or fs_current_dir() | ||
| 146 | if pathname:sub(1,1) == "/" then | ||
| 147 | return pathname | ||
| 148 | else | ||
| 149 | return relative_to .. "/" .. pathname | ||
| 150 | end | ||
| 151 | end | ||
| 152 | |||
| 153 | --- Split protocol and path from an URL or local pathname. | ||
| 154 | -- URLs should be in the "protocol://path" format. | ||
| 155 | -- For local pathnames, "file" is returned as the protocol. | ||
| 156 | -- @param url string: an URL or a local pathname. | ||
| 157 | -- @return string, string: the protocol, and the absolute pathname without the protocol. | ||
| 158 | function split_url(url) | ||
| 159 | assert(type(url) == "string") | ||
| 160 | |||
| 161 | local protocol, pathname = url:match("^([^:]*)://(.*)") | ||
| 162 | if not protocol then | ||
| 163 | protocol = "file" | ||
| 164 | pathname = url | ||
| 165 | end | ||
| 166 | if protocol == "file" then | ||
| 167 | pathname = fs_absolute_name(pathname) | ||
| 168 | end | ||
| 169 | return protocol, pathname | ||
| 170 | end | ||
| 171 | |||
| 172 | --------------------------------------------------------------------- | ||
| 173 | -- LuaFileSystem functions | ||
| 174 | --------------------------------------------------------------------- | ||
| 175 | |||
| 176 | if lfs_ok then | ||
| 177 | |||
| 178 | --- Obtain current directory. | ||
| 179 | -- Uses the module's internal dir stack. | ||
| 180 | -- @return string: the absolute pathname of the current directory. | ||
| 181 | function current_dir() | ||
| 182 | return lfs.currentdir() | ||
| 183 | end | ||
| 184 | |||
| 185 | --- Change the current directory. | ||
| 186 | -- Uses the module's internal dir stack. This does not have exact | ||
| 187 | -- semantics of chdir, as it does not handle errors the same way, | ||
| 188 | -- but works well for our purposes for now. | ||
| 189 | -- @param d string: The directory to switch to. | ||
| 190 | function change_dir(d) | ||
| 191 | table.insert(dir_stack, lfs.currentdir()) | ||
| 192 | lfs.chdir(d) | ||
| 193 | --local x="CHDIR: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x) | ||
| 194 | end | ||
| 195 | |||
| 196 | --- Change directory to root. | ||
| 197 | -- Allows leaving a directory (e.g. for deleting it) in | ||
| 198 | -- a crossplatform way. | ||
| 199 | function change_dir_to_root() | ||
| 200 | table.insert(dir_stack, lfs.currentdir()) | ||
| 201 | -- TODO Does this work on Windows? | ||
| 202 | lfs.chdir("/") | ||
| 203 | --local x="CHDIR ROOT: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x) | ||
| 204 | end | ||
| 205 | |||
| 206 | --- Change working directory to the previous in the dir stack. | ||
| 207 | -- @return true if a pop ocurred, false if the stack was empty. | ||
| 208 | function pop_dir() | ||
| 209 | local d = table.remove(dir_stack) | ||
| 210 | --local x="POP DIR: " for _,d in ipairs(dir_stack) do x=x.." "..d end print(x) | ||
| 211 | if d then | ||
| 212 | lfs.chdir(d) | ||
| 213 | return true | ||
| 214 | else | ||
| 215 | return false | ||
| 216 | end | ||
| 217 | end | ||
| 218 | |||
| 219 | --- Create a directory if it does not already exist. | ||
| 220 | -- If any of the higher levels in the path name does not exist | ||
| 221 | -- too, they are created as well. | ||
| 222 | -- @param directory string: pathname of directory to create. | ||
| 223 | -- @return boolean: true on success, false on failure. | ||
| 224 | function make_dir(directory) | ||
| 225 | assert(type(directory) == "string") | ||
| 226 | local path = nil | ||
| 227 | for d in directory:gmatch("[^"..dir.separator.."]*"..dir.separator.."*") do | ||
| 228 | path = path and path..d or d | ||
| 229 | local mode = lfs.attributes(path, "mode") | ||
| 230 | if not mode then | ||
| 231 | if not lfs.mkdir(path) then | ||
| 232 | return false | ||
| 233 | end | ||
| 234 | elseif mode ~= "directory" then | ||
| 235 | return false | ||
| 236 | end | ||
| 237 | end | ||
| 238 | return true | ||
| 239 | end | ||
| 240 | |||
| 241 | --- Remove a directory if it is empty. | ||
| 242 | -- Does not return errors (for example, if directory is not empty or | ||
| 243 | -- if already does not exist) | ||
| 244 | -- @param d string: pathname of directory to remove. | ||
| 245 | function remove_dir_if_empty(d) | ||
| 246 | assert(d) | ||
| 247 | lfs.rmdir(d) | ||
| 248 | end | ||
| 249 | |||
| 250 | --- Copy a file. | ||
| 251 | -- @param src string: Pathname of source | ||
| 252 | -- @param dest string: Pathname of destination | ||
| 253 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 254 | -- plus an error message. | ||
| 255 | function copy(src, dest) | ||
| 256 | assert(src and dest) | ||
| 257 | local destmode = lfs.attributes(dest, "mode") | ||
| 258 | if destmode == "directory" then | ||
| 259 | dest = dir.path(dest, dir.base_name(src)) | ||
| 260 | end | ||
| 261 | local src_h, err = io.open(src, "r") | ||
| 262 | if not src_h then return nil, err end | ||
| 263 | local dest_h, err = io.open(dest, "w+") | ||
| 264 | if not dest_h then src_h:close() return nil, err end | ||
| 265 | while true do | ||
| 266 | local block = src_h:read(8192) | ||
| 267 | if not block then break end | ||
| 268 | dest_h:write(block) | ||
| 269 | end | ||
| 270 | src_h:close() | ||
| 271 | dest_h:close() | ||
| 272 | return true | ||
| 273 | end | ||
| 274 | |||
| 275 | --- Implementation function for recursive copy of directory contents. | ||
| 276 | -- @param src string: Pathname of source | ||
| 277 | -- @param dest string: Pathname of destination | ||
| 278 | -- @return boolean or (boolean, string): true on success, false on failure | ||
| 279 | local function recursive_copy(src, dest) | ||
| 280 | local srcmode = lfs.attributes(src, "mode") | ||
| 281 | |||
| 282 | if srcmode == "file" then | ||
| 283 | local ok = fs_copy(src, dest) | ||
| 284 | if not ok then return false end | ||
| 285 | elseif srcmode == "directory" then | ||
| 286 | local subdir = dir.path(dest, dir.base_name(src)) | ||
| 287 | fs_make_dir(subdir) | ||
| 288 | for file in lfs.dir(src) do | ||
| 289 | if file ~= "." and file ~= ".." then | ||
| 290 | local ok = recursive_copy(dir.path(src, file), subdir) | ||
| 291 | if not ok then return false end | ||
| 292 | end | ||
| 293 | end | ||
| 294 | end | ||
| 295 | return true | ||
| 296 | end | ||
| 297 | |||
| 298 | --- Recursively copy the contents of a directory. | ||
| 299 | -- @param src string: Pathname of source | ||
| 300 | -- @param dest string: Pathname of destination | ||
| 301 | -- @return boolean or (boolean, string): true on success, false on failure, | ||
| 302 | -- plus an error message. | ||
| 303 | function copy_contents(src, dest) | ||
| 304 | assert(src and dest) | ||
| 305 | assert(lfs.attributes(src, "mode") == "directory") | ||
| 306 | |||
| 307 | for file in lfs.dir(src) do | ||
| 308 | local ok = recursive_copy(dir.path(src, file), dest) | ||
| 309 | if not ok then | ||
| 310 | return false, "Failed copying "..src.." to "..dest | ||
| 311 | end | ||
| 312 | end | ||
| 313 | return true | ||
| 314 | end | ||
| 315 | |||
| 316 | --- Implementation function for recursive removal of directories. | ||
| 317 | -- @param src string: Pathname of source | ||
| 318 | -- @param dest string: Pathname of destination | ||
| 319 | -- @return boolean or (boolean, string): true on success, | ||
| 320 | -- or nil and an error message on failure. | ||
| 321 | local function recursive_delete(src) | ||
| 322 | local srcmode = lfs.attributes(src, "mode") | ||
| 323 | |||
| 324 | if srcmode == "file" then | ||
| 325 | return os.remove(src) | ||
| 326 | elseif srcmode == "directory" then | ||
| 327 | for file in lfs.dir(src) do | ||
| 328 | if file ~= "." and file ~= ".." then | ||
| 329 | local ok, err = recursive_delete(dir.path(src, file)) | ||
| 330 | if not ok then return nil, err end | ||
| 331 | end | ||
| 332 | end | ||
| 333 | local ok, err = lfs.rmdir(src) | ||
| 334 | if not ok then return nil, err end | ||
| 335 | end | ||
| 336 | return true | ||
| 337 | end | ||
| 338 | |||
| 339 | --- Delete a file or a directory and all its contents. | ||
| 340 | -- For safety, this only accepts absolute paths. | ||
| 341 | -- @param arg string: Pathname of source | ||
| 342 | -- @return boolean: true on success, false on failure. | ||
| 343 | function delete(arg) | ||
| 344 | assert(arg) | ||
| 345 | assert(arg:sub(1,1) == "/") | ||
| 346 | return recursive_delete(arg) or false | ||
| 347 | end | ||
| 348 | |||
| 349 | --- List the contents of a directory. | ||
| 350 | -- @param at string or nil: directory to list (will be the current | ||
| 351 | -- directory if none is given). | ||
| 352 | -- @return table: an array of strings with the filenames representing | ||
| 353 | -- the contents of a directory. | ||
| 354 | function list_dir(at) | ||
| 355 | assert(type(at) == "string" or not at) | ||
| 356 | if not at then | ||
| 357 | at = fs_current_dir() | ||
| 358 | end | ||
| 359 | if not fs_is_dir(at) then | ||
| 360 | return {} | ||
| 361 | end | ||
| 362 | local result = {} | ||
| 363 | for file in lfs.dir(at) do | ||
| 364 | if file ~= "." and file ~= ".." then | ||
| 365 | table.insert(result, file) | ||
| 366 | end | ||
| 367 | end | ||
| 368 | return result | ||
| 369 | end | ||
| 370 | |||
| 371 | --- Implementation function for recursive find. | ||
| 372 | -- @param cwd string: Current working directory in recursion. | ||
| 373 | -- @param prefix string: Auxiliary prefix string to form pathname. | ||
| 374 | -- @param result table: Array of strings where results are collected. | ||
| 375 | local function recursive_find(cwd, prefix, result) | ||
| 376 | for file in lfs.dir(cwd) do | ||
| 377 | if file ~= "." and file ~= ".." then | ||
| 378 | local item = prefix .. file | ||
| 379 | table.insert(result, item) | ||
| 380 | if lfs.attributes(item, "mode") == "directory" then | ||
| 381 | recursive_find(item, item..dir_separator, result) | ||
| 382 | end | ||
| 383 | end | ||
| 384 | end | ||
| 385 | end | ||
| 386 | |||
| 387 | --- Recursively scan the contents of a directory. | ||
| 388 | -- @param at string or nil: directory to scan (will be the current | ||
| 389 | -- directory if none is given). | ||
| 390 | -- @return table: an array of strings with the filenames representing | ||
| 391 | -- the contents of a directory. | ||
| 392 | function find(at) | ||
| 393 | assert(type(at) == "string" or not at) | ||
| 394 | if not at then | ||
| 395 | at = fs_current_dir() | ||
| 396 | end | ||
| 397 | if not fs_is_dir(at) then | ||
| 398 | return {} | ||
| 399 | end | ||
| 400 | local result = {} | ||
| 401 | recursive_find(lfs.currentdir(), "", result) | ||
| 402 | return result | ||
| 403 | end | ||
| 404 | |||
| 405 | --- Test for existance of a file. | ||
| 406 | -- @param file string: filename to test | ||
| 407 | -- @return boolean: true if file exists, false otherwise. | ||
| 408 | function exists(file) | ||
| 409 | assert(file) | ||
| 410 | return type(lfs.attributes(file)) == "table" | ||
| 411 | end | ||
| 412 | |||
| 413 | --- Test is pathname is a directory. | ||
| 414 | -- @param file string: pathname to test | ||
| 415 | -- @return boolean: true if it is a directory, false otherwise. | ||
| 416 | function is_dir(file) | ||
| 417 | assert(file) | ||
| 418 | return lfs.attributes(file, "mode") == "directory" | ||
| 419 | end | ||
| 420 | |||
| 421 | --- Test is pathname is a regular file. | ||
| 422 | -- @param file string: pathname to test | ||
| 423 | -- @return boolean: true if it is a file, false otherwise. | ||
| 424 | function is_file(file) | ||
| 425 | assert(file) | ||
| 426 | return lfs.attributes(file, "mode") == "file" | ||
| 427 | end | ||
| 428 | |||
| 429 | function set_time(file, time) | ||
| 430 | return lfs.touch(file, time) | ||
| 431 | end | ||
| 432 | |||
| 433 | end | ||
| 434 | |||
| 435 | --------------------------------------------------------------------- | ||
| 436 | -- LuaZip functions | ||
| 437 | --------------------------------------------------------------------- | ||
| 438 | |||
| 439 | if zip_ok then | ||
| 440 | |||
| 441 | local function zip(zipfile, ...) | ||
| 442 | return zip.zip(zipfile, ...) | ||
| 443 | end | ||
| 444 | |||
| 445 | --- Uncompress files from a .zip archive. | ||
| 446 | -- @param zipfile string: pathname of .zip archive to be extracted. | ||
| 447 | -- @return boolean: true on success, false on failure. | ||
| 448 | function unzip(zipfile) | ||
| 449 | assert(zipfile) | ||
| 450 | -- FIXME!!!! | ||
| 451 | return fs_execute("unzip", zipfile) | ||
| 452 | end | ||
| 453 | |||
| 454 | end | ||
| 455 | |||
| 456 | --------------------------------------------------------------------- | ||
| 457 | -- LuaCurl functions | ||
| 458 | --------------------------------------------------------------------- | ||
| 459 | |||
| 460 | if curl_ok then | ||
| 461 | |||
| 462 | --- Download a remote file. | ||
| 463 | -- @param url string: URL to be fetched. | ||
| 464 | -- @param filename string or nil: this function attempts to detect the | ||
| 465 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 466 | -- if that is not correct (due to a redirection, for example), the local | ||
| 467 | -- filename can be given explicitly as this second argument. | ||
| 468 | -- @return boolean: true on success, false on failure. | ||
| 469 | function download(url, filename) | ||
| 470 | assert(type(url) == "string") | ||
| 471 | assert(type(filename) == "string" or not filename) | ||
| 472 | |||
| 473 | filename = filename or dir.base_name(url) | ||
| 474 | |||
| 475 | local c = curl.new() | ||
| 476 | if not c then return false end | ||
| 477 | local file = io.open(filename, "wb") | ||
| 478 | if not file then return false end | ||
| 479 | local ok = c:setopt(curl.OPT_WRITEFUNCTION, function (stream, buffer) | ||
| 480 | stream:write(buffer) | ||
| 481 | return string.len(buffer) | ||
| 482 | end) | ||
| 483 | ok = ok and c:setopt(curl.OPT_WRITEDATA, file) | ||
| 484 | ok = ok and c:setopt(curl.OPT_BUFFERSIZE, 5000) | ||
| 485 | ok = ok and c:setopt(curl.OPT_HTTPHEADER, "Connection: Keep-Alive") | ||
| 486 | ok = ok and c:setopt(curl.OPT_URL, url) | ||
| 487 | ok = ok and c:setopt(curl.OPT_CONNECTTIMEOUT, 15) | ||
| 488 | ok = ok and c:perform() | ||
| 489 | ok = ok and c:close() | ||
| 490 | file:close() | ||
| 491 | return ok | ||
| 492 | end | ||
| 493 | |||
| 494 | end | ||
| 495 | |||
| 496 | --------------------------------------------------------------------- | ||
| 497 | -- MD5 functions | ||
| 498 | --------------------------------------------------------------------- | ||
| 499 | |||
| 500 | if md5_ok then | ||
| 501 | |||
| 502 | --- Check the MD5 checksum for a file. | ||
| 503 | -- @param file string: The file to be checked. | ||
| 504 | -- @param md5sum string: The string with the expected MD5 checksum. | ||
| 505 | -- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false if not | ||
| 506 | -- or if it could not perform the check for any reason. | ||
| 507 | function check_md5(file, md5sum) | ||
| 508 | file = fs_absolute_name(file) | ||
| 509 | local file = io.open(file, "r") | ||
| 510 | if not file then return false end | ||
| 511 | local computed = md5.sumhexa(file:read("*a")) | ||
| 512 | file:close() | ||
| 513 | if not computed then | ||
| 514 | return false | ||
| 515 | end | ||
| 516 | if computed:match("^"..md5sum) then | ||
| 517 | return true | ||
| 518 | else | ||
| 519 | return false | ||
| 520 | end | ||
| 521 | end | ||
| 522 | |||
| 523 | end | ||
| 524 | |||
| 525 | --- Apply a patch. | ||
| 526 | -- @param patchname string: The filename of the patch. | ||
| 527 | function apply_patch(patchname, patchdata) | ||
| 528 | local p, all_ok = patch.read_patch(patchname, patchdata) | ||
| 529 | if not all_ok then | ||
| 530 | return nil, "Failed reading patch "..patchname | ||
| 531 | end | ||
| 532 | if p then | ||
| 533 | return patch.apply_patch(p, 1) | ||
| 534 | end | ||
| 535 | end | ||
| 536 | |||
| 537 | --- Unpack an archive. | ||
| 538 | -- Extract the contents of an archive, detecting its format by | ||
| 539 | -- filename extension. | ||
| 540 | -- @param archive string: Filename of archive. | ||
| 541 | -- @return boolean or (boolean, string): true on success, false and an error message on failure. | ||
| 542 | function unpack_archive(archive) | ||
| 543 | assert(type(archive) == "string") | ||
| 544 | |||
| 545 | local ok | ||
| 546 | if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then | ||
| 547 | -- ok = fs_execute("tar zxvpf ", archive) | ||
| 548 | ok = fs_execute_string("gunzip -c "..archive.."|tar -xf -") | ||
| 549 | elseif archive:match("%.tar%.bz2$") then | ||
| 550 | -- ok = fs_execute("tar jxvpf ", archive) | ||
| 551 | ok = fs_execute_string("bunzip2 -c "..archive.."|tar -xf -") | ||
| 552 | elseif archive:match("%.zip$") then | ||
| 553 | ok = fs_execute("unzip ", archive) | ||
| 554 | elseif archive:match("%.lua$") or archive:match("%.c$") then | ||
| 555 | -- Ignore .lua and .c files; they don't need to be extracted. | ||
| 556 | return true | ||
| 557 | else | ||
| 558 | local ext = archive:match(".*(%..*)") | ||
| 559 | return false, "Unrecognized filename extension "..(ext or "") | ||
| 560 | end | ||
| 561 | if not ok then | ||
| 562 | return false, "Failed extracting "..archive | ||
| 563 | end | ||
| 564 | return true | ||
| 565 | end | ||
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua index bf72988f..11378854 100644 --- a/src/luarocks/fs/unix.lua +++ b/src/luarocks/fs/unix.lua | |||
| @@ -6,43 +6,40 @@ local assert, type, table, io, package, math, os, ipairs = | |||
| 6 | module("luarocks.fs.unix", package.seeall) | 6 | module("luarocks.fs.unix", package.seeall) |
| 7 | 7 | ||
| 8 | local cfg = require("luarocks.cfg") | 8 | local cfg = require("luarocks.cfg") |
| 9 | local dir = require("luarocks.dir") | ||
| 9 | 10 | ||
| 10 | math.randomseed(os.time()) | 11 | math.randomseed(os.time()) |
| 11 | 12 | ||
| 12 | dir_stack = {} | 13 | dir_stack = {} |
| 13 | 14 | ||
| 14 | local fs_absolute_name, | 15 | local fs_absolute_name, |
| 15 | fs_base_name, | ||
| 16 | fs_copy, | 16 | fs_copy, |
| 17 | fs_current_dir, | 17 | fs_current_dir, |
| 18 | fs_dir_stack, | 18 | fs_dir_stack, |
| 19 | fs_execute, | 19 | fs_execute, |
| 20 | fs_execute_string, | 20 | fs_execute_string, |
| 21 | fs_is_dir, | 21 | fs_is_dir, |
| 22 | fs_is_file, | ||
| 22 | fs_make_dir, | 23 | fs_make_dir, |
| 23 | fs_make_path, | ||
| 24 | fs_exists, | 24 | fs_exists, |
| 25 | fs_find, | 25 | fs_find, |
| 26 | fs_Q | 26 | fs_Q |
| 27 | 27 | ||
| 28 | function init_fs_functions(impl) | 28 | function init_fs_functions(impl) |
| 29 | fs_absolute_name = impl.absolute_name | 29 | fs_absolute_name = impl.absolute_name |
| 30 | fs_base_name = impl.base_name | ||
| 31 | fs_copy = impl.copy | 30 | fs_copy = impl.copy |
| 32 | fs_current_dir = impl.current_dir | 31 | fs_current_dir = impl.current_dir |
| 33 | fs_dir_stack = impl.dir_stack | 32 | fs_dir_stack = impl.dir_stack |
| 34 | fs_execute = impl.execute | 33 | fs_execute = impl.execute |
| 35 | fs_execute_string = impl.execute_string | 34 | fs_execute_string = impl.execute_string |
| 36 | fs_is_dir = impl.is_dir | 35 | fs_is_dir = impl.is_dir |
| 36 | fs_is_file = impl.is_file | ||
| 37 | fs_make_dir = impl.make_dir | 37 | fs_make_dir = impl.make_dir |
| 38 | fs_make_path = impl.make_path | ||
| 39 | fs_exists = impl.exists | 38 | fs_exists = impl.exists |
| 40 | fs_find = impl.find | 39 | fs_find = impl.find |
| 41 | fs_Q = impl.Q | 40 | fs_Q = impl.Q |
| 42 | end | 41 | end |
| 43 | 42 | ||
| 44 | dir_separator = "/" | ||
| 45 | |||
| 46 | --- Quote argument for shell processing. | 43 | --- Quote argument for shell processing. |
| 47 | -- Adds single quotes and escapes. | 44 | -- Adds single quotes and escapes. |
| 48 | -- @param arg string: Unquoted argument. | 45 | -- @param arg string: Unquoted argument. |
| @@ -63,8 +60,8 @@ function current_dir() | |||
| 63 | current = pipe:read("*l") | 60 | current = pipe:read("*l") |
| 64 | pipe:close() | 61 | pipe:close() |
| 65 | end | 62 | end |
| 66 | for _, dir in ipairs(fs_dir_stack) do | 63 | for _, d in ipairs(fs_dir_stack) do |
| 67 | current = fs_absolute_name(dir, current) | 64 | current = fs_absolute_name(d, current) |
| 68 | end | 65 | end |
| 69 | return current | 66 | return current |
| 70 | end | 67 | end |
| @@ -103,10 +100,10 @@ end | |||
| 103 | -- Uses the module's internal dir stack. This does not have exact | 100 | -- Uses the module's internal dir stack. This does not have exact |
| 104 | -- semantics of chdir, as it does not handle errors the same way, | 101 | -- semantics of chdir, as it does not handle errors the same way, |
| 105 | -- but works well for our purposes for now. | 102 | -- but works well for our purposes for now. |
| 106 | -- @param dir string: The directory to switch to. | 103 | -- @param d string: The directory to switch to. |
| 107 | function change_dir(dir) | 104 | function change_dir(d) |
| 108 | assert(type(dir) == "string") | 105 | assert(type(d) == "string") |
| 109 | table.insert(fs_dir_stack, dir) | 106 | table.insert(fs_dir_stack, d) |
| 110 | end | 107 | end |
| 111 | 108 | ||
| 112 | --- Change directory to root. | 109 | --- Change directory to root. |
| @@ -118,26 +115,27 @@ end | |||
| 118 | 115 | ||
| 119 | --- Change working directory to the previous in the dir stack. | 116 | --- Change working directory to the previous in the dir stack. |
| 120 | function pop_dir() | 117 | function pop_dir() |
| 121 | table.remove(fs_dir_stack) | 118 | local d = table.remove(fs_dir_stack) |
| 119 | return d ~= nil | ||
| 122 | end | 120 | end |
| 123 | 121 | ||
| 124 | --- Create a directory if it does not already exist. | 122 | --- Create a directory if it does not already exist. |
| 125 | -- If any of the higher levels in the path name does not exist | 123 | -- If any of the higher levels in the path name does not exist |
| 126 | -- too, they are created as well. | 124 | -- too, they are created as well. |
| 127 | -- @param dir string: pathname of directory to create. | 125 | -- @param d string: pathname of directory to create. |
| 128 | -- @return boolean: true on success, false on failure. | 126 | -- @return boolean: true on success, false on failure. |
| 129 | function make_dir(dir) | 127 | function make_dir(d) |
| 130 | assert(dir) | 128 | assert(d) |
| 131 | return fs_execute("mkdir -p", dir) | 129 | return fs_execute("mkdir -p", d) |
| 132 | end | 130 | end |
| 133 | 131 | ||
| 134 | --- Remove a directory if it is empty. | 132 | --- Remove a directory if it is empty. |
| 135 | -- Does not return errors (for example, if directory is not empty or | 133 | -- Does not return errors (for example, if directory is not empty or |
| 136 | -- if already does not exist) | 134 | -- if already does not exist) |
| 137 | -- @param dir string: pathname of directory to remove. | 135 | -- @param dir string: pathname of directory to remove. |
| 138 | function remove_dir_if_empty(dir) | 136 | function remove_dir_if_empty(d) |
| 139 | assert(dir) | 137 | assert(d) |
| 140 | fs_execute_string("rmdir "..fs_Q(dir).." 1> /dev/null 2> /dev/null") | 138 | fs_execute_string("rmdir "..fs_Q(d).." 1> /dev/null 2> /dev/null") |
| 141 | end | 139 | end |
| 142 | 140 | ||
| 143 | --- Copy a file. | 141 | --- Copy a file. |
| @@ -183,7 +181,7 @@ end | |||
| 183 | -- directory if none is given). | 181 | -- directory if none is given). |
| 184 | -- @return table: an array of strings with the filenames representing | 182 | -- @return table: an array of strings with the filenames representing |
| 185 | -- the contents of a directory. | 183 | -- the contents of a directory. |
| 186 | function dir(at) | 184 | function list_dir(at) |
| 187 | assert(type(at) == "string" or not at) | 185 | assert(type(at) == "string" or not at) |
| 188 | if not at then | 186 | if not at then |
| 189 | at = fs_current_dir() | 187 | at = fs_current_dir() |
| @@ -263,6 +261,14 @@ function is_dir(file) | |||
| 263 | return fs_execute("test -d", file) | 261 | return fs_execute("test -d", file) |
| 264 | end | 262 | end |
| 265 | 263 | ||
| 264 | --- Test is pathname is a regular file. | ||
| 265 | -- @param file string: pathname to test | ||
| 266 | -- @return boolean: true if it is a regular file, false otherwise. | ||
| 267 | function is_file(file) | ||
| 268 | assert(file) | ||
| 269 | return fs_execute("test -f", file) | ||
| 270 | end | ||
| 271 | |||
| 266 | --- Download a remote file. | 272 | --- Download a remote file. |
| 267 | -- @param url string: URL to be fetched. | 273 | -- @param url string: URL to be fetched. |
| 268 | -- @param filename string or nil: this function attempts to detect the | 274 | -- @param filename string or nil: this function attempts to detect the |
| @@ -275,39 +281,17 @@ function download(url, filename) | |||
| 275 | assert(type(filename) == "string" or not filename) | 281 | assert(type(filename) == "string" or not filename) |
| 276 | 282 | ||
| 277 | if cfg.downloader == "wget" then | 283 | if cfg.downloader == "wget" then |
| 278 | if filename then | 284 | if filename then |
| 279 | return fs_execute("wget --quiet --continue --output-document ", filename, url) | 285 | return fs_execute("wget --quiet --continue --output-document ", filename, url) |
| 280 | else | 286 | else |
| 281 | return fs_execute("wget --quiet --continue ", url) | 287 | return fs_execute("wget --quiet --continue ", url) |
| 282 | end | 288 | end |
| 283 | elseif cfg.downloader == "curl" then | 289 | elseif cfg.downloader == "curl" then |
| 284 | filename = filename or fs_base_name(url) | 290 | filename = filename or dir.base_name(url) |
| 285 | return fs_execute_string("curl "..fs_Q(url).." 2> /dev/null 1> "..fs_Q(filename)) | 291 | return fs_execute_string("curl "..fs_Q(url).." 2> /dev/null 1> "..fs_Q(filename)) |
| 286 | end | 292 | end |
| 287 | end | 293 | end |
| 288 | 294 | ||
| 289 | --- Strip the path off a path+filename. | ||
| 290 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 291 | -- @return string: The filename without its path, such as "c". | ||
| 292 | function base_name(pathname) | ||
| 293 | assert(type(pathname) == "string") | ||
| 294 | |||
| 295 | local base = pathname:match(".*/([^/]*)") | ||
| 296 | return base or pathname | ||
| 297 | end | ||
| 298 | |||
| 299 | --- Strip the name off a path+filename. | ||
| 300 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 301 | -- @return string: The filename without its path, such as "/a/b/". | ||
| 302 | -- For entries such as "/a/b/", "/a/" is returned. If there are | ||
| 303 | -- no directory separators in input, "" is returned. | ||
| 304 | function dir_name(pathname) | ||
| 305 | assert(type(pathname) == "string") | ||
| 306 | |||
| 307 | local dir = pathname:gsub("/*$", ""):match("(.*/)[^/]*") | ||
| 308 | return dir or "" | ||
| 309 | end | ||
| 310 | |||
| 311 | --- Create a temporary directory. | 295 | --- Create a temporary directory. |
| 312 | -- @param name string: name pattern to use for avoiding conflicts | 296 | -- @param name string: name pattern to use for avoiding conflicts |
| 313 | -- when creating temporary directory. | 297 | -- when creating temporary directory. |
| @@ -315,7 +299,7 @@ end | |||
| 315 | function make_temp_dir(name) | 299 | function make_temp_dir(name) |
| 316 | assert(type(name) == "string") | 300 | assert(type(name) == "string") |
| 317 | 301 | ||
| 318 | local temp_dir = (os.getenv("TMP") or "/tmp") .. "/" .. name .. "-" .. tostring(math.floor(math.random() * 10000)) | 302 | local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) |
| 319 | if fs_make_dir(temp_dir) then | 303 | if fs_make_dir(temp_dir) then |
| 320 | return temp_dir | 304 | return temp_dir |
| 321 | else | 305 | else |
| @@ -323,9 +307,13 @@ function make_temp_dir(name) | |||
| 323 | end | 307 | end |
| 324 | end | 308 | end |
| 325 | 309 | ||
| 310 | function chmod(pathname, mode) | ||
| 311 | return fs_execute("chmod "..mode, pathname) | ||
| 312 | end | ||
| 313 | |||
| 326 | --- Apply a patch. | 314 | --- Apply a patch. |
| 327 | -- @param patchname string: The filename of the patch. | 315 | -- @param patchname string: The filename of the patch. |
| 328 | function patch(patchname) | 316 | function apply_patch(patchname) |
| 329 | return fs_execute("patch -p1 -f -i ", patchname) | 317 | return fs_execute("patch -p1 -f -i ", patchname) |
| 330 | end | 318 | end |
| 331 | 319 | ||
| @@ -417,27 +405,6 @@ function absolute_name(pathname, relative_to) | |||
| 417 | end | 405 | end |
| 418 | end | 406 | end |
| 419 | 407 | ||
| 420 | --- Describe a path in a cross-platform way. | ||
| 421 | -- Use this function to avoid platform-specific directory | ||
| 422 | -- separators in other modules. If the first item contains a | ||
| 423 | -- protocol descriptor (e.g. "http:"), paths are always constituted | ||
| 424 | -- with forward slashes. | ||
| 425 | -- @param ... strings representing directories | ||
| 426 | -- @return string: a string with a platform-specific representation | ||
| 427 | -- of the path. | ||
| 428 | function make_path(...) | ||
| 429 | local items = {...} | ||
| 430 | local i = 1 | ||
| 431 | while items[i] do | ||
| 432 | if items[i] == "" then | ||
| 433 | table.remove(items, i) | ||
| 434 | else | ||
| 435 | i = i + 1 | ||
| 436 | end | ||
| 437 | end | ||
| 438 | return table.concat(items, "/") | ||
| 439 | end | ||
| 440 | |||
| 441 | --- Split protocol and path from an URL or local pathname. | 408 | --- Split protocol and path from an URL or local pathname. |
| 442 | -- URLs should be in the "protocol://path" format. | 409 | -- URLs should be in the "protocol://path" format. |
| 443 | -- For local pathnames, "file" is returned as the protocol. | 410 | -- For local pathnames, "file" is returned as the protocol. |
| @@ -466,7 +433,7 @@ function wrap_script(file, dest) | |||
| 466 | assert(type(file) == "string") | 433 | assert(type(file) == "string") |
| 467 | assert(type(dest) == "string") | 434 | assert(type(dest) == "string") |
| 468 | 435 | ||
| 469 | local base = fs_base_name(file) | 436 | local base = dir.base_name(file) |
| 470 | local wrapname = dest.."/"..base | 437 | local wrapname = dest.."/"..base |
| 471 | local wrapper = io.open(wrapname, "w") | 438 | local wrapper = io.open(wrapname, "w") |
| 472 | if not wrapper then | 439 | if not wrapper then |
| @@ -476,7 +443,7 @@ function wrap_script(file, dest) | |||
| 476 | wrapper:write('LUA_PATH="'..package.path..';$LUA_PATH"\n') | 443 | wrapper:write('LUA_PATH="'..package.path..';$LUA_PATH"\n') |
| 477 | wrapper:write('LUA_CPATH="'..package.cpath..';$LUA_CPATH"\n') | 444 | wrapper:write('LUA_CPATH="'..package.cpath..';$LUA_CPATH"\n') |
| 478 | wrapper:write('export LUA_PATH LUA_CPATH\n') | 445 | wrapper:write('export LUA_PATH LUA_CPATH\n') |
| 479 | wrapper:write('exec "'..fs_make_path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" "$@"\n') | 446 | wrapper:write('exec "'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" "$@"\n') |
| 480 | wrapper:close() | 447 | wrapper:close() |
| 481 | if fs_execute("chmod +x",wrapname) then | 448 | if fs_execute("chmod +x",wrapname) then |
| 482 | return true | 449 | return true |
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua index 5702a3aa..cafdca7d 100644 --- a/src/luarocks/fs/win32.lua +++ b/src/luarocks/fs/win32.lua | |||
| @@ -4,24 +4,21 @@ | |||
| 4 | module("luarocks.fs.win32", package.seeall) | 4 | module("luarocks.fs.win32", package.seeall) |
| 5 | 5 | ||
| 6 | local cfg = require("luarocks.cfg") | 6 | local cfg = require("luarocks.cfg") |
| 7 | local dir = require("luarocks.dir") | ||
| 7 | 8 | ||
| 8 | local fs_base_name, | 9 | local fs_copy, |
| 9 | fs_copy, | ||
| 10 | fs_current_dir, | 10 | fs_current_dir, |
| 11 | fs_execute, | 11 | fs_execute, |
| 12 | fs_execute_string, | 12 | fs_execute_string, |
| 13 | fs_is_dir, | 13 | fs_is_dir, |
| 14 | fs_make_path, | ||
| 15 | fs_Q | 14 | fs_Q |
| 16 | 15 | ||
| 17 | function init_fs_functions(impl) | 16 | function init_fs_functions(impl) |
| 18 | fs_base_name = impl.base_name | ||
| 19 | fs_copy = impl.copy | 17 | fs_copy = impl.copy |
| 20 | fs_current_dir = impl.current_dir | 18 | fs_current_dir = impl.current_dir |
| 21 | fs_execute = impl.execute | 19 | fs_execute = impl.execute |
| 22 | fs_execute_string = impl.execute_string | 20 | fs_execute_string = impl.execute_string |
| 23 | fs_is_dir = impl.is_dir | 21 | fs_is_dir = impl.is_dir |
| 24 | fs_make_path = impl.make_path | ||
| 25 | fs_Q = impl.Q | 22 | fs_Q = impl.Q |
| 26 | end | 23 | end |
| 27 | 24 | ||
| @@ -39,9 +36,9 @@ function Q(arg) | |||
| 39 | return '"' .. arg:gsub('"', '\\"') .. '"' | 36 | return '"' .. arg:gsub('"', '\\"') .. '"' |
| 40 | end | 37 | end |
| 41 | 38 | ||
| 42 | local function command_at(dir, cmd) | 39 | local function command_at(directory, cmd) |
| 43 | local drive = dir:match("^([A-Za-z]:)") | 40 | local drive = directory:match("^([A-Za-z]:)") |
| 44 | cmd = "cd " .. fs_Q(dir) .. " & " .. cmd | 41 | cmd = "cd " .. fs_Q(directory) .. " & " .. cmd |
| 45 | if drive then | 42 | if drive then |
| 46 | cmd = drive .. " & " .. cmd | 43 | cmd = drive .. " & " .. cmd |
| 47 | end | 44 | end |
| @@ -78,6 +75,14 @@ function is_dir(file) | |||
| 78 | return fs_execute("chdir /D " .. fs_Q(file) .. " 2>NUL 1>NUL") | 75 | return fs_execute("chdir /D " .. fs_Q(file) .. " 2>NUL 1>NUL") |
| 79 | end | 76 | end |
| 80 | 77 | ||
| 78 | --- Test is pathname is a regular file. | ||
| 79 | -- @param file string: pathname to test | ||
| 80 | -- @return boolean: true if it is a regular file, false otherwise. | ||
| 81 | function is_dir(file) | ||
| 82 | assert(file) | ||
| 83 | return fs_execute("test -d" .. fs_Q(file) .. " 2>NUL 1>NUL") | ||
| 84 | end | ||
| 85 | |||
| 81 | --- Test is file/dir is writable. | 86 | --- Test is file/dir is writable. |
| 82 | -- @param file string: filename to test | 87 | -- @param file string: filename to test |
| 83 | -- @return boolean: true if file exists, false otherwise. | 88 | -- @return boolean: true if file exists, false otherwise. |
| @@ -102,21 +107,21 @@ end | |||
| 102 | --- Create a directory if it does not already exist. | 107 | --- Create a directory if it does not already exist. |
| 103 | -- If any of the higher levels in the path name does not exist | 108 | -- If any of the higher levels in the path name does not exist |
| 104 | -- too, they are created as well. | 109 | -- too, they are created as well. |
| 105 | -- @param dir string: pathname of directory to create. | 110 | -- @param d string: pathname of directory to create. |
| 106 | -- @return boolean: true on success, false on failure. | 111 | -- @return boolean: true on success, false on failure. |
| 107 | function make_dir(dir) | 112 | function make_dir(d) |
| 108 | assert(dir) | 113 | assert(d) |
| 109 | fs_execute("mkdir "..fs_Q(dir).." 1> NUL 2> NUL") | 114 | fs_execute("mkdir "..fs_Q(d).." 1> NUL 2> NUL") |
| 110 | return 1 | 115 | return 1 |
| 111 | end | 116 | end |
| 112 | 117 | ||
| 113 | --- Remove a directory if it is empty. | 118 | --- Remove a directory if it is empty. |
| 114 | -- Does not return errors (for example, if directory is not empty or | 119 | -- Does not return errors (for example, if directory is not empty or |
| 115 | -- if already does not exist) | 120 | -- if already does not exist) |
| 116 | -- @param dir string: pathname of directory to remove. | 121 | -- @param d string: pathname of directory to remove. |
| 117 | function remove_dir_if_empty(dir) | 122 | function remove_dir_if_empty(d) |
| 118 | assert(dir) | 123 | assert(d) |
| 119 | fs_execute_string("rmdir "..fs_Q(dir).." 1> NUL 2> NUL") | 124 | fs_execute_string("rmdir "..fs_Q(d).." 1> NUL 2> NUL") |
| 120 | end | 125 | end |
| 121 | 126 | ||
| 122 | --- Copy a file. | 127 | --- Copy a file. |
| @@ -164,7 +169,7 @@ end | |||
| 164 | -- directory if none is given). | 169 | -- directory if none is given). |
| 165 | -- @return table: an array of strings with the filenames representing | 170 | -- @return table: an array of strings with the filenames representing |
| 166 | -- the contents of a directory. | 171 | -- the contents of a directory. |
| 167 | function dir(at) | 172 | function list_dir(at) |
| 168 | assert(type(at) == "string" or not at) | 173 | assert(type(at) == "string" or not at) |
| 169 | if not at then | 174 | if not at then |
| 170 | at = fs_current_dir() | 175 | at = fs_current_dir() |
| @@ -226,16 +231,6 @@ function download(url, filename) | |||
| 226 | end | 231 | end |
| 227 | end | 232 | end |
| 228 | 233 | ||
| 229 | --- Strip the path off a path+filename. | ||
| 230 | -- @param pathname string: A path+name, such as "/a/b/c". | ||
| 231 | -- @return string: The filename without its path, such as "c". | ||
| 232 | function base_name(pathname) | ||
| 233 | assert(type(pathname) == "string") | ||
| 234 | |||
| 235 | local base = pathname:match(".*[/\\]([^/\\]*)") | ||
| 236 | return base or pathname | ||
| 237 | end | ||
| 238 | |||
| 239 | --- Strip the last extension of a filename. | 234 | --- Strip the last extension of a filename. |
| 240 | -- Example: "foo.tar.gz" becomes "foo.tar". | 235 | -- Example: "foo.tar.gz" becomes "foo.tar". |
| 241 | -- If filename has no dots, returns it unchanged. | 236 | -- If filename has no dots, returns it unchanged. |
| @@ -323,7 +318,7 @@ function wrap_script(file, dest) | |||
| 323 | assert(type(file) == "string") | 318 | assert(type(file) == "string") |
| 324 | assert(type(dest) == "string") | 319 | assert(type(dest) == "string") |
| 325 | 320 | ||
| 326 | local base = fs_base_name(file) | 321 | local base = dir.base_name(file) |
| 327 | local wrapname = dest.."/"..base..".bat" | 322 | local wrapname = dest.."/"..base..".bat" |
| 328 | local wrapper = io.open(wrapname, "w") | 323 | local wrapper = io.open(wrapname, "w") |
| 329 | if not wrapper then | 324 | if not wrapper then |
| @@ -333,7 +328,7 @@ function wrap_script(file, dest) | |||
| 333 | wrapper:write("setlocal\n") | 328 | wrapper:write("setlocal\n") |
| 334 | wrapper:write('set LUA_PATH='..package.path..";%LUA_PATH%\n") | 329 | wrapper:write('set LUA_PATH='..package.path..";%LUA_PATH%\n") |
| 335 | wrapper:write('set LUA_CPATH='..package.cpath..";%LUA_CPATH%\n") | 330 | wrapper:write('set LUA_CPATH='..package.cpath..";%LUA_CPATH%\n") |
| 336 | wrapper:write('"'..fs_make_path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" %*\n') | 331 | wrapper:write('"'..dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)..'" -lluarocks.require "'..file..'" %*\n') |
| 337 | wrapper:write("endlocal\n") | 332 | wrapper:write("endlocal\n") |
| 338 | wrapper:close() | 333 | wrapper:close() |
| 339 | return true | 334 | return true |
| @@ -353,7 +348,7 @@ function copy_binary(filename, dest) | |||
| 353 | return nil, err | 348 | return nil, err |
| 354 | end | 349 | end |
| 355 | local exe_pattern = "%.[Ee][Xx][Ee]$" | 350 | local exe_pattern = "%.[Ee][Xx][Ee]$" |
| 356 | local base = fs_base_name(filename) | 351 | local base = dir.base_name(filename) |
| 357 | if base:match(exe_pattern) then | 352 | if base:match(exe_pattern) then |
| 358 | base = base:gsub(exe_pattern, ".lua") | 353 | base = base:gsub(exe_pattern, ".lua") |
| 359 | local helpname = dest.."/"..base | 354 | local helpname = dest.."/"..base |
diff --git a/src/luarocks/install.lua b/src/luarocks/install.lua index b115a8a8..3ef32c99 100644 --- a/src/luarocks/install.lua +++ b/src/luarocks/install.lua | |||
| @@ -23,15 +23,17 @@ or a filename of a locally available rock. | |||
| 23 | 23 | ||
| 24 | --- Install a binary rock. | 24 | --- Install a binary rock. |
| 25 | -- @param rock_file string: local or remote filename of a rock. | 25 | -- @param rock_file string: local or remote filename of a rock. |
| 26 | -- @return boolean or (nil, string): True if succeeded or | 26 | -- @return boolean or (nil, string, [string]): True if succeeded or |
| 27 | -- nil and an error message. | 27 | -- nil and an error message and an optional error code. |
| 28 | function install_binary_rock(rock_file) | 28 | function install_binary_rock(rock_file) |
| 29 | assert(type(rock_file) == "string") | ||
| 30 | |||
| 29 | local name, version, arch = path.parse_rock_name(rock_file) | 31 | local name, version, arch = path.parse_rock_name(rock_file) |
| 30 | if not name then | 32 | if not name then |
| 31 | return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." | 33 | return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." |
| 32 | end | 34 | end |
| 33 | if arch ~= "all" and arch ~= cfg.arch then | 35 | if arch ~= "all" and arch ~= cfg.arch then |
| 34 | return nil, "Incompatible architecture "..arch | 36 | return nil, "Incompatible architecture "..arch, "arch" |
| 35 | end | 37 | end |
| 36 | if rep.is_installed(name, version) then | 38 | if rep.is_installed(name, version) then |
| 37 | rep.delete_version(name, version) | 39 | rep.delete_version(name, version) |
| @@ -40,23 +42,23 @@ function install_binary_rock(rock_file) | |||
| 40 | fs.delete(path.install_dir(name, version)) | 42 | fs.delete(path.install_dir(name, version)) |
| 41 | fs.remove_dir_if_empty(path.versions_dir(name)) | 43 | fs.remove_dir_if_empty(path.versions_dir(name)) |
| 42 | end) | 44 | end) |
| 43 | local ok, err = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) | 45 | local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) |
| 44 | if not ok then return nil, err end | 46 | if not ok then return nil, err, errcode end |
| 45 | ok, err = rep.install_bins(name, version) | 47 | ok, err = rep.install_bins(name, version) |
| 46 | 48 | ||
| 47 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(name, version)) | 49 | local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) |
| 48 | if err then | 50 | if err then |
| 49 | return nil, "Failed loading rockspec for installed package: "..err | 51 | return nil, "Failed loading rockspec for installed package: "..err, errcode |
| 50 | end | 52 | end |
| 51 | 53 | ||
| 52 | ok, err = deps.check_external_deps(rockspec, "install") | 54 | ok, err, errcode = deps.check_external_deps(rockspec, "install") |
| 53 | if err then | 55 | if err then |
| 54 | return nil, err | 56 | return nil, err, errcode |
| 55 | end | 57 | end |
| 56 | 58 | ||
| 57 | ok, err = deps.fulfill_dependencies(rockspec) | 59 | ok, err, errcode = deps.fulfill_dependencies(rockspec) |
| 58 | if err then | 60 | if err then |
| 59 | return nil, err | 61 | return nil, err, errcode |
| 60 | end | 62 | end |
| 61 | 63 | ||
| 62 | ok, err = rep.run_hook(rockspec, "post_install") | 64 | ok, err = rep.run_hook(rockspec, "post_install") |
diff --git a/src/luarocks/list.lua b/src/luarocks/list.lua index 1251653c..f918640c 100644 --- a/src/luarocks/list.lua +++ b/src/luarocks/list.lua | |||
| @@ -6,7 +6,7 @@ module("luarocks.list", package.seeall) | |||
| 6 | local search = require("luarocks.search") | 6 | local search = require("luarocks.search") |
| 7 | local cfg = require("luarocks.cfg") | 7 | local cfg = require("luarocks.cfg") |
| 8 | local util = require("luarocks.util") | 8 | local util = require("luarocks.util") |
| 9 | local fs = require("luarocks.fs") | 9 | local dir = require("luarocks.dir") |
| 10 | 10 | ||
| 11 | help_summary = "Lists currently installed rocks." | 11 | help_summary = "Lists currently installed rocks." |
| 12 | 12 | ||
| @@ -24,7 +24,7 @@ function run(...) | |||
| 24 | local query = search.make_query(filter or "", version) | 24 | local query = search.make_query(filter or "", version) |
| 25 | query.exact_name = false | 25 | query.exact_name = false |
| 26 | for _, tree in ipairs(cfg.rocks_trees) do | 26 | for _, tree in ipairs(cfg.rocks_trees) do |
| 27 | search.manifest_search(results, fs.make_path(tree, "rocks"), query) | 27 | search.manifest_search(results, dir.path(tree, "rocks"), query) |
| 28 | end | 28 | end |
| 29 | print() | 29 | print() |
| 30 | print("Installed rocks:") | 30 | print("Installed rocks:") |
diff --git a/src/luarocks/make.lua b/src/luarocks/make.lua index 1c116cdb..1365cf13 100644 --- a/src/luarocks/make.lua +++ b/src/luarocks/make.lua | |||
| @@ -32,7 +32,7 @@ function run(...) | |||
| 32 | assert(type(rockspec) == "string" or not rockspec) | 32 | assert(type(rockspec) == "string" or not rockspec) |
| 33 | 33 | ||
| 34 | if not rockspec then | 34 | if not rockspec then |
| 35 | local files = fs.dir(fs.current_dir()) | 35 | local files = fs.list_dir(fs.current_dir()) |
| 36 | for _, file in pairs(files) do | 36 | for _, file in pairs(files) do |
| 37 | if file:match(".rockspec$") then | 37 | if file:match(".rockspec$") then |
| 38 | if rockspec then | 38 | if rockspec then |
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua index 98772319..3e5790ae 100644 --- a/src/luarocks/manif.lua +++ b/src/luarocks/manif.lua | |||
| @@ -10,98 +10,35 @@ local deps = require("luarocks.deps") | |||
| 10 | local cfg = require("luarocks.cfg") | 10 | local cfg = require("luarocks.cfg") |
| 11 | local persist = require("luarocks.persist") | 11 | local persist = require("luarocks.persist") |
| 12 | local fetch = require("luarocks.fetch") | 12 | local fetch = require("luarocks.fetch") |
| 13 | local type_check = require("luarocks.type_check") | 13 | local dir = require("luarocks.dir") |
| 14 | 14 | local manif_core = require("luarocks.manif_core") | |
| 15 | manifest_cache = {} | ||
| 16 | |||
| 17 | --- Get all versions of a package listed in a manifest file. | ||
| 18 | -- @param name string: a package name. | ||
| 19 | -- @param manifest table or nil: a manifest table; if not given, the | ||
| 20 | -- default local manifest table is used. | ||
| 21 | -- @return table: An array of strings listing installed | ||
| 22 | -- versions of a package. | ||
| 23 | function get_versions(name, manifest) | ||
| 24 | assert(type(name) == "string") | ||
| 25 | assert(type(manifest) == "table" or not manifest) | ||
| 26 | |||
| 27 | if not manifest then | ||
| 28 | manifest = load_local_manifest(cfg.rocks_dir) | ||
| 29 | if not manifest then | ||
| 30 | return {} | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | local item = manifest.repository[name] | ||
| 35 | if item then | ||
| 36 | return util.keys(item) | ||
| 37 | end | ||
| 38 | return {} | ||
| 39 | end | ||
| 40 | |||
| 41 | --- Back-end function that actually loads the manifest | ||
| 42 | -- and stores it in the manifest cache. | ||
| 43 | -- @param file string: The local filename of the manifest file. | ||
| 44 | -- @param repo_url string: The repository identifier. | ||
| 45 | local function manifest_loader(file, repo_url, quick) | ||
| 46 | local manifest = persist.load_into_table(file) | ||
| 47 | if not manifest then | ||
| 48 | return nil, "Failed loading manifest for "..repo_url | ||
| 49 | end | ||
| 50 | if not quick then | ||
| 51 | local ok, err = type_check.type_check_manifest(manifest) | ||
| 52 | if not ok then | ||
| 53 | return nil, "Error checking manifest: "..err | ||
| 54 | end | ||
| 55 | end | ||
| 56 | |||
| 57 | manifest_cache[repo_url] = manifest | ||
| 58 | return manifest | ||
| 59 | end | ||
| 60 | 15 | ||
| 61 | --- Load a local or remote manifest describing a repository. | 16 | --- Load a local or remote manifest describing a repository. |
| 62 | -- All functions that use manifest tables assume they were obtained | 17 | -- All functions that use manifest tables assume they were obtained |
| 63 | -- through either this function or load_local_manifest. | 18 | -- through either this function or load_local_manifest. |
| 64 | -- @param repo_url string: URL or pathname for the repository. | 19 | -- @param repo_url string: URL or pathname for the repository. |
| 65 | -- @return table or (nil, string): A table representing the manifest, | 20 | -- @return table or (nil, string, [string]): A table representing the manifest, |
| 66 | -- or nil followed by an error message. | 21 | -- or nil followed by an error message and an optional error code. |
| 67 | function load_manifest(repo_url) | 22 | function load_manifest(repo_url) |
| 68 | assert(type(repo_url) == "string") | 23 | assert(type(repo_url) == "string") |
| 69 | 24 | ||
| 70 | if manifest_cache[repo_url] then | 25 | if manif_core.manifest_cache[repo_url] then |
| 71 | return manifest_cache[repo_url] | 26 | return manif_core.manifest_cache[repo_url] |
| 72 | end | 27 | end |
| 73 | 28 | ||
| 74 | local protocol, pathname = fs.split_url(repo_url) | 29 | local protocol, pathname = fs.split_url(repo_url) |
| 75 | if protocol == "file" then | 30 | if protocol == "file" then |
| 76 | pathname = fs.make_path(pathname, "manifest") | 31 | pathname = dir.path(pathname, "manifest") |
| 77 | else | 32 | else |
| 78 | local url = fs.make_path(repo_url, "manifest") | 33 | local url = dir.path(repo_url, "manifest") |
| 79 | local name = repo_url:gsub("[/:]","_") | 34 | local name = repo_url:gsub("[/:]","_") |
| 80 | local file, dir = fetch.fetch_url_at_temp_dir(url, "luarocks-manifest-"..name) | 35 | local file, err, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-manifest-"..name) |
| 81 | if not file then | 36 | if not file then |
| 82 | return nil, "Failed fetching manifest for "..repo_url | 37 | return nil, "Failed fetching manifest for "..repo_url, errcode |
| 83 | end | 38 | end |
| 84 | pathname = file | 39 | pathname = file |
| 85 | end | 40 | end |
| 86 | return manifest_loader(pathname, repo_url) | 41 | return manif_core.manifest_loader(pathname, repo_url) |
| 87 | end | ||
| 88 | |||
| 89 | --- Load a local manifest describing a repository. | ||
| 90 | -- All functions that use manifest tables assume they were obtained | ||
| 91 | -- through either this function or load_manifest. | ||
| 92 | -- @param repo_url string: URL or pathname for the repository. | ||
| 93 | -- @return table or (nil, string): A table representing the manifest, | ||
| 94 | -- or nil followed by an error message. | ||
| 95 | function load_local_manifest(repo_url) | ||
| 96 | assert(type(repo_url) == "string") | ||
| 97 | |||
| 98 | if manifest_cache[repo_url] then | ||
| 99 | return manifest_cache[repo_url] | ||
| 100 | end | ||
| 101 | |||
| 102 | local pathname = fs.make_path(repo_url, "manifest") | ||
| 103 | |||
| 104 | return manifest_loader(pathname, repo_url, true) | ||
| 105 | end | 42 | end |
| 106 | 43 | ||
| 107 | --- Sort function for ordering rock identifiers in a manifest's | 44 | --- Sort function for ordering rock identifiers in a manifest's |
| @@ -183,7 +120,7 @@ local function save_manifest(repo, manifest) | |||
| 183 | assert(type(repo) == "string") | 120 | assert(type(repo) == "string") |
| 184 | assert(type(manifest) == "table") | 121 | assert(type(manifest) == "table") |
| 185 | 122 | ||
| 186 | local filename = fs.make_path(repo, "manifest") | 123 | local filename = dir.path(repo, "manifest") |
| 187 | return persist.save_from_table(filename, manifest) | 124 | return persist.save_from_table(filename, manifest) |
| 188 | end | 125 | end |
| 189 | 126 | ||
| @@ -302,7 +239,7 @@ function make_manifest(repo) | |||
| 302 | local results = search.disk_search(repo, query) | 239 | local results = search.disk_search(repo, query) |
| 303 | 240 | ||
| 304 | local manifest = { repository = {}, modules = {}, commands = {} } | 241 | local manifest = { repository = {}, modules = {}, commands = {} } |
| 305 | manifest_cache[repo] = manifest | 242 | manif_core.manifest_cache[repo] = manifest |
| 306 | store_results(results, manifest) | 243 | store_results(results, manifest) |
| 307 | return save_manifest(repo, manifest) | 244 | return save_manifest(repo, manifest) |
| 308 | end | 245 | end |
| @@ -388,7 +325,7 @@ function make_index(repo) | |||
| 388 | end | 325 | end |
| 389 | local manifest = load_manifest(repo) | 326 | local manifest = load_manifest(repo) |
| 390 | files = fs.find(repo) | 327 | files = fs.find(repo) |
| 391 | local out = io.open(fs.make_path(repo, "index.html"), "w") | 328 | local out = io.open(dir.path(repo, "index.html"), "w") |
| 392 | out:write(index_header) | 329 | out:write(index_header) |
| 393 | for package, version_list in util.sortedpairs(manifest.repository) do | 330 | for package, version_list in util.sortedpairs(manifest.repository) do |
| 394 | local latest_rockspec = nil | 331 | local latest_rockspec = nil |
| @@ -414,7 +351,7 @@ function make_index(repo) | |||
| 414 | end | 351 | end |
| 415 | output = output .. index_package_end | 352 | output = output .. index_package_end |
| 416 | if latest_rockspec then | 353 | if latest_rockspec then |
| 417 | local rockspec = persist.load_into_table(fs.make_path(repo, latest_rockspec)) | 354 | local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec)) |
| 418 | local vars = { | 355 | local vars = { |
| 419 | anchor = package, | 356 | anchor = package, |
| 420 | package = rockspec.package, | 357 | package = rockspec.package, |
diff --git a/src/luarocks/manif_core.lua b/src/luarocks/manif_core.lua new file mode 100644 index 00000000..941c569d --- /dev/null +++ b/src/luarocks/manif_core.lua | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | |||
| 2 | --- Core functions for querying manifest files. | ||
| 3 | module("luarocks.manif_core", package.seeall) | ||
| 4 | |||
| 5 | local persist = require("luarocks.persist") | ||
| 6 | local type_check = require("luarocks.type_check") | ||
| 7 | local dir = require("luarocks.dir") | ||
| 8 | local util = require("luarocks.util") | ||
| 9 | local cfg = require("luarocks.cfg") | ||
| 10 | |||
| 11 | manifest_cache = {} | ||
| 12 | |||
| 13 | --- Back-end function that actually loads the manifest | ||
| 14 | -- and stores it in the manifest cache. | ||
| 15 | -- @param file string: The local filename of the manifest file. | ||
| 16 | -- @param repo_url string: The repository identifier. | ||
| 17 | function manifest_loader(file, repo_url, quick) | ||
| 18 | local manifest = persist.load_into_table(file) | ||
| 19 | if not manifest then | ||
| 20 | return nil, "Failed loading manifest for "..repo_url | ||
| 21 | end | ||
| 22 | if not quick then | ||
| 23 | local ok, err = type_check.type_check_manifest(manifest) | ||
| 24 | if not ok then | ||
| 25 | return nil, "Error checking manifest: "..err | ||
| 26 | end | ||
| 27 | end | ||
| 28 | |||
| 29 | manifest_cache[repo_url] = manifest | ||
| 30 | return manifest | ||
| 31 | end | ||
| 32 | |||
| 33 | --- Load a local manifest describing a repository. | ||
| 34 | -- All functions that use manifest tables assume they were obtained | ||
| 35 | -- through either this function or load_manifest. | ||
| 36 | -- @param repo_url string: URL or pathname for the repository. | ||
| 37 | -- @return table or (nil, string): A table representing the manifest, | ||
| 38 | -- or nil followed by an error message. | ||
| 39 | function load_local_manifest(repo_url) | ||
| 40 | assert(type(repo_url) == "string") | ||
| 41 | |||
| 42 | if manifest_cache[repo_url] then | ||
| 43 | return manifest_cache[repo_url] | ||
| 44 | end | ||
| 45 | |||
| 46 | local pathname = dir.path(repo_url, "manifest") | ||
| 47 | |||
| 48 | return manifest_loader(pathname, repo_url, true) | ||
| 49 | end | ||
| 50 | |||
| 51 | --- Get all versions of a package listed in a manifest file. | ||
| 52 | -- @param name string: a package name. | ||
| 53 | -- @param manifest table or nil: a manifest table; if not given, the | ||
| 54 | -- default local manifest table is used. | ||
| 55 | -- @return table: An array of strings listing installed | ||
| 56 | -- versions of a package. | ||
| 57 | function get_versions(name, manifest) | ||
| 58 | assert(type(name) == "string") | ||
| 59 | assert(type(manifest) == "table" or not manifest) | ||
| 60 | |||
| 61 | if not manifest then | ||
| 62 | manifest = load_local_manifest(cfg.rocks_dir) | ||
| 63 | if not manifest then | ||
| 64 | return {} | ||
| 65 | end | ||
| 66 | end | ||
| 67 | |||
| 68 | local item = manifest.repository[name] | ||
| 69 | if item then | ||
| 70 | return util.keys(item) | ||
| 71 | end | ||
| 72 | return {} | ||
| 73 | end | ||
diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index 3f64b1c0..7b1c68dd 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua | |||
| @@ -9,6 +9,7 @@ local fetch = require("luarocks.fetch") | |||
| 9 | local fs = require("luarocks.fs") | 9 | local fs = require("luarocks.fs") |
| 10 | local cfg = require("luarocks.cfg") | 10 | local cfg = require("luarocks.cfg") |
| 11 | local util = require("luarocks.util") | 11 | local util = require("luarocks.util") |
| 12 | local dir = require("luarocks.dir") | ||
| 12 | 13 | ||
| 13 | help_summary = "Create a rock, packing sources or binaries." | 14 | help_summary = "Create a rock, packing sources or binaries." |
| 14 | help_arguments = "{<rockspec>|<name> [<version>]}" | 15 | help_arguments = "{<rockspec>|<name> [<version>]}" |
| @@ -38,15 +39,15 @@ local function pack_source_rock(rockspec_file) | |||
| 38 | local name_version = rockspec.name .. "-" .. rockspec.version | 39 | local name_version = rockspec.name .. "-" .. rockspec.version |
| 39 | local rock_file = fs.absolute_name(name_version .. ".src.rock") | 40 | local rock_file = fs.absolute_name(name_version .. ".src.rock") |
| 40 | 41 | ||
| 41 | local source_file, dir = fetch.fetch_sources(rockspec, false) | 42 | local source_file, source_dir = fetch.fetch_sources(rockspec, false) |
| 42 | if not source_file then | 43 | if not source_file then |
| 43 | return nil, dir | 44 | return nil, source_dir |
| 44 | end | 45 | end |
| 45 | fs.change_dir(dir) | 46 | fs.change_dir(source_dir) |
| 46 | 47 | ||
| 47 | fs.delete(rock_file) | 48 | fs.delete(rock_file) |
| 48 | fs.copy(rockspec_file, dir) | 49 | fs.copy(rockspec_file, source_dir) |
| 49 | if not fs.zip(rock_file, fs.base_name(rockspec_file), fs.base_name(source_file)) then | 50 | if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then |
| 50 | return nil, "Failed packing "..rock_file | 51 | return nil, "Failed packing "..rock_file |
| 51 | end | 52 | end |
| 52 | fs.pop_dir() | 53 | fs.pop_dir() |
| @@ -87,7 +88,7 @@ local function pack_binary_rock(name, version) | |||
| 87 | rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") | 88 | rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") |
| 88 | end | 89 | end |
| 89 | fs.delete(rock_file) | 90 | fs.delete(rock_file) |
| 90 | if not fs.zip(rock_file, unpack(fs.dir())) then | 91 | if not fs.zip(rock_file, unpack(fs.list_dir())) then |
| 91 | return nil, "Failed packing "..rock_file | 92 | return nil, "Failed packing "..rock_file |
| 92 | end | 93 | end |
| 93 | fs.pop_dir() | 94 | fs.pop_dir() |
diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua index e236db01..ae9c7fae 100644 --- a/src/luarocks/path.lua +++ b/src/luarocks/path.lua | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | -- point where the layout of the local installation is defined in LuaRocks. | 4 | -- point where the layout of the local installation is defined in LuaRocks. |
| 5 | module("luarocks.path", package.seeall) | 5 | module("luarocks.path", package.seeall) |
| 6 | 6 | ||
| 7 | local fs = require("luarocks.fs") | 7 | local dir = require("luarocks.dir") |
| 8 | local cfg = require("luarocks.cfg") | 8 | local cfg = require("luarocks.cfg") |
| 9 | 9 | ||
| 10 | --- Infer rockspec filename from a rock filename. | 10 | --- Infer rockspec filename from a rock filename. |
| @@ -12,7 +12,7 @@ local cfg = require("luarocks.cfg") | |||
| 12 | -- @return string: Filename of the rockspec, without path. | 12 | -- @return string: Filename of the rockspec, without path. |
| 13 | function rockspec_name_from_rock(rock_name) | 13 | function rockspec_name_from_rock(rock_name) |
| 14 | assert(type(rock_name) == "string") | 14 | assert(type(rock_name) == "string") |
| 15 | local base_name = fs.base_name(rock_name) | 15 | local base_name = dir.base_name(rock_name) |
| 16 | return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" | 16 | return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" |
| 17 | end | 17 | end |
| 18 | 18 | ||
| @@ -25,7 +25,7 @@ function versions_dir(name, repo) | |||
| 25 | assert(type(name) == "string") | 25 | assert(type(name) == "string") |
| 26 | assert(not repo or type(repo) == "string") | 26 | assert(not repo or type(repo) == "string") |
| 27 | 27 | ||
| 28 | return fs.make_path(repo or cfg.rocks_dir, name) | 28 | return dir.path(repo or cfg.rocks_dir, name) |
| 29 | end | 29 | end |
| 30 | 30 | ||
| 31 | --- Get the local installation directory (prefix) for a package. | 31 | --- Get the local installation directory (prefix) for a package. |
| @@ -39,7 +39,7 @@ function install_dir(name, version, repo) | |||
| 39 | assert(type(version) == "string") | 39 | assert(type(version) == "string") |
| 40 | assert(not repo or type(repo) == "string") | 40 | assert(not repo or type(repo) == "string") |
| 41 | 41 | ||
| 42 | return fs.make_path(repo or cfg.rocks_dir, name, version) | 42 | return dir.path(repo or cfg.rocks_dir, name, version) |
| 43 | end | 43 | end |
| 44 | 44 | ||
| 45 | --- Get the local filename of the rockspec of an installed rock. | 45 | --- Get the local filename of the rockspec of an installed rock. |
| @@ -53,7 +53,7 @@ function rockspec_file(name, version, repo) | |||
| 53 | assert(type(version) == "string") | 53 | assert(type(version) == "string") |
| 54 | assert(not repo or type(repo) == "string") | 54 | assert(not repo or type(repo) == "string") |
| 55 | 55 | ||
| 56 | return fs.make_path(repo or cfg.rocks_dir, name, version, name.."-"..version..".rockspec") | 56 | return dir.path(repo or cfg.rocks_dir, name, version, name.."-"..version..".rockspec") |
| 57 | end | 57 | end |
| 58 | 58 | ||
| 59 | --- Get the local installation directory for C libraries of a package. | 59 | --- Get the local installation directory for C libraries of a package. |
| @@ -67,7 +67,7 @@ function lib_dir(name, version, repo) | |||
| 67 | assert(type(version) == "string") | 67 | assert(type(version) == "string") |
| 68 | assert(not repo or type(repo) == "string") | 68 | assert(not repo or type(repo) == "string") |
| 69 | 69 | ||
| 70 | return fs.make_path(repo or cfg.rocks_dir, name, version, "lib") | 70 | return dir.path(repo or cfg.rocks_dir, name, version, "lib") |
| 71 | end | 71 | end |
| 72 | 72 | ||
| 73 | --- Get the local installation directory for Lua modules of a package. | 73 | --- Get the local installation directory for Lua modules of a package. |
| @@ -81,7 +81,7 @@ function lua_dir(name, version, repo) | |||
| 81 | assert(type(version) == "string") | 81 | assert(type(version) == "string") |
| 82 | assert(not repo or type(repo) == "string") | 82 | assert(not repo or type(repo) == "string") |
| 83 | 83 | ||
| 84 | return fs.make_path(repo or cfg.rocks_dir, name, version, "lua") | 84 | return dir.path(repo or cfg.rocks_dir, name, version, "lua") |
| 85 | end | 85 | end |
| 86 | 86 | ||
| 87 | --- Get the local installation directory for documentation of a package. | 87 | --- Get the local installation directory for documentation of a package. |
| @@ -95,7 +95,7 @@ function doc_dir(name, version, repo) | |||
| 95 | assert(type(version) == "string") | 95 | assert(type(version) == "string") |
| 96 | assert(not repo or type(repo) == "string") | 96 | assert(not repo or type(repo) == "string") |
| 97 | 97 | ||
| 98 | return fs.make_path(repo or cfg.rocks_dir, name, version, "doc") | 98 | return dir.path(repo or cfg.rocks_dir, name, version, "doc") |
| 99 | end | 99 | end |
| 100 | 100 | ||
| 101 | --- Get the local installation directory for configuration files of a package. | 101 | --- Get the local installation directory for configuration files of a package. |
| @@ -109,7 +109,7 @@ function conf_dir(name, version, repo) | |||
| 109 | assert(type(version) == "string") | 109 | assert(type(version) == "string") |
| 110 | assert(not repo or type(repo) == "string") | 110 | assert(not repo or type(repo) == "string") |
| 111 | 111 | ||
| 112 | return fs.make_path(repo or cfg.rocks_dir, name, version, "conf") | 112 | return dir.path(repo or cfg.rocks_dir, name, version, "conf") |
| 113 | end | 113 | end |
| 114 | 114 | ||
| 115 | --- Get the local installation directory for command-line scripts | 115 | --- Get the local installation directory for command-line scripts |
| @@ -124,7 +124,7 @@ function bin_dir(name, version, repo) | |||
| 124 | assert(type(version) == "string") | 124 | assert(type(version) == "string") |
| 125 | assert(not repo or type(repo) == "string") | 125 | assert(not repo or type(repo) == "string") |
| 126 | 126 | ||
| 127 | return fs.make_path(repo or cfg.rocks_dir, name, version, "bin") | 127 | return dir.path(repo or cfg.rocks_dir, name, version, "bin") |
| 128 | end | 128 | end |
| 129 | 129 | ||
| 130 | --- Extract name, version and arch of a rock filename. | 130 | --- Extract name, version and arch of a rock filename. |
| @@ -133,7 +133,7 @@ end | |||
| 133 | -- of rock, or nil if name could not be parsed | 133 | -- of rock, or nil if name could not be parsed |
| 134 | function parse_rock_name(rock_file) | 134 | function parse_rock_name(rock_file) |
| 135 | assert(type(rock_file) == "string") | 135 | assert(type(rock_file) == "string") |
| 136 | return fs.base_name(rock_file):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") | 136 | return dir.base_name(rock_file):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") |
| 137 | end | 137 | end |
| 138 | 138 | ||
| 139 | --- Extract name and version of a rockspec filename. | 139 | --- Extract name and version of a rockspec filename. |
| @@ -142,7 +142,7 @@ end | |||
| 142 | -- of rockspec, or nil if name could not be parsed | 142 | -- of rockspec, or nil if name could not be parsed |
| 143 | function parse_rockspec_name(rockspec_file) | 143 | function parse_rockspec_name(rockspec_file) |
| 144 | assert(type(rockspec_file) == "string") | 144 | assert(type(rockspec_file) == "string") |
| 145 | return fs.base_name(rockspec_file):match("(.*)-([^-]+-%d+)%.(rockspec)") | 145 | return dir.base_name(rockspec_file):match("(.*)-([^-]+-%d+)%.(rockspec)") |
| 146 | end | 146 | end |
| 147 | 147 | ||
| 148 | --- Make a rockspec or rock URL. | 148 | --- Make a rockspec or rock URL. |
| @@ -159,13 +159,13 @@ function make_url(pathname, name, version, arch) | |||
| 159 | 159 | ||
| 160 | local filename = name.."-"..version | 160 | local filename = name.."-"..version |
| 161 | if arch == "installed" then | 161 | if arch == "installed" then |
| 162 | filename = fs.make_path(name, version, filename..".rockspec") | 162 | filename = dir.path(name, version, filename..".rockspec") |
| 163 | elseif arch == "rockspec" then | 163 | elseif arch == "rockspec" then |
| 164 | filename = filename..".rockspec" | 164 | filename = filename..".rockspec" |
| 165 | else | 165 | else |
| 166 | filename = filename.."."..arch..".rock" | 166 | filename = filename.."."..arch..".rock" |
| 167 | end | 167 | end |
| 168 | return fs.make_path(pathname, filename) | 168 | return dir.path(pathname, filename) |
| 169 | end | 169 | end |
| 170 | 170 | ||
| 171 | --- Convert a pathname to a module identifier. | 171 | --- Convert a pathname to a module identifier. |
| @@ -180,7 +180,7 @@ function path_to_module(file) | |||
| 180 | 180 | ||
| 181 | local name = file:match("(.*)%."..cfg.lua_extension.."$") | 181 | local name = file:match("(.*)%."..cfg.lua_extension.."$") |
| 182 | if name then | 182 | if name then |
| 183 | name = name:gsub(fs.dir_separator, ".") | 183 | name = name:gsub(dir.separator, ".") |
| 184 | local init = name:match("(.*)%.init$") | 184 | local init = name:match("(.*)%.init$") |
| 185 | if init then | 185 | if init then |
| 186 | name = init | 186 | name = init |
| @@ -188,7 +188,7 @@ function path_to_module(file) | |||
| 188 | else | 188 | else |
| 189 | name = file:match("(.*)%."..cfg.lib_extension.."$") | 189 | name = file:match("(.*)%."..cfg.lib_extension.."$") |
| 190 | if name then | 190 | if name then |
| 191 | name = name:gsub(fs.dir_separator, ".") | 191 | name = name:gsub(dir.separator, ".") |
| 192 | end | 192 | end |
| 193 | end | 193 | end |
| 194 | return name | 194 | return name |
| @@ -200,7 +200,7 @@ end | |||
| 200 | -- @return string: A directory name using the platform's separator. | 200 | -- @return string: A directory name using the platform's separator. |
| 201 | function module_to_path(mod) | 201 | function module_to_path(mod) |
| 202 | assert(type(mod) == "string") | 202 | assert(type(mod) == "string") |
| 203 | return (mod:gsub("[^.]*$", ""):gsub("%.", fs.dir_separator)) | 203 | return (mod:gsub("[^.]*$", ""):gsub("%.", dir.separator)) |
| 204 | end | 204 | end |
| 205 | 205 | ||
| 206 | --- Set up path-related variables for a given rock. | 206 | --- Set up path-related variables for a given rock. |
diff --git a/src/luarocks/rep.lua b/src/luarocks/rep.lua index b5797803..304832f1 100644 --- a/src/luarocks/rep.lua +++ b/src/luarocks/rep.lua | |||
| @@ -6,6 +6,7 @@ local fs = require("luarocks.fs") | |||
| 6 | local path = require("luarocks.path") | 6 | local path = require("luarocks.path") |
| 7 | local cfg = require("luarocks.cfg") | 7 | local cfg = require("luarocks.cfg") |
| 8 | local util = require("luarocks.util") | 8 | local util = require("luarocks.util") |
| 9 | local dir = require("luarocks.dir") | ||
| 9 | 10 | ||
| 10 | --- Get all installed versions of a package. | 11 | --- Get all installed versions of a package. |
| 11 | -- @param name string: a package name. | 12 | -- @param name string: a package name. |
| @@ -14,7 +15,7 @@ local util = require("luarocks.util") | |||
| 14 | function get_versions(name) | 15 | function get_versions(name) |
| 15 | assert(type(name) == "string") | 16 | assert(type(name) == "string") |
| 16 | 17 | ||
| 17 | local dirs = fs.dir(path.versions_dir(name)) | 18 | local dirs = fs.list_dir(path.versions_dir(name)) |
| 18 | return (dirs and #dirs > 0) and dirs or nil | 19 | return (dirs and #dirs > 0) and dirs or nil |
| 19 | end | 20 | end |
| 20 | 21 | ||
| @@ -41,7 +42,7 @@ function delete_version(name, version) | |||
| 41 | 42 | ||
| 42 | fs.delete(path.install_dir(name, version)) | 43 | fs.delete(path.install_dir(name, version)) |
| 43 | if not get_versions(name) then | 44 | if not get_versions(name) then |
| 44 | fs.delete(fs.make_path(cfg.rocks_dir, name)) | 45 | fs.delete(dir.path(cfg.rocks_dir, name)) |
| 45 | end | 46 | end |
| 46 | end | 47 | end |
| 47 | 48 | ||
| @@ -50,7 +51,7 @@ end | |||
| 50 | function delete_bin(command) | 51 | function delete_bin(command) |
| 51 | assert(type(command) == "string") | 52 | assert(type(command) == "string") |
| 52 | 53 | ||
| 53 | fs.delete(fs.make_path(cfg.scripts_dir, command)) | 54 | fs.delete(dir.path(cfg.scripts_dir, command)) |
| 54 | end | 55 | end |
| 55 | 56 | ||
| 56 | --- Install bin entries in the repository bin dir. | 57 | --- Install bin entries in the repository bin dir. |
| @@ -71,9 +72,9 @@ function install_bins(name, version, single_file) | |||
| 71 | if not ok then | 72 | if not ok then |
| 72 | return nil, "Could not create "..cfg.scripts_dir | 73 | return nil, "Could not create "..cfg.scripts_dir |
| 73 | end | 74 | end |
| 74 | local files = single_file and {single_file} or fs.dir(bindir) | 75 | local files = single_file and {single_file} or fs.list_dir(bindir) |
| 75 | for _, file in pairs(files) do | 76 | for _, file in pairs(files) do |
| 76 | local fullname = fs.make_path(bindir, file) | 77 | local fullname = dir.path(bindir, file) |
| 77 | local match = file:match("%.lua$") | 78 | local match = file:match("%.lua$") |
| 78 | local file | 79 | local file |
| 79 | if not match then | 80 | if not match then |
| @@ -140,7 +141,7 @@ function package_commands(package, version) | |||
| 140 | local bins = fs.find(bindir) | 141 | local bins = fs.find(bindir) |
| 141 | for _, file in ipairs(bins) do | 142 | for _, file in ipairs(bins) do |
| 142 | if file then | 143 | if file then |
| 143 | result[file] = fs.make_path(bindir, file) | 144 | result[file] = dir.path(bindir, file) |
| 144 | end | 145 | end |
| 145 | end | 146 | end |
| 146 | return result | 147 | return result |
| @@ -159,7 +160,7 @@ function is_binary_rock(name, version) | |||
| 159 | end | 160 | end |
| 160 | if fs.exists(bin_dir) then | 161 | if fs.exists(bin_dir) then |
| 161 | for _, name in pairs(fs.find(bin_dir)) do | 162 | for _, name in pairs(fs.find(bin_dir)) do |
| 162 | if fs.is_actual_binary(fs.make_path(bin_dir, name)) then | 163 | if fs.is_actual_binary(dir.path(bin_dir, name)) then |
| 163 | return true | 164 | return true |
| 164 | end | 165 | end |
| 165 | end | 166 | end |
diff --git a/src/luarocks/search.lua b/src/luarocks/search.lua index a3ac68f3..eae3809f 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/search.lua | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | module("luarocks.search", package.seeall) | 4 | module("luarocks.search", package.seeall) |
| 5 | 5 | ||
| 6 | local fs = require("luarocks.fs") | 6 | local fs = require("luarocks.fs") |
| 7 | local dir = require("luarocks.dir") | ||
| 7 | local path = require("luarocks.path") | 8 | local path = require("luarocks.path") |
| 8 | local manif = require("luarocks.manif") | 9 | local manif = require("luarocks.manif") |
| 9 | local deps = require("luarocks.deps") | 10 | local deps = require("luarocks.deps") |
| @@ -133,14 +134,14 @@ function disk_search(repo, query, results) | |||
| 133 | end | 134 | end |
| 134 | query_arch_as_table(query) | 135 | query_arch_as_table(query) |
| 135 | 136 | ||
| 136 | for _, name in pairs(fs.dir(repo)) do | 137 | for _, name in pairs(fs.list_dir(repo)) do |
| 137 | local pathname = fs.make_path(repo, name) | 138 | local pathname = dir.path(repo, name) |
| 138 | local rname, rversion, rarch = path.parse_rock_name(name) | 139 | local rname, rversion, rarch = path.parse_rock_name(name) |
| 139 | if not rname then | 140 | if not rname then |
| 140 | rname, rversion, rarch = path.parse_rockspec_name(name) | 141 | rname, rversion, rarch = path.parse_rockspec_name(name) |
| 141 | end | 142 | end |
| 142 | if fs.is_dir(pathname) then | 143 | if fs.is_dir(pathname) then |
| 143 | for _, version in pairs(fs.dir(pathname)) do | 144 | for _, version in pairs(fs.list_dir(pathname)) do |
| 144 | if version:match("-%d+$") then | 145 | if version:match("-%d+$") then |
| 145 | store_if_match(results, repo, name, version, "installed", query) | 146 | store_if_match(results, repo, name, version, "installed", query) |
| 146 | end | 147 | end |
diff --git a/src/luarocks/tools/patch.lua b/src/luarocks/tools/patch.lua new file mode 100644 index 00000000..4fc5c3b5 --- /dev/null +++ b/src/luarocks/tools/patch.lua | |||
| @@ -0,0 +1,708 @@ | |||
| 1 | -- patch.lua - Patch utility to apply unified diffs | ||
| 2 | -- | ||
| 3 | -- http://lua-users.org/wiki/LuaPatch | ||
| 4 | -- | ||
| 5 | -- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license). | ||
| 6 | -- Code is heavilly based on the Python-based patch.py version 8.06-1 | ||
| 7 | -- Copyright (c) 2008 rainforce.org, MIT License | ||
| 8 | -- Project home: http://code.google.com/p/python-patch/ . | ||
| 9 | |||
| 10 | module("luarocks.tools.patch", package.seeall) | ||
| 11 | |||
| 12 | local version = '0.1' | ||
| 13 | |||
| 14 | local io = io | ||
| 15 | local os = os | ||
| 16 | local string = string | ||
| 17 | local table = table | ||
| 18 | local format = string.format | ||
| 19 | |||
| 20 | -- logging | ||
| 21 | local debugmode = false | ||
| 22 | local function debug(s) end | ||
| 23 | local function info(s) end | ||
| 24 | local function warning(s) io.stderr:write(s .. '\n') end | ||
| 25 | |||
| 26 | -- Returns boolean whether string s2 starts with string s. | ||
| 27 | local function startswith(s, s2) | ||
| 28 | return s:sub(1, #s2) == s2 | ||
| 29 | end | ||
| 30 | |||
| 31 | -- Returns boolean whether string s2 ends with string s. | ||
| 32 | local function endswith(s, s2) | ||
| 33 | return #s >= #s2 and s:sub(#s-#s2+1) == s2 | ||
| 34 | end | ||
| 35 | |||
| 36 | -- Returns string s after filtering out any new-line characters from end. | ||
| 37 | local function endlstrip(s) | ||
| 38 | return s:gsub('[\r\n]+$', '') | ||
| 39 | end | ||
| 40 | |||
| 41 | -- Returns shallow copy of table t. | ||
| 42 | local function table_copy(t) | ||
| 43 | local t2 = {} | ||
| 44 | for k,v in pairs(t) do t2[k] = v end | ||
| 45 | return t2 | ||
| 46 | end | ||
| 47 | |||
| 48 | -- Returns boolean whether array t contains value v. | ||
| 49 | local function array_contains(t, v) | ||
| 50 | for _,v2 in ipairs(t) do if v == v2 then return true end end | ||
| 51 | return false | ||
| 52 | end | ||
| 53 | |||
| 54 | local function exists(filename) | ||
| 55 | local fh = io.open(filename) | ||
| 56 | local result = fh ~= nil | ||
| 57 | if fh then fh:close() end | ||
| 58 | return result | ||
| 59 | end | ||
| 60 | local function isfile() return true end --FIX? | ||
| 61 | |||
| 62 | local function read_file(filename) | ||
| 63 | local fh, err, oserr = io.open(filename, 'rb') | ||
| 64 | if not fh then return fh, err, oserr end | ||
| 65 | local data, err, oserr = fh:read'*a' | ||
| 66 | fh:close() | ||
| 67 | if not data then return nil, err, oserr end | ||
| 68 | return data | ||
| 69 | end | ||
| 70 | |||
| 71 | local function write_file(filename, data) | ||
| 72 | local fh, err, oserr = io.open(filename 'wb') | ||
| 73 | if not fh then return fh, err, oserr end | ||
| 74 | local status, err, oserr = fh:write(data) | ||
| 75 | fh:close() | ||
| 76 | if not status then return nil, err, oserr end | ||
| 77 | return true | ||
| 78 | end | ||
| 79 | |||
| 80 | local function file_copy(src, dest) | ||
| 81 | local data, err, oserr = read_file(src) | ||
| 82 | if not data then return data, err, oserr end | ||
| 83 | local status, err, oserr = write_file(dest) | ||
| 84 | if not status then return status, err, oserr end | ||
| 85 | return true | ||
| 86 | end | ||
| 87 | |||
| 88 | local function string_as_file(s) | ||
| 89 | return { | ||
| 90 | at = 0, | ||
| 91 | str = s, | ||
| 92 | len = #s, | ||
| 93 | eof = false, | ||
| 94 | read = function(self, n) | ||
| 95 | if self.eof then return nil end | ||
| 96 | local chunk = self.str:sub(at, at+n) | ||
| 97 | self.at = self.at + n | ||
| 98 | if at > self.len then | ||
| 99 | self.eof = true | ||
| 100 | end | ||
| 101 | end, | ||
| 102 | close = function(self) | ||
| 103 | self.eof = true | ||
| 104 | end, | ||
| 105 | } | ||
| 106 | end | ||
| 107 | |||
| 108 | -- | ||
| 109 | -- file_lines(f) is similar to f:lines() for file f. | ||
| 110 | -- The main difference is that read_lines includes | ||
| 111 | -- new-line character sequences ("\n", "\r\n", "\r"), | ||
| 112 | -- if any, at the end of each line. Embedded "\0" are also handled. | ||
| 113 | -- Caution: The newline behavior can depend on whether f is opened | ||
| 114 | -- in binary or ASCII mode. | ||
| 115 | -- (file_lines - version 20080913) | ||
| 116 | -- | ||
| 117 | local function file_lines(f) | ||
| 118 | local CHUNK_SIZE = 1024 | ||
| 119 | local buffer = "" | ||
| 120 | local pos_beg = 1 | ||
| 121 | return function() | ||
| 122 | local pos, chars | ||
| 123 | while 1 do | ||
| 124 | pos, chars = buffer:match('()([\r\n].)', pos_beg) | ||
| 125 | if pos or not f then | ||
| 126 | break | ||
| 127 | elseif f then | ||
| 128 | local chunk = f:read(CHUNK_SIZE) | ||
| 129 | if chunk then | ||
| 130 | buffer = buffer:sub(pos_beg) .. chunk | ||
| 131 | pos_beg = 1 | ||
| 132 | else | ||
| 133 | f = nil | ||
| 134 | end | ||
| 135 | end | ||
| 136 | end | ||
| 137 | if not pos then | ||
| 138 | pos = #buffer | ||
| 139 | elseif chars == '\r\n' then | ||
| 140 | pos = pos + 1 | ||
| 141 | end | ||
| 142 | local line = buffer:sub(pos_beg, pos) | ||
| 143 | pos_beg = pos + 1 | ||
| 144 | if #line > 0 then | ||
| 145 | return line | ||
| 146 | end | ||
| 147 | end | ||
| 148 | end | ||
| 149 | |||
| 150 | local function match_linerange(line) | ||
| 151 | local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") | ||
| 152 | if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end | ||
| 153 | if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end | ||
| 154 | if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end | ||
| 155 | return m1, m2, m3, m4 | ||
| 156 | end | ||
| 157 | |||
| 158 | function read_patch(filename, data) | ||
| 159 | -- define possible file regions that will direct the parser flow | ||
| 160 | local state = 'header' | ||
| 161 | -- 'header' - comments before the patch body | ||
| 162 | -- 'filenames' - lines starting with --- and +++ | ||
| 163 | -- 'hunkhead' - @@ -R +R @@ sequence | ||
| 164 | -- 'hunkbody' | ||
| 165 | -- 'hunkskip' - skipping invalid hunk mode | ||
| 166 | |||
| 167 | local all_ok = true | ||
| 168 | local lineends = {lf=0, crlf=0, cr=0} | ||
| 169 | local files = {source={}, target={}, hunks={}, fileends={}, hunkends={}} | ||
| 170 | local nextfileno = 0 | ||
| 171 | local nexthunkno = 0 --: even if index starts with 0 user messages | ||
| 172 | -- number hunks from 1 | ||
| 173 | |||
| 174 | -- hunkinfo holds parsed values, hunkactual - calculated | ||
| 175 | local hunkinfo = { | ||
| 176 | startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil, | ||
| 177 | invalid=false, text={} | ||
| 178 | } | ||
| 179 | local hunkactual = {linessrc=nil, linestgt=nil} | ||
| 180 | |||
| 181 | info(format("reading patch %s", filename)) | ||
| 182 | |||
| 183 | local fp | ||
| 184 | if data then | ||
| 185 | fp = string_as_file(data) | ||
| 186 | else | ||
| 187 | fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) | ||
| 188 | end | ||
| 189 | local lineno = 0 | ||
| 190 | for line in file_lines(fp) do | ||
| 191 | lineno = lineno + 1 | ||
| 192 | if state == 'header' then | ||
| 193 | if startswith(line, "--- ") then | ||
| 194 | state = 'filenames' | ||
| 195 | end | ||
| 196 | -- state is 'header' or 'filenames' | ||
| 197 | end | ||
| 198 | if state == 'hunkbody' then | ||
| 199 | -- skip hunkskip and hunkbody code until definition of hunkhead read | ||
| 200 | |||
| 201 | -- process line first | ||
| 202 | if line:match"^[- +\\]" or line:match"^[\r\n]*$" then | ||
| 203 | -- gather stats about line endings | ||
| 204 | local he = files.hunkends[nextfileno] | ||
| 205 | if endswith(line, "\r\n") then | ||
| 206 | he.crlf = he.crlf + 1 | ||
| 207 | elseif endswith(line, "\n") then | ||
| 208 | he.lf = he.lf + 1 | ||
| 209 | elseif endswith(line, "\r") then | ||
| 210 | he.cr = he.cr + 1 | ||
| 211 | end | ||
| 212 | if startswith(line, "-") then | ||
| 213 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
| 214 | elseif startswith(line, "+") then | ||
| 215 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
| 216 | elseif startswith(line, "\\") then | ||
| 217 | -- nothing | ||
| 218 | else | ||
| 219 | hunkactual.linessrc = hunkactual.linessrc + 1 | ||
| 220 | hunkactual.linestgt = hunkactual.linestgt + 1 | ||
| 221 | end | ||
| 222 | table.insert(hunkinfo.text, line) | ||
| 223 | -- todo: handle \ No newline cases | ||
| 224 | else | ||
| 225 | warning(format("invalid hunk no.%d at %d for target file %s", | ||
| 226 | nexthunkno, lineno, files.target[nextfileno])) | ||
| 227 | -- add hunk status node | ||
| 228 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 229 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
| 230 | all_ok = false | ||
| 231 | state = 'hunkskip' | ||
| 232 | end | ||
| 233 | |||
| 234 | -- check exit conditions | ||
| 235 | if hunkactual.linessrc > hunkinfo.linessrc or | ||
| 236 | hunkactual.linestgt > hunkinfo.linestgt | ||
| 237 | then | ||
| 238 | warning(format("extra hunk no.%d lines at %d for target %s", | ||
| 239 | nexthunkno, lineno, files.target[nextfileno])) | ||
| 240 | -- add hunk status node | ||
| 241 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 242 | files.hunks[nextfileno][nexthunkno].invalid = true | ||
| 243 | state = 'hunkskip' | ||
| 244 | elseif hunkinfo.linessrc == hunkactual.linessrc and | ||
| 245 | hunkinfo.linestgt == hunkactual.linestgt | ||
| 246 | then | ||
| 247 | table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) | ||
| 248 | state = 'hunkskip' | ||
| 249 | |||
| 250 | -- detect mixed window/unix line ends | ||
| 251 | local ends = files.hunkends[nextfileno] | ||
| 252 | if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) + | ||
| 253 | (ends.lf~=0 and 1 or 0) > 1 | ||
| 254 | then | ||
| 255 | warning(format("inconsistent line ends in patch hunks for %s", | ||
| 256 | files.source[nextfileno])) | ||
| 257 | end | ||
| 258 | if debugmode then | ||
| 259 | local debuglines = {crlf=ends.crlf, lf=ends.lf, cr=ends.cr, | ||
| 260 | file=files.target[nextfileno], hunk=nexthunkno} | ||
| 261 | debug(format("crlf: %(crlf)d lf: %(lf)d cr: %(cr)d\t " .. | ||
| 262 | "- file: %(file)s hunk: %(hunk)d", debuglines)) | ||
| 263 | end | ||
| 264 | end | ||
| 265 | -- state is 'hunkbody' or 'hunkskip' | ||
| 266 | end | ||
| 267 | |||
| 268 | if state == 'hunkskip' then | ||
| 269 | if match_linerange(line) then | ||
| 270 | state = 'hunkhead' | ||
| 271 | elseif startswith(line, "--- ") then | ||
| 272 | state = 'filenames' | ||
| 273 | if debugmode and #files.source > 0 then | ||
| 274 | debug(format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
| 275 | files.source[nextfileno])) | ||
| 276 | end | ||
| 277 | end | ||
| 278 | -- state is 'hunkskip', 'hunkhead', or 'filenames' | ||
| 279 | end | ||
| 280 | local advance | ||
| 281 | if state == 'filenames' then | ||
| 282 | if startswith(line, "--- ") then | ||
| 283 | if array_contains(files.source, nextfileno) then | ||
| 284 | all_ok = false | ||
| 285 | warning(format("skipping invalid patch for %s", | ||
| 286 | files.source[nextfileno+1])) | ||
| 287 | table.remove(files.source, nextfileno+1) | ||
| 288 | -- double source filename line is encountered | ||
| 289 | -- attempt to restart from this second line | ||
| 290 | end | ||
| 291 | -- Accept a space as a terminator, like GNU patch does. | ||
| 292 | -- Breaks patches containing filenames with spaces... | ||
| 293 | -- FIXME Figure out what does GNU patch do in those cases. | ||
| 294 | local match = line:match("^--- ([^\t ]+)") | ||
| 295 | if not match then | ||
| 296 | all_ok = false | ||
| 297 | warning(format("skipping invalid filename at line %d", lineno+1)) | ||
| 298 | state = 'header' | ||
| 299 | else | ||
| 300 | table.insert(files.source, match) | ||
| 301 | end | ||
| 302 | elseif not startswith(line, "+++ ") then | ||
| 303 | if array_contains(files.source, nextfileno) then | ||
| 304 | all_ok = false | ||
| 305 | warning(format("skipping invalid patch with no target for %s", | ||
| 306 | files.source[nextfileno+1])) | ||
| 307 | table.remove(files.source, nextfileno+1) | ||
| 308 | else | ||
| 309 | -- this should be unreachable | ||
| 310 | warning("skipping invalid target patch") | ||
| 311 | end | ||
| 312 | state = 'header' | ||
| 313 | else | ||
| 314 | if array_contains(files.target, nextfileno) then | ||
| 315 | all_ok = false | ||
| 316 | warning(format("skipping invalid patch - double target at line %d", | ||
| 317 | lineno+1)) | ||
| 318 | table.remove(files.source, nextfileno+1) | ||
| 319 | table.remove(files.target, nextfileno+1) | ||
| 320 | nextfileno = nextfileno - 1 | ||
| 321 | -- double target filename line is encountered | ||
| 322 | -- switch back to header state | ||
| 323 | state = 'header' | ||
| 324 | else | ||
| 325 | -- Accept a space as a terminator, like GNU patch does. | ||
| 326 | -- Breaks patches containing filenames with spaces... | ||
| 327 | -- FIXME Figure out what does GNU patch do in those cases. | ||
| 328 | local re_filename = "^%+%+%+ ([^ \t]+)" | ||
| 329 | local match = line:match(re_filename) | ||
| 330 | if not match then | ||
| 331 | all_ok = false | ||
| 332 | warning(format( | ||
| 333 | "skipping invalid patch - no target filename at line %d", | ||
| 334 | lineno+1)) | ||
| 335 | state = 'header' | ||
| 336 | else | ||
| 337 | table.insert(files.target, match) | ||
| 338 | nextfileno = nextfileno + 1 | ||
| 339 | nexthunkno = 0 | ||
| 340 | table.insert(files.hunks, {}) | ||
| 341 | table.insert(files.hunkends, table_copy(lineends)) | ||
| 342 | table.insert(files.fileends, table_copy(lineends)) | ||
| 343 | state = 'hunkhead' | ||
| 344 | advance = true | ||
| 345 | end | ||
| 346 | end | ||
| 347 | end | ||
| 348 | -- state is 'filenames', 'header', or ('hunkhead' with advance) | ||
| 349 | end | ||
| 350 | if not advance and state == 'hunkhead' then | ||
| 351 | local m1, m2, m3, m4 = match_linerange(line) | ||
| 352 | if not m1 then | ||
| 353 | if not array_contains(files.hunks, nextfileno-1) then | ||
| 354 | all_ok = false | ||
| 355 | warning(format("skipping invalid patch with no hunks for file %s", | ||
| 356 | files.target[nextfileno])) | ||
| 357 | end | ||
| 358 | state = 'header' | ||
| 359 | else | ||
| 360 | hunkinfo.startsrc = tonumber(m1) | ||
| 361 | hunkinfo.linessrc = tonumber(m2 or 1) | ||
| 362 | hunkinfo.starttgt = tonumber(m3) | ||
| 363 | hunkinfo.linestgt = tonumber(m4 or 1) | ||
| 364 | hunkinfo.invalid = false | ||
| 365 | hunkinfo.text = {} | ||
| 366 | |||
| 367 | hunkactual.linessrc = 0 | ||
| 368 | hunkactual.linestgt = 0 | ||
| 369 | |||
| 370 | state = 'hunkbody' | ||
| 371 | nexthunkno = nexthunkno + 1 | ||
| 372 | end | ||
| 373 | -- state is 'header' or 'hunkbody' | ||
| 374 | end | ||
| 375 | end | ||
| 376 | if state ~= 'hunkskip' then | ||
| 377 | warning(format("patch file incomplete - %s", filename)) | ||
| 378 | all_ok = false | ||
| 379 | -- os.exit(?) | ||
| 380 | else | ||
| 381 | -- duplicated message when an eof is reached | ||
| 382 | if debugmode and #files.source > 0 then | ||
| 383 | debug(format("- %2d hunks for %s", #files.hunks[nextfileno], | ||
| 384 | files.source[nextfileno])) | ||
| 385 | end | ||
| 386 | end | ||
| 387 | |||
| 388 | local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end | ||
| 389 | info(format("total files: %d total hunks: %d", #files.source, sum)) | ||
| 390 | fp:close() | ||
| 391 | return files, all_ok | ||
| 392 | end | ||
| 393 | |||
| 394 | local function find_hunk(file, h, hno) | ||
| 395 | for fuzz=0,2 do | ||
| 396 | local lineno = h.startsrc | ||
| 397 | for i=0,#file do | ||
| 398 | local found = true | ||
| 399 | local location = lineno | ||
| 400 | local total = #h.text - fuzz | ||
| 401 | for l, hline in ipairs(h.text) do | ||
| 402 | if l > fuzz then | ||
| 403 | -- todo: \ No newline at the end of file | ||
| 404 | if startswith(hline, " ") or startswith(hline, "-") then | ||
| 405 | local line = file[lineno] | ||
| 406 | lineno = lineno + 1 | ||
| 407 | if not line or #line == 0 then | ||
| 408 | found = false | ||
| 409 | break | ||
| 410 | end | ||
| 411 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
| 412 | found = false | ||
| 413 | break | ||
| 414 | end | ||
| 415 | end | ||
| 416 | end | ||
| 417 | end | ||
| 418 | if found then | ||
| 419 | local offset = location - h.startsrc - fuzz | ||
| 420 | if offset ~= 0 then | ||
| 421 | warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz))) | ||
| 422 | end | ||
| 423 | h.startsrc = location | ||
| 424 | h.starttgt = h.starttgt + offset | ||
| 425 | for i=1,fuzz do | ||
| 426 | table.remove(h.text, 1) | ||
| 427 | table.remove(h.text, #h.text) | ||
| 428 | end | ||
| 429 | return true | ||
| 430 | end | ||
| 431 | lineno = i | ||
| 432 | end | ||
| 433 | end | ||
| 434 | return false | ||
| 435 | end | ||
| 436 | |||
| 437 | local function load_file(filename) | ||
| 438 | local fp = assert(io.open(filename)) | ||
| 439 | local file = {} | ||
| 440 | local readline = file_lines(fp) | ||
| 441 | while true do | ||
| 442 | local line = readline() | ||
| 443 | if not line then break end | ||
| 444 | table.insert(file, line) | ||
| 445 | end | ||
| 446 | fp:close() | ||
| 447 | return file | ||
| 448 | end | ||
| 449 | |||
| 450 | local function find_hunks(file, hunks) | ||
| 451 | local matched = true | ||
| 452 | local lineno = 1 | ||
| 453 | local hno = nil | ||
| 454 | for hno, h in ipairs(hunks) do | ||
| 455 | find_hunk(file, h, hno) | ||
| 456 | end | ||
| 457 | end | ||
| 458 | |||
| 459 | local function check_patched(file, hunks) | ||
| 460 | local matched = true | ||
| 461 | local lineno = 1 | ||
| 462 | local hno = nil | ||
| 463 | local ok, err = pcall(function() | ||
| 464 | if #file == 0 then | ||
| 465 | error 'nomatch' | ||
| 466 | end | ||
| 467 | for hno, h in ipairs(hunks) do | ||
| 468 | -- skip to line just before hunk starts | ||
| 469 | if #file < h.starttgt then | ||
| 470 | error 'nomatch' | ||
| 471 | end | ||
| 472 | lineno = h.starttgt | ||
| 473 | print() for k,v in pairs(h) do print(k,v) end print() | ||
| 474 | for _, hline in ipairs(h.text) do | ||
| 475 | -- todo: \ No newline at the end of file | ||
| 476 | if not startswith(hline, "-") and not startswith(hline, "\\") then | ||
| 477 | local line = file[lineno] | ||
| 478 | lineno = lineno + 1 | ||
| 479 | if #line == 0 then | ||
| 480 | error 'nomatch' | ||
| 481 | end | ||
| 482 | if endlstrip(line) ~= endlstrip(hline:sub(2)) then | ||
| 483 | warning(format("file is not patched - failed hunk: %d", hno)) | ||
| 484 | error 'nomatch' | ||
| 485 | end | ||
| 486 | end | ||
| 487 | end | ||
| 488 | end | ||
| 489 | end) | ||
| 490 | if err == 'nomatch' then | ||
| 491 | matched = false | ||
| 492 | end | ||
| 493 | -- todo: display failed hunk, i.e. expected/found | ||
| 494 | |||
| 495 | return matched | ||
| 496 | end | ||
| 497 | |||
| 498 | local function patch_hunks(srcname, tgtname, hunks) | ||
| 499 | local src = assert(io.open(srcname, "rb")) | ||
| 500 | local tgt = assert(io.open(tgtname, "wb")) | ||
| 501 | |||
| 502 | local src_readline = file_lines(src) | ||
| 503 | |||
| 504 | -- todo: detect linefeeds early - in apply_files routine | ||
| 505 | -- to handle cases when patch starts right from the first | ||
| 506 | -- line and no lines are processed. At the moment substituted | ||
| 507 | -- lineends may not be the same at the start and at the end | ||
| 508 | -- of patching. Also issue a warning about mixed lineends | ||
| 509 | |||
| 510 | local srclineno = 1 | ||
| 511 | local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0} | ||
| 512 | for hno, h in ipairs(hunks) do | ||
| 513 | debug(format("processing hunk %d for file %s", hno, tgtname)) | ||
| 514 | -- skip to line just before hunk starts | ||
| 515 | while srclineno < h.startsrc do | ||
| 516 | local line = src_readline() | ||
| 517 | -- Python 'U' mode works only with text files | ||
| 518 | if endswith(line, "\r\n") then | ||
| 519 | lineends["\r\n"] = lineends["\r\n"] + 1 | ||
| 520 | elseif endswith(line, "\n") then | ||
| 521 | lineends["\n"] = lineends["\n"] + 1 | ||
| 522 | elseif endswith(line, "\r") then | ||
| 523 | lineends["\r"] = lineends["\r"] + 1 | ||
| 524 | end | ||
| 525 | tgt:write(line) | ||
| 526 | srclineno = srclineno + 1 | ||
| 527 | end | ||
| 528 | |||
| 529 | for _,hline in ipairs(h.text) do | ||
| 530 | -- todo: check \ No newline at the end of file | ||
| 531 | if startswith(hline, "-") or startswith(hline, "\\") then | ||
| 532 | src_readline() | ||
| 533 | srclineno = srclineno + 1 | ||
| 534 | else | ||
| 535 | if not startswith(hline, "+") then | ||
| 536 | src_readline() | ||
| 537 | srclineno = srclineno + 1 | ||
| 538 | end | ||
| 539 | local line2write = hline:sub(2) | ||
| 540 | -- detect if line ends are consistent in source file | ||
| 541 | local sum = 0 | ||
| 542 | for k,v in pairs(lineends) do if v > 0 then sum=sum+1 end end | ||
| 543 | if sum == 1 then | ||
| 544 | local newline | ||
| 545 | for k,v in pairs(lineends) do if v ~= 0 then newline = k end end | ||
| 546 | tgt:write(endlstrip(line2write) .. newline) | ||
| 547 | else -- newlines are mixed or unknown | ||
| 548 | tgt:write(line2write) | ||
| 549 | end | ||
| 550 | end | ||
| 551 | end | ||
| 552 | end | ||
| 553 | for line in src_readline do | ||
| 554 | tgt:write(line) | ||
| 555 | end | ||
| 556 | tgt:close() | ||
| 557 | src:close() | ||
| 558 | return true | ||
| 559 | end | ||
| 560 | |||
| 561 | local function strip_dirs(filename, strip) | ||
| 562 | if strip == nil then return filename end | ||
| 563 | for i=1,strip do | ||
| 564 | filename=filename:gsub("^[^/]*/", "") | ||
| 565 | end | ||
| 566 | return filename | ||
| 567 | end | ||
| 568 | |||
| 569 | function apply_patch(patch, strip) | ||
| 570 | local all_ok = true | ||
| 571 | local total = #patch.source | ||
| 572 | for fileno, filename in ipairs(patch.source) do | ||
| 573 | filename = strip_dirs(filename, strip) | ||
| 574 | local continue | ||
| 575 | local f2patch = filename | ||
| 576 | if not exists(f2patch) then | ||
| 577 | f2patch = strip_dirs(patch.target[fileno], strip) | ||
| 578 | if not exists(f2patch) then --FIX:if f2patch nil | ||
| 579 | warning(format("source/target file does not exist\n--- %s\n+++ %s", | ||
| 580 | filename, f2patch)) | ||
| 581 | all_ok = false | ||
| 582 | continue = true | ||
| 583 | end | ||
| 584 | end | ||
| 585 | if not continue and not isfile(f2patch) then | ||
| 586 | warning(format("not a file - %s", f2patch)) | ||
| 587 | all_ok = false | ||
| 588 | continue = true | ||
| 589 | end | ||
| 590 | if not continue then | ||
| 591 | |||
| 592 | filename = f2patch | ||
| 593 | |||
| 594 | info(format("processing %d/%d:\t %s", fileno, total, filename)) | ||
| 595 | |||
| 596 | -- validate before patching | ||
| 597 | local hunks = patch.hunks[fileno] | ||
| 598 | local file = load_file(filename) | ||
| 599 | local hunkno = 1 | ||
| 600 | local hunk = hunks[hunkno] | ||
| 601 | local hunkfind = {} | ||
| 602 | local hunkreplace = {} | ||
| 603 | local validhunks = 0 | ||
| 604 | local canpatch = false | ||
| 605 | local hunklineno | ||
| 606 | local isbreak | ||
| 607 | local lineno = 0 | ||
| 608 | |||
| 609 | find_hunks(file, hunks) | ||
| 610 | |||
| 611 | for _, line in ipairs(file) do | ||
| 612 | lineno = lineno + 1 | ||
| 613 | local continue | ||
| 614 | if not hunk or lineno < hunk.startsrc then | ||
| 615 | continue = true | ||
| 616 | elseif lineno == hunk.startsrc then | ||
| 617 | hunkfind = {} | ||
| 618 | for _,x in ipairs(hunk.text) do | ||
| 619 | if x:sub(1,1) == ' ' or x:sub(1,1) == '-' then | ||
| 620 | hunkfind[#hunkfind+1] = endlstrip(x:sub(2)) | ||
| 621 | end end | ||
| 622 | hunkreplace = {} | ||
| 623 | for _,x in ipairs(hunk.text) do | ||
| 624 | if x:sub(1,1) == ' ' or x:sub(1,1) == '+' then | ||
| 625 | hunkreplace[#hunkreplace+1] = endlstrip(x:sub(2)) | ||
| 626 | end end | ||
| 627 | --pprint(hunkreplace) | ||
| 628 | hunklineno = 1 | ||
| 629 | |||
| 630 | -- todo \ No newline at end of file | ||
| 631 | end | ||
| 632 | -- check hunks in source file | ||
| 633 | if not continue and lineno < hunk.startsrc + #hunkfind - 1 then | ||
| 634 | if endlstrip(line) == hunkfind[hunklineno] then | ||
| 635 | hunklineno = hunklineno + 1 | ||
| 636 | else | ||
| 637 | debug(format("hunk no.%d doesn't match source file %s", | ||
| 638 | hunkno, filename)) | ||
| 639 | -- file may be already patched, but check other hunks anyway | ||
| 640 | hunkno = hunkno + 1 | ||
| 641 | if hunkno <= #hunks then | ||
| 642 | hunk = hunks[hunkno] | ||
| 643 | continue = true | ||
| 644 | else | ||
| 645 | isbreak = true; break | ||
| 646 | end | ||
| 647 | end | ||
| 648 | end | ||
| 649 | -- check if processed line is the last line | ||
| 650 | if not continue and lineno == hunk.startsrc + #hunkfind - 1 then | ||
| 651 | debug(format("file %s hunk no.%d -- is ready to be patched", | ||
| 652 | filename, hunkno)) | ||
| 653 | hunkno = hunkno + 1 | ||
| 654 | validhunks = validhunks + 1 | ||
| 655 | if hunkno <= #hunks then | ||
| 656 | hunk = hunks[hunkno] | ||
| 657 | else | ||
| 658 | if validhunks == #hunks then | ||
| 659 | -- patch file | ||
| 660 | canpatch = true | ||
| 661 | isbreak = true; break | ||
| 662 | end | ||
| 663 | end | ||
| 664 | end | ||
| 665 | end | ||
| 666 | if not isbreak then | ||
| 667 | if hunkno <= #hunks then | ||
| 668 | warning(format("premature end of source file %s at hunk %d", | ||
| 669 | filename, hunkno)) | ||
| 670 | all_ok = false | ||
| 671 | end | ||
| 672 | end | ||
| 673 | if validhunks < #hunks then | ||
| 674 | if check_patched(file, hunks) then | ||
| 675 | warning(format("already patched %s", filename)) | ||
| 676 | else | ||
| 677 | warning(format("source file is different - %s", filename)) | ||
| 678 | all_ok = false | ||
| 679 | end | ||
| 680 | end | ||
| 681 | if canpatch then | ||
| 682 | local backupname = filename .. ".orig" | ||
| 683 | if exists(backupname) then | ||
| 684 | warning(format("can't backup original file to %s - aborting", | ||
| 685 | backupname)) | ||
| 686 | all_ok = false | ||
| 687 | else | ||
| 688 | assert(os.rename(filename, backupname)) | ||
| 689 | if patch_hunks(backupname, filename, hunks) then | ||
| 690 | warning(format("successfully patched %s", filename)) | ||
| 691 | assert(os.remove(backupname)) | ||
| 692 | else | ||
| 693 | warning(format("error patching file %s", filename)) | ||
| 694 | assert(file_copy(filename, filename .. ".invalid")) | ||
| 695 | warning(format("invalid version is saved to %s", | ||
| 696 | filename .. ".invalid")) | ||
| 697 | -- todo: proper rejects | ||
| 698 | assert(os.rename(backupname, filename)) | ||
| 699 | all_ok = false | ||
| 700 | end | ||
| 701 | end | ||
| 702 | end | ||
| 703 | |||
| 704 | end -- if not continue | ||
| 705 | end -- for | ||
| 706 | -- todo: check for premature eof | ||
| 707 | return all_ok | ||
| 708 | end | ||
diff --git a/src/luarocks/tools/tar.lua b/src/luarocks/tools/tar.lua new file mode 100644 index 00000000..0a3e07a4 --- /dev/null +++ b/src/luarocks/tools/tar.lua | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | |||
| 2 | module("luarocks.tools.tar", package.seeall) | ||
| 3 | |||
| 4 | local fs = require("luarocks.fs") | ||
| 5 | local dir = require("luarocks.dir") | ||
| 6 | |||
| 7 | local blocksize = 512 | ||
| 8 | |||
| 9 | local function get_typeflag(flag) | ||
| 10 | if flag == "0" or flag == "\0" then return "file" | ||
| 11 | elseif flag == "1" then return "link" | ||
| 12 | elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU | ||
| 13 | elseif flag == "3" then return "character" | ||
| 14 | elseif flag == "4" then return "block" | ||
| 15 | elseif flag == "5" then return "directory" | ||
| 16 | elseif flag == "6" then return "fifo" | ||
| 17 | elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU | ||
| 18 | elseif flag == "x" then return "next file" | ||
| 19 | elseif flag == "g" then return "global extended header" | ||
| 20 | elseif flag == "L" then return "long name" | ||
| 21 | elseif flag == "K" then return "long link name" | ||
| 22 | end | ||
| 23 | return "unknown" | ||
| 24 | end | ||
| 25 | |||
| 26 | local function octal_to_number(octal) | ||
| 27 | local exp = 0 | ||
| 28 | local number = 0 | ||
| 29 | for i = #octal,1,-1 do | ||
| 30 | local digit = tonumber(octal:sub(i,i)) | ||
| 31 | if not digit then break end | ||
| 32 | number = number + (digit * 8^exp) | ||
| 33 | exp = exp + 1 | ||
| 34 | end | ||
| 35 | return number | ||
| 36 | end | ||
| 37 | |||
| 38 | local function checksum_header(block) | ||
| 39 | local sum = 256 | ||
| 40 | for i = 1,148 do | ||
| 41 | sum = sum + block:byte(i) | ||
| 42 | end | ||
| 43 | for i = 157,500 do | ||
| 44 | sum = sum + block:byte(i) | ||
| 45 | end | ||
| 46 | return sum | ||
| 47 | end | ||
| 48 | |||
| 49 | local function nullterm(s) | ||
| 50 | return s:match("^[^%z]*") | ||
| 51 | end | ||
| 52 | |||
| 53 | local function read_header_block(block) | ||
| 54 | local header = {} | ||
| 55 | header.name = nullterm(block:sub(1,100)) | ||
| 56 | header.mode = nullterm(block:sub(101,108)) | ||
| 57 | header.uid = octal_to_number(nullterm(block:sub(109,116))) | ||
| 58 | header.gid = octal_to_number(nullterm(block:sub(117,124))) | ||
| 59 | header.size = octal_to_number(nullterm(block:sub(125,136))) | ||
| 60 | header.mtime = octal_to_number(nullterm(block:sub(137,148))) | ||
| 61 | header.chksum = octal_to_number(nullterm(block:sub(149,156))) | ||
| 62 | header.typeflag = get_typeflag(block:sub(157,157)) | ||
| 63 | header.linkname = nullterm(block:sub(158,257)) | ||
| 64 | header.magic = block:sub(258,263) | ||
| 65 | header.version = block:sub(264,265) | ||
| 66 | header.uname = nullterm(block:sub(266,297)) | ||
| 67 | header.gname = nullterm(block:sub(298,329)) | ||
| 68 | header.devmajor = octal_to_number(nullterm(block:sub(330,337))) | ||
| 69 | header.devminor = octal_to_number(nullterm(block:sub(338,345))) | ||
| 70 | header.prefix = block:sub(346,500) | ||
| 71 | if header.magic ~= "ustar " and header.magic ~= "ustar\0" then | ||
| 72 | return false, "Invalid header magic "..header.magic | ||
| 73 | end | ||
| 74 | if header.version ~= "00" and header.version ~= " \0" then | ||
| 75 | return false, "Unknown version "..header.version | ||
| 76 | end | ||
| 77 | if not checksum_header(block) == header.chksum then | ||
| 78 | return false, "Failed header checksum" | ||
| 79 | end | ||
| 80 | return header | ||
| 81 | end | ||
| 82 | |||
| 83 | function untar(filename, destdir) | ||
| 84 | assert(type(filename) == "string") | ||
| 85 | assert(type(destdir) == "string") | ||
| 86 | |||
| 87 | local tar_handle = io.open(filename, "r") | ||
| 88 | if not tar_handle then return nil, "Error opening file "..filename end | ||
| 89 | |||
| 90 | local long_name, long_link_name | ||
| 91 | while true do | ||
| 92 | local block | ||
| 93 | repeat | ||
| 94 | block = tar_handle:read(blocksize) | ||
| 95 | until (not block) or checksum_header(block) > 256 | ||
| 96 | if not block then break end | ||
| 97 | local header, err = read_header_block(block) | ||
| 98 | if not header then | ||
| 99 | print(err) | ||
| 100 | end | ||
| 101 | |||
| 102 | local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) | ||
| 103 | |||
| 104 | if header.typeflag == "long name" then | ||
| 105 | long_name = nullterm(file_data) | ||
| 106 | elseif header.typeflag == "long link name" then | ||
| 107 | long_link_name = nullterm(file_data) | ||
| 108 | else | ||
| 109 | if long_name then | ||
| 110 | header.name = long_name | ||
| 111 | long_name = nil | ||
| 112 | end | ||
| 113 | if long_link_name then | ||
| 114 | header.name = long_link_name | ||
| 115 | long_link_name = nil | ||
| 116 | end | ||
| 117 | end | ||
| 118 | local pathname = dir.path(destdir, header.name) | ||
| 119 | if header.typeflag == "directory" then | ||
| 120 | fs.make_dir(pathname) | ||
| 121 | elseif header.typeflag == "file" then | ||
| 122 | local dirname = dir.dir_name(pathname) | ||
| 123 | if dirname ~= "" then | ||
| 124 | fs.make_dir(dirname) | ||
| 125 | end | ||
| 126 | local file_handle = io.open(pathname, "wb") | ||
| 127 | file_handle:write(file_data) | ||
| 128 | file_handle:close() | ||
| 129 | fs.set_time(pathname, header.mtime) | ||
| 130 | if fs.chmod then | ||
| 131 | fs.chmod(pathname, header.mode) | ||
| 132 | end | ||
| 133 | end | ||
| 134 | print(pathname) | ||
| 135 | --[[ | ||
| 136 | for k,v in pairs(header) do | ||
| 137 | print("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) | ||
| 138 | end | ||
| 139 | print() | ||
| 140 | --]] | ||
| 141 | end | ||
| 142 | return true | ||
| 143 | end | ||
diff --git a/src/luarocks/tools/zip.lua b/src/luarocks/tools/zip.lua new file mode 100644 index 00000000..621beae1 --- /dev/null +++ b/src/luarocks/tools/zip.lua | |||
| @@ -0,0 +1,230 @@ | |||
| 1 | |||
| 2 | module("luarocks.tools.zip", package.seeall) | ||
| 3 | |||
| 4 | local zlib = require "zlib" | ||
| 5 | |||
| 6 | local function number_to_bytestring(number, nbytes) | ||
| 7 | local out = {} | ||
| 8 | for i = 1, nbytes do | ||
| 9 | local byte = number % 256 | ||
| 10 | table.insert(out, string.char(byte)) | ||
| 11 | number = (number - byte) / 256 | ||
| 12 | end | ||
| 13 | return table.concat(out) | ||
| 14 | end | ||
| 15 | |||
| 16 | --- Return a zip handle open for writing. | ||
| 17 | -- @param name filename of the zipfile to be created. | ||
| 18 | -- @return a zip handle, or nil in case of error. | ||
| 19 | function write_open(name) | ||
| 20 | |||
| 21 | local zf = {} | ||
| 22 | |||
| 23 | zf.ziphandle = io.open(name, "w") | ||
| 24 | if not zf.ziphandle then | ||
| 25 | return nil | ||
| 26 | end | ||
| 27 | zf.files = {} | ||
| 28 | zf.in_open_file = false | ||
| 29 | |||
| 30 | return zf | ||
| 31 | end | ||
| 32 | |||
| 33 | --- Begin a new file to be stored inside the zipfile. | ||
| 34 | -- @param zf handle of the zipfile being written. | ||
| 35 | -- @param filename filenome of the file to be added to the zipfile. | ||
| 36 | -- @return true if succeeded, nil in case of failure. | ||
| 37 | function write_open_new_file_in_zip(zf, filename) | ||
| 38 | if zf.in_open_file then | ||
| 39 | close_file_in_zip(zf) | ||
| 40 | return nil | ||
| 41 | end | ||
| 42 | local lfh = {} | ||
| 43 | zf.local_file_header = lfh | ||
| 44 | lfh.last_mod_file_time = 0 -- TODO | ||
| 45 | lfh.last_mod_file_date = 0 -- TODO | ||
| 46 | lfh.crc32 = 0 -- initial value | ||
| 47 | lfh.compressed_size = 0 -- unknown yet | ||
| 48 | lfh.uncompressed_size = 0 -- unknown yet | ||
| 49 | lfh.file_name_length = #filename | ||
| 50 | lfh.extra_field_length = 0 | ||
| 51 | lfh.file_name = filename:gsub("\\", "/") | ||
| 52 | zf.in_open_file = true | ||
| 53 | zf.data = {} | ||
| 54 | return true | ||
| 55 | end | ||
| 56 | |||
| 57 | --- Write data to the file currently being stored in the zipfile. | ||
| 58 | -- @param zf handle of the zipfile being written. | ||
| 59 | -- @param buf string containing data to be written. | ||
| 60 | -- @return true if succeeded, nil in case of failure. | ||
| 61 | function write_in_file_in_zip(zf, buf) | ||
| 62 | if not zf.in_open_file then | ||
| 63 | return nil | ||
| 64 | end | ||
| 65 | local lfh = zf.local_file_header | ||
| 66 | local cbuf = zlib.compress(buf):sub(3, -5) | ||
| 67 | lfh.crc32 = zlib.crc32(lfh.crc32, buf) | ||
| 68 | lfh.compressed_size = lfh.compressed_size + #cbuf | ||
| 69 | lfh.uncompressed_size = lfh.uncompressed_size + #buf | ||
| 70 | table.insert(zf.data, cbuf) | ||
| 71 | return true | ||
| 72 | end | ||
| 73 | |||
| 74 | --- Complete the writing of a file stored in the zipfile. | ||
| 75 | -- @param zf handle of the zipfile being written. | ||
| 76 | -- @return true if succeeded, nil in case of failure. | ||
| 77 | function write_close_file_in_zip(zf) | ||
| 78 | local zh = zf.ziphandle | ||
| 79 | |||
| 80 | if not zf.in_open_file then | ||
| 81 | return nil | ||
| 82 | end | ||
| 83 | |||
| 84 | -- Local file header | ||
| 85 | local lfh = zf.local_file_header | ||
| 86 | lfh.offset = zh:seek() | ||
| 87 | zh:write(number_to_bytestring(0x04034b50, 4)) -- signature | ||
| 88 | zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 | ||
| 89 | zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag | ||
| 90 | zh:write(number_to_bytestring(8, 2)) -- compression method: deflate | ||
| 91 | zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) | ||
| 92 | zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) | ||
| 93 | zh:write(number_to_bytestring(lfh.crc32, 4)) | ||
| 94 | zh:write(number_to_bytestring(lfh.compressed_size, 4)) | ||
| 95 | zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) | ||
| 96 | zh:write(number_to_bytestring(lfh.file_name_length, 2)) | ||
| 97 | zh:write(number_to_bytestring(lfh.extra_field_length, 2)) | ||
| 98 | zh:write(lfh.file_name) | ||
| 99 | |||
| 100 | -- File data | ||
| 101 | for _, cbuf in ipairs(zf.data) do | ||
| 102 | zh:write(cbuf) | ||
| 103 | end | ||
| 104 | |||
| 105 | -- Data descriptor | ||
| 106 | zh:write(number_to_bytestring(lfh.crc32, 4)) | ||
| 107 | zh:write(number_to_bytestring(lfh.compressed_size, 4)) | ||
| 108 | zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) | ||
| 109 | |||
| 110 | table.insert(zf.files, lfh) | ||
| 111 | zf.in_open_file = false | ||
| 112 | |||
| 113 | return true | ||
| 114 | end | ||
| 115 | |||
| 116 | --- Complete the writing of the zipfile. | ||
| 117 | -- @param zf handle of the zipfile being written. | ||
| 118 | -- @return true if succeeded, nil in case of failure. | ||
| 119 | function write_close(zf) | ||
| 120 | local zh = zf.ziphandle | ||
| 121 | |||
| 122 | local central_directory_offset = zh:seek() | ||
| 123 | |||
| 124 | local size_of_central_directory = 0 | ||
| 125 | -- Central directory structure | ||
| 126 | for _, lfh in ipairs(zf.files) do | ||
| 127 | zh:write(number_to_bytestring(0x02014b50, 4)) -- signature | ||
| 128 | zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX | ||
| 129 | zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 | ||
| 130 | zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag | ||
| 131 | zh:write(number_to_bytestring(8, 2)) -- compression method: deflate | ||
| 132 | zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) | ||
| 133 | zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) | ||
| 134 | zh:write(number_to_bytestring(lfh.crc32, 4)) | ||
| 135 | zh:write(number_to_bytestring(lfh.compressed_size, 4)) | ||
| 136 | zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) | ||
| 137 | zh:write(number_to_bytestring(lfh.file_name_length, 2)) | ||
| 138 | zh:write(number_to_bytestring(lfh.extra_field_length, 2)) | ||
| 139 | zh:write(number_to_bytestring(0, 2)) -- file comment length | ||
| 140 | zh:write(number_to_bytestring(0, 2)) -- disk number start | ||
| 141 | zh:write(number_to_bytestring(0, 2)) -- internal file attributes | ||
| 142 | zh:write(number_to_bytestring(0, 4)) -- external file attributes | ||
| 143 | zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header | ||
| 144 | zh:write(lfh.file_name) | ||
| 145 | size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length | ||
| 146 | end | ||
| 147 | |||
| 148 | -- End of central directory record | ||
| 149 | zh:write(number_to_bytestring(0x06054b50, 4)) -- signature | ||
| 150 | zh:write(number_to_bytestring(0, 2)) -- number of this disk | ||
| 151 | zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory | ||
| 152 | zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir on this disk | ||
| 153 | zh:write(number_to_bytestring(#zf.files, 2)) -- total number of entries in the central dir | ||
| 154 | zh:write(number_to_bytestring(size_of_central_directory, 4)) | ||
| 155 | zh:write(number_to_bytestring(central_directory_offset, 4)) | ||
| 156 | zh:write(number_to_bytestring(0, 2)) -- zip file comment length | ||
| 157 | zh:close() | ||
| 158 | |||
| 159 | return true | ||
| 160 | end | ||
| 161 | |||
| 162 | -- @return boolean or (boolean, string): true on success, | ||
| 163 | -- false and an error message on failure. | ||
| 164 | local function add_to_zip(zf, file) | ||
| 165 | local fin | ||
| 166 | local ok, err = write_open_new_file_in_zip(zf, file) | ||
| 167 | if not ok then | ||
| 168 | err = "error in opening "..file.." in zipfile" | ||
| 169 | else | ||
| 170 | fin = io.open(file, "rb") | ||
| 171 | if not fin then | ||
| 172 | ok = false | ||
| 173 | err = "error opening "..file.." for reading" | ||
| 174 | end | ||
| 175 | end | ||
| 176 | while ok do | ||
| 177 | local buf = fin:read(size_buf) | ||
| 178 | if not buf then | ||
| 179 | break | ||
| 180 | end | ||
| 181 | ok = write_in_file_in_zip(zf, buf) | ||
| 182 | if not ok then | ||
| 183 | err = "error in writing "..file.." in the zipfile" | ||
| 184 | end | ||
| 185 | end | ||
| 186 | if fin then | ||
| 187 | fin:close() | ||
| 188 | end | ||
| 189 | if ok then | ||
| 190 | ok = write_close_file_in_zip(zf) | ||
| 191 | if not ok then | ||
| 192 | err = "error in writing "..file.." in the zipfile" | ||
| 193 | end | ||
| 194 | end | ||
| 195 | return ok == true, err | ||
| 196 | end | ||
| 197 | |||
| 198 | --- Compress files in a .zip archive. | ||
| 199 | -- @param zipfile string: pathname of .zip archive to be created. | ||
| 200 | -- @param ... Filenames to be stored in the archive are given as | ||
| 201 | -- additional arguments. | ||
| 202 | -- @return boolean or (boolean, string): true on success, | ||
| 203 | -- false and an error message on failure. | ||
| 204 | function zip(zipfile, ...) | ||
| 205 | local zf = write_open(filename) | ||
| 206 | if not zf then | ||
| 207 | return nil, "error opening "..filename | ||
| 208 | end | ||
| 209 | |||
| 210 | local ok, err | ||
| 211 | for _, file in pairs({...}) do | ||
| 212 | if fs_is_dir(file) then | ||
| 213 | for _, file in pairs(fs_find(file)) do | ||
| 214 | if fs_is_file(file) then | ||
| 215 | ok, err = add_to_zip(file) | ||
| 216 | if not ok then break end | ||
| 217 | end | ||
| 218 | end | ||
| 219 | else | ||
| 220 | ok, err = add_to_zip(file) | ||
| 221 | if not ok then break end | ||
| 222 | end | ||
| 223 | end | ||
| 224 | |||
| 225 | local ok = write_close(zf) | ||
| 226 | if not ok then | ||
| 227 | return false, "error closing "..filename | ||
| 228 | end | ||
| 229 | return ok, err | ||
| 230 | end | ||
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua index ec9d6c5c..0e4a73af 100644 --- a/src/luarocks/type_check.lua +++ b/src/luarocks/type_check.lua | |||
| @@ -40,6 +40,7 @@ rockspec_types = { | |||
| 40 | dir = "string", | 40 | dir = "string", |
| 41 | tag = "string", | 41 | tag = "string", |
| 42 | branch = "string", | 42 | branch = "string", |
| 43 | module = "string", | ||
| 43 | cvs_tag = "string", | 44 | cvs_tag = "string", |
| 44 | cvs_module = "string" | 45 | cvs_module = "string" |
| 45 | }, | 46 | }, |
| @@ -187,7 +188,6 @@ end | |||
| 187 | type_check_table = function(tbl, types, context) | 188 | type_check_table = function(tbl, types, context) |
| 188 | assert(type(tbl) == "table") | 189 | assert(type(tbl) == "table") |
| 189 | assert(type(types) == "table") | 190 | assert(type(types) == "table") |
| 190 | |||
| 191 | for k, v in pairs(tbl) do | 191 | for k, v in pairs(tbl) do |
| 192 | local t = types[k] or (type(k) == "string" and types["MUST_"..k]) or types.ANY | 192 | local t = types[k] or (type(k) == "string" and types["MUST_"..k]) or types.ANY |
| 193 | if t then | 193 | if t then |
diff --git a/src/luarocks/unpack.lua b/src/luarocks/unpack.lua index 60bc1295..c73264d0 100644 --- a/src/luarocks/unpack.lua +++ b/src/luarocks/unpack.lua | |||
| @@ -7,6 +7,7 @@ local fetch = require("luarocks.fetch") | |||
| 7 | local fs = require("luarocks.fs") | 7 | local fs = require("luarocks.fs") |
| 8 | local util = require("luarocks.util") | 8 | local util = require("luarocks.util") |
| 9 | local build = require("luarocks.build") | 9 | local build = require("luarocks.build") |
| 10 | local dir = require("luarocks.dir") | ||
| 10 | 11 | ||
| 11 | help_summary = "Unpack the contents of a rock." | 12 | help_summary = "Unpack the contents of a rock." |
| 12 | help_arguments = "{<rock>|<name> [<version>]}" | 13 | help_arguments = "{<rock>|<name> [<version>]}" |
| @@ -50,9 +51,9 @@ local function unpack_rock(rock_file, dir_name, kind) | |||
| 50 | assert(type(rock_file) == "string") | 51 | assert(type(rock_file) == "string") |
| 51 | assert(type(dir_name) == "string") | 52 | assert(type(dir_name) == "string") |
| 52 | 53 | ||
| 53 | local ok, err = fetch.fetch_and_unpack_rock(rock_file, dir_name) | 54 | local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) |
| 54 | if not ok then | 55 | if not ok then |
| 55 | return nil, "Failed unzipping rock "..rock_file | 56 | return nil, "Failed unzipping rock "..rock_file, errcode |
| 56 | end | 57 | end |
| 57 | fs.change_dir(dir_name) | 58 | fs.change_dir(dir_name) |
| 58 | local rockspec_file = dir_name..".rockspec" | 59 | local rockspec_file = dir_name..".rockspec" |
| @@ -83,7 +84,7 @@ end | |||
| 83 | local function run_unpacker(file) | 84 | local function run_unpacker(file) |
| 84 | assert(type(file) == "string") | 85 | assert(type(file) == "string") |
| 85 | 86 | ||
| 86 | local base_name = fs.base_name(file) | 87 | local base_name = dir.base_name(file) |
| 87 | local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") | 88 | local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") |
| 88 | if not extension then | 89 | if not extension then |
| 89 | dir_name, extension = base_name:match("(.*)%.(rockspec)$") | 90 | dir_name, extension = base_name:match("(.*)%.(rockspec)$") |
| @@ -117,7 +118,7 @@ local function run_unpacker(file) | |||
| 117 | end | 118 | end |
| 118 | print() | 119 | print() |
| 119 | print("Done. You may now enter directory ") | 120 | print("Done. You may now enter directory ") |
| 120 | print(fs.make_path(dir_name, rockspec.source.dir)) | 121 | print(dir.path(dir_name, rockspec.source.dir)) |
| 121 | print("and type 'luarocks make' to build.") | 122 | print("and type 'luarocks make' to build.") |
| 122 | end | 123 | end |
| 123 | util.remove_scheduled_function(rollback) | 124 | util.remove_scheduled_function(rollback) |
diff --git a/src/luarocks/validate.lua b/src/luarocks/validate.lua new file mode 100644 index 00000000..1bf001c7 --- /dev/null +++ b/src/luarocks/validate.lua | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | |||
| 2 | module("luarocks.validate", package.seeall) | ||
| 3 | |||
| 4 | local fs = require("luarocks.fs") | ||
| 5 | local dir = require("luarocks.dir") | ||
| 6 | local cfg = require("luarocks.cfg") | ||
| 7 | local build = require("luarocks.build") | ||
| 8 | local install = require("luarocks.install") | ||
| 9 | local util = require("luarocks.util") | ||
| 10 | |||
| 11 | help_summary = "Sandboxed test of build/install of all packages in a repository." | ||
| 12 | |||
| 13 | help = [[ | ||
| 14 | <argument>, if given, is a local repository pathname. | ||
| 15 | ]] | ||
| 16 | |||
| 17 | local function save_settings(repo) | ||
| 18 | local protocol, path = fs.split_url(repo) | ||
| 19 | table.insert(cfg.rocks_servers, 1, protocol.."://"..path) | ||
| 20 | return { | ||
| 21 | root_dir = cfg.root_dir, | ||
| 22 | rocks_dir = cfg.rocks_dir, | ||
| 23 | scripts_dir = cfg.scripts_dir, | ||
| 24 | } | ||
| 25 | end | ||
| 26 | |||
| 27 | local function restore_settings(settings) | ||
| 28 | cfg.root_dir = settings.root_dir | ||
| 29 | cfg.rocks_dir = settings.rocks_dir | ||
| 30 | cfg.scripts_dir = settings.scripts_dir | ||
| 31 | cfg.variables.ROCKS_TREE = settings.root_dir | ||
| 32 | cfg.variables.SCRIPTS_DIR = settings.scripts_dir | ||
| 33 | table.remove(cfg.rocks_servers, 1) | ||
| 34 | end | ||
| 35 | |||
| 36 | local function prepare_sandbox(file) | ||
| 37 | local root_dir = fs.make_temp_dir(file):gsub("/+$", "") | ||
| 38 | cfg.root_dir = root_dir | ||
| 39 | cfg.rocks_dir = root_dir.."/rocks" | ||
| 40 | cfg.scripts_dir = root_dir.."/bin" | ||
| 41 | cfg.variables.ROCKS_TREE = cfg.root_dir | ||
| 42 | cfg.variables.SCRIPTS_DIR = cfg.scripts_dir | ||
| 43 | return root_dir | ||
| 44 | end | ||
| 45 | |||
| 46 | local function validate_rockspec(file) | ||
| 47 | local ok, err, errcode = build.build_rockspec(file, true) | ||
| 48 | if not ok then | ||
| 49 | print(err) | ||
| 50 | end | ||
| 51 | return ok, err, errcode | ||
| 52 | end | ||
| 53 | |||
| 54 | local function validate_src_rock(file) | ||
| 55 | local ok, err, errcode = build.build_rock(file, false) | ||
| 56 | if not ok then | ||
| 57 | print(err) | ||
| 58 | end | ||
| 59 | return ok, err, errcode | ||
| 60 | end | ||
| 61 | |||
| 62 | local function validate_rock(file) | ||
| 63 | local ok, err, errcode = install.install_binary_rock(file) | ||
| 64 | if not ok then | ||
| 65 | print(err) | ||
| 66 | end | ||
| 67 | return ok, err, errcode | ||
| 68 | end | ||
| 69 | |||
| 70 | local function validate(repo, flags) | ||
| 71 | local results = { | ||
| 72 | ok = {} | ||
| 73 | } | ||
| 74 | local settings = save_settings(repo) | ||
| 75 | local sandbox | ||
| 76 | if flags["quick"] then | ||
| 77 | sandbox = prepare_sandbox("luarocks_validate") | ||
| 78 | end | ||
| 79 | if not fs.exists(repo) then | ||
| 80 | return nil, repo.." is not a local repository." | ||
| 81 | end | ||
| 82 | for _, file in pairs(fs.list_dir(repo)) do for _=1,1 do | ||
| 83 | if file == "manifest" or file == "index.html" then | ||
| 84 | break -- continue for | ||
| 85 | end | ||
| 86 | local pathname = fs.absolute_name(dir.path(repo, file)) | ||
| 87 | if not flags["quick"] then | ||
| 88 | sandbox = prepare_sandbox(file) | ||
| 89 | end | ||
| 90 | local ok, err, errcode | ||
| 91 | print() | ||
| 92 | print("Verifying "..pathname) | ||
| 93 | if file:match("%.rockspec$") then | ||
| 94 | ok, err, errcode = validate_rockspec(pathname) | ||
| 95 | elseif file:match("%.src%.rock$") then | ||
| 96 | ok, err, errcode = validate_src_rock(pathname) | ||
| 97 | elseif file:match("%.rock$") then | ||
| 98 | ok, err, errcode = validate_rock(pathname) | ||
| 99 | end | ||
| 100 | if ok then | ||
| 101 | table.insert(results.ok, {file=file} ) | ||
| 102 | else | ||
| 103 | if not errcode then | ||
| 104 | errcode = "misc" | ||
| 105 | end | ||
| 106 | if not results[errcode] then | ||
| 107 | results[errcode] = {} | ||
| 108 | end | ||
| 109 | table.insert(results[errcode], {file=file, err=err} ) | ||
| 110 | end | ||
| 111 | util.run_scheduled_functions() | ||
| 112 | if not flags["quick"] then | ||
| 113 | fs.delete(sandbox) | ||
| 114 | end | ||
| 115 | repeat until not fs.pop_dir() | ||
| 116 | end end | ||
| 117 | if flags["quick"] then | ||
| 118 | fs.delete(sandbox) | ||
| 119 | end | ||
| 120 | restore_settings(settings) | ||
| 121 | print() | ||
| 122 | print("Results:") | ||
| 123 | print("--------") | ||
| 124 | print("OK: "..tostring(#results.ok)) | ||
| 125 | for _, entry in ipairs(results.ok) do | ||
| 126 | print(entry.file) | ||
| 127 | end | ||
| 128 | for errcode, errors in pairs(results) do | ||
| 129 | if errcode ~= "ok" then | ||
| 130 | print() | ||
| 131 | print(errcode.." errors: "..tostring(#errors)) | ||
| 132 | for _, entry in ipairs(errors) do | ||
| 133 | print(entry.file, entry.err) | ||
| 134 | end | ||
| 135 | end | ||
| 136 | end | ||
| 137 | |||
| 138 | print() | ||
| 139 | print("Summary:") | ||
| 140 | print("--------") | ||
| 141 | local total = 0 | ||
| 142 | for errcode, errors in pairs(results) do | ||
| 143 | print(errcode..": "..tostring(#errors)) | ||
| 144 | total = total + #errors | ||
| 145 | end | ||
| 146 | print("Total: "..total) | ||
| 147 | return true | ||
| 148 | end | ||
| 149 | |||
| 150 | function run(...) | ||
| 151 | local flags, repo = util.parse_flags(...) | ||
| 152 | assert(type(repo) == "string" or not repo) | ||
| 153 | repo = repo or cfg.rocks_dir | ||
| 154 | |||
| 155 | print("Verifying contents of "..repo) | ||
| 156 | |||
| 157 | return validate(repo, flags) | ||
| 158 | end | ||
| 159 | |||
