Posted 10 February 2015 - 09:02 PM
Hey everybody,
I am experimenting with ways to put a program and its external dependencies (i.e. "APIs") in a single file that can be executed and will run like the normal "dynamically linked" version. This is because I often intend to share my code and don't want to force users to download multiple files they don't need and adapt my file hierarchy.
The only somewhat reliable way I have found so far is to replace os.loadAPI in the resulting program with one that looks the filename up in a table, then loads and runs the associated string value (the source) and puts the resulting environment in _G like the default os.loadAPI does. This works okay, but I am wondering if there are more elegant ways to do this. Everything I thought of so far goes down the write-a-parser path which I am not intending to walk down for mental health reasons.
I am also not quite sure how os.loadAPI exactly works. Does every loaded api get a pristine environment or do they share one? And how is the table populated that os.loadAPI puts in _G?
Finally, I have been thinking about just inlining functions by replacing the os.loadAPI call with their code (like #include in c). Users could put decorators in front of loadAPI calls with a list of needed functions. This would only work if the inlined functions work like blackboxes, but most of my code that I regularly need consists of less than a dozen functions that have no dependencies on their own. Do you guys have any better ideas how to achieve this in a reliable way?
Thanks!
This is the resulting program that my bake program currently creates. I still need to do a lot of testing.
I am experimenting with ways to put a program and its external dependencies (i.e. "APIs") in a single file that can be executed and will run like the normal "dynamically linked" version. This is because I often intend to share my code and don't want to force users to download multiple files they don't need and adapt my file hierarchy.
The only somewhat reliable way I have found so far is to replace os.loadAPI in the resulting program with one that looks the filename up in a table, then loads and runs the associated string value (the source) and puts the resulting environment in _G like the default os.loadAPI does. This works okay, but I am wondering if there are more elegant ways to do this. Everything I thought of so far goes down the write-a-parser path which I am not intending to walk down for mental health reasons.
I am also not quite sure how os.loadAPI exactly works. Does every loaded api get a pristine environment or do they share one? And how is the table populated that os.loadAPI puts in _G?
Finally, I have been thinking about just inlining functions by replacing the os.loadAPI call with their code (like #include in c). Users could put decorators in front of loadAPI calls with a list of needed functions. This would only work if the inlined functions work like blackboxes, but most of my code that I regularly need consists of less than a dozen functions that have no dependencies on their own. Do you guys have any better ideas how to achieve this in a reliable way?
Thanks!
This is the resulting program that my bake program currently creates. I still need to do a lot of testing.
local __bake__ = {}
__bake__.loaded = {} -- avoid multiple chunk evaluation
__bake__.old_loadAPI = os.loadAPI
os.loadAPI = function(url)
-- load and run string in __bake__[api_filename]
-- put resulting env in _G[api_filename]
-- set __bake__.loaded[api_filename] = true
end
os.unloadAPI = function(url)
-- set _G[api_filename] = nil
-- set __bake__.loaded[api_filename] = nil
end
__bake__.apis = {
["/lib/util"] = [========[ -- source -- ]========],
-- etc.
}
-- flatcopy current environment for inlined apis to run in.
__bake__.default_env = {}
for ident, value in pairs(_G) do
__bake__.default_env[ident] = value
end
__bake__.program = [==========[ --the original program-- ]==========]
local f = loadstring(__bake__.program)
assert(f, '[bake] Unable to load program, does the original program contain syntax errors?')
local success, error = pcall(f, ...)
if not success then
printError(error)
end
-- clean up:
os.loadAPI = __bake__.old_loadAPI