Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add win32/win64 option to precompile bytecode #44

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A packaging tool for [löve](https://love2d.org) games
* Proper handling of shared libraries (both Lua modules and FFI)!
* Packaging of those binaries in archives, including extra files
* Versioned builds
* Precompile lua scripts to bytecode (win32 and win64 and only on Windows)
* Control and customization along the way:
- Configure which targets to build
- Which files to include in the .love with a list of include/exclude patterns
Expand Down
9 changes: 9 additions & 0 deletions how_to_x.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,12 @@ And finally build without prebuild, but with postbuild
```
makelove -d prebuild -v 1.2.3 appimage (--resume)
```

## Compile lua scripts to bytecode
Use the option `compile_lua = true` in `makelove.toml` in the `win32` or `win64` sections.
The created .exe will now contain bytecode instead of scripts.

To check if a script is compiled as bytecode, you can add this code to your game:
```lua
is_compiled = (love.filesystem.read('main.lua', 1) == '\x1b' and true or false)
```
31 changes: 31 additions & 0 deletions makelove/bytecode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
import subprocess
import zipfile
import base64

def compile_file(love_binary, data, filename):
# Compile a single .lua file to bytecode using love.
# To support this, we have a folder 'love-luac' that will be recognized as 'game' by love
compiler = os.path.join(os.path.dirname(__file__), 'love-luac')
compile_args = [love_binary, compiler]
proc = subprocess.Popen(compile_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# (Binary) data is base64 encoded to work around the fact that lua opens stdin/stdout in text mode on windows,
# which would break the bytecode
out = proc.communicate(input=base64.b64encode(data))
if proc.returncode == 0:
return base64.b64decode(out[0])
print('Failed to compile {}: {}'.format(filename, out[0]))
return data


def create_compiled_lovezip(love_binary, love_file_path_in, love_file_path_out):
# Create a new zip with all .lua files converted to bytecode
with zipfile.ZipFile(love_file_path_in, 'r') as zip_in:
with zipfile.ZipFile(love_file_path_out, 'w') as zip_out:
zip_out.comment = zip_in.comment # preserve the comment
for item in zip_in.infolist():
data = zip_in.read(item.filename)
if item.filename.endswith('.lua'):
data = compile_file(love_binary, data, item.filename)
zip_out.writestr(item, data)

2 changes: 2 additions & 0 deletions makelove/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,15 @@
"love_binaries": val.Path(),
"shared_libraries": val.List(val.Path()),
"artifacts": val.ValueOrList(val.Choice("directory", "archive")),
"compile_lua": val.Bool(),
}
),
"win64": val.Section(
{
"love_binaries": val.Path(),
"shared_libraries": val.List(val.Path()),
"artifacts": val.ValueOrList(val.Choice("directory", "archive")),
"compile_lua": val.Bool(),
}
),
"linux": val.Section(
Expand Down
5 changes: 5 additions & 0 deletions makelove/love-luac/conf.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- love configuration handler to suppress the window popping up.

function love.conf(t)
t.window = nil
end
26 changes: 26 additions & 0 deletions makelove/love-luac/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--[[
Handler used to convert lua code to bytecode.
This is setup as a 'game', so that the love binary that we are appending to can run this.
]]

function love.load()
-- Read input from stdin
-- This is base64 encoded to ensure no translation occurs on windows
local source = love.data.decode("string", "base64", io.read())
-- Load the data
local lua_data = assert(load(source))
-- Convert to bytecode, strip debug symbols
local stripped = assert(string.dump(lua_data, true))
-- Encode the bytecode to base64 again
local encoded = love.data.encode('string', 'base64', stripped, 0)
-- Send it back
io.write(encoded)
-- Great success
os.exit(0, true)
end

function love.errorhandler(msg)
msg = tostring(msg)
print(msg)
os.exit(1, true)
end
16 changes: 16 additions & 0 deletions makelove/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from .util import get_default_love_binary_dir, get_download_url, tmpfile, eprint
from .config import should_build_artifact
from .bytecode import create_compiled_lovezip


def common_prefix(l):
Expand Down Expand Up @@ -217,12 +218,27 @@ def build_windows(config, version, target, target_directory, love_file_path):
)
print("If you are using a POSIX-compliant system, try installing WINE.")

temp_love_archive = None
if config[target].get("compile_lua", False):
if not sys.platform.startswith("win32"):
# TODO: For x64 targets we could try to grab the win64 binary?
sys.exit("Cannot compile lua on this platform, please run me on windows")
original_love_file = love_file_path
temp_love_archive = dest("compiled.love")
create_compiled_lovezip(src("love_orig.exe"), original_love_file, temp_love_archive)
# Fuse with the generated archive instead the original one
love_file_path = temp_love_archive

with open(target_exe_path, "wb") as fused:
with open(src("love.exe"), "rb") as loveExe:
with open(love_file_path, "rb") as loveZip:
fused.write(loveExe.read())
fused.write(loveZip.read())

# Did we create a temp file?
if temp_love_archive:
os.remove(temp_love_archive)

copy("license.txt")
for f in os.listdir(love_binaries):
if f.endswith(".dll"):
Expand Down
5 changes: 5 additions & 0 deletions makelove_full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ shared_libraries = [
# after the .zip has been built.
artifacts = "archive"

# Whether or not to compile lua code to bytecode.
# If this option is set to true, the build needs to run on windows, because love.exe for windows
# is used to compile the lua files.
compile_lua = false

# The values above for the target win32 can also be set for the win64 target

[macos]
Expand Down