{"id":16658216,"url":"https://github.com/connorrigby/zig-lua-wrapper","last_synced_at":"2025-03-21T16:32:24.841Z","repository":{"id":152452243,"uuid":"455381243","full_name":"ConnorRigby/zig-lua-wrapper","owner":"ConnorRigby","description":"example zig project that compiles a lua file into a static executable","archived":false,"fork":false,"pushed_at":"2022-02-04T02:59:56.000Z","size":456,"stargazers_count":31,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-19T14:07:07.572Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ConnorRigby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-04T00:58:17.000Z","updated_at":"2024-10-15T06:04:24.000Z","dependencies_parsed_at":"2023-06-07T22:00:11.622Z","dependency_job_id":null,"html_url":"https://github.com/ConnorRigby/zig-lua-wrapper","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ConnorRigby%2Fzig-lua-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ConnorRigby%2Fzig-lua-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ConnorRigby%2Fzig-lua-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ConnorRigby%2Fzig-lua-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ConnorRigby","download_url":"https://codeload.github.com/ConnorRigby/zig-lua-wrapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221817078,"owners_count":16885456,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T10:03:44.270Z","updated_at":"2024-10-28T10:29:46.360Z","avatar_url":"https://github.com/ConnorRigby.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Using Zig to build Native Lua Scripts\n\nI've been playing with [Zig](https://ziglang.org/) a lot lately.\nIt's one of my favorite pieces of tech I've found in\nthe last few years. One of my favorite features is how easy it\nis to compile C libraries with it. Of course\nwhen I think \"C libraries\", the first that comes to mind is `Lua`.\n\nLua is a really cool \"embeddable\" progrramming language. It's\nmade to be put \"inside\" larger projects primarily. Some examples\nof things that use Lua include\n\n* [LÖVE](https://love2d.org/)\n* [Nginx](https://www.nginx.com/resources/wiki/modules/lua/)\n* [World of Warcraft](https://wowwiki-archive.fandom.com/wiki/Lua)\n\nSo of course, to run in so many places, Lua itself has been built\nfrom the ground up to be \"embedded\". It is distributed as an archive\nC source files and documentation. This is great news for us with Zig,\nsince [Zig is a c compiler](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html)!\n\nGetting Lua to compile inside a Zig project is *really* easy! easier than\nC/C++ in my opinion. I'll glaze the details, plucking the important parts\n\nFirst, in `build.zig`, we need to link libc, and add it's source files.\nThis looks like:\n\n```zig\nconst exe = b.addExecutable(\"wrapper\", \"wrapper.zig\");\nexe.setTarget(target);\nexe.setBuildMode(mode);\nexe.linkLibC();\nexe.addIncludeDir(\"lua-5.3.4/src\");\n\nconst lua_c_files = [_][]const u8{\n    \"lapi.c\",\n    \"lauxlib.c\",\n    \"lbaselib.c\",\n    \"lbitlib.c\",\n    \"lcode.c\",\n    \"lcorolib.c\",\n    \"lctype.c\",\n    \"ldblib.c\",\n    \"ldebug.c\",\n    \"ldo.c\",\n    \"ldump.c\",\n    \"lfunc.c\",\n    \"lgc.c\",\n    \"linit.c\",\n    \"liolib.c\",\n    \"llex.c\",\n    \"lmathlib.c\",\n    \"lmem.c\",\n    \"loadlib.c\",\n    \"lobject.c\",\n    \"lopcodes.c\",\n    \"loslib.c\",\n    \"lparser.c\",\n    \"lstate.c\",\n    \"lstring.c\",\n    \"lstrlib.c\",\n    \"ltable.c\",\n    \"ltablib.c\",\n    \"ltm.c\",\n    \"lundump.c\",\n    \"lutf8lib.c\",\n    \"lvm.c\",\n    \"lzio.c\",\n};\n\nif(target.os_tag == std.Target.Os.Tag.windows) {\n    const c_flags = [_][]const u8{\n        \"-std=c99\",\n        \"-O2\",\n        \"-DLUA_USE_WINDOWS\"\n    };\n    inline for (lua_c_files) |c_file| {\n        exe.addCSourceFile(\"lua-5.3.4/src/\" ++ c_file, \u0026c_flags);\n    }\n} else {\n    const c_flags = [_][]const u8{\n        \"-std=c99\",\n        \"-O2\",\n        \"-DLUA_USE_POSIX\",\n    };\n    inline for (lua_c_files) |c_file| {\n        exe.addCSourceFile(\"lua-5.3.4/src/\" ++ c_file, \u0026c_flags);\n    }\n}\nexe.install();\n```\n\nThat's really it! It even adds support for Windows. All that's\nleft is to just use it. This works like any other C library with Zig.\nFor this project, I decided I would make a single executable out of a\nLua script. Here's the source of the Lua script to give additional context:\n\n```lua\nprint(\"press Y\")\n\nlocal input = \"\\0\"\n\nwhile(input ~= 'y' and input ~= 'Y') do\n  input = io.read(1)\nend\n\nprint(\"🥧\")\n```\n\nSo all the Zig code needs to do is somehow \"embed\" that script, and execute it\ninside of the Lua VM. Lua offers a `luac` executable to compile a script file\ninto a chunk of Lua bytecode that can then be executed. This isn't strictly\nnecessary, but i compiled the `luac` executable with Zig:\n\n```bash\nmake -C lua-5.3.4/ generic CC=\"zig cc\"\n```\n\nNext I compiled my script:\n\n```bash\n./lua-5.3.4/src/luac main.lua\n```\n\nWhich outputs a `luac.out` file. This itself obviously isn't an executable tho.\nLuckily, Zig has a built-in for us to use:\n\n```zig\npub const LUA_BYTECODE = @embedFile(\"luac.out\");\n```\n\nFinally, all that's left is to execute the bytecode with `lua_pcallk`:\n\n```zig\nconst lua = @cImport({\n    @cInclude(\"lua.h\");\n    @cInclude(\"lualib.h\");\n    @cInclude(\"lauxlib.h\");\n});\n\npub fn main() anyerror!void {\n    var s = lua.luaL_newstate();\n    lua.luaL_openlibs(s);\n    const load_status = lua.luaL_loadbufferx(s, LUA_BYTECODE, LUA_BYTECODE.len, \"main.lua\", \"bt\");\n    if (load_status != 0) {\n        std.log.info(\"Couldn't load lua bytecode: {s}\", .{lua.lua_tolstring(s, -1, null)});\n        return;\n    }\n    const call_status = lua.lua_pcallk(s, 0, lua.LUA_MULTRET, 0, 0, null);\n    if (call_status != 0) {\n        std.log.info(\"{s}\", .{lua.lua_tolstring(s, -1, null)});\n        return;\n    }\n}\n```\n\nI tested this out on my arm Mac, an x86 Mac, my Windows PC, WSL, and on several\nLinux installations and it works great!\n\nCompiling is done with:\n\n```bash\nzig build -Drelease-small -Dtarget=\u003ctarget\u003e\n```\n\nWhere target can be one of:\n\n* `x86_64-windows`\n* `x86_64-macos`\n* `aarch64-macos`\n* `aarch64-linux-musl`\n* `x86_64-linux-musl`\n\nI'm sure there are other targets that work, but those are the ones\nI tested.\n\nAll the source for this project is [On Github](https://github.com/ConnorRigby/zig-lua-wrapper)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnorrigby%2Fzig-lua-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconnorrigby%2Fzig-lua-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnorrigby%2Fzig-lua-wrapper/lists"}