diff options
author | hisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c> | 2009-04-01 19:15:49 +0000 |
---|---|---|
committer | hisham <hisham@9ca3f7c1-7366-0410-b1a3-b5c78f85698c> | 2009-04-01 19:15:49 +0000 |
commit | 831a3c8a20780cb56a3e6544c62ba7eedf751ed2 (patch) | |
tree | a9a100743024d98e347939b1ce75b72f0a178454 /src | |
parent | b25419643e3adab298363c32fe19e4532d1f9c0b (diff) | |
download | luarocks-831a3c8a20780cb56a3e6544c62ba7eedf751ed2.tar.gz luarocks-831a3c8a20780cb56a3e6544c62ba7eedf751ed2.tar.bz2 luarocks-831a3c8a20780cb56a3e6544c62ba7eedf751ed2.zip |
LuaRocks 1.x development: reorganization, fixes and work on tool independence
git-svn-id: http://luarocks.org/svn/luarocks/trunk@4 9ca3f7c1-7366-0410-b1a3-b5c78f85698c
Diffstat (limited to 'src')
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 | |||