Windows: replace cl target with vsXX_YY targets

When using vsXX_YY target, set up paths for Visual Studio 20XX
(YY bits) by writing a temporary batch file that calls appropriate
vcvarsall.bat and calls hererocks again, passing arguments through
another temporary file because escaping special batch characters
is difficult.

Related changes:
* Default target on Windows is now vs15_32, unless gcc is in PATH
  and cl.exe isn't.
* Fixed mingw LuaJIT build attempting to install lua51.lib which isn't built.
* Fixed list flatten in run(): consider unicode strings.
This commit is contained in:
mpeterv 2016-03-30 13:53:26 +03:00
parent 6fa4496a8c
commit eb7f0d55fc
2 changed files with 108 additions and 31 deletions

View File

@ -4,22 +4,19 @@ shallow_clone: true
environment: environment:
matrix: matrix:
- LUA: "lua 5.1" - Lua: lua 5.3
- LUA: "lua 5.2" - Lua: luajit 2.0
- LUA: "lua 5.3"
- LUA: "luajit 2.0"
configuration: configuration:
- 2015 - vs08_32
- vs15_64
- mingw
platform: x86 build_script:
- PATH %CD%\here\bin;C:\mingw\bin;%PATH%
before_build: '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"' - python hererocks.py here --%Lua% --luarocks ^^ --verbose --target=%Configuration%
build_script: python hererocks.py here --%LUA% --luarocks ^^ --verbose
test_script: test_script:
- PATH %CD%\here\bin;%PATH%
- lua -v - lua -v
- luarocks --version - luarocks --version
- luarocks install lua-term 0.3 - luarocks install lua-term 0.3

View File

@ -53,11 +53,14 @@ def program_exists(prog):
platform_to_lua_target = { platform_to_lua_target = {
"linux": "linux", "linux": "linux",
"win": "cl" if os.name == "nt" and program_exists("cl") else "mingw", "win": "mingw" if os.name == "nt" and program_exists("gcc") and not program_exists("cl") else "vs15_32",
"darwin": "macosx", "darwin": "macosx",
"freebsd": "freebsd" "freebsd": "freebsd"
} }
def using_cl():
return opts.target.startswith("vs")
def get_default_lua_target(): def get_default_lua_target():
for platform, lua_target in platform_to_lua_target.items(): for platform, lua_target in platform_to_lua_target.items():
if sys.platform.startswith(platform): if sys.platform.startswith(platform):
@ -85,7 +88,7 @@ def run(*args, **kwargs):
""" """
capture = kwargs.get("get_output", False) capture = kwargs.get("get_output", False)
args = [arg for arglist in args for arg in ([arglist] if isinstance(arglist, str) else arglist)] args = [arg for arglist in args for arg in (arglist if isinstance(arglist, list) else [arglist])]
if opts.verbose: if opts.verbose:
print("Running {}".format(" ".join(args))) print("Running {}".format(" ".join(args)))
@ -181,7 +184,7 @@ def exe(name):
return name return name
def objext(): def objext():
return ".obj" if opts.target == "cl" else ".o" return ".obj" if using_cl() else ".o"
def sha256_of_file(filename): def sha256_of_file(filename):
fileobj = open(filename, "rb") fileobj = open(filename, "rb")
@ -517,12 +520,12 @@ class RioLua(Lua):
self.lua_file = exe("lua") self.lua_file = exe("lua")
self.luac_file = exe("luac") self.luac_file = exe("luac")
if opts.target == "cl": if using_cl():
self.arch_file = "lua5" + self.major_version[2] + ".lib" self.arch_file = "lua5" + self.major_version[2] + ".lib"
else: else:
self.arch_file = "liblua5" + self.major_version[2] + ".a" self.arch_file = "liblua5" + self.major_version[2] + ".a"
if opts.target == "mingw" or opts.target == "cl": if opts.target == "mingw" or using_cl():
self.dll_file = "lua5" + self.major_version[2] + ".dll" self.dll_file = "lua5" + self.major_version[2] + ".dll"
else: else:
self.dll_file = None self.dll_file = None
@ -612,7 +615,7 @@ class RioLua(Lua):
if opts.cflags is not None: if opts.cflags is not None:
cflags.extend(opts.cflags.split()) cflags.extend(opts.cflags.split())
if opts.target == "cl": if using_cl():
cc = ["cl", "/nologo", "/MD", "/O2", "/W3", "/c", "/D_CRT_SECURE_NO_DEPRECATE"] cc = ["cl", "/nologo", "/MD", "/O2", "/W3", "/c", "/D_CRT_SECURE_NO_DEPRECATE"]
else: else:
cflags = ["-O2", "-Wall", "-Wextra"] + cflags cflags = ["-O2", "-Wall", "-Wextra"] + cflags
@ -622,7 +625,7 @@ class RioLua(Lua):
if opts.target == "mingw": if opts.target == "mingw":
cflags.insert(3, "-DLUA_BUILD_AS_DLL") cflags.insert(3, "-DLUA_BUILD_AS_DLL")
elif opts.target == "cl": elif using_cl():
cflags.insert(0, "-DLUA_BUILD_AS_DLL") cflags.insert(0, "-DLUA_BUILD_AS_DLL")
os.chdir("src") os.chdir("src")
@ -636,7 +639,7 @@ class RioLua(Lua):
obj = base + objext() obj = base + objext()
objs.append(obj) objs.append(obj)
cmd_suffix = src if opts.target == "cl" else ["-c", "-o", obj, src] cmd_suffix = src if using_cl() else ["-c", "-o", obj, src]
run(cc, static_cflags if obj in luac_objs else cflags, cmd_suffix) 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())] lib_objs = [obj_ for obj_ in objs if obj_ not in luac_objs and (obj_ != "lua" + objext())]
@ -645,7 +648,7 @@ class RioLua(Lua):
if "print" + objext() in objs: if "print" + objext() in objs:
luac_objs.append("print" + objext()) luac_objs.append("print" + objext())
if opts.target == "cl": if using_cl():
run("link", "/nologo", "/out:luac.exe", luac_objs, lib_objs) run("link", "/nologo", "/out:luac.exe", luac_objs, lib_objs)
if os.path.exists("luac.exe.manifest"): if os.path.exists("luac.exe.manifest"):
@ -659,7 +662,7 @@ class RioLua(Lua):
run(cc, "-shared", "-o", self.dll_file, lib_objs) run(cc, "-shared", "-o", self.dll_file, lib_objs)
run("strip", "--strip-unneeded", self.dll_file) run("strip", "--strip-unneeded", self.dll_file)
run(cc, "-o", self.lua_file, "-s", "lua.o", self.dll_file) run(cc, "-o", self.lua_file, "-s", "lua.o", self.dll_file)
elif opts.target == "cl": elif using_cl():
run("link", "/nologo", "/DLL", "/out:" + self.dll_file, lib_objs) run("link", "/nologo", "/DLL", "/out:" + self.dll_file, lib_objs)
if os.path.exists(self.dll_file + ".manifest"): if os.path.exists(self.dll_file + ".manifest"):
@ -760,7 +763,7 @@ class LuaJIT(Lua):
if opts.cflags is not None: if opts.cflags is not None:
cflags.extend(opts.cflags.split()) cflags.extend(opts.cflags.split())
if opts.target == "cl": if using_cl():
os.chdir("src") os.chdir("src")
if cflags: if cflags:
@ -801,7 +804,9 @@ class LuaJIT(Lua):
"lua.h", "luaconf.h", "lualib.h", "lauxlib.h", "lua.hpp", "luajit.h") "lua.h", "luaconf.h", "lualib.h", "lauxlib.h", "lua.hpp", "luajit.h")
copy_files(os.path.join(opts.location, "lib")) copy_files(os.path.join(opts.location, "lib"))
shutil.copy(arch_file, os.path.join(opts.location, "lib", target_arch_file))
if opts.target != "mingw":
shutil.copy(arch_file, os.path.join(opts.location, "lib", target_arch_file))
if os.name != "nt": if os.name != "nt":
shutil.copy(so_file, os.path.join(opts.location, "lib", target_so_file)) shutil.copy(so_file, os.path.join(opts.location, "lib", target_so_file))
@ -954,7 +959,36 @@ def save_installed_identifiers(identifiers):
manifest_h.close() manifest_h.close()
def main(): vs_year_to_version = {
"08": "9.0",
"10": "10.0",
"12": "11.0",
"13": "12.0",
"15": "14.0"
}
def get_vs_setup_cmd(target):
vs_version = vs_year_to_version[target[2:4]]
vcvarsall_path = "C:\\Program Files (x86)\\Microsoft Visual Studio {}\\VC\\vcvarsall.bat".format(vs_version)
if not os.path.exists(vcvarsall_path):
sys.exit("Error: couldn't set up Visual Studio: {} does not exist".format(vcvarsall_path))
cmd = 'call "{}"'.format(vcvarsall_path)
if target.endswith("64"):
cmd = cmd + " amd64"
return cmd
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( parser = argparse.ArgumentParser(
description=hererocks_version + ", a tool for installing Lua and/or LuaRocks locally.", description=hererocks_version + ", a tool for installing Lua and/or LuaRocks locally.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
@ -996,9 +1030,17 @@ def main():
parser.add_argument( parser.add_argument(
"--cflags", default=None, "--cflags", default=None,
help="Pass additional options to C compiler when building Lua or LuaJIT.") help="Pass additional options to C compiler when building Lua or LuaJIT.")
parser.add_argument("--target", help="Emulate 'make TARGET' when building standard Lua.", parser.add_argument(
choices=["linux", "macosx", "freebsd", "cl", "mingw", "posix", "generic"], "--target", help="Select how to build Lua. "
default=get_default_lua_target()) "Windows-specific targets (mingw and vsXX_YY) also affect LuaJIT. "
"vsXX_YY targets set up Visual Studio 20XX (YYbit) automatically "
"and use cl.exe as compiler. "
"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",
"vs08_32", "vs10_32", "vs12_32", "vs12_64", "vs13_32", "vs13_64", "vs15_32", "vs15_64"
], default=get_default_lua_target())
parser.add_argument("--no-readline", help="Don't use readline library when building standard Lua.", parser.add_argument("--no-readline", help="Don't use readline library when building standard Lua.",
action="store_true", default=False) action="store_true", default=False)
parser.add_argument("--downloads", parser.add_argument("--downloads",
@ -1019,22 +1061,59 @@ def main():
action="version", version=hererocks_version) action="version", version=hererocks_version)
parser.add_argument("-h", "--help", help="Show this help message and exit.", action="help") parser.add_argument("-h", "--help", help="Show this help message and exit.", action="help")
global opts, temp_dir if os.name == "nt" and argv is None:
opts = parser.parse_args() 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: if not opts.lua and not opts.luajit and not opts.luarocks:
parser.error("nothing to install") parser.error("nothing to install")
if opts.lua and opts.luajit: if opts.lua and opts.luajit:
parser.error("can't install both PUC-Rio Lua and LuaJIT") parser.error("can't install both PUC-Rio Lua and LuaJIT")
global temp_dir
temp_dir = tempfile.mkdtemp()
# If using vsXX_YY 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 (opts.lua or opts.luajit) and os.name == "nt" and argv is None and using_cl():
bat_name = os.path.join(temp_dir, "hererocks.bat")
argv_name = os.path.join(temp_dir, "argv")
bat_h = open(bat_name, "wb")
bat_h.write(b"@echo off\r\n")
vs_setup_cmd = get_vs_setup_cmd(opts.target)
bat_h.write(vs_setup_cmd.encode("UTF-8") + b"\r\n")
script_arg = '"' + sys.argv[0] + '"'
if sys.executable:
script_arg = '"' + sys.executable + '" ' + script_arg
recursive_call = script_arg + ' --actual-argv-file "' + argv_name + '"\r\n'
bat_h.write(recursive_call.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)
start_dir = os.getcwd()
opts.location = os.path.abspath(opts.location) opts.location = os.path.abspath(opts.location)
opts.downloads = os.path.abspath(opts.downloads) opts.downloads = os.path.abspath(opts.downloads)
if opts.builds is not None: if opts.builds is not None:
opts.builds = os.path.abspath(opts.builds) opts.builds = os.path.abspath(opts.builds)
start_dir = os.getcwd()
temp_dir = tempfile.mkdtemp()
identifiers = get_installed_identifiers() identifiers = get_installed_identifiers()
identifiers_changed = False identifiers_changed = False
@ -1062,6 +1141,7 @@ def main():
shutil.rmtree(temp_dir) shutil.rmtree(temp_dir)
print("Done.") print("Done.")
sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":
main() main()