First, a word about how rtk approaches versioning ...
Releases are versioned according to semantic versioning, and applies the following philosophy:
Scripts/rtk/
, there is a subdirectory for all API versions ever released
When rtk is installed via ReaPack, it will live within REAPER's
resource folder under Scripts/rtk
. Because it's installed at this well-known location, when your script targets the ReaPack install, it can be loaded like this:
-- Set package path to find rtk installed via ReaPack
package.path = reaper.GetResourcePath() .. '/Scripts/rtk/1/?.lua'
local rtk = require('rtk')
Notice the 1/
component of package.path
in the above example. This indicates the API
version that the script is targeting.
Here's a slightly more complex example, which includes not just the path to rtk, but also the directory that holds the entrypoint script (i.e. the one that invoked the action), so you're able to load other scripts that exist alongside the entrypoint script:
local entrypath = ({reaper.get_action_context()})[2]:match('^.+[\\//]')
package.path = string.format('%s/Scripts/rtk/1/?.lua;%s?.lua;', reaper.GetResourcePath(), entrypath)
While the above examples are straightforward, they aren't not very robust and will error out ungracefully if the rtk ReaPack isn't installed. Here's a more practical if more complex snippet:
package.path = reaper.GetResourcePath() .. '/Scripts/rtk/1/?.lua'
local ok, rtk = pcall(function() return require('rtk') end)
if not ok then
reaper.MB(
'This script requires the REAPER Toolkit ReaPack. Visit https://reapertoolkit.dev for instructions.',
'Missing Library',
0
)
return
end
As long as the user has the ReaPack extension installed, we can get fairly clever by using the ReaPack extension API to automatically install rtk if the user so chooses.
This is a significantly more complex bit of logic, but it provides a nicer user experience, and it's copy-pastable directly into your script.
-- Setup package path locations to find rtk via ReaPack
local entrypath = ({reaper.get_action_context()})[2]:match('^.+[\\//]')
package.path = string.format('%s/Scripts/rtk/1/?.lua;%s?.lua;', reaper.GetResourcePath(), entrypath)
-- Loads rtk in the global scope, and, if missing, attempts to install using
-- ReaPack APIs.
local function init(attempts)
local ok
ok, rtk = pcall(function() return require('rtk') end)
if ok then
-- Import worked. We can invoke the main function.
return rtk.call(main)
end
local installmsg = 'Visit https://reapertoolkit.dev for installation instructions.'
if not attempts then
-- This is our first failed attempt, so prompt the user if they want us to install
-- rtk via ReaPack automatically.
if not reaper.ReaPack_AddSetRepository then
-- The ReaPack extension isn't installed, so inform the user they need to do a
-- manual install.
return reaper.MB(
'This script requires the REAPER Toolkit ReaPack. ' .. installmsg,
'Missing Library',
0 -- Ok
)
end
-- Ask the user if they want us to install rtk
local response = reaper.MB(
'This script requires the REAPER Toolkit ReaPack. Would you like to automatically install it?',
'Automatically install REAPER Toolkit ReaPack?',
4 -- Yes/No
)
if response ~= 6 then
-- User said no, we're done.
return reaper.MB(installmsg, 'Automatic Installation Refused', 0)
end
-- User said yes, so add the ReaPack repository.
local ok, err = reaper.ReaPack_AddSetRepository('rtk', 'https://reapertoolkit.dev/index.xml', true, 1)
if not ok then
return reaper.MB(
string.format('Automatic install failed: %s.\n\n%s', err, installmsg),
'ReaPack installation failed',
0 -- Ok
)
end
reaper.ReaPack_ProcessQueue(true)
elseif attempts > 150 then
-- After about 5 seconds we still couldn't find rtk, so give up.
return reaper.MB(
'Installation took too long. Assuming a ReaPack error occurred and giving up. ' .. installmsg,
'ReaPack installation failed',
0 -- Ok
)
end
-- If we've made it this far we keep trying to load rtk
reaper.defer(function() init((attempts or 0) + 1) end)
end
-- Invoked by init() when rtk has successfully been loaded. Your script's main content
-- goes here.
function main()
local window = rtk.Window()
window:add(rtk.Text{'Hello world!'})
window:open()
end
init()
In lieu of using the global ReaPack install, you can distribute rtk along with your own projects. You might choose to do this because:
rtk uses a custom tool called LuaKnit to assemble all of the project files into a single
packed rtk.lua
source file that makes it convenient
for script authors to import and distribute.
Suppose your script is called myapp.lua
and you have
rtk.lua
in the same directory alongside it:
-- Setup package path locations to find rtk via ReaPack
local entrypath = ({reaper.get_action_context()})[2]:match('^.+[\\//]')
package.path = string.format('%s?.lua;', entrypath)
local rtk = require('rtk')
And that's all there is to it.
You can use LuaKnit yourself to combine your own source files, and even include
rtk.lua
so you're able to distribute a single executable script.
In addition to consolidating into a single file, LuaKnit also does its best to minify the code, stripping out comments and reducing the amount of redundant whitespace. It's not perfect -- it doesn't tokenize Lua source code, only uses naive regexps -- but it does a decent enough job not to have motivated anything more sophisticated.
Fetch luaknit.py
from rtk's git
repository.
LuaKnit is a Python script and requires Python 3.6 or later.
Suppose you have a directory containing rtk.lua
, and your own project files main.lua
and commands.lua
. It's easy to bundle all these together:
# Linux and OS X
$ python3 /path/to/luaknit.py rtk.lua main.lua commands.lua -o myscript.lua
# Windows
C:\projects\myscript> python \path\to\luaknit.py rtk.lua main.lua commands.lua -o myscript.lua
That's really all there is to it. The file myscript.lua
can now be executed directly by
REAPER.
If your script require()
s another module, if the module is located in the current
directory, it will be automatically processed. If not, you can pass it (either a
file or directory) directly to LuaKnit and specify the module name.
For example, you could bundle your script directly with rtk's raw source directory which
contains all its original files. Suppose you have cloned rtk's git repo
at /path/to/rtk
, then:
$ python3 /path/to/luaknit.py rtk=/path/to/rtk/src main.lua commands.lua -o myscript.lua