diff options
| author | Hisham Muhammad <hisham@gobolinux.org> | 2016-05-22 19:51:45 -0300 |
|---|---|---|
| committer | Hisham Muhammad <hisham@gobolinux.org> | 2016-05-22 19:51:45 -0300 |
| commit | a3650d51e96aef84657cb29e5f52e570fed828c9 (patch) | |
| tree | 08a820f19862486b88f39b671803d87f42436abc /src | |
| parent | 3f6eda258d5c6aee610b095a20beb12e026e6714 (diff) | |
| parent | 69a6aff477fd794b3b594bc7b9f20775b8e07986 (diff) | |
| download | luarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.tar.gz luarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.tar.bz2 luarocks-a3650d51e96aef84657cb29e5f52e570fed828c9.zip | |
Merge pull request #561 from mpeterv/common-fs-tools-funcs
Move common `luarocks.fs.{unix,win32}.tools` functions into a new module
Diffstat (limited to 'src')
| -rw-r--r-- | src/luarocks/fs.lua | 5 | ||||
| -rw-r--r-- | src/luarocks/fs/tools.lua | 156 | ||||
| -rw-r--r-- | src/luarocks/fs/unix.lua | 7 | ||||
| -rw-r--r-- | src/luarocks/fs/unix/tools.lua | 151 | ||||
| -rw-r--r-- | src/luarocks/fs/win32.lua | 7 | ||||
| -rw-r--r-- | src/luarocks/fs/win32/tools.lua | 170 |
6 files changed, 194 insertions, 302 deletions
diff --git a/src/luarocks/fs.lua b/src/luarocks/fs.lua index 57302c7f..f3d86a13 100644 --- a/src/luarocks/fs.lua +++ b/src/luarocks/fs.lua | |||
| @@ -68,7 +68,10 @@ load_fns(fs_lua) | |||
| 68 | 68 | ||
| 69 | -- Load platform-specific fallbacks for missing Lua modules | 69 | -- Load platform-specific fallbacks for missing Lua modules |
| 70 | local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools") | 70 | local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools") |
| 71 | if ok and fs_plat_tools then load_fns(fs_plat_tools) end | 71 | if ok and fs_plat_tools then |
| 72 | load_fns(fs_plat_tools) | ||
| 73 | load_fns(require("luarocks.fs.tools")) | ||
| 74 | end | ||
| 72 | 75 | ||
| 73 | 76 | ||
| 74 | return fs | 77 | return fs |
diff --git a/src/luarocks/fs/tools.lua b/src/luarocks/fs/tools.lua new file mode 100644 index 00000000..ed51b545 --- /dev/null +++ b/src/luarocks/fs/tools.lua | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | |||
| 2 | --- Common fs operations implemented with third-party tools. | ||
| 3 | local tools = {} | ||
| 4 | |||
| 5 | local fs = require("luarocks.fs") | ||
| 6 | local dir = require("luarocks.dir") | ||
| 7 | local cfg = require("luarocks.cfg") | ||
| 8 | |||
| 9 | local vars = cfg.variables | ||
| 10 | |||
| 11 | local dir_stack = {} | ||
| 12 | |||
| 13 | --- Obtain current directory. | ||
| 14 | -- Uses the module's internal directory stack. | ||
| 15 | -- @return string: the absolute pathname of the current directory. | ||
| 16 | function tools.current_dir() | ||
| 17 | local current = cfg.cache_pwd | ||
| 18 | if not current then | ||
| 19 | local pipe = io.popen(fs.quiet_stderr(fs.Q(vars.PWD))) | ||
| 20 | current = pipe:read("*l") | ||
| 21 | pipe:close() | ||
| 22 | cfg.cache_pwd = current | ||
| 23 | end | ||
| 24 | for _, directory in ipairs(dir_stack) do | ||
| 25 | current = fs.absolute_name(directory, current) | ||
| 26 | end | ||
| 27 | return current | ||
| 28 | end | ||
| 29 | |||
| 30 | --- Change the current directory. | ||
| 31 | -- Uses the module's internal directory stack. This does not have exact | ||
| 32 | -- semantics of chdir, as it does not handle errors the same way, | ||
| 33 | -- but works well for our purposes for now. | ||
| 34 | -- @param directory string: The directory to switch to. | ||
| 35 | -- @return boolean or (nil, string): true if successful, (nil, error message) if failed. | ||
| 36 | function tools.change_dir(directory) | ||
| 37 | assert(type(directory) == "string") | ||
| 38 | if fs.is_dir(directory) then | ||
| 39 | table.insert(dir_stack, directory) | ||
| 40 | return true | ||
| 41 | end | ||
| 42 | return nil, "directory not found: "..directory | ||
| 43 | end | ||
| 44 | |||
| 45 | --- Change directory to root. | ||
| 46 | -- Allows leaving a directory (e.g. for deleting it) in | ||
| 47 | -- a crossplatform way. | ||
| 48 | function tools.change_dir_to_root() | ||
| 49 | table.insert(dir_stack, "/") | ||
| 50 | end | ||
| 51 | |||
| 52 | --- Change working directory to the previous in the directory stack. | ||
| 53 | function tools.pop_dir() | ||
| 54 | local directory = table.remove(dir_stack) | ||
| 55 | return directory ~= nil | ||
| 56 | end | ||
| 57 | |||
| 58 | --- Run the given command. | ||
| 59 | -- The command is executed in the current directory in the directory stack. | ||
| 60 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 61 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 62 | -- otherwise. | ||
| 63 | function tools.execute_string(cmd) | ||
| 64 | local current = fs.current_dir() | ||
| 65 | if not current then return false end | ||
| 66 | cmd = fs.command_at(current, cmd) | ||
| 67 | local code = os.execute(cmd) | ||
| 68 | if code == 0 or code == true then | ||
| 69 | return true | ||
| 70 | else | ||
| 71 | return false | ||
| 72 | end | ||
| 73 | end | ||
| 74 | |||
| 75 | --- Internal implementation function for fs.dir. | ||
| 76 | -- Yields a filename on each iteration. | ||
| 77 | -- @param at string: directory to list | ||
| 78 | -- @return nil | ||
| 79 | function tools.dir_iterator(at) | ||
| 80 | local pipe = io.popen(fs.command_at(at, fs.Q(vars.LS))) | ||
| 81 | for file in pipe:lines() do | ||
| 82 | if file ~= "." and file ~= ".." then | ||
| 83 | coroutine.yield(file) | ||
| 84 | end | ||
| 85 | end | ||
| 86 | pipe:close() | ||
| 87 | end | ||
| 88 | |||
| 89 | --- Download a remote file. | ||
| 90 | -- @param url string: URL to be fetched. | ||
| 91 | -- @param filename string or nil: this function attempts to detect the | ||
| 92 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 93 | -- if that is not correct (due to a redirection, for example), the local | ||
| 94 | -- filename can be given explicitly as this second argument. | ||
| 95 | -- @return (boolean, string): true and the filename on success, | ||
| 96 | -- false and the error message on failure. | ||
| 97 | function tools.use_downloader(url, filename, cache) | ||
| 98 | assert(type(url) == "string") | ||
| 99 | assert(type(filename) == "string" or not filename) | ||
| 100 | |||
| 101 | filename = fs.absolute_name(filename or dir.base_name(url)) | ||
| 102 | |||
| 103 | local ok | ||
| 104 | if cfg.downloader == "wget" then | ||
| 105 | local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet " | ||
| 106 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 107 | wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " | ||
| 108 | end | ||
| 109 | if cache then | ||
| 110 | -- --timestamping is incompatible with --output-document, | ||
| 111 | -- but that's not a problem for our use cases. | ||
| 112 | fs.change_dir(dir.dir_name(filename)) | ||
| 113 | ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) | ||
| 114 | fs.pop_dir() | ||
| 115 | elseif filename then | ||
| 116 | ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) | ||
| 117 | else | ||
| 118 | ok = fs.execute_quiet(wget_cmd, url) | ||
| 119 | end | ||
| 120 | elseif cfg.downloader == "curl" then | ||
| 121 | local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" " | ||
| 122 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 123 | curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " | ||
| 124 | end | ||
| 125 | ok = fs.execute_string(fs.quiet_stderr(curl_cmd..fs.Q(url).." > "..fs.Q(filename))) | ||
| 126 | end | ||
| 127 | if ok then | ||
| 128 | return true, filename | ||
| 129 | else | ||
| 130 | return false | ||
| 131 | end | ||
| 132 | end | ||
| 133 | |||
| 134 | local md5_cmd = { | ||
| 135 | md5sum = fs.Q(vars.MD5SUM), | ||
| 136 | openssl = fs.Q(vars.OPENSSL).." md5", | ||
| 137 | md5 = fs.Q(vars.MD5), | ||
| 138 | } | ||
| 139 | |||
| 140 | --- Get the MD5 checksum for a file. | ||
| 141 | -- @param file string: The file to be computed. | ||
| 142 | -- @return string: The MD5 checksum or nil + message | ||
| 143 | function tools.get_md5(file) | ||
| 144 | local cmd = md5_cmd[cfg.md5checker] | ||
| 145 | if not cmd then return nil, "no MD5 checker command configured" end | ||
| 146 | local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) | ||
| 147 | local computed = pipe:read("*a") | ||
| 148 | pipe:close() | ||
| 149 | if computed then | ||
| 150 | computed = computed:match("("..("%x"):rep(32)..")") | ||
| 151 | end | ||
| 152 | if computed then return computed end | ||
| 153 | return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) | ||
| 154 | end | ||
| 155 | |||
| 156 | return tools | ||
diff --git a/src/luarocks/fs/unix.lua b/src/luarocks/fs/unix.lua index e2cc825b..520b3e99 100644 --- a/src/luarocks/fs/unix.lua +++ b/src/luarocks/fs/unix.lua | |||
| @@ -16,6 +16,13 @@ function unix.quiet(cmd) | |||
| 16 | return cmd.." 1> /dev/null 2> /dev/null" | 16 | return cmd.." 1> /dev/null 2> /dev/null" |
| 17 | end | 17 | end |
| 18 | 18 | ||
| 19 | --- Annotate command string for execution with quiet stderr. | ||
| 20 | -- @param cmd string: A command-line string. | ||
| 21 | -- @return string: The command-line, with stderr silencing annotation. | ||
| 22 | function unix.quiet_stderr(cmd) | ||
| 23 | return cmd.." 2> /dev/null" | ||
| 24 | end | ||
| 25 | |||
| 19 | --- Return an absolute pathname from a potentially relative one. | 26 | --- Return an absolute pathname from a potentially relative one. |
| 20 | -- @param pathname string: pathname to convert. | 27 | -- @param pathname string: pathname to convert. |
| 21 | -- @param relative_to string or nil: path to prepend when making | 28 | -- @param relative_to string or nil: path to prepend when making |
diff --git a/src/luarocks/fs/unix/tools.lua b/src/luarocks/fs/unix/tools.lua index ab55897e..84bd53fd 100644 --- a/src/luarocks/fs/unix/tools.lua +++ b/src/luarocks/fs/unix/tools.lua | |||
| @@ -7,74 +7,16 @@ local fs = require("luarocks.fs") | |||
| 7 | local dir = require("luarocks.dir") | 7 | local dir = require("luarocks.dir") |
| 8 | local cfg = require("luarocks.cfg") | 8 | local cfg = require("luarocks.cfg") |
| 9 | 9 | ||
| 10 | local dir_stack = {} | ||
| 11 | |||
| 12 | local vars = cfg.variables | 10 | local vars = cfg.variables |
| 13 | 11 | ||
| 14 | local function command_at(directory, cmd) | 12 | --- Adds prefix to command to make it run from a directory. |
| 13 | -- @param directory string: Path to a directory. | ||
| 14 | -- @param cmd string: A command-line string. | ||
| 15 | -- @return string: The command-line with prefix. | ||
| 16 | function tools.command_at(directory, cmd) | ||
| 15 | return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd | 17 | return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd |
| 16 | end | 18 | end |
| 17 | 19 | ||
| 18 | --- Obtain current directory. | ||
| 19 | -- Uses the module's internal directory stack. | ||
| 20 | -- @return string: the absolute pathname of the current directory. | ||
| 21 | function tools.current_dir() | ||
| 22 | local current = cfg.cache_pwd | ||
| 23 | if not current then | ||
| 24 | local pipe = io.popen(fs.Q(vars.PWD).." 2> /dev/null") | ||
| 25 | current = pipe:read("*l") | ||
| 26 | pipe:close() | ||
| 27 | cfg.cache_pwd = current | ||
| 28 | end | ||
| 29 | for _, directory in ipairs(dir_stack) do | ||
| 30 | current = fs.absolute_name(directory, current) | ||
| 31 | end | ||
| 32 | return current | ||
| 33 | end | ||
| 34 | |||
| 35 | --- Run the given command. | ||
| 36 | -- The command is executed in the current directory in the directory stack. | ||
| 37 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 38 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 39 | -- otherwise. | ||
| 40 | function tools.execute_string(cmd) | ||
| 41 | local current = fs.current_dir() | ||
| 42 | if not current then return false end | ||
| 43 | local code, err = os.execute(command_at(current, cmd)) | ||
| 44 | if code == 0 or code == true then | ||
| 45 | return true | ||
| 46 | else | ||
| 47 | return false | ||
| 48 | end | ||
| 49 | end | ||
| 50 | |||
| 51 | --- Change the current directory. | ||
| 52 | -- Uses the module's internal directory stack. This does not have exact | ||
| 53 | -- semantics of chdir, as it does not handle errors the same way, | ||
| 54 | -- but works well for our purposes for now. | ||
| 55 | -- @param directory string: The directory to switch to. | ||
| 56 | function tools.change_dir(directory) | ||
| 57 | assert(type(directory) == "string") | ||
| 58 | if fs.is_dir(directory) then | ||
| 59 | table.insert(dir_stack, directory) | ||
| 60 | return true | ||
| 61 | end | ||
| 62 | return nil, "directory not found: "..directory | ||
| 63 | end | ||
| 64 | |||
| 65 | --- Change directory to root. | ||
| 66 | -- Allows leaving a directory (e.g. for deleting it) in | ||
| 67 | -- a crossplatform way. | ||
| 68 | function tools.change_dir_to_root() | ||
| 69 | table.insert(dir_stack, "/") | ||
| 70 | end | ||
| 71 | |||
| 72 | --- Change working directory to the previous in the directory stack. | ||
| 73 | function tools.pop_dir() | ||
| 74 | local directory = table.remove(dir_stack) | ||
| 75 | return directory ~= nil | ||
| 76 | end | ||
| 77 | |||
| 78 | --- Create a directory if it does not already exist. | 20 | --- Create a directory if it does not already exist. |
| 79 | -- If any of the higher levels in the path name does not exist | 21 | -- If any of the higher levels in the path name does not exist |
| 80 | -- too, they are created as well. | 22 | -- too, they are created as well. |
| @@ -155,20 +97,6 @@ function tools.delete(arg) | |||
| 155 | fs.execute_quiet(vars.RM, "-rf", arg) | 97 | fs.execute_quiet(vars.RM, "-rf", arg) |
| 156 | end | 98 | end |
| 157 | 99 | ||
| 158 | --- Internal implementation function for fs.dir. | ||
| 159 | -- Yields a filename on each iteration. | ||
| 160 | -- @param at string: directory to list | ||
| 161 | -- @return nil | ||
| 162 | function tools.dir_iterator(at) | ||
| 163 | local pipe = io.popen(command_at(at, vars.LS)) | ||
| 164 | for file in pipe:lines() do | ||
| 165 | if file ~= "." and file ~= ".." then | ||
| 166 | coroutine.yield(file) | ||
| 167 | end | ||
| 168 | end | ||
| 169 | pipe:close() | ||
| 170 | end | ||
| 171 | |||
| 172 | --- Recursively scan the contents of a directory. | 100 | --- Recursively scan the contents of a directory. |
| 173 | -- @param at string or nil: directory to scan (will be the current | 101 | -- @param at string or nil: directory to scan (will be the current |
| 174 | -- directory if none is given). | 102 | -- directory if none is given). |
| @@ -183,7 +111,7 @@ function tools.find(at) | |||
| 183 | return {} | 111 | return {} |
| 184 | end | 112 | end |
| 185 | local result = {} | 113 | local result = {} |
| 186 | local pipe = io.popen(command_at(at, vars.FIND.." * 2>/dev/null")) | 114 | local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND.." *"))) |
| 187 | for file in pipe:lines() do | 115 | for file in pipe:lines() do |
| 188 | table.insert(result, file) | 116 | table.insert(result, file) |
| 189 | end | 117 | end |
| @@ -232,51 +160,6 @@ function tools.is_file(file) | |||
| 232 | return fs.execute(vars.TEST, "-f", file) | 160 | return fs.execute(vars.TEST, "-f", file) |
| 233 | end | 161 | end |
| 234 | 162 | ||
| 235 | --- Download a remote file. | ||
| 236 | -- @param url string: URL to be fetched. | ||
| 237 | -- @param filename string or nil: this function attempts to detect the | ||
| 238 | -- resulting local filename of the remote file as the basename of the URL; | ||
| 239 | -- if that is not correct (due to a redirection, for example), the local | ||
| 240 | -- filename can be given explicitly as this second argument. | ||
| 241 | -- @return (boolean, string): true and the filename on success, | ||
| 242 | -- false and the error message on failure. | ||
| 243 | function tools.use_downloader(url, filename, cache) | ||
| 244 | assert(type(url) == "string") | ||
| 245 | assert(type(filename) == "string" or not filename) | ||
| 246 | |||
| 247 | filename = fs.absolute_name(filename or dir.base_name(url)) | ||
| 248 | |||
| 249 | local ok | ||
| 250 | if cfg.downloader == "wget" then | ||
| 251 | local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent='"..cfg.user_agent.." via wget' --quiet " | ||
| 252 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 253 | wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " | ||
| 254 | end | ||
| 255 | if cache then | ||
| 256 | -- --timestamping is incompatible with --output-document, | ||
| 257 | -- but that's not a problem for our use cases. | ||
| 258 | fs.change_dir(dir.dir_name(filename)) | ||
| 259 | ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) | ||
| 260 | fs.pop_dir() | ||
| 261 | elseif filename then | ||
| 262 | ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) | ||
| 263 | else | ||
| 264 | ok = fs.execute_quiet(wget_cmd, url) | ||
| 265 | end | ||
| 266 | elseif cfg.downloader == "curl" then | ||
| 267 | local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent '"..cfg.user_agent.." via curl' " | ||
| 268 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 269 | curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " | ||
| 270 | end | ||
| 271 | ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> /dev/null 1> "..fs.Q(filename)) | ||
| 272 | end | ||
| 273 | if ok then | ||
| 274 | return true, filename | ||
| 275 | else | ||
| 276 | return false | ||
| 277 | end | ||
| 278 | end | ||
| 279 | |||
| 280 | function tools.chmod(pathname, mode) | 163 | function tools.chmod(pathname, mode) |
| 281 | if mode then | 164 | if mode then |
| 282 | return fs.execute(vars.CHMOD, mode, pathname) | 165 | return fs.execute(vars.CHMOD, mode, pathname) |
| @@ -319,28 +202,6 @@ function tools.unpack_archive(archive) | |||
| 319 | return true | 202 | return true |
| 320 | end | 203 | end |
| 321 | 204 | ||
| 322 | local md5_cmd = { | ||
| 323 | md5sum = vars.MD5SUM, | ||
| 324 | openssl = vars.OPENSSL.." md5", | ||
| 325 | md5 = vars.MD5, | ||
| 326 | } | ||
| 327 | |||
| 328 | --- Get the MD5 checksum for a file. | ||
| 329 | -- @param file string: The file to be computed. | ||
| 330 | -- @return string: The MD5 checksum | ||
| 331 | function tools.get_md5(file) | ||
| 332 | local cmd = md5_cmd[cfg.md5checker] | ||
| 333 | if not cmd then return nil, "no MD5 checker command configured" end | ||
| 334 | local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) | ||
| 335 | local computed = pipe:read("*a") | ||
| 336 | pipe:close() | ||
| 337 | if computed then | ||
| 338 | computed = computed:match("("..("%x"):rep(32)..")") | ||
| 339 | end | ||
| 340 | if computed then return computed end | ||
| 341 | return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) | ||
| 342 | end | ||
| 343 | |||
| 344 | function tools.get_permissions(filename) | 205 | function tools.get_permissions(filename) |
| 345 | local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) | 206 | local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) |
| 346 | local ret = pipe:read("*l") | 207 | local ret = pipe:read("*l") |
diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua index c14c421b..74f3ed69 100644 --- a/src/luarocks/fs/win32.lua +++ b/src/luarocks/fs/win32.lua | |||
| @@ -27,6 +27,13 @@ function win32.quiet(cmd) | |||
| 27 | return cmd.." 2> NUL 1> NUL" | 27 | return cmd.." 2> NUL 1> NUL" |
| 28 | end | 28 | end |
| 29 | 29 | ||
| 30 | --- Annotate command string for execution with quiet stderr. | ||
| 31 | -- @param cmd string: A command-line string. | ||
| 32 | -- @return string: The command-line, with stderr silencing annotation. | ||
| 33 | function win32.quiet_stderr(cmd) | ||
| 34 | return cmd.." 2> NUL" | ||
| 35 | end | ||
| 36 | |||
| 30 | local drive_letter = "[%.a-zA-Z]?:?[\\/]" | 37 | local drive_letter = "[%.a-zA-Z]?:?[\\/]" |
| 31 | 38 | ||
| 32 | local win_escape_chars = { | 39 | local win_escape_chars = { |
diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua index b9dce85c..9cb6d47a 100644 --- a/src/luarocks/fs/win32/tools.lua +++ b/src/luarocks/fs/win32/tools.lua | |||
| @@ -9,22 +9,13 @@ local fs = require("luarocks.fs") | |||
| 9 | local dir = require("luarocks.dir") | 9 | local dir = require("luarocks.dir") |
| 10 | local cfg = require("luarocks.cfg") | 10 | local cfg = require("luarocks.cfg") |
| 11 | 11 | ||
| 12 | local dir_stack = {} | ||
| 13 | |||
| 14 | local vars = cfg.variables | 12 | local vars = cfg.variables |
| 15 | 13 | ||
| 16 | --- Strip the last extension of a filename. | 14 | --- Adds prefix to command to make it run from a directory. |
| 17 | -- Example: "foo.tar.gz" becomes "foo.tar". | 15 | -- @param directory string: Path to a directory. |
| 18 | -- If filename has no dots, returns it unchanged. | 16 | -- @param cmd string: A command-line string. |
| 19 | -- @param filename string: The file name to strip. | 17 | -- @return string: The command-line with prefix. |
| 20 | -- @return string: The stripped name. | 18 | function tools.command_at(directory, cmd) |
| 21 | local function strip_extension(filename) | ||
| 22 | assert(type(filename) == "string") | ||
| 23 | |||
| 24 | return (filename:gsub("%.[^.]+$", "")) or filename | ||
| 25 | end | ||
| 26 | |||
| 27 | local function command_at(directory, cmd) | ||
| 28 | local drive = directory:match("^([A-Za-z]:)") | 19 | local drive = directory:match("^([A-Za-z]:)") |
| 29 | cmd = "cd " .. fs.Q(directory) .. " & " .. cmd | 20 | cmd = "cd " .. fs.Q(directory) .. " & " .. cmd |
| 30 | if drive then | 21 | if drive then |
| @@ -33,68 +24,6 @@ local function command_at(directory, cmd) | |||
| 33 | return cmd | 24 | return cmd |
| 34 | end | 25 | end |
| 35 | 26 | ||
| 36 | --- Obtain current directory. | ||
| 37 | -- Uses the module's internal directory stack. | ||
| 38 | -- @return string: the absolute pathname of the current directory. | ||
| 39 | function tools.current_dir() | ||
| 40 | local current = cfg.cache_pwd | ||
| 41 | if not current then | ||
| 42 | local pipe = io.popen(fs.Q(vars.PWD).. " 2> NUL") | ||
| 43 | current = pipe:read("*l") | ||
| 44 | pipe:close() | ||
| 45 | cfg.cache_pwd = current | ||
| 46 | end | ||
| 47 | for _, directory in ipairs(dir_stack) do | ||
| 48 | current = fs.absolute_name(directory, current) | ||
| 49 | end | ||
| 50 | return current | ||
| 51 | end | ||
| 52 | |||
| 53 | --- Run the given command. | ||
| 54 | -- The command is executed in the current directory in the directory stack. | ||
| 55 | -- @param cmd string: No quoting/escaping is applied to the command. | ||
| 56 | -- @return boolean: true if command succeeds (status code 0), false | ||
| 57 | -- otherwise. | ||
| 58 | function tools.execute_string(cmd) | ||
| 59 | local current = fs.current_dir() | ||
| 60 | if not current then return false end | ||
| 61 | cmd = command_at(current, cmd) | ||
| 62 | local code = os.execute(cmd) | ||
| 63 | if code == 0 or code == true then | ||
| 64 | return true | ||
| 65 | else | ||
| 66 | return false | ||
| 67 | end | ||
| 68 | end | ||
| 69 | |||
| 70 | --- Change the current directory. | ||
| 71 | -- Uses the module's internal directory stack. This does not have exact | ||
| 72 | -- semantics of chdir, as it does not handle errors the same way, | ||
| 73 | -- but works well for our purposes for now. | ||
| 74 | -- @param directory string: The directory to switch to. | ||
| 75 | -- @return boolean or (nil, string): true if successful, (nil, error message) if failed. | ||
| 76 | function tools.change_dir(directory) | ||
| 77 | assert(type(directory) == "string") | ||
| 78 | if fs.is_dir(directory) then | ||
| 79 | table.insert(dir_stack, directory) | ||
| 80 | return true | ||
| 81 | end | ||
| 82 | return nil, "directory not found: "..directory | ||
| 83 | end | ||
| 84 | |||
| 85 | --- Change directory to root. | ||
| 86 | -- Allows leaving a directory (e.g. for deleting it) in | ||
| 87 | -- a crossplatform way. | ||
| 88 | function tools.change_dir_to_root() | ||
| 89 | table.insert(dir_stack, "/") | ||
| 90 | end | ||
| 91 | |||
| 92 | --- Change working directory to the previous in the directory stack. | ||
| 93 | function tools.pop_dir() | ||
| 94 | local directory = table.remove(dir_stack) | ||
| 95 | return directory ~= nil | ||
| 96 | end | ||
| 97 | |||
| 98 | --- Create a directory if it does not already exist. | 27 | --- Create a directory if it does not already exist. |
| 99 | -- If any of the higher levels in the path name does not exist | 28 | -- If any of the higher levels in the path name does not exist |
| 100 | -- too, they are created as well. | 29 | -- too, they are created as well. |
| @@ -168,20 +97,6 @@ function tools.delete(arg) | |||
| 168 | fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )") | 97 | fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )") |
| 169 | end | 98 | end |
| 170 | 99 | ||
| 171 | --- Internal implementation function for fs.dir. | ||
| 172 | -- Yields a filename on each iteration. | ||
| 173 | -- @param at string: directory to list | ||
| 174 | -- @return nil | ||
| 175 | function tools.dir_iterator(at) | ||
| 176 | local pipe = io.popen(command_at(at, fs.Q(vars.LS))) | ||
| 177 | for file in pipe:lines() do | ||
| 178 | if file ~= "." and file ~= ".." then | ||
| 179 | coroutine.yield(file) | ||
| 180 | end | ||
| 181 | end | ||
| 182 | pipe:close() | ||
| 183 | end | ||
| 184 | |||
| 185 | --- Recursively scan the contents of a directory. | 100 | --- Recursively scan the contents of a directory. |
| 186 | -- @param at string or nil: directory to scan (will be the current | 101 | -- @param at string or nil: directory to scan (will be the current |
| 187 | -- directory if none is given). | 102 | -- directory if none is given). |
| @@ -196,7 +111,7 @@ function tools.find(at) | |||
| 196 | return {} | 111 | return {} |
| 197 | end | 112 | end |
| 198 | local result = {} | 113 | local result = {} |
| 199 | local pipe = io.popen(command_at(at, fs.Q(vars.FIND).." 2> NUL")) | 114 | local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(fs.Q(vars.FIND)))) |
| 200 | for file in pipe:lines() do | 115 | for file in pipe:lines() do |
| 201 | -- Windows find is a bit different | 116 | -- Windows find is a bit different |
| 202 | local first_two = file:sub(1,2) | 117 | local first_two = file:sub(1,2) |
| @@ -242,49 +157,14 @@ function tools.is_file(file) | |||
| 242 | return fs.execute(fs.Q(vars.TEST).." -f", file) | 157 | return fs.execute(fs.Q(vars.TEST).." -f", file) |
| 243 | end | 158 | end |
| 244 | 159 | ||
| 245 | --- Download a remote file. | 160 | --- Strip the last extension of a filename. |
| 246 | -- @param url string: URL to be fetched. | 161 | -- Example: "foo.tar.gz" becomes "foo.tar". |
| 247 | -- @param filename string or nil: this function attempts to detect the | 162 | -- If filename has no dots, returns it unchanged. |
| 248 | -- resulting local filename of the remote file as the basename of the URL; | 163 | -- @param filename string: The file name to strip. |
| 249 | -- if that is not correct (due to a redirection, for example), the local | 164 | -- @return string: The stripped name. |
| 250 | -- filename can be given explicitly as this second argument. | 165 | local function strip_extension(filename) |
| 251 | -- @return (boolean, string): true and the filename on success, | 166 | assert(type(filename) == "string") |
| 252 | -- false and the error message on failure. | 167 | return (filename:gsub("%.[^.]+$", "")) or filename |
| 253 | function tools.use_downloader(url, filename, cache) | ||
| 254 | assert(type(url) == "string") | ||
| 255 | assert(type(filename) == "string" or not filename) | ||
| 256 | |||
| 257 | filename = fs.absolute_name(filename or dir.base_name(url)) | ||
| 258 | |||
| 259 | local ok | ||
| 260 | if cfg.downloader == "wget" then | ||
| 261 | local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet " | ||
| 262 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 263 | wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " | ||
| 264 | end | ||
| 265 | if cache then | ||
| 266 | -- --timestamping is incompatible with --output-document, | ||
| 267 | -- but that's not a problem for our use cases. | ||
| 268 | fs.change_dir(dir.dir_name(filename)) | ||
| 269 | ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) | ||
| 270 | fs.pop_dir() | ||
| 271 | elseif filename then | ||
| 272 | ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) | ||
| 273 | else | ||
| 274 | ok = fs.execute_quiet(wget_cmd, url) | ||
| 275 | end | ||
| 276 | elseif cfg.downloader == "curl" then | ||
| 277 | local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" " | ||
| 278 | if cfg.connection_timeout and cfg.connection_timeout > 0 then | ||
| 279 | curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " | ||
| 280 | end | ||
| 281 | ok = fs.execute_string(curl_cmd..fs.Q(url).." 2> NUL 1> "..fs.Q(filename)) | ||
| 282 | end | ||
| 283 | if ok then | ||
| 284 | return true, filename | ||
| 285 | else | ||
| 286 | return false | ||
| 287 | end | ||
| 288 | end | 168 | end |
| 289 | 169 | ||
| 290 | --- Uncompress gzip file. | 170 | --- Uncompress gzip file. |
| @@ -334,28 +214,6 @@ function tools.unpack_archive(archive) | |||
| 334 | return true | 214 | return true |
| 335 | end | 215 | end |
| 336 | 216 | ||
| 337 | local md5_cmd = { | ||
| 338 | md5sum = fs.Q(vars.MD5SUM), | ||
| 339 | openssl = fs.Q(vars.OPENSSL).." md5", | ||
| 340 | md5 = fs.Q(vars.MD5), | ||
| 341 | } | ||
| 342 | |||
| 343 | --- Get the MD5 checksum for a file. | ||
| 344 | -- @param file string: The file to be computed. | ||
| 345 | -- @return string: The MD5 checksum or nil + message | ||
| 346 | function tools.get_md5(file) | ||
| 347 | local cmd = md5_cmd[cfg.md5checker] | ||
| 348 | if not cmd then return nil, "no MD5 checker command configured" end | ||
| 349 | local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) | ||
| 350 | local computed = pipe:read("*a") | ||
| 351 | pipe:close() | ||
| 352 | if computed then | ||
| 353 | computed = computed:match("("..("%x"):rep(32)..")") | ||
| 354 | end | ||
| 355 | if computed then return computed end | ||
| 356 | return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) | ||
| 357 | end | ||
| 358 | |||
| 359 | --- Test for existance of a file. | 217 | --- Test for existance of a file. |
| 360 | -- @param file string: filename to test | 218 | -- @param file string: filename to test |
| 361 | -- @return boolean: true if file exists, false otherwise. | 219 | -- @return boolean: true if file exists, false otherwise. |
