From 0ee1494093763b872de3c4e74575ff8e1aa122bd Mon Sep 17 00:00:00 2001 From: Fabio Mascarenhas Date: Fri, 15 Jan 2010 15:29:44 -0200 Subject: changes to make LR2 work in windows: filesystem changes, install.bat changes, executable wrappers for scripts in builtin build type --- src/luarocks/build/builtin.lua | 64 +++++++++++++++++++++ src/luarocks/cfg.lua | 4 +- src/luarocks/fs/lua.lua | 21 ++++--- src/luarocks/fs/win32.lua | 11 ---- src/luarocks/fs/win32/tools.lua | 122 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 196 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua index 30159352..d04b69d5 100644 --- a/src/luarocks/build/builtin.lua +++ b/src/luarocks/build/builtin.lua @@ -29,6 +29,28 @@ local function execute(...) return fs.execute(...) end +--- Makes an RC file with an embedded Lua script, for building .exes on Windows +-- @return nil if could open files, error otherwise +local function make_rc(luafilename, rcfilename) + local rcfile = io.open(rcfilename, "w") + if not rcfile then + error("Could not open "..rcfilename.." for writing.") + end + rcfile:write("#define IDS_RCLAUNCHER 1\r\n") + rcfile:write("STRINGTABLE\r\nBEGIN\r\n") + rcfile:write("IDS_RCLAUNCHER \"") + + for line in io.lines(luafilename) do + if not line:match("^#!") then + line = line:gsub("\\", "\\\\"):gsub('"', '""'):gsub("[\r\n]+", "") + rcfile:write(line .. "\\n\\\r\n") + end + end + + rcfile:write("\"\r\nEND\r\n") + rcfile:close() +end + --- Driver function for the builtin build back-end. -- @param rockspec table: the loaded rockspec. -- @return boolean or (nil, string): true if no errors ocurred, @@ -76,6 +98,23 @@ function run(rockspec) end return ok end + compile_wrapper_binary = function(fullname, name) + local fullbasename = fullname:gsub("%.lua$", ""):gsub("/", "\\") + local basename = name:gsub("%.lua$", ""):gsub("/", "\\") + local rcname = basename..".rc" + local resname = basename..".res" + local wrapname = basename..".exe" + make_rc(fullname, fullbasename..".rc") + local ok = execute(variables.RC, "-r", "-fo"..resname, rcname) + if not ok then return ok end + ok = execute(variables.LD, "-out:"..wrapname, resname, variables.WRAPPER, + fs.make_path(variables.LUA_LIBDIR, "lua5.1.lib"), "user32.lib") + local manifestfile = wrapname..".manifest" + if ok and fs.exists(manifestfile) then + ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..wrapname..";1") + end + return ok, wrapname + end else compile_object = function(object, source, defines, incdirs) local extras = {} @@ -92,6 +131,7 @@ function run(rockspec) end return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, "-L"..variables.LUA_LIBDIR, unpack(extras)) end + compile_wrapper_binary = function(fullname, name) return true, name end end local ok = true @@ -99,6 +139,30 @@ function run(rockspec) local luadir = path.lua_dir(rockspec.name, rockspec.version) local libdir = path.lib_dir(rockspec.name, rockspec.version) local docdir = path.doc_dir(rockspec.name, rockspec.version) + -- On Windows, compiles an .exe for each Lua file in build.install.bin, and + -- replaces the filename with the .exe name. Strips the .lua extension if it exists, + -- otherwise just appends .exe to the name + if build.install and build.install.bin then + for i, name in ipairs(build.install.bin) do + local fullname = fs.make_path(fs.current_dir(), name) + local match = name:match("%.lua$") + local basename = name:gsub("%.lua$", "") + local file + if not match then + file = io.open(fullname) + end + if match or (file and file:read():match("#!.*lua.*")) then + ok, name = compile_wrapper_binary(fullname, name) + if ok then + build.install.bin[i] = name + else + if file then file:close() end + return nil, "Build error in wrapper binaries" + end + end + if file then file:close() end + end + end for name, info in pairs(build.modules) do local moddir = path.module_to_path(name) if type(info) == "string" then diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index 0cf58ba3..c39056b4 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -22,7 +22,7 @@ if not ok then config = {} end -program_version = "2.0.1" +program_version = "2.0.2" user_agent = "LuaRocks/"..program_version local persist = require("luarocks.persist") @@ -169,6 +169,8 @@ if detected.windows then defaults.make = "nmake" -- TODO: Split Windows flavors between mingw and msvc defaults.makefile = "Makefile.win" defaults.variables.CC = "cl" + defaults.variables.RC = "rc" + defaults.variables.WRAPPER = config.LUAROCKS_PREFIX .. "\\2.0\\rclauncher.obj" defaults.variables.LD = "link" defaults.variables.MT = "mt" defaults.variables.CFLAGS = "/MD /O2" diff --git a/src/luarocks/fs/lua.lua b/src/luarocks/fs/lua.lua index c6c16236..3314d394 100644 --- a/src/luarocks/fs/lua.lua +++ b/src/luarocks/fs/lua.lua @@ -45,12 +45,12 @@ function is_writable(file) local result if fs.is_dir(file) then local file2 = file .. '/.tmpluarockstestwritable' - local fh = io.open(file2, 'w') + local fh = io.open(file2, 'wb') result = fh ~= nil if fh then fh:close() end os.remove(file2) else - local fh = io.open(file, 'r+') + local fh = io.open(file, 'rb+') result = fh ~= nil if fh then fh:close() end end @@ -64,6 +64,7 @@ end function make_temp_dir(name) assert(type(name) == "string") + name = name:gsub("\\", "/") local temp_dir = (os.getenv("TMP") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) if fs.make_dir(temp_dir) then return temp_dir @@ -170,9 +171,14 @@ end -- @return boolean: true on success, false on failure. function make_dir(directory) assert(type(directory) == "string") + directory = directory:gsub("\\", "/") local path = nil - for d in directory:gmatch("[^"..dir.separator.."]*"..dir.separator.."*") do - path = path and path..d or d + if directory:sub(2, 2) == ":" then + path = directory:sub(1, 2) + directory = directory:sub(4) + end + for d in directory:gmatch("([^"..dir.separator.."]+)"..dir.separator.."*") do + path = path and path .. dir.separator .. d or d local mode = lfs.attributes(path, "mode") if not mode then if not lfs.mkdir(path) then @@ -217,9 +223,9 @@ function copy(src, dest) if destmode == "directory" then dest = dir.path(dest, dir.base_name(src)) end - local src_h, err = io.open(src, "r") + local src_h, err = io.open(src, "rb") if not src_h then return nil, err end - local dest_h, err = io.open(dest, "w+") + local dest_h, err = io.open(dest, "wb+") if not dest_h then src_h:close() return nil, err end while true do local block = src_h:read(8192) @@ -303,7 +309,6 @@ end -- @return boolean: true on success, false on failure. function delete(arg) assert(arg) - assert(arg:sub(1,1) == "/") return recursive_delete(arg) or false end @@ -467,7 +472,7 @@ if md5_ok then -- @return string: The MD5 checksum function get_md5(file) file = fs.absolute_name(file) - local file = io.open(file, "r") + local file = io.open(file, "rb") if not file then return false end local computed = md5.sumhexa(file:read("*a")) file:close() diff --git a/src/luarocks/fs/win32.lua b/src/luarocks/fs/win32.lua index 0fccdf86..87ad26b2 100644 --- a/src/luarocks/fs/win32.lua +++ b/src/luarocks/fs/win32.lua @@ -22,17 +22,6 @@ function Q(arg) return '"' .. arg:gsub('"', '\\"') .. '"' end ---- Strip the last extension of a filename. --- Example: "foo.tar.gz" becomes "foo.tar". --- If filename has no dots, returns it unchanged. --- @param filename string: The file name to strip. --- @return string: The stripped name. -local function strip_extension(filename) - assert(type(filename) == "string") - - return (filename:gsub("%.[^.]+$", "")) or filename -end - --- Return an absolute pathname from a potentially relative one. -- @param pathname string: pathname to convert. -- @param relative_to string or nil: path to prepend when making diff --git a/src/luarocks/fs/win32/tools.lua b/src/luarocks/fs/win32/tools.lua index 60578d8d..2bd2b8c5 100644 --- a/src/luarocks/fs/win32/tools.lua +++ b/src/luarocks/fs/win32/tools.lua @@ -5,6 +5,20 @@ module("luarocks.fs.win32.tools", package.seeall) local fs = require("luarocks.fs") +local cfg = require("luarocks.cfg") + +local dir_stack = {} + +--- Strip the last extension of a filename. +-- Example: "foo.tar.gz" becomes "foo.tar". +-- If filename has no dots, returns it unchanged. +-- @param filename string: The file name to strip. +-- @return string: The stripped name. +local function strip_extension(filename) + assert(type(filename) == "string") + + return (filename:gsub("%.[^.]+$", "")) or filename +end local function command_at(directory, cmd) local drive = directory:match("^([A-Za-z]:)") @@ -24,12 +38,82 @@ function exists(file) " invalidcommandname 2>NUL 1>NUL") end ---- Test is pathname is a directory. +--- Obtain current directory. +-- Uses the module's internal dir stack. +-- @return string: the absolute pathname of the current directory. +function current_dir() + local current = os.getenv("PWD") + if not current then + local pipe = io.popen("pwd") + current = pipe:read("*l") + pipe:close() + end + for _, d in ipairs(dir_stack) do + current = fs.absolute_name(d, current) + end + return current +end + +--- Test is pathname is a regular file. -- @param file string: pathname to test --- @return boolean: true if it is a directory, false otherwise. -function is_dir(file) +-- @return boolean: true if it is a regular file, false otherwise. +function is_file(file) assert(file) - return fs.execute("chdir /D " .. fs.Q(file) .. " 2>NUL 1>NUL") + return fs.execute("test -f", file) +end + +--- Get the MD5 checksum for a file. +-- @param file string: The file to be computed. +-- @return string: The MD5 checksum +function get_md5(file, md5sum) + file = fs.absolute_name(file) + local computed + if cfg.md5checker == "md5sum" then + local pipe = io.popen("md5sum "..file) + computed = pipe:read("*l") + pipe:close() + if computed then + computed = computed:gsub("[^%x]+", ""):sub(1,32) + end + elseif cfg.md5checker == "openssl" then + local pipe = io.popen("openssl md5 "..file) + computed = pipe:read("*l") + pipe:close() + if computed then + computed = computed:sub(-32) + end + elseif cfg.md5checker == "md5" then + local pipe = io.popen("md5 "..file) + computed = pipe:read("*l") + pipe:close() + if computed then + computed = computed:sub(-32) + end + end + return computed +end + +--- Change the current directory. +-- Uses the module's internal dir stack. This does not have exact +-- semantics of chdir, as it does not handle errors the same way, +-- but works well for our purposes for now. +-- @param d string: The directory to switch to. +function change_dir(d) + assert(type(d) == "string") + table.insert(dir_stack, d) +end + +--- Change directory to root. +-- Allows leaving a directory (e.g. for deleting it) in +-- a crossplatform way. +function change_dir_to_root() + table.insert(dir_stack, "/") +end + +--- Change working directory to the previous in the dir stack. +function pop_dir() + local d = table.remove(dir_stack) + return d ~= nil end --- Run the given command. @@ -50,7 +134,7 @@ end -- @return boolean: true if it is a regular file, false otherwise. function is_dir(file) assert(file) - return fs.execute("test -d" .. fs.Q(file) .. " 2>NUL 1>NUL") + return fs.execute("test -d " .. fs.Q(file) .. " 2>NUL 1>NUL") end --- Create a directory if it does not already exist. @@ -73,6 +157,15 @@ function remove_dir_if_empty(d) fs.execute_string("rmdir "..fs.Q(d).." 1> NUL 2> NUL") end +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param dir string: pathname of directory to remove. +function remove_dir_tree_if_empty(d) + assert(d) + fs.execute_string("rmdir "..fs.Q(d).." 1> NUL 2> NUL") +end + --- Copy a file. -- @param src string: Pathname of source -- @param dest string: Pathname of destination @@ -171,7 +264,7 @@ end function download(url, filename) assert(type(url) == "string") assert(type(filename) == "string" or not filename) - local wget_cmd = "wget --no-cache --user-agent="..cfg.user_agent.." --quiet --continue " + local wget_cmd = "wget --cache=off --user-agent="..cfg.user_agent.." --quiet --continue " if filename then return fs.execute(wget_cmd.." --output-document ", filename, url) @@ -180,6 +273,23 @@ function download(url, filename) end end +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @param ... Filenames to be stored in the archive are given as +-- additional arguments. +-- @return boolean: true on success, false on failure. +function zip(zipfile, ...) + return fs.execute("zip -r", zipfile, ...) +end + +--- Uncompress files from a .zip archive. +-- @param zipfile string: pathname of .zip archive to be extracted. +-- @return boolean: true on success, false on failure. +function unzip(zipfile) + assert(zipfile) + return fs.execute("unzip", zipfile) +end + --- Uncompress gzip file. -- @param archive string: Filename of archive. -- @return boolean : success status -- cgit v1.2.3-55-g6feb