* vs on x64: first try to set up tools for x64, then for x86 using all VS verions instead of trying x64 for 11.0+ and x86 for 9.0 and 10.0 * vs_32, vs_64: do setup even if cl.exe is present. That behaviour is only needed as a default.
1701 lines
61 KiB
Python
Executable File
1701 lines
61 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""A tool for installing Lua and LuaRocks locally."""
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import platform
|
|
import re
|
|
import shutil
|
|
import string
|
|
import subprocess
|
|
import sys
|
|
import tarfile
|
|
import tempfile
|
|
import zipfile
|
|
|
|
try:
|
|
from urllib import urlretrieve
|
|
except ImportError:
|
|
from urllib.request import urlretrieve
|
|
|
|
if os.name == "nt":
|
|
try:
|
|
import _winreg as winreg
|
|
except ImportError:
|
|
import winreg
|
|
|
|
hererocks_version = "Hererocks 0.6.2"
|
|
__all__ = ["main"]
|
|
|
|
opts = None
|
|
temp_dir = None
|
|
|
|
def is_executable(path):
|
|
return (os.path.exists(path) and
|
|
os.access(path, os.F_OK | os.X_OK) and
|
|
not os.path.isdir(path))
|
|
|
|
def program_exists(prog):
|
|
path = os.environ.get("PATH", os.defpath)
|
|
|
|
if not path:
|
|
return False
|
|
|
|
if os.name == "nt":
|
|
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
|
|
candidates = [prog + ext for ext in pathext]
|
|
else:
|
|
candidates = [prog]
|
|
|
|
for directory in path.split(os.pathsep):
|
|
for candidate in candidates:
|
|
if is_executable(os.path.join(directory, candidate)):
|
|
return True
|
|
|
|
return False
|
|
|
|
platform_to_lua_target = {
|
|
"linux": "linux",
|
|
"win": "mingw" if os.name == "nt" and program_exists("gcc") and not program_exists("cl") else "vs",
|
|
"darwin": "macosx",
|
|
"freebsd": "freebsd"
|
|
}
|
|
|
|
def using_cl():
|
|
return opts.target.startswith("vs")
|
|
|
|
def get_default_lua_target():
|
|
for plat, lua_target in platform_to_lua_target.items():
|
|
if sys.platform.startswith(plat):
|
|
return lua_target
|
|
|
|
return "posix" if os.name == "posix" else "generic"
|
|
|
|
def get_default_cache():
|
|
if os.name == "nt":
|
|
cache_root = os.getenv("LOCALAPPDATA")
|
|
|
|
if cache_root is None:
|
|
cache_root = os.getenv("USERPROFILE")
|
|
|
|
if cache_root is None:
|
|
return None
|
|
|
|
cache_root = os.path.join(cache_root, "Local Settings", "Application Data")
|
|
|
|
return os.path.join(cache_root, "HereRocks", "Cache")
|
|
else:
|
|
home = os.getenv("HOME")
|
|
|
|
if home is None:
|
|
return None
|
|
else:
|
|
return os.path.join(home, ".cache", "hererocks")
|
|
|
|
def run(*args, **kwargs):
|
|
"""Execute a command.
|
|
|
|
Command can be passed as several arguments, each being a string
|
|
or a list of strings; lists are flattened.
|
|
If opts.verbose is True, output of the command is shown.
|
|
If the command exits with non-zero, print an error message and exit.
|
|
If keyward argument get_output is True, output is returned.
|
|
Additionally, non-zero exit code with empty output is ignored.
|
|
"""
|
|
|
|
capture = kwargs.get("get_output", False)
|
|
args = [arg for arglist in args for arg in (arglist if isinstance(arglist, list) else [arglist])]
|
|
|
|
if opts.verbose:
|
|
print("Running {}".format(" ".join(args)))
|
|
|
|
live_output = opts.verbose and not capture
|
|
runner = subprocess.check_call if live_output else subprocess.check_output
|
|
|
|
try:
|
|
output = runner(args, stderr=subprocess.STDOUT)
|
|
except subprocess.CalledProcessError as exception:
|
|
if capture and not exception.output.strip():
|
|
# Ignore errors if output is empty.
|
|
return ""
|
|
|
|
if not live_output:
|
|
sys.stdout.write(exception.output.decode("UTF-8"))
|
|
|
|
sys.exit("Error: got exitcode {} from command {}".format(
|
|
exception.returncode, " ".join(args)))
|
|
except OSError:
|
|
sys.exit("Error: couldn't run {}: is {} in PATH?".format(" ".join(args), args[0]))
|
|
|
|
if opts.verbose and capture:
|
|
sys.stdout.write(output.decode("UTF-8"))
|
|
|
|
return capture and output.decode("UTF-8").strip()
|
|
|
|
def get_output(*args):
|
|
return run(get_output=True, *args)
|
|
|
|
def memoize(func):
|
|
cache = {}
|
|
|
|
def wrapper(arg):
|
|
if cache.get(arg) is None:
|
|
cache[arg] = func(arg)
|
|
|
|
return cache[arg]
|
|
|
|
return wrapper
|
|
|
|
@memoize
|
|
def query_registry(key, value):
|
|
keys = [key, key.replace("\\", "\\Wow6432Node\\", 1)]
|
|
|
|
for candidate in keys:
|
|
if opts.verbose:
|
|
print("Querying registry key HKEY_LOCAL_MACHINE\\{}:{}".format(candidate, value))
|
|
|
|
try:
|
|
handle = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, candidate)
|
|
except WindowsError:
|
|
pass
|
|
else:
|
|
res = winreg.QueryValueEx(handle, value)[0]
|
|
winreg.CloseKey(handle)
|
|
return res
|
|
|
|
@memoize
|
|
def check_existence(path):
|
|
if opts.verbose:
|
|
print("Checking existence of {}".format(path))
|
|
|
|
return os.path.exists(path)
|
|
|
|
def copy_dir(src, dst):
|
|
shutil.copytree(src, dst, ignore=lambda _, __: {".git"})
|
|
|
|
clever_http_git_whitelist = [
|
|
"http://github.com/", "https://github.com/",
|
|
"http://bitbucket.com/", "https://bitbucket.com/"
|
|
]
|
|
|
|
git_branch_does_accept_tags = None
|
|
|
|
def git_branch_accepts_tags():
|
|
global git_branch_does_accept_tags
|
|
|
|
if git_branch_does_accept_tags is None:
|
|
version_output = get_output("git", "--version")
|
|
match = re.search(r"(\d+)\.(\d+)\.?(\d*)", version_output)
|
|
|
|
if match:
|
|
major = int(match.group(1))
|
|
minor = int(match.group(2))
|
|
tiny = int(match.group(3) or "0")
|
|
git_branch_does_accept_tags = (major, minor, tiny) >= (1, 7, 10)
|
|
else:
|
|
git_branch_does_accept_tags = False
|
|
|
|
return git_branch_does_accept_tags
|
|
|
|
def git_clone_command(repo, ref, is_cache):
|
|
if is_cache:
|
|
# Cache full repos.
|
|
return ["git", "clone"], True
|
|
|
|
# Http(s) transport may be dumb and not understand --depth.
|
|
if repo.startswith("http://") or repo.startswith("https://"):
|
|
if not any(map(repo.startswith, clever_http_git_whitelist)):
|
|
return ["git", "clone"], True
|
|
|
|
# Have to clone whole repo to get a specific commit.
|
|
if all(c in string.hexdigits for c in ref):
|
|
return ["git", "clone"], True
|
|
|
|
if git_branch_accepts_tags():
|
|
return ["git", "clone", "--depth=1", "--branch=" + ref], False
|
|
else:
|
|
return ["git", "clone", "--depth=1"], True
|
|
|
|
important_identifiers = ["name", "source", "version", "repo", "commit", "location"]
|
|
other_identifiers = ["target", "compat", "c flags", "patched", "readline"]
|
|
|
|
def escape_path(s):
|
|
return re.sub(r"[^\w]", "_", s)
|
|
|
|
def hash_identifiers(identifiers):
|
|
return "-".join(escape_path(
|
|
identifiers.get(name, "")) for name in important_identifiers + other_identifiers)
|
|
|
|
def show_identifiers(identifiers):
|
|
title = identifiers["name"]
|
|
|
|
if "version" in identifiers:
|
|
title += " " + identifiers["version"]
|
|
elif "major version" in identifiers and title != "LuaJIT":
|
|
title += " " + identifiers["major version"]
|
|
|
|
if identifiers["source"] == "release":
|
|
print(title)
|
|
elif identifiers["source"] == "git":
|
|
print("{} @{} (cloned from {})".format(title, identifiers["commit"][:7], identifiers["repo"]))
|
|
else:
|
|
print("{} (from local sources)".format(title))
|
|
|
|
for name in other_identifiers:
|
|
if identifiers.get(name):
|
|
print(" {}: {}".format(name.capitalize(), identifiers[name]))
|
|
|
|
def copy_files(path, *files):
|
|
if not os.path.exists(path):
|
|
os.makedirs(path)
|
|
|
|
for src in files:
|
|
if src is not None:
|
|
shutil.copy(src, path)
|
|
|
|
def exe(name):
|
|
if os.name == "nt":
|
|
return name + ".exe"
|
|
else:
|
|
return name
|
|
|
|
def objext():
|
|
return ".obj" if using_cl() else ".o"
|
|
|
|
def sha256_of_file(filename):
|
|
fileobj = open(filename, "rb")
|
|
contents = fileobj.read()
|
|
fileobj.close()
|
|
return hashlib.sha256(contents).hexdigest()
|
|
|
|
class Program(object):
|
|
def __init__(self, version):
|
|
version = self.translations.get(version, version)
|
|
|
|
if version in self.versions:
|
|
# Simple version.
|
|
self.source = "release"
|
|
self.fetched = False
|
|
self.version = version
|
|
self.fixed_version = version
|
|
self.version_suffix = " " + version
|
|
elif "@" in version:
|
|
# Version from a git repo.
|
|
self.source = "git"
|
|
|
|
if version.startswith("@"):
|
|
# Use the default git repo for this program.
|
|
self.repo = self.default_repo
|
|
ref = version[1:] or "master"
|
|
else:
|
|
self.repo, _, ref = version.partition("@")
|
|
|
|
# Have to clone the repo to get the commit ref points to.
|
|
self.fetch_repo(ref)
|
|
self.commit = get_output("git", "rev-parse", "HEAD")
|
|
self.version_suffix = " @" + self.commit[:7]
|
|
else:
|
|
# Local directory.
|
|
self.source = "local"
|
|
|
|
if not os.path.exists(version):
|
|
sys.exit("Error: bad {} version {}".format(self.title, version))
|
|
|
|
print("Using {} from {}".format(self.title, version))
|
|
result_dir = os.path.join(temp_dir, self.name)
|
|
copy_dir(version, result_dir)
|
|
os.chdir(result_dir)
|
|
self.fetched = True
|
|
self.version_suffix = ""
|
|
|
|
def fetch_repo(self, ref):
|
|
message = "Cloning {} from {} @{}".format(self.title, self.repo, ref)
|
|
|
|
if self.repo == self.default_repo and not opts.no_git_cache and opts.downloads is not None:
|
|
# Default repos are cached.
|
|
if not os.path.exists(opts.downloads):
|
|
os.makedirs(opts.downloads)
|
|
|
|
repo_path = os.path.join(opts.downloads, self.name)
|
|
self.fetched = False
|
|
|
|
if os.path.exists(repo_path):
|
|
print(message + " (cached)")
|
|
# Sync with origin first.
|
|
os.chdir(repo_path)
|
|
|
|
if not get_output("git", "rev-parse", "--quiet", "--verify", ref):
|
|
run("git", "fetch")
|
|
|
|
run("git", "checkout", ref)
|
|
|
|
# If HEAD is not detached, we are on a branch that must be synced.
|
|
if get_output("git", "symbolic-ref", "-q", "HEAD"):
|
|
run("git", "pull", "--rebase")
|
|
|
|
return
|
|
else:
|
|
self.fetched = True
|
|
repo_path = os.path.join(temp_dir, self.name)
|
|
|
|
print(message)
|
|
clone_command, need_checkout = git_clone_command(self.repo, ref, not self.fetched)
|
|
run(clone_command, self.repo, repo_path)
|
|
os.chdir(repo_path)
|
|
|
|
if need_checkout and ref != "master":
|
|
run("git", "checkout", ref)
|
|
|
|
def get_download_name(self):
|
|
return self.name + "-" + self.fixed_version + ("-win32" if self.win32_zip else "")
|
|
|
|
def get_file_name(self):
|
|
return self.get_download_name() + (".zip" if self.win32_zip else ".tar.gz")
|
|
|
|
def get_download_url(self):
|
|
return self.downloads + "/" + self.get_file_name()
|
|
|
|
def fetch(self):
|
|
if self.fetched:
|
|
return
|
|
|
|
if self.source == "git":
|
|
# Currently inside the cached git repo, just copy it somewhere.
|
|
result_dir = os.path.join(temp_dir, self.name)
|
|
copy_dir(".", result_dir)
|
|
os.chdir(result_dir)
|
|
return
|
|
|
|
if opts.downloads is None:
|
|
archive_name = os.path.join(temp_dir, self.get_file_name())
|
|
else:
|
|
if not os.path.exists(opts.downloads):
|
|
os.makedirs(opts.downloads)
|
|
|
|
archive_name = os.path.join(opts.downloads, self.get_file_name())
|
|
|
|
url = self.get_download_url()
|
|
message = "Fetching {} from {}".format(self.title, url)
|
|
|
|
if not opts.downloads or not os.path.exists(archive_name):
|
|
print(message)
|
|
urlretrieve(url, archive_name)
|
|
else:
|
|
print(message + " (cached)")
|
|
|
|
print("Verifying SHA256 checksum")
|
|
expected_checksum = self.checksums[self.get_file_name()]
|
|
observed_checksum = sha256_of_file(archive_name)
|
|
if expected_checksum != observed_checksum:
|
|
message = "SHA256 checksum mismatch for {}\nExpected: {}\nObserved: {}".format(
|
|
archive_name, expected_checksum, observed_checksum)
|
|
|
|
if opts.ignore_checksums:
|
|
print("Warning: " + message)
|
|
else:
|
|
sys.exit("Error: " + message)
|
|
|
|
if self.win32_zip:
|
|
archive = zipfile.ZipFile(archive_name)
|
|
else:
|
|
archive = tarfile.open(archive_name, "r:gz")
|
|
|
|
archive.extractall(temp_dir)
|
|
archive.close()
|
|
os.chdir(os.path.join(temp_dir, self.get_download_name()))
|
|
self.fetched = True
|
|
|
|
def set_identifiers(self):
|
|
self.identifiers = {
|
|
"name": self.title,
|
|
"source": self.source
|
|
}
|
|
|
|
if self.source == "release":
|
|
self.identifiers["version"] = self.version
|
|
elif self.source == "git":
|
|
self.identifiers["repo"] = self.repo
|
|
self.identifiers["commit"] = self.commit
|
|
|
|
def update_identifiers(self, all_identifiers):
|
|
self.all_identifiers = all_identifiers
|
|
installed_identifiers = all_identifiers.get(self.name)
|
|
self.set_identifiers()
|
|
|
|
if not opts.ignore_installed and self.source != "local" and installed_identifiers is not None:
|
|
if hash_identifiers(self.identifiers) == hash_identifiers(installed_identifiers):
|
|
print(self.title + self.version_suffix + " already installed")
|
|
return False
|
|
|
|
self.build()
|
|
self.install()
|
|
all_identifiers[self.name] = self.identifiers
|
|
return True
|
|
|
|
class Lua(Program):
|
|
def __init__(self, version):
|
|
super(Lua, self).__init__(version)
|
|
|
|
if self.source == "release":
|
|
self.major_version = self.major_version_from_version()
|
|
else:
|
|
self.major_version = self.major_version_from_source()
|
|
|
|
if not self.version_suffix:
|
|
self.set_version_suffix()
|
|
|
|
self.set_compat()
|
|
self.add_options_to_version_suffix()
|
|
|
|
self.redefines = []
|
|
self.compat_cflags = []
|
|
self.set_package_paths()
|
|
self.add_package_paths_redefines()
|
|
self.add_compat_cflags_and_redefines()
|
|
|
|
@staticmethod
|
|
def major_version_from_source():
|
|
lua_h = open(os.path.join("src", "lua.h"))
|
|
|
|
for line in lua_h:
|
|
match = re.match(r"^\s*#define\s+LUA_VERSION_NUM\s+50(\d)\s*$", line)
|
|
|
|
if match:
|
|
return "5." + match.group(1)
|
|
|
|
sys.exit("Error: couldn't infer Lua major version from lua.h")
|
|
|
|
def set_identifiers(self):
|
|
super(Lua, self).set_identifiers()
|
|
|
|
self.identifiers["target"] = opts.target
|
|
self.identifiers["compat"] = self.compat
|
|
self.identifiers["c flags"] = opts.cflags or ""
|
|
self.identifiers["location"] = opts.location
|
|
self.identifiers["major version"] = self.major_version
|
|
|
|
if using_cl():
|
|
cl_help = get_output("cl")
|
|
cl_version = re.search(r"(1[56789])\.\d+", cl_help)
|
|
cl_arch = re.search(r"(x(?:86)|(?:64))", cl_help)
|
|
|
|
if not cl_version or not cl_arch:
|
|
sys.exit("Error: couldn't determine cl.exe version and architecture")
|
|
|
|
cl_version = cl_version.group(1)
|
|
cl_arch = cl_arch.group(1)
|
|
|
|
self.identifiers["vs year"] = cl_version_to_vs_year[cl_version]
|
|
self.identifiers["vs arch"] = cl_arch
|
|
|
|
def add_options_to_version_suffix(self):
|
|
options = []
|
|
|
|
if os.name == "nt" or opts.target != get_default_lua_target():
|
|
options.append(("target", opts.target))
|
|
|
|
if self.compat != "default":
|
|
options.append(("compat", self.compat))
|
|
|
|
if opts.cflags is not None:
|
|
options.append(("cflags", opts.cflags))
|
|
|
|
if opts.no_readline:
|
|
options.append(("readline", "false"))
|
|
|
|
if options:
|
|
self.version_suffix += " (" + (", ".join(
|
|
opt + ": " + value for opt, value in options)) + ")"
|
|
|
|
def set_package_paths(self):
|
|
local_paths_first = self.major_version == "5.1"
|
|
|
|
module_path = os.path.join(opts.location, "share", "lua", self.major_version)
|
|
module_path_parts = [
|
|
os.path.join(module_path, "?.lua"),
|
|
os.path.join(module_path, "?", "init.lua")
|
|
]
|
|
module_path_parts.insert(0 if local_paths_first else 2, os.path.join(".", "?.lua"))
|
|
self.package_path = ";".join(module_path_parts)
|
|
|
|
cmodule_path = os.path.join(opts.location, "lib", "lua", self.major_version)
|
|
so_extension = ".dll" if os.name == "nt" else ".so"
|
|
cmodule_path_parts = [
|
|
os.path.join(cmodule_path, "?" + so_extension),
|
|
os.path.join(cmodule_path, "loadall" + so_extension)
|
|
]
|
|
cmodule_path_parts.insert(0 if local_paths_first else 2,
|
|
os.path.join(".", "?" + so_extension))
|
|
self.package_cpath = ";".join(cmodule_path_parts)
|
|
|
|
def add_package_paths_redefines(self):
|
|
package_path = self.package_path.replace("\\", "\\\\")
|
|
package_cpath = self.package_cpath.replace("\\", "\\\\")
|
|
self.redefines.extend([
|
|
"#undef LUA_PATH_DEFAULT",
|
|
"#undef LUA_CPATH_DEFAULT",
|
|
"#define LUA_PATH_DEFAULT \"{}\"".format(package_path),
|
|
"#define LUA_CPATH_DEFAULT \"{}\"".format(package_cpath)
|
|
])
|
|
|
|
def patch_redefines(self):
|
|
redefines = "\n".join(self.redefines)
|
|
|
|
luaconf_h = open(os.path.join("src", "luaconf.h"), "rb")
|
|
luaconf_src = luaconf_h.read()
|
|
luaconf_h.close()
|
|
|
|
body, _, tail = luaconf_src.rpartition(b"#endif")
|
|
|
|
luaconf_h = open(os.path.join("src", "luaconf.h"), "wb")
|
|
luaconf_h.write(body)
|
|
luaconf_h.write(redefines.encode("UTF-8"))
|
|
luaconf_h.write(b"\n#endif")
|
|
luaconf_h.write(tail)
|
|
luaconf_h.close()
|
|
|
|
def build(self):
|
|
if opts.builds and self.source != "local":
|
|
self.cached_build_path = os.path.join(opts.builds,
|
|
hash_identifiers(self.identifiers))
|
|
|
|
if os.path.exists(self.cached_build_path):
|
|
print("Building " + self.title + self.version_suffix + " (cached)")
|
|
os.chdir(self.cached_build_path)
|
|
return
|
|
else:
|
|
self.cached_build_path = None
|
|
|
|
self.fetch()
|
|
print("Building " + self.title + self.version_suffix)
|
|
self.patch_redefines()
|
|
self.make()
|
|
|
|
if self.cached_build_path is not None:
|
|
copy_dir(".", self.cached_build_path)
|
|
|
|
def install(self):
|
|
print("Installing " + self.title + self.version_suffix)
|
|
self.make_install()
|
|
|
|
class PatchError(Exception):
|
|
pass
|
|
|
|
class LineScanner(object):
|
|
def __init__(self, lines):
|
|
self.lines = lines
|
|
self.line_number = 1
|
|
|
|
def consume_line(self):
|
|
if self.line_number > len(self.lines):
|
|
raise PatchError("source is too short")
|
|
else:
|
|
self.line_number += 1
|
|
return self.lines[self.line_number - 2]
|
|
|
|
class Hunk(object):
|
|
def __init__(self, start_line, lines):
|
|
self.start_line = start_line
|
|
self.lines = lines
|
|
|
|
def add_new_lines(self, old_lines_scanner, new_lines):
|
|
while old_lines_scanner.line_number < self.start_line:
|
|
new_lines.append(old_lines_scanner.consume_line())
|
|
|
|
for line in self.lines:
|
|
first_char, rest = line[0], line[1:]
|
|
|
|
if first_char in " -":
|
|
# Deleting or copying a line: it must match what's in the diff.
|
|
if rest != old_lines_scanner.consume_line():
|
|
raise PatchError("source is different")
|
|
|
|
if first_char in " +":
|
|
# Adding or copying a line: add it to the line list.
|
|
new_lines.append(rest)
|
|
|
|
class FilePatch(object):
|
|
def __init__(self, file_name, lines):
|
|
self.file_name = file_name
|
|
self.hunks = []
|
|
self.new_lines = []
|
|
hunk_lines = None
|
|
start_line = None
|
|
|
|
for line in lines:
|
|
first_char = line[0]
|
|
|
|
if first_char == "@":
|
|
if start_line is not None:
|
|
self.hunks.append(Hunk(start_line, hunk_lines))
|
|
|
|
match = re.match(r"^@@ \-(\d+)", line)
|
|
start_line = int(match.group(1))
|
|
hunk_lines = []
|
|
else:
|
|
hunk_lines.append(line)
|
|
|
|
if start_line is not None:
|
|
self.hunks.append(Hunk(start_line, hunk_lines))
|
|
|
|
def prepare_application(self):
|
|
if not os.path.exists(self.file_name):
|
|
raise PatchError("{} doesn't exist".format(self.file_name))
|
|
|
|
with open(self.file_name, "r") as handler:
|
|
source = handler.read()
|
|
|
|
old_lines = source.splitlines()
|
|
old_lines_scanner = LineScanner(old_lines)
|
|
|
|
for hunk in self.hunks:
|
|
hunk.add_new_lines(old_lines_scanner, self.new_lines)
|
|
|
|
while old_lines_scanner.line_number <= len(old_lines):
|
|
self.new_lines.append(old_lines_scanner.consume_line())
|
|
|
|
self.new_lines.append("")
|
|
|
|
def apply(self):
|
|
with open(self.file_name, "wb") as handler:
|
|
handler.write("\n".join(self.new_lines).encode("UTF-8"))
|
|
|
|
class Patch(object):
|
|
def __init__(self, src):
|
|
# The first and the last lines are empty.
|
|
lines = src.splitlines()[1:-1]
|
|
indent_length = len(lines[0]) - len(lines[0].lstrip())
|
|
lines = [line[indent_length:] or " " for line in lines]
|
|
self.file_patches = []
|
|
file_lines = None
|
|
file_name = None
|
|
|
|
for line in lines:
|
|
match = re.match(r"^([\w\.]+):$", line)
|
|
|
|
if match:
|
|
if file_name is not None:
|
|
self.file_patches.append(FilePatch(file_name, file_lines))
|
|
|
|
file_name = match.group(1)
|
|
file_lines = []
|
|
else:
|
|
file_lines.append(line)
|
|
|
|
if file_name is not None:
|
|
self.file_patches.append(FilePatch(file_name, file_lines))
|
|
|
|
def apply(self):
|
|
try:
|
|
for file_patch in self.file_patches:
|
|
file_patch.prepare_application()
|
|
except PatchError as e:
|
|
return e.args[0]
|
|
|
|
for file_patch in self.file_patches:
|
|
file_patch.apply()
|
|
|
|
class RioLua(Lua):
|
|
name = "lua"
|
|
title = "Lua"
|
|
downloads = "http://www.lua.org/ftp"
|
|
win32_zip = False
|
|
default_repo = "https://github.com/lua/lua"
|
|
versions = [
|
|
"5.1", "5.1.1", "5.1.2", "5.1.3", "5.1.4", "5.1.5",
|
|
"5.2.0", "5.2.1", "5.2.2", "5.2.3", "5.2.4",
|
|
"5.3.0", "5.3.1", "5.3.2"
|
|
]
|
|
translations = {
|
|
"5": "5.3.2",
|
|
"5.1": "5.1.5",
|
|
"5.1.0": "5.1",
|
|
"5.2": "5.2.4",
|
|
"5.3": "5.3.2",
|
|
"^": "5.3.2",
|
|
"latest": "5.3.2"
|
|
}
|
|
checksums = {
|
|
"lua-5.1.tar.gz" : "7f5bb9061eb3b9ba1e406a5aa68001a66cb82bac95748839dc02dd10048472c1",
|
|
"lua-5.1.1.tar.gz": "c5daeed0a75d8e4dd2328b7c7a69888247868154acbda69110e97d4a6e17d1f0",
|
|
"lua-5.1.2.tar.gz": "5cf098c6fe68d3d2d9221904f1017ff0286e4a9cc166a1452a456df9b88b3d9e",
|
|
"lua-5.1.3.tar.gz": "6b5df2edaa5e02bf1a2d85e1442b2e329493b30b0c0780f77199d24f087d296d",
|
|
"lua-5.1.4.tar.gz": "b038e225eaf2a5b57c9bcc35cd13aa8c6c8288ef493d52970c9545074098af3a",
|
|
"lua-5.1.5.tar.gz": "2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333",
|
|
"lua-5.2.0.tar.gz": "cabe379465aa8e388988073d59b69e76ba0025429d2c1da80821a252cdf6be0d",
|
|
"lua-5.2.1.tar.gz": "64304da87976133196f9e4c15250b70f444467b6ed80d7cfd7b3b982b5177be5",
|
|
"lua-5.2.2.tar.gz": "3fd67de3f5ed133bf312906082fa524545c6b9e1b952e8215ffbd27113f49f00",
|
|
"lua-5.2.3.tar.gz": "13c2fb97961381f7d06d5b5cea55b743c163800896fd5c5e2356201d3619002d",
|
|
"lua-5.2.4.tar.gz": "b9e2e4aad6789b3b63a056d442f7b39f0ecfca3ae0f1fc0ae4e9614401b69f4b",
|
|
"lua-5.3.0.tar.gz": "ae4a5eb2d660515eb191bfe3e061f2b8ffe94dce73d32cfd0de090ddcc0ddb01",
|
|
"lua-5.3.1.tar.gz": "072767aad6cc2e62044a66e8562f51770d941e972dc1e4068ba719cd8bffac17",
|
|
"lua-5.3.2.tar.gz": "c740c7bb23a936944e1cc63b7c3c5351a8976d7867c5252c8854f7b2af9da68f",
|
|
}
|
|
all_patches = {
|
|
"When loading a file, Lua may call the reader function again after it returned end of input": """
|
|
lzio.h:
|
|
@@ -59,6 +59,7 @@
|
|
lua_Reader reader;
|
|
void* data;\t\t\t/* additional data */
|
|
lua_State *L;\t\t\t/* Lua state (for reader) */
|
|
+ int eoz;\t\t\t/* true if reader has no more data */
|
|
};
|
|
|
|
|
|
lzio.c:
|
|
@@ -22,10 +22,14 @@
|
|
size_t size;
|
|
lua_State *L = z->L;
|
|
const char *buff;
|
|
+ if (z->eoz) return EOZ;
|
|
lua_unlock(L);
|
|
buff = z->reader(L, z->data, &size);
|
|
lua_lock(L);
|
|
- if (buff == NULL || size == 0) return EOZ;
|
|
+ if (buff == NULL || size == 0) {
|
|
+ z->eoz = 1; /* avoid calling reader function next time */
|
|
+ return EOZ;
|
|
+ }
|
|
z->n = size - 1;
|
|
z->p = buff;
|
|
return char2int(*(z->p++));
|
|
@@ -51,6 +55,7 @@
|
|
z->data = data;
|
|
z->n = 0;
|
|
z->p = NULL;
|
|
+ z->eoz = 0;
|
|
}
|
|
""",
|
|
"Metatable may access its own deallocated field when it has a self reference in __newindex": """
|
|
lvm.c:
|
|
@@ -190,18 +190,19 @@
|
|
for (loop = 0; loop < MAXTAGLOOP; loop++) {
|
|
const TValue *tm;
|
|
if (oldval != NULL) {
|
|
- lua_assert(ttistable(t) && ttisnil(oldval));
|
|
+ Table *h = hvalue(t); /* save 't' table */
|
|
+ lua_assert(ttisnil(oldval));
|
|
/* must check the metamethod */
|
|
- if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL &&
|
|
+ if ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&
|
|
/* no metamethod; is there a previous entry in the table? */
|
|
(oldval != luaO_nilobject ||
|
|
/* no previous entry; must create one. (The next test is
|
|
always true; we only need the assignment.) */
|
|
- (oldval = luaH_newkey(L, hvalue(t), key), 1))) {
|
|
+ (oldval = luaH_newkey(L, h, key), 1))) {
|
|
/* no metamethod and (now) there is an entry with given key */
|
|
setobj2t(L, cast(TValue *, oldval), val);
|
|
- invalidateTMcache(hvalue(t));
|
|
- luaC_barrierback(L, hvalue(t), val);
|
|
+ invalidateTMcache(h);
|
|
+ luaC_barrierback(L, h, val);
|
|
return;
|
|
}
|
|
/* else will try the metamethod */
|
|
""",
|
|
"Label between local definitions can mix-up their initializations": """
|
|
lparser.c:
|
|
@@ -1226,7 +1226,7 @@
|
|
checkrepeated(fs, ll, label); /* check for repeated labels */
|
|
checknext(ls, TK_DBCOLON); /* skip double colon */
|
|
/* create new entry for this label */
|
|
- l = newlabelentry(ls, ll, label, line, fs->pc);
|
|
+ l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
|
|
skipnoopstat(ls); /* skip other no-op statements */
|
|
if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
|
|
/* assume that locals are already out of scope */
|
|
""",
|
|
"gmatch iterator fails when called from a coroutine different from the one that created it": """
|
|
lstrlib.c:
|
|
@@ -688,6 +688,7 @@
|
|
static int gmatch_aux (lua_State *L) {
|
|
GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
|
|
const char *src;
|
|
+ gm->ms.L = L;
|
|
for (src = gm->src; src <= gm->ms.src_end; src++) {
|
|
const char *e;
|
|
reprepstate(&gm->ms);
|
|
"""
|
|
}
|
|
patches_per_version = {
|
|
"5.1": {
|
|
"5": [
|
|
"When loading a file, Lua may call the reader function again after it returned end of input"
|
|
]
|
|
},
|
|
"5.3": {
|
|
"2": [
|
|
"Metatable may access its own deallocated field when it has a self reference in __newindex",
|
|
"Label between local definitions can mix-up their initializations",
|
|
"gmatch iterator fails when called from a coroutine different from the one that created it"
|
|
]
|
|
}
|
|
}
|
|
|
|
def __init__(self, version):
|
|
super(RioLua, self).__init__(version)
|
|
|
|
self.lua_file = exe("lua")
|
|
self.luac_file = exe("luac")
|
|
|
|
if using_cl():
|
|
self.arch_file = "lua5" + self.major_version[2] + ".lib"
|
|
else:
|
|
self.arch_file = "liblua5" + self.major_version[2] + ".a"
|
|
|
|
if opts.target == "mingw" or using_cl():
|
|
self.dll_file = "lua5" + self.major_version[2] + ".dll"
|
|
else:
|
|
self.dll_file = None
|
|
|
|
def set_identifiers(self):
|
|
super(RioLua, self).set_identifiers()
|
|
|
|
self.identifiers["readline"] = str(not opts.no_readline).lower()
|
|
self.identifiers["patched"] = str(opts.patch).lower()
|
|
|
|
def major_version_from_version(self):
|
|
return self.version[:3]
|
|
|
|
def set_version_suffix(self):
|
|
self.version_suffix = " " + self.major_version
|
|
|
|
def set_compat(self):
|
|
if self.major_version == "5.1":
|
|
self.compat = "none" if opts.compat == "none" else "default"
|
|
elif self.major_version == "5.2":
|
|
self.compat = "none" if opts.compat in ["none", "5.2"] else "default"
|
|
else:
|
|
self.compat = "default" if opts.compat in ["default", "5.2"] else opts.compat
|
|
|
|
def add_compat_cflags_and_redefines(self):
|
|
if self.major_version == "5.1":
|
|
if self.compat == "none":
|
|
self.redefines.extend([
|
|
"#undef LUA_COMPAT_VARARG", "#undef LUA_COMPAT_MOD",
|
|
"#undef LUA_COMPAT_LSTR", "#undef LUA_COMPAT_GFIND",
|
|
"#undef LUA_COMPAT_OPENLIB"
|
|
])
|
|
elif self.major_version == "5.2":
|
|
if self.compat == "default":
|
|
self.compat_cflags.append("-DLUA_COMPAT_ALL")
|
|
else:
|
|
if self.compat in ["5.1", "all"]:
|
|
self.compat_cflags.append("-DLUA_COMPAT_5_1")
|
|
|
|
if self.compat in ["default", "5.2", "all"]:
|
|
self.compat_cflags.append("-DLUA_COMPAT_5_2")
|
|
|
|
def apply_patch(self, patch_name):
|
|
patch = self.all_patches[patch_name]
|
|
err = Patch(patch).apply()
|
|
|
|
if opts.verbose:
|
|
status = "OK" if err is None else "fail - {}".format(err)
|
|
print('Patch for "{}": {}'.format(patch_name, status))
|
|
|
|
return err is None
|
|
|
|
@staticmethod
|
|
def minor_version_from_source():
|
|
regexps = [
|
|
# Lua 5.1.x, but not Lua 5.1(.0)
|
|
r'^\s*#define\s+LUA_RELEASE\s+"Lua 5\.1\.(\d)"\s*$',
|
|
# Lua 5.2.x and 5.3.x
|
|
r'^\s*#define LUA_VERSION_RELEASE\s+"(\d)"\s*$'
|
|
]
|
|
|
|
lua_h = open(os.path.join("lua.h"))
|
|
|
|
for line in lua_h:
|
|
for regexp in regexps:
|
|
match = re.match(regexp, line)
|
|
|
|
if match:
|
|
return match.group(1)
|
|
|
|
# Reachable only for Lua 5.1(.0) or if lua.h is strange.
|
|
return "0"
|
|
|
|
def get_minor_version(self):
|
|
if self.source == "release":
|
|
return "0" if self.version == "5.1" else self.version[-1:]
|
|
else:
|
|
return self.minor_version_from_source()
|
|
|
|
def handle_patches(self):
|
|
patches = self.patches_per_version.get(self.major_version, {})
|
|
|
|
if not patches:
|
|
print("No patches available for Lua {}".format(self.major_version))
|
|
return
|
|
|
|
minor_version = self.get_minor_version()
|
|
patches = patches.get(minor_version, [])
|
|
|
|
if not patches:
|
|
print("No patches available for Lua {}.{}".format(self.major_version, minor_version))
|
|
return
|
|
|
|
if not opts.patch:
|
|
print("Skipping {} patch{}, use --patch to apply {}".format(
|
|
len(patches), "" if len(patches) == 1 else "es",
|
|
"it" if len(patches) == 1 else "them"))
|
|
return
|
|
|
|
applied = sum(map(self.apply_patch, patches))
|
|
print("Using {} patch{} ({} available)".format(
|
|
applied, "" if applied == 1 else "es", len(patches)))
|
|
|
|
def make(self):
|
|
if self.major_version == "5.3":
|
|
cc = ["gcc", "-std=gnu99"]
|
|
else:
|
|
cc = "gcc"
|
|
|
|
if opts.target in ["linux", "freebsd", "macosx"]:
|
|
cflags = ["-DLUA_USE_POSIX", "-DLUA_USE_DLOPEN"]
|
|
|
|
if self.major_version == "5.2":
|
|
cflags.extend(["-DLUA_USE_STRTODHEX", "-DLUA_USE_AFORMAT", "-DLUA_USE_LONGLONG"])
|
|
|
|
if not opts.no_readline:
|
|
cflags.append("-DLUA_USE_READLINE")
|
|
|
|
if opts.target == "linux":
|
|
lflags = ["-Wl,-E", "-ldl"]
|
|
|
|
if not opts.no_readline:
|
|
if self.major_version == "5.1":
|
|
lflags.extend(["-lreadline", "-lhistory", "-lncurses"])
|
|
else:
|
|
lflags.append("-lreadline")
|
|
elif opts.target == "freebsd":
|
|
lflags = []
|
|
|
|
if not opts.no_readline:
|
|
lflags.extend(["-Wl,-E", "-lreadline"])
|
|
else:
|
|
lflags = []
|
|
cc = "cc"
|
|
|
|
if not opts.no_readline:
|
|
lflags.append("-lreadline")
|
|
else:
|
|
lflags = []
|
|
|
|
if opts.target == "posix":
|
|
cflags = ["-DLUA_USE_POSIX"]
|
|
else:
|
|
cflags = []
|
|
|
|
cflags.extend(self.compat_cflags)
|
|
|
|
if opts.cflags is not None:
|
|
cflags.extend(opts.cflags.split())
|
|
|
|
if using_cl():
|
|
cc = ["cl", "/nologo", "/MD", "/O2", "/W3", "/c", "/D_CRT_SECURE_NO_DEPRECATE"]
|
|
else:
|
|
cflags = ["-O2", "-Wall", "-Wextra"] + cflags
|
|
|
|
lflags.append("-lm")
|
|
static_cflags = list(cflags)
|
|
|
|
if opts.target == "mingw":
|
|
cflags.insert(3, "-DLUA_BUILD_AS_DLL")
|
|
elif using_cl():
|
|
cflags.insert(0, "-DLUA_BUILD_AS_DLL")
|
|
|
|
os.chdir("src")
|
|
self.handle_patches()
|
|
objs = []
|
|
luac_objs = ["luac" + objext(), "print" + objext()]
|
|
|
|
for src in sorted(os.listdir(".")):
|
|
base, ext = os.path.splitext(src)
|
|
|
|
if ext == ".c":
|
|
obj = base + objext()
|
|
objs.append(obj)
|
|
|
|
cmd_suffix = src if using_cl() else ["-c", "-o", obj, src]
|
|
run(cc, static_cflags if obj in luac_objs else cflags, cmd_suffix)
|
|
|
|
lib_objs = [obj_ for obj_ in objs if obj_ not in luac_objs and (obj_ != "lua" + objext())]
|
|
luac_objs = ["luac" + objext()]
|
|
|
|
if "print" + objext() in objs:
|
|
luac_objs.append("print" + objext())
|
|
|
|
if using_cl():
|
|
run("link", "/nologo", "/out:luac.exe", luac_objs, lib_objs)
|
|
|
|
if os.path.exists("luac.exe.manifest"):
|
|
run("mt", "/nologo", "-manifest", "luac.exe.manifest", "-outputresource:luac.exe")
|
|
else:
|
|
run("ar", "rcu", self.arch_file, lib_objs)
|
|
run("ranlib", self.arch_file)
|
|
run(cc, "-o", self.luac_file, luac_objs, self.arch_file, lflags)
|
|
|
|
if opts.target == "mingw":
|
|
run(cc, "-shared", "-o", self.dll_file, lib_objs)
|
|
run("strip", "--strip-unneeded", self.dll_file)
|
|
run(cc, "-o", self.lua_file, "-s", "lua.o", self.dll_file)
|
|
elif using_cl():
|
|
run("link", "/nologo", "/DLL", "/out:" + self.dll_file, lib_objs)
|
|
|
|
if os.path.exists(self.dll_file + ".manifest"):
|
|
run("mt", "/nologo", "-manifest", self.dll_file + ".manifest",
|
|
"-outputresource:" + self.dll_file)
|
|
|
|
run("link", "/nologo", "/out:lua.exe", "lua.obj", self.arch_file)
|
|
|
|
if os.path.exists("lua.exe.manifest"):
|
|
run("mt", "/nologo", "-manifest", "lua.exe.manifest", "-outputresource:lua.exe")
|
|
else:
|
|
run(cc, "-o", self.lua_file, "lua.o", self.arch_file, lflags)
|
|
|
|
os.chdir("..")
|
|
|
|
def make_install(self):
|
|
os.chdir("src")
|
|
copy_files(os.path.join(opts.location, "bin"),
|
|
self.lua_file, self.luac_file, self.dll_file)
|
|
|
|
lua_hpp = "lua.hpp"
|
|
|
|
if not os.path.exists(lua_hpp):
|
|
lua_hpp = "../etc/lua.hpp"
|
|
|
|
copy_files(os.path.join(opts.location, "include"),
|
|
"lua.h", "luaconf.h", "lualib.h", "lauxlib.h", lua_hpp)
|
|
|
|
copy_files(os.path.join(opts.location, "lib"), self.arch_file)
|
|
|
|
class LuaJIT(Lua):
|
|
name = "LuaJIT"
|
|
title = "LuaJIT"
|
|
downloads = "https://github.com/LuaJIT/LuaJIT/archive"
|
|
win32_zip = False
|
|
default_repo = "https://github.com/LuaJIT/LuaJIT"
|
|
versions = [
|
|
"2.0.0", "2.0.1", "2.0.2", "2.0.3", "2.0.4",
|
|
"2.1.0-beta1", "2.1.0-beta2"
|
|
]
|
|
translations = {
|
|
"2": "2.0.4",
|
|
"2.0": "2.0.4",
|
|
"2.1": "2.1.0-beta2",
|
|
"^": "2.0.4",
|
|
"latest": "2.0.4"
|
|
}
|
|
checksums = {
|
|
"LuaJIT-2.0.0.tar.gz" : "778650811bdd9fc55bbb6a0e845e4c0101001ce5ca1ab95001f0d289c61760ab",
|
|
"LuaJIT-2.0.1-fixed.tar.gz": "d33e91f347c0d79aa4fb1bd835df282a25f7ef9c3395928a1183947667c2d6b2",
|
|
"LuaJIT-2.0.2.tar.gz" : "7cf1bdcd89452f64ed994cff85ae32613a876543a81a88939155266558a669bc",
|
|
"LuaJIT-2.0.3.tar.gz" : "8da3d984495a11ba1bce9a833ba60e18b532ca0641e7d90d97fafe85ff014baa",
|
|
"LuaJIT-2.0.4.tar.gz" : "d2abdf16bd3556c41c0aaedad76b6c227ca667be8350111d037a4c54fd43abad",
|
|
"LuaJIT-2.1.0-beta1.tar.gz": "3d10de34d8020d7035193013f07c93fc7f16fcf0bb28fc03f572a21a368a5f2a",
|
|
"LuaJIT-2.1.0-beta2.tar.gz": "82e115b21aa74634b2d9f3cb3164c21f3cde7750ba3258d8820f500f6a36b651",
|
|
}
|
|
|
|
def __init__(self, version):
|
|
super(LuaJIT, self).__init__(version)
|
|
|
|
if self.source == "release" and self.version == "2.0.1":
|
|
# v2.0.1 tag is broken, use v2.0.1-fixed.
|
|
self.fixed_version = "2.0.1-fixed"
|
|
|
|
def get_download_url(self):
|
|
return self.downloads + "/v" + self.fixed_version + ".tar.gz"
|
|
|
|
@staticmethod
|
|
def major_version_from_version():
|
|
return "5.1"
|
|
|
|
@staticmethod
|
|
def set_version_suffix():
|
|
pass
|
|
|
|
def set_compat(self):
|
|
self.compat = "5.2" if opts.compat in ["all", "5.2"] else "default"
|
|
|
|
def add_compat_cflags_and_redefines(self):
|
|
if self.compat == "5.2":
|
|
self.compat_cflags.append("-DLUAJIT_ENABLE_LUA52COMPAT")
|
|
|
|
@staticmethod
|
|
def add_cflags_to_msvcbuild(cflags):
|
|
msvcbuild_file = open("msvcbuild.bat", "rb")
|
|
msvcbuild_src = msvcbuild_file.read()
|
|
msvcbuild_file.close()
|
|
|
|
start, assignment, value_and_rest = msvcbuild_src.partition(b"@set LJCOMPILE")
|
|
value_and_rest = value_and_rest.decode("UTF-8")
|
|
|
|
msvcbuild_file = open("msvcbuild.bat", "wb")
|
|
msvcbuild_file.write(start)
|
|
msvcbuild_file.write(assignment)
|
|
msvcbuild_file.write(value_and_rest.replace("\r\n", " {}\r\n".format(cflags), 1).encode("UTF-8"))
|
|
msvcbuild_file.close()
|
|
|
|
def make(self):
|
|
cflags = list(self.compat_cflags)
|
|
|
|
if opts.cflags is not None:
|
|
cflags.extend(opts.cflags.split())
|
|
|
|
if using_cl():
|
|
os.chdir("src")
|
|
|
|
if cflags:
|
|
self.add_cflags_to_msvcbuild(" ".join(cflags))
|
|
|
|
run("msvcbuild.bat")
|
|
os.chdir("..")
|
|
else:
|
|
if opts.target == "mingw" and program_exists("mingw32-make"):
|
|
make = "mingw32-make"
|
|
else:
|
|
make = "make"
|
|
|
|
if not cflags:
|
|
run(make)
|
|
else:
|
|
run(make, "XCFLAGS=" + " ".join(cflags))
|
|
|
|
def make_install(self):
|
|
luajit_file = exe("luajit")
|
|
lua_file = exe("lua")
|
|
arch_file = "libluajit.a"
|
|
target_arch_file = "libluajit-5.1.a"
|
|
so_file = "libluajit.so"
|
|
target_so_file = "libluajit-5.1.so.2"
|
|
dll_file = None
|
|
|
|
if os.name == "nt":
|
|
arch_file = "lua51.lib"
|
|
target_arch_file = "lua51.lib"
|
|
dll_file = "lua51.dll"
|
|
|
|
os.chdir("src")
|
|
copy_files(os.path.join(opts.location, "bin"), dll_file)
|
|
shutil.copy(luajit_file, os.path.join(opts.location, "bin", lua_file))
|
|
|
|
copy_files(os.path.join(opts.location, "include"),
|
|
"lua.h", "luaconf.h", "lualib.h", "lauxlib.h", "lua.hpp", "luajit.h")
|
|
|
|
copy_files(os.path.join(opts.location, "lib"))
|
|
|
|
if opts.target != "mingw":
|
|
shutil.copy(arch_file, os.path.join(opts.location, "lib", target_arch_file))
|
|
|
|
if os.name != "nt":
|
|
shutil.copy(so_file, os.path.join(opts.location, "lib", target_so_file))
|
|
|
|
jitlib_path = os.path.join(
|
|
opts.location, "share", "lua", self.major_version, "jit")
|
|
|
|
if os.path.exists(jitlib_path):
|
|
shutil.rmtree(jitlib_path)
|
|
|
|
copy_dir("jit", jitlib_path)
|
|
|
|
class LuaRocks(Program):
|
|
name = "luarocks"
|
|
title = "LuaRocks"
|
|
downloads = "http://keplerproject.github.io/luarocks/releases"
|
|
win32_zip = os.name == "nt"
|
|
default_repo = "https://github.com/keplerproject/luarocks"
|
|
versions = [
|
|
"2.0.8", "2.0.9", "2.0.10", "2.0.11", "2.0.12",
|
|
"2.1.0", "2.1.1", "2.1.2",
|
|
"2.2.0", "2.2.1", "2.2.2",
|
|
"2.3.0"
|
|
]
|
|
translations = {
|
|
"2": "2.3.0",
|
|
"2.0": "2.0.12",
|
|
"2.1": "2.1.2",
|
|
"2.2": "2.2.2",
|
|
"2.3": "2.3.0",
|
|
"3": "@luarocks-3",
|
|
"^": "2.3.0",
|
|
"latest": "2.3.0"
|
|
}
|
|
checksums = {
|
|
"luarocks-2.0.10.tar.gz" : "11731dfe6e210a962cb2a857b8b2f14a9ab1043e13af09a1b9455b486401b46e",
|
|
"luarocks-2.0.10-win32.zip": "bc00dbc80da6939f372bace50ea68d1746111280862858ecef9fcaaa3d70661f",
|
|
"luarocks-2.0.11.tar.gz" : "feee5a606938604f4fef1fdadc29692b9b7cdfb76fa537908d772adfb927741e",
|
|
"luarocks-2.0.11-win32.zip": "b0c2c149da49d70972178e3aec0a92a678b3daa2993dd6d6cdd56269730f8e12",
|
|
"luarocks-2.0.12.tar.gz" : "ad4b465c5dfbdce436ef746a434317110d79f18ff79202a2697e215f4ac407ed",
|
|
"luarocks-2.0.12-win32.zip": "dfb7c7429541628903ec811f151ea19435d2182a9515db57542f6825802a1ae7",
|
|
"luarocks-2.0.8.tar.gz" : "f8abf1ab03b744a817721a0ff4a0ee454e068735efaa8d1aadcfcd0f07cdaa88",
|
|
"luarocks-2.0.8-win32.zip" : "109e2dd91c66a7fd69471fcd56b3276f57aef334a4a8f53776b94b1ebd58334e",
|
|
"luarocks-2.0.9.tar.gz" : "4e25a8052c6abe1685da1093e1adb59aa034106c9d335aa932f7b3b51297c63d",
|
|
"luarocks-2.0.9-win32.zip" : "c9389c288bac2c276e363ffbaaa6356119adefed243f0c47bf74611f9296bd94",
|
|
"luarocks-2.1.0.tar.gz" : "69bf4cb40c8010a5d434f70d26c9885f4260ac265fdaa848c0edb50cc8e53f88",
|
|
"luarocks-2.1.0-win32.zip" : "363ecc0d09b70179735eef0dae158f98733e6d34226d6b5243bcbdc50d5987ca",
|
|
"luarocks-2.1.1.tar.gz" : "995ba1b9c982b503fd6fc61c905dc07c3a7533c06587616d9f00d9f62bd318ac",
|
|
"luarocks-2.1.1-win32.zip" : "5fa8eccc91c7c1431480257cb1cf99fff902cf762576e1cd208762f01003e780",
|
|
"luarocks-2.1.2.tar.gz" : "62625c7609c886bae23f8db55dba45dbb083bae0d19bf12fe29ec95f7d389ff3",
|
|
"luarocks-2.1.2-win32.zip" : "66beb4318261bc3e91544ba8672f04f3057137d32b2c33275ab6a355a7b5a546",
|
|
"luarocks-2.2.0.tar.gz" : "9b1a4ec7b103e2fb90a7ba8589d7e0c8523a3d6d54ac469b0bbc144292b9279c",
|
|
"luarocks-2.2.0-win32.zip" : "0fb56f40f09352567c66318018b52b9fa9e055f318b8589abed24eb1e76a3def",
|
|
"luarocks-2.2.1.tar.gz" : "713f8a7e33f1e6dc77ba2eec849a80a95f24f82382e0abc4523c2b8d435f7c55",
|
|
"luarocks-2.2.1-win32.zip" : "01b0410eb19f6e31342cbc12524f2e00eddfdf0bd9edcc325def7bcd93e331be",
|
|
"luarocks-2.2.2.tar.gz" : "4f0427706873f30d898aeb1dfb6001b8a3478e46a5249d015c061fe675a1f022",
|
|
"luarocks-2.2.2-win32.zip" : "576721fb6fe224bbf5f60bd4c94c7c6f686889bb452ae1923a46d56f02df6588",
|
|
"luarocks-2.3.0.tar.gz" : "68e38feeb66052e29ad1935a71b875194ed8b9c67c2223af5f4d4e3e2464ed97",
|
|
"luarocks-2.3.0-win32.zip" : "7aa02e7249906563a7ab8bb9db497cdeab0506328e4c8d45ffba120526dfec2a",
|
|
}
|
|
|
|
def is_luarocks_2_0(self):
|
|
if self.source == "release":
|
|
return self.versions.index(self.version) < self.versions.index("2.1.0")
|
|
|
|
makefile = open("Makefile")
|
|
|
|
for line in makefile:
|
|
if re.match(r"^\s*all:\s+built\s*$", line):
|
|
return True
|
|
|
|
return False
|
|
|
|
@staticmethod
|
|
def get_cmake_generator(lua_identifiers):
|
|
lua_target = lua_identifiers["target"]
|
|
|
|
if lua_target == "mingw":
|
|
return "MinGW Makefiles"
|
|
elif using_cl():
|
|
vs_year = lua_identifiers["vs year"]
|
|
vs_arch = lua_identifiers["vs arch"]
|
|
vs_short_version = vs_year_to_version[vs_year][:-2]
|
|
return "Visual Studio {} 20{}{}".format(
|
|
vs_short_version, vs_year, " Win64" if vs_arch == "x64" else "")
|
|
|
|
def build(self):
|
|
lua_identifiers = self.all_identifiers.get("lua", self.all_identifiers.get("LuaJIT"))
|
|
|
|
if lua_identifiers is None:
|
|
sys.exit("Error: can't install LuaRocks: Lua is not present in {}".format(opts.location))
|
|
|
|
self.fetch()
|
|
|
|
if os.name == "nt":
|
|
print("Building and installing LuaRocks" + self.version_suffix)
|
|
|
|
help_text = get_output("install.bat", "/?")
|
|
args = [
|
|
"install.bat",
|
|
"/P", os.path.join(opts.location, "luarocks"),
|
|
"/LUA", opts.location,
|
|
"/FORCECONFIG",
|
|
]
|
|
if opts.target == "mingw":
|
|
args += ["/MW"]
|
|
# Since LuaRocks 2.0.13
|
|
if "/LV" in help_text:
|
|
args += ["/LV", lua_identifiers["major version"]]
|
|
# Since LuaRocks 2.1.2
|
|
if "/NOREG" in help_text:
|
|
args += ["/NOREG", "/Q"]
|
|
if "/NOADMIN" in help_text:
|
|
args += ["/NOADMIN"]
|
|
|
|
run(args)
|
|
|
|
for script in ["luarocks.bat", "luarocks-admin.bat"]:
|
|
for subdir in [".", "2.2", "2.1", "2.0"]:
|
|
script_path = os.path.join(opts.location, "luarocks", subdir, script)
|
|
|
|
if os.path.exists(script_path):
|
|
shutil.copy(script_path, os.path.join(opts.location, "bin"))
|
|
break
|
|
else:
|
|
sys.exit("Error: can't find {} in {}".format(script, os.path.join(opts.location, "luarocks")))
|
|
|
|
cmake_generator = self.get_cmake_generator(lua_identifiers)
|
|
|
|
if cmake_generator is not None:
|
|
config_path = os.path.join(
|
|
opts.location, "luarocks", "config-{}.lua".format(lua_identifiers["major version"]))
|
|
config_h = open(config_path, "ab")
|
|
config_h.write('\r\ncmake_generator = "{}"\r\n'.format(cmake_generator).encode("UTF-8"))
|
|
config_h.close()
|
|
|
|
else:
|
|
print("Building LuaRocks" + self.version_suffix)
|
|
run("./configure", "--prefix=" + opts.location,
|
|
"--with-lua=" + opts.location, "--force-config")
|
|
|
|
if self.is_luarocks_2_0():
|
|
run("make")
|
|
else:
|
|
run("make", "build")
|
|
|
|
def install(self):
|
|
if os.name != "nt":
|
|
print("Installing LuaRocks" + self.version_suffix)
|
|
run("make", "install")
|
|
|
|
def get_manifest_name():
|
|
return os.path.join(opts.location, "hererocks.manifest")
|
|
|
|
manifest_version = 3
|
|
|
|
def get_installed_identifiers():
|
|
if not os.path.exists(get_manifest_name()):
|
|
return {}
|
|
|
|
with open(get_manifest_name()) as manifest_h:
|
|
try:
|
|
identifiers = json.load(manifest_h)
|
|
except ValueError:
|
|
return {}
|
|
|
|
if identifiers.get("version") == manifest_version:
|
|
return identifiers
|
|
else:
|
|
return {}
|
|
|
|
def save_installed_identifiers(all_identifiers):
|
|
all_identifiers["version"] = manifest_version
|
|
|
|
with open(get_manifest_name(), "w") as manifest_h:
|
|
json.dump(all_identifiers, manifest_h)
|
|
|
|
cl_version_to_vs_year = {
|
|
"15": "08",
|
|
"16": "10",
|
|
"17": "12",
|
|
"18": "13",
|
|
"19": "15"
|
|
}
|
|
|
|
vs_year_to_version = {
|
|
"08": "9.0",
|
|
"10": "10.0",
|
|
"12": "11.0",
|
|
"13": "12.0",
|
|
"15": "14.0"
|
|
}
|
|
|
|
@memoize
|
|
def get_vs_directory(vs_version):
|
|
keys = [
|
|
"Software\\Microsoft\\VisualStudio\\{}\\Setup\\VC".format(vs_version),
|
|
"Software\\Microsoft\\VCExpress\\{}\\Setup\\VS".format(vs_version)
|
|
]
|
|
|
|
for key in keys:
|
|
vs_directory = query_registry(key, "ProductDir")
|
|
|
|
if vs_directory is not None:
|
|
return vs_directory
|
|
|
|
@memoize
|
|
def get_wsdk_directory(vs_version):
|
|
if vs_version == "9.0":
|
|
wsdk_version = "v6.1"
|
|
elif vs_version == "10.0":
|
|
wsdk_version = "v7.1"
|
|
else:
|
|
return
|
|
|
|
return query_registry(
|
|
"Software\\Microsoft\\Microsoft SDKs\\Windows\\{}".format(wsdk_version), "InstallationFolder")
|
|
|
|
def get_vs_setup_cmd(vs_version, arch):
|
|
vs_directory = get_vs_directory(vs_version)
|
|
|
|
if vs_directory is not None:
|
|
vcvars_all_path = os.path.join(vs_directory, "vcvarsall.bat")
|
|
|
|
if check_existence(vcvars_all_path):
|
|
return 'call "{}"{}'.format(vcvars_all_path, " x86_amd64" if arch == "x64" else "")
|
|
|
|
vcvars_arch_path = os.path.join(
|
|
vs_directory, "bin", "amd64\\vcvars64.bat" if arch == "x64" else "vcvars32.bat")
|
|
|
|
if check_existence(vcvars_arch_path):
|
|
return 'call "{}"'.format(vcvars_arch_path)
|
|
|
|
wsdk_directory = get_wsdk_directory(vs_version)
|
|
|
|
if wsdk_directory is not None:
|
|
setenv_path = os.path.join(wsdk_directory, "bin", "setenv.cmd")
|
|
|
|
if check_existence(setenv_path):
|
|
return 'call "{}" /{}'.format(setenv_path, arch)
|
|
|
|
def setup_vs_and_rerun(vs_version, arch):
|
|
vs_setup_cmd = get_vs_setup_cmd(vs_version, arch)
|
|
|
|
if vs_setup_cmd is None:
|
|
return
|
|
|
|
print("Setting up VS {} ({})".format(vs_version, arch))
|
|
bat_name = os.path.join(temp_dir, "hererocks.bat")
|
|
argv_name = os.path.join(temp_dir, "argv")
|
|
setup_output_name = os.path.join(temp_dir, "setup_out")
|
|
|
|
script_arg = '"{}"'.format(sys.argv[0])
|
|
|
|
if sys.executable:
|
|
script_arg = '"{}" {}'.format(sys.executable, script_arg)
|
|
|
|
recursive_call = '{} --actual-argv-file "{}"'.format(script_arg, argv_name)
|
|
|
|
bat_lines = [
|
|
"@echo off",
|
|
"setlocal enabledelayedexpansion enableextensions"
|
|
]
|
|
|
|
if opts.verbose:
|
|
bat_lines.extend([
|
|
"echo Running {}".format(vs_setup_cmd),
|
|
vs_setup_cmd
|
|
])
|
|
else:
|
|
bat_lines.append('{} > "{}" 2>&1'.format(vs_setup_cmd, setup_output_name))
|
|
|
|
bat_lines.extend([
|
|
"set exitcode=%errorlevel%",
|
|
"if %exitcode% equ 0 (",
|
|
" {}".format(recursive_call),
|
|
") else ("
|
|
])
|
|
|
|
if not opts.verbose:
|
|
bat_lines.append(' type "{}"'.format(setup_output_name))
|
|
|
|
bat_lines.extend([
|
|
" echo Error: got exitcode %exitcode% from command {}".format(vs_setup_cmd),
|
|
" exit /b 1",
|
|
")"
|
|
])
|
|
|
|
bat_h = open(bat_name, "wb")
|
|
bat_h.write("\r\n".join(bat_lines).encode("UTF-8"))
|
|
bat_h.close()
|
|
|
|
argv_h = open(argv_name, "wb")
|
|
argv_h.write("\r\n".join(sys.argv).encode("UTF-8"))
|
|
argv_h.close()
|
|
|
|
exit_code = subprocess.call([bat_name])
|
|
shutil.rmtree(temp_dir)
|
|
sys.exit(exit_code)
|
|
|
|
def setup_vs(target):
|
|
# If using vsXX_YY or vs_XX target, set VS up by writing a .bat file calling corresponding vcvarsall.bat
|
|
# before recursively calling hererocks, passing arguments through a temporary file using
|
|
# --actual-argv-file because passing special characters like '^' as an argument to a batch file is not fun.
|
|
# If using vs target, do nothing if cl.exe is in PATH or setup latest possible VS, preferably with host arch.
|
|
if target == "vs" and program_exists("cl"):
|
|
print("Using cl.exe found in PATH.")
|
|
return
|
|
|
|
preferred_arch = "x64" if (platform.machine() if target == "vs" else target).endswith("64") else "x86"
|
|
|
|
possible_arches = [preferred_arch]
|
|
|
|
if target == "vs" and preferred_arch == "x64":
|
|
possible_arches.append("x86")
|
|
|
|
if target in ["vs", "vs_32", "vs_64"]:
|
|
possible_versions = ["14.0", "12.0", "11.0", "10.0", "9.0"]
|
|
else:
|
|
possible_versions = [vs_year_to_version[target[2:4]]]
|
|
|
|
for arch in possible_arches:
|
|
for vs_version in possible_versions:
|
|
setup_vs_and_rerun(vs_version, arch)
|
|
|
|
sys.exit("Error: couldn't set up MSVC toolchain")
|
|
|
|
class UseActualArgsFileAction(argparse.Action):
|
|
def __call__(self, parser, namespace, fname, option_string=None):
|
|
args_h = open(fname, "rb")
|
|
args_content = args_h.read().decode("UTF-8")
|
|
args_h.close()
|
|
main(args_content.split("\r\n")[1:])
|
|
|
|
def main(argv=None):
|
|
parser = argparse.ArgumentParser(
|
|
description=hererocks_version + ", a tool for installing Lua and/or LuaRocks locally.",
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
|
parser.add_argument(
|
|
"location", help="Path to directory in which Lua and/or LuaRocks will be installed. "
|
|
"Their binaries will be found in its 'bin' subdirectory. "
|
|
"Scripts from modules installed using LuaRocks will also turn up there. "
|
|
"If an incompatible version of Lua is already installed there it should be "
|
|
"removed before installing the new one. ")
|
|
parser.add_argument(
|
|
"-l", "--lua", help="Version of standard PUC-Rio Lua to install. "
|
|
"Version can be specified as a version number, e.g. 5.2 or 5.3.1. "
|
|
"Versions 5.1.0 - 5.3.2 are supported, "
|
|
"'^' or 'latest' can be used to install the latest stable version. "
|
|
"If the argument contains '@', sources will be downloaded "
|
|
"from a git repo using URI before '@' and using part after '@' as git reference "
|
|
"to checkout, 'master' by default. "
|
|
"Default git repo is https://github.com/lua/lua which contains tags for most "
|
|
"unstable versions, i.e. Lua 5.3.2-rc1 can be installed using '@5.3.2-rc1' as version. "
|
|
"The argument can also be a path to local directory.")
|
|
parser.add_argument(
|
|
"-j", "--luajit", help="Version of LuaJIT to install. "
|
|
"Version can be specified in the same way as for standard Lua. "
|
|
"Versions 2.0.0 - 2.1.0-beta2 are supported. "
|
|
"When installing from the LuaJIT main git repo its URI can be left out, "
|
|
"so that '@458a40b' installs from a commit and '@' installs from the master branch.")
|
|
parser.add_argument(
|
|
"-r", "--luarocks", help="Version of LuaRocks to install. "
|
|
"As with Lua, a version number (in range 2.0.8 - 2.3.0), '^', git URI with reference or "
|
|
"a local path can be used. '3' can be used as a version number and installs from "
|
|
"the 'luarocks-3' branch of the standard LuaRocks git repo. "
|
|
"Note that Lua 5.2 is not supported in LuaRocks 2.0.8 "
|
|
"and Lua 5.3 is supported only since LuaRocks 2.2.0.")
|
|
parser.add_argument("--show", default=False, action="store_true",
|
|
help="Instead of installing show programs already present in <location>")
|
|
parser.add_argument("-i", "--ignore-installed", default=False, action="store_true",
|
|
help="Install even if requested version is already present.")
|
|
parser.add_argument(
|
|
"--compat", default="default", choices=["default", "none", "all", "5.1", "5.2"],
|
|
help="Select compatibility flags for Lua.")
|
|
parser.add_argument(
|
|
"--patch", default=False, action="store_true",
|
|
help="Apply latest PUC-Rio Lua patches from https://www.lua.org/bugs.html when available.")
|
|
parser.add_argument(
|
|
"--cflags", default=None,
|
|
help="Pass additional options to C compiler when building Lua or LuaJIT.")
|
|
parser.add_argument(
|
|
"--target", help="Select how to build Lua. "
|
|
"Windows-specific targets (mingw, vs and vsXX_YY) also affect LuaJIT. "
|
|
"vs, vs_XX and vsXX_YY targets compile using cl.exe. "
|
|
"vsXX_YY targets (such as vs15_32) always set up Visual Studio 20XX (YYbit). "
|
|
"vs_32 and vs_64 pick latest version supporting selected architecture. "
|
|
"vs target uses cl.exe that's already in PATH or sets up "
|
|
"latest available Visual Studio, preferring tools for host architecture. "
|
|
"It's the default target on Windows unless cl.exe is not in PATH but gcc is, "
|
|
"in which case mingw target is used."
|
|
"macosx target uses cc and the remaining targets use gcc, passing compiler "
|
|
"and linker flags the same way Lua's Makefile does when running make <target>.",
|
|
choices=[
|
|
"linux", "macosx", "freebsd", "mingw", "posix", "generic", "mingw", "vs", "vs_32", "vs_64",
|
|
"vs08_32", "vs08_64", "vs10_32", "vs10_64", "vs12_32", "vs12_64",
|
|
"vs13_32", "vs13_64", "vs15_32", "vs15_64"
|
|
], metavar="{linux,macosx,freebsd,mingw,posix,generic,mingw,vs,vs_XX,vsXX_YY}",
|
|
default=get_default_lua_target())
|
|
parser.add_argument("--no-readline", help="Don't use readline library when building standard Lua.",
|
|
action="store_true", default=False)
|
|
parser.add_argument("--downloads",
|
|
help="Cache downloads and default git repos in 'DOWNLOADS' directory.",
|
|
default=get_default_cache())
|
|
parser.add_argument("--no-git-cache",
|
|
help="Do not cache default git repos.",
|
|
action="store_true", default=False)
|
|
parser.add_argument("--ignore-checksums",
|
|
help="Ignore checksum mismatches for downloads.",
|
|
action="store_true", default=False)
|
|
parser.add_argument("--builds",
|
|
# help="Cache Lua and LuaJIT builds in 'BUILDS' directory.",
|
|
help=argparse.SUPPRESS, default=None)
|
|
parser.add_argument("--verbose", default=False, action="store_true",
|
|
help="Show executed commands and their output.")
|
|
parser.add_argument("-v", "--version", help="Show program's version number and exit.",
|
|
action="version", version=hererocks_version)
|
|
parser.add_argument("-h", "--help", help="Show this help message and exit.", action="help")
|
|
|
|
if os.name == "nt" and argv is None:
|
|
parser.add_argument("--actual-argv-file", action=UseActualArgsFileAction,
|
|
# help="Load argv from a file, used when setting up cl toolchain."
|
|
help=argparse.SUPPRESS)
|
|
|
|
global opts
|
|
opts = parser.parse_args(argv)
|
|
if not opts.lua and not opts.luajit and not opts.luarocks and not opts.show:
|
|
parser.error("nothing to do")
|
|
|
|
if opts.lua and opts.luajit:
|
|
parser.error("can't install both PUC-Rio Lua and LuaJIT")
|
|
|
|
if (opts.lua or opts.luajit or opts.luarocks) and opts.show:
|
|
parser.error("can't both install and show")
|
|
|
|
if opts.show:
|
|
if os.path.exists(opts.location):
|
|
all_identifiers = get_installed_identifiers()
|
|
|
|
if all_identifiers:
|
|
print("Programs installed in {}:".format(opts.location))
|
|
|
|
for program in [RioLua, LuaJIT, LuaRocks]:
|
|
if program.name in all_identifiers:
|
|
show_identifiers(all_identifiers[program.name])
|
|
else:
|
|
print("No programs installed in {}.".format(opts.location))
|
|
else:
|
|
print("Location does not exist.")
|
|
|
|
sys.exit(0)
|
|
|
|
global temp_dir
|
|
temp_dir = tempfile.mkdtemp()
|
|
|
|
if (opts.lua or opts.luajit) and os.name == "nt" and argv is None and using_cl():
|
|
setup_vs()
|
|
|
|
start_dir = os.getcwd()
|
|
opts.location = os.path.abspath(opts.location)
|
|
|
|
if opts.downloads is not None:
|
|
opts.downloads = os.path.abspath(opts.downloads)
|
|
|
|
if opts.builds is not None:
|
|
opts.builds = os.path.abspath(opts.builds)
|
|
|
|
identifiers = get_installed_identifiers()
|
|
|
|
if not os.path.exists(opts.location):
|
|
os.makedirs(opts.location)
|
|
|
|
if opts.lua:
|
|
if "LuaJIT" in identifiers:
|
|
del identifiers["LuaJIT"]
|
|
|
|
if RioLua(opts.lua).update_identifiers(identifiers):
|
|
save_installed_identifiers(identifiers)
|
|
|
|
os.chdir(start_dir)
|
|
|
|
if opts.luajit:
|
|
if "lua" in identifiers:
|
|
del identifiers["lua"]
|
|
|
|
if LuaJIT(opts.luajit).update_identifiers(identifiers):
|
|
save_installed_identifiers(identifiers)
|
|
|
|
os.chdir(start_dir)
|
|
|
|
if opts.luarocks:
|
|
if LuaRocks(opts.luarocks).update_identifiers(identifiers):
|
|
save_installed_identifiers(identifiers)
|
|
|
|
os.chdir(start_dir)
|
|
|
|
shutil.rmtree(temp_dir)
|
|
print("Done.")
|
|
sys.exit(0)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|