├── CHANGELOG.md
├── LICENSE
├── README.md
├── rtc.png
└── src
├── gui.lua
├── img
├── exec.ico
├── open.ico
└── rtc.ico
└── rtc.lua
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # rtc Changelog
2 |
3 | ## rtc v1.6.0 (Nov 26 2023)
4 |
5 | ### General
6 | - Updated rtc with LuaRT toolchain to v1.6.0
7 |
8 | ### rtc
9 | - Fixed: `rtc` compiled executables with embedded modules can have now multiple running instances
10 |
11 |
12 | ## rtc v1.5.2 (Sept 17 2023)
13 |
14 | ### General
15 | - Updated rtc with LuaRT toolchain to v1.5.2
16 | - Updated Lua VM to 5.4.6
17 | - Release package now compiled using Visual C++
18 |
19 | ### rtc
20 | - New: -l option to embed binary modules in the executable. Modules should be put in the `modules\` subfolder where `rtc.exe` is.
21 | - Updated: the main Lua file is now precompiled to bytecode
22 |
23 | ### wrtc GUI frontend
24 | - Updated : `wrtc` has a new field to embed modules (just type the name of the modules without the .DLL extension, separated by spaces)
25 |
26 |
27 | ## rtc v1.3.1 (Feb 04 2023)
28 |
29 | ### General
30 | - Updated rtc with LuaRT toolchain to v1.3.1
31 | - Updated to LuaRT UTF8 functions
32 | - Updated `rtc.lua` for new LuaRT `compression` module
33 |
34 | ### wrtc GUI frontend
35 | - Fixed the compilation of only console executable (Fixes #3)
36 | - Removed the message box after compilation succeeded
37 | - Fixed widget disposition with new Segoe UI default font
38 | - Display a warning when generated executable will overwrite a preexisting file
39 | - Explorer window don't show up anymore upon compilation
40 | - Fixed empty icon when cancelling icon selection
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Samir Tine
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ![rtc][title]
4 |
5 | [](https://www.luart.org/)
6 | 
7 | [](#)
8 |
9 | Build standalone Windows executables from your Lua scripts.
10 |
11 | [Features](#features) |
12 | [Installation](#installation) |
13 | [Usage](#usage) |
14 | [Documentation](https://www.luart.org/doc/toolchain/rtc.html) |
15 | [Links](#links) |
16 | [License](#license)
17 |
18 |
19 | ## Features
20 |
21 | - Standalone tool : no Makefile, no C compiler needed
22 | - Compile from command line or using a GUI frontend
23 | - Build Windows native executable (.exe) from your Lua 5.4.6 scripts
24 | - Windows desktop or console applications
25 | - Static executables (without `lua54.dll` dependency)
26 | - Dynamic executables (with `lua54.dll` dependency)
27 | - Embed any files with your executable, even Lua binary modules with seamless loading using `require()`
28 | - Access embedded files seamlessly from your Lua scripts
29 | - Deploy your applications easily without the need to install Lua
30 |
31 | ## Installation
32 |
33 | #### Using latest release package
34 |
35 | The easiest way to install is to download the latest binary release from the Github repository.
36 | Just add the directory where you have unpacked the release package to your Windows system PATH variable.
37 |
38 | If you use the release package, you don't need any other dependencies to compile Lua scripts.
39 |
40 | #### Build rtc
41 |
42 | To build **rtc**, you will need to install the LuaRT programming framework sources before to proceed.
43 | Go to the ```src\``` directory in the LuaRT folder and type "**make rtc**" in a command prompt.
44 | It should produce a "**rtc.exe**" and "**wrtc.exe**"executable.
45 |
46 | ## Usage
47 |
48 | #### rtc command line options
49 |
50 | ```
51 | usage: rtc.exe [-s][-c][-w][-i icon][-o output] [-lmodname] [directory] main.lua
52 | -s create static executable (without LUA54.DLL dependency)
53 | -c create executable for console (default)
54 | -w create executable for Windows desktop
55 | -i icon set executable icon (expects an .ico file)
56 | -o output set executable name to 'output'
57 | -lmodname link the LuaRT binary module 'modname.dll'
58 | directory the content of the directory to be embedded in the executable
59 | main.lua the Lua script to be executed
60 | ```
61 |
62 | The specified optional directory will then be embedded within the executable with all its content archived in the ZIP format, bundled with the generated executable.
63 | As an alternative, you can use **wrtc.exe**, the GUI frontend which is more usert friendly.
64 |
65 | > **Warning**
66 | > Compiled dynamic executable depends on the provided `lua54.dll` library. Do not use any other library or your application will throw an error or may crash.
67 |
68 | #### Accessing embedded files from your LuaRT application
69 |
70 | To access embedded files from your LuaRT application, just use the global "**embed**" module. It will return a Zip instance, already open for read access, that contains the directory content provided on the command line during compilation with rtc :
71 |
72 | ```lua
73 | -- extract all the embedded content
74 | embed:extractall("c:/installdir/")
75 | ```
76 |
77 | If no embedded content exists, "**embed**" will be set to a **nil** value. You should check that the current script is compiled embed table before using it.
78 | See the [LuaRT Zip object documentation](https://www.luart.org/doc/compression/Zip.html) for more information.
79 |
80 | #### Requiring Lua modules from embedded files
81 |
82 | To require a LuaRT script file in the embedded files, use **require** with the name of the module. You can require Lua modules, and binary modules (DLL) without the need to extract first (please note that in-memory binary modules loading works only with dynamic executables and may not work for some modules).
83 |
84 | ```lua
85 | -- require for the english.lua module, that must have been previously embedded with rtc
86 | local english = require "english"
87 | print(english.hello)
88 | ```
89 |
90 | ## Links
91 |
92 | - [rtc Documentation](https://www.luart.org/doc/toolchain/rtc.html)
93 | - [rtc Tutorial](https://www.luart.org/doc/tutorial/rtc.html)
94 | - [LuaRT Homepage](https://www.luart.org/)
95 | - [LuaRT Community](https://community.luart.org/)
96 |
97 | ## License
98 |
99 | LuaRT and rtc are copyright (c) 2023 Samir Tine.
100 | rtc is open source, released under the MIT License.
101 | See full copyright notice in the LICENSE file.
102 |
103 | [title]: rtc.png
104 |
--------------------------------------------------------------------------------
/rtc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyeyo/rtc/d2b908d8c10930f1714f4edb98b818fe734a3089/rtc.png
--------------------------------------------------------------------------------
/src/gui.lua:
--------------------------------------------------------------------------------
1 | local File = embed == nil and sys.File or embed.File
2 | local icon
3 |
4 | local win = ui.Window("rtc - Lua script to executable compiler", "fixed", 400, 300, "fixed")
5 | win:loadicon(File("img/rtc.ico"))
6 |
7 | local function setLabelBtn(widget, text, x, y, icon, tooltip, nobtn, delta)
8 | widget.label = ui.Label(win, text, x, y)
9 | widget.x = nobtn and (widget.label.x + widget.label.width + delta) or 106
10 | widget.y = widget.label.y - 3
11 | if not nobtn then
12 | widget.chooseBtn = ui.Button(win, "", widget.x + widget.width + 1, widget.y-2, 24, 24)
13 | widget.chooseBtn:loadicon(icon)
14 | widget.chooseBtn.hastext = false
15 | end
16 | widget.tooltip = tooltip
17 | return widget
18 | end
19 |
20 | local script = setLabelBtn(ui.Entry(win, "", 10, 10, win.width-142), "Main Lua script : ", 10, 26, File("img/open.ico"), "The Lua script to be run when the generated executable starts")
21 | local embed = setLabelBtn(ui.Entry(win, "", 10, 10, win.width-142), "Embed directory: ", 10, 52, File("img/open.ico"), "The content of the directory will be embedded in the generated executable")
22 | local modules = setLabelBtn(ui.Entry(win, "", 10, 10, win.width-142), "Embed modules: ", 10, 78, nil, "modules to be embedded, by name, separated with a space", true, 0)
23 | local output = setLabelBtn(ui.Entry(win, "", 10, 10, win.width-142), "Executable : ", 10, 104, nil, "The generated executable name", true, 26)
24 |
25 | local group = ui.Groupbox(win, "Windows subsystem", 10, 142, 130, 90)
26 | local radioconsole= ui.Radiobutton(group, "Console", 10, 26)
27 | radioconsole.checked = true
28 | local radiodesktop = ui.Radiobutton(group, "Windows desktop", 10, 50)
29 |
30 | local group = ui.Groupbox(win, "Runtime library", 150, 142, 130, 90)
31 | local radiodynamic = ui.Radiobutton(group, "Dynamic", 26, 26)
32 | radiodynamic.checked = true
33 | local radiostatic = ui.Radiobutton(group, "Static", 26, 50)
34 |
35 | local lbl = ui.Label(win, "Executable icon", 300, 162)
36 | local iconBtn = ui.Button(win, "", math.floor(300+lbl.width/2-16), lbl.y + lbl.height+4, 32, 32)
37 | iconBtn.hastext = false
38 |
39 | local execBtn = ui.Button(win, "Generate executable", 142, 256)
40 | execBtn:loadicon(File("img/exec.ico"))
41 |
42 | function iconBtn:onClick()
43 | local f = ui.opendialog("Select an icon", false, "ICO files (*.ico)|*.ico")
44 | if f ~= nil then
45 | icon = f.fullpath
46 | iconBtn:loadicon(f)
47 | else
48 | icon = nil
49 | end
50 | end
51 |
52 | function update_icon()
53 | target = radioconsole.checked and "luart" or "wluart"
54 | if icon == nil then
55 | iconBtn:loadicon(File(target..".exe"))
56 | end
57 | end
58 |
59 | radioconsole.onClick = update_icon
60 | radiodesktop.onClick = update_icon
61 |
62 | if #arg >= 1 and arg[1]:sub(1,1) ~= '-' then
63 | script.text = sys.File(arg[1]).fullpath
64 | if arg[2] ~= nil and arg[2]:sub(1,1) ~= '-' then
65 | embed.text = arg[2]
66 | end
67 | end
68 |
69 | function script:onChange()
70 | radioconsole.checked = script.text:match("(%.%w+)$") ~= ".wlua"
71 | radiodesktop.checked = not radioconsole.checked
72 | update_icon()
73 | output.text = script.text:gsub("(%.%w+)$", ".exe")
74 | end
75 |
76 | function script.chooseBtn:onClick()
77 | local file = ui.opendialog("Open main Lua script...", false, "Lua script (*.lua, *.wlua)|*.lua;*.wlua|Lua desktop script (*.wlua)|*.wlua|All files (*.*)|*.*")
78 | if file ~= nil then
79 | script.text = file.fullpath
80 | end
81 | end
82 |
83 | function embed.chooseBtn:onClick()
84 | local dir = ui.dirdialog("Select a directory...")
85 | if dir ~= nil then
86 | embed.text = dir.fullpath
87 | end
88 | end
89 |
90 | function radiodynamic:onClick()
91 | modules.enabled = radiodynamic.checked;
92 | end
93 |
94 |
95 | function radiostatic:onClick()
96 | if self.checked and ui.confirm( "It is strongly discouraged to use the static runtime library if you are using Lua binary modules.\nThis can lead to bugs and crashes of your application.\n\nPlease confirm that you want to use the static runtime library.", "Using static LuaRT runtime") == "cancel" then
97 | self.checked = false
98 | radiodynamic.checked = true
99 | end
100 | modules.enabled = radiodynamic.checked;
101 | end
102 |
103 | function win:onClose()
104 | sys.exit()
105 | end
106 |
107 | function execBtn:onClick()
108 | if #embed.text > 0 and not sys.Directory(embed.text).exists then
109 | ui.error("Embedded directory does not exists")
110 | else
111 | local f = sys.File(output.text)
112 | if f.exists and ui.confirm(f.name.." already exist.\nDo you want to proceed and replace the existing file ?") ~= "yes" then
113 | return
114 | end
115 | win:hide()
116 | end
117 | end
118 |
119 | update_icon()
120 | win:center()
121 | win:show()
122 |
123 | while win.visible do
124 | ui.update()
125 | end
126 |
127 | local directory = #embed.text > 0 and sys.Directory(embed.text) or nil
128 |
129 | if radiodesktop.checked then
130 | target = "wluart"
131 | else
132 | target = "luart"
133 | end
134 |
135 | if radiostatic.checked then
136 | target = target.."-static"
137 | end
138 |
139 | local libs = {}
140 | for mod in modules.text:gmatch("(%w+)") do
141 | libs[#libs+1] = mod
142 | end
143 |
144 | return {
145 | sys.File(script.text),
146 | directory,
147 | target..".exe",
148 | output.text,
149 | icon,
150 | libs
151 | }
152 |
--------------------------------------------------------------------------------
/src/img/exec.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyeyo/rtc/d2b908d8c10930f1714f4edb98b818fe734a3089/src/img/exec.ico
--------------------------------------------------------------------------------
/src/img/open.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyeyo/rtc/d2b908d8c10930f1714f4edb98b818fe734a3089/src/img/open.ico
--------------------------------------------------------------------------------
/src/img/rtc.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/samyeyo/rtc/d2b908d8c10930f1714f4edb98b818fe734a3089/src/img/rtc.ico
--------------------------------------------------------------------------------
/src/rtc.lua:
--------------------------------------------------------------------------------
1 | -- | RTc - Lua script to executable compiler
2 | -- | Luart.org, Copyright (c) Tine Samir 2023
3 | -- | See Copyright Notice in LICENSE.TXT
4 | -- |---------------------------------------------------
5 | -- | RTc.lua | LuaRT executable compiler
6 |
7 | local zip = require "compression"
8 |
9 | local output -- output executable filename
10 | local icon -- executable icon
11 | local directory -- Directory to be embedded in the executable
12 | local file -- main Lua File to be executed when running the executable
13 | local target = "luart.exe" -- target interpreter for console or window subsystem
14 | local setoutput, set_icon, static, forceconsole = false, false, false, false
15 | local console, gui
16 |
17 | ui = false
18 | local libs = {}
19 |
20 | if arg[0]:find("wrtc%.exe") == nil then
21 | console = require "console"
22 | local idx = (package.loaded["embed"] == nil) and 1 or 0
23 | if #arg == idx then
24 | console.writecolor("lightblue", "rt")
25 | console.writecolor('yellow', "c ")
26 | print(_VERSION:match(" (.*)$")..[[ - Lua script to executable compiler.
27 | Copyright (c) 2023, Samir Tine.
28 |
29 | usage: rtc.exe [-s][-c][-w][-i icon][-o output] [-lmodname] [directory] main.lua
30 |
31 | -s create static executable (without LUA54.DLL dependency)
32 | -c create executable for console (default)
33 | -w create executable for Windows desktop
34 | -i icon set executable icon (expects an .ico file)
35 | -o output set executable name to 'output'
36 | -lmodname link the LuaRT binary module 'modname.dll'
37 | directory the content of the directory to be embedded in the executable
38 | main.lua the Lua script to be executed]])
39 | sys.exit()
40 | end
41 |
42 | ------------------------------------| Parse commande line
43 | for i, option in ipairs(arg) do
44 | if setoutput then
45 | output = option
46 | setoutput = false
47 | elseif set_icon then
48 | icon = option
49 | set_icon = false
50 | elseif option == "-i" then
51 | set_icon = true
52 | elseif option == "-c" then
53 | forceconsole = true
54 | target = "luart.exe"
55 | elseif option == "-w" then
56 | target = "wluart.exe"
57 | elseif option == "-o" then
58 | setoutput = true
59 | elseif option == "-s" then
60 | static = true
61 | elseif option:find("-l") == 1 then
62 | libs[#libs+1] = option:match("%-l(%w+)")
63 | elseif option:usub(1,1) == "-" then
64 | print("invalid option "..option)
65 | sys.exit(-1)
66 | elseif directory == nil and not sys.File(option).exists then
67 | directory = sys.Directory(option)
68 | if not directory.exists then
69 | error("cannot find file "..option)
70 | end
71 | else
72 | file = sys.File(option)
73 | if not file.exists then
74 | error("cannot find file "..option)
75 | end
76 | end
77 | end
78 | else
79 | ui = require "ui"
80 | local result = require("gui")
81 | file, directory, target, output, icon, libs = table.unpack(result)
82 | end
83 |
84 | if #libs > 0 and static then
85 | error("could not link modules in static mode")
86 | end
87 |
88 | if file == nil then
89 | error("no input file")
90 | end
91 |
92 | if file.extension == ".wlua" and target == "luart.exe" and not forceconsole then
93 | target = "wluart.exe"
94 | end
95 |
96 | if static then
97 | if target == "luart.exe" then
98 | target = "luart-static.exe"
99 | else
100 | target = "wluart-static.exe"
101 | end
102 | end
103 |
104 | result, err = load(file:open():read())
105 | if result == nil then
106 | error(err)
107 | end
108 |
109 | if embed == nil then
110 | target = sys.File(sys.File(arg[0]).path..target)
111 | else
112 | target = embed.File(target)
113 | end
114 |
115 | if not target.exists then
116 | error("Fatal error: compilation failed, check your LuaRT installation")
117 | end
118 |
119 | local fs = sys.tempfile("luartc_")
120 | local fname = sys.tempfile("luartc_main_")
121 |
122 | fname:open("write", "binary")
123 | fname:write(string.dump(result, true))
124 | fname:close()
125 |
126 | local z = zip.Zip(fs.fullpath, "write")
127 | z:write(fname, "__mainLuaRTStartup__.lua")
128 |
129 | for lib in each(libs) do
130 | local libpath = sys.Directory(sys.File(arg[0]).path.."/../modules/").exists and sys.File(arg[0]).path.."/../modules/" or sys.File(arg[0]).path.."/modules/"
131 | local libfile = sys.File(libpath..lib.."/"..lib..".dll")
132 | if not libfile.exists then
133 | libfile = sys.File(libpath.."/"..lib..".dll")
134 | end
135 | if libfile.exists then
136 | z:write(libfile)
137 | else
138 | error("module '"..lib.."' not found")
139 | end
140 | end
141 |
142 | if directory ~= nil then
143 | z:write(directory)
144 | end
145 | z:close()
146 |
147 | output = sys.File(output or file.name:gusub("(%w+)$", "exe"))
148 | output:remove()
149 | target:copy(output.fullpath)
150 | target:remove()
151 |
152 | if icon ~= nil then
153 | seticon(output.fullpath, icon)
154 | end
155 |
156 | if link(fs.fullpath, output.fullpath) == false then
157 | error('Cannot write "'..output.fullpath..'" to disk')
158 | end
159 |
160 | if ui then
161 | print("Successfully compiled "..output.name.." ("..math.floor(output.size/1024).."Kb)")
162 | else
163 | print(output.name)
164 | end
165 | fs:close()
166 | fs:remove()
167 | fname:remove()
--------------------------------------------------------------------------------