comment = [[
ListDep.lua: "List module dependencies"
Version 1.0 - April, 5, 2002
This program output a list of dependencies between Lua files,
such as when one Lua file calls {{dofile}} on another Lua file.
The output of this program is a graph which can be viewed by the aiSee program.
Download aiSee at http://www.aisee.com, available for unixes and windows
To see how to use it, call "lua ListDep.lua" for the usage description.
Please, send any comments to Luiz Carlos Silveira <luiz@fabricadigital.com.br>
Limitations:
- the "parser" don't have states, so it assumes your code behaves well.
For example, comment starters inside a string confuses the parser
- directories are not correctly resolved, so only the module's basename
is considered. In other words, the dependencies will not be correctly
listed if there are two modules with the same name.
- dofile's that appear anywhere in the code are considered (except in
comments), including those that are in strings.
]]
MODULES = {}
INFO = {}
function newModule(module_label, module_title)
MODULES[module_label] = {label = module_label, title = module_title,
dofiles={}}
end
function connectModules(from, to)
if (MODULES[to] == nil) then
INFO.HAS_MODULE_NOT_FOUND = 1
to = "MODULE_NOT_FOUND"
end
if (MODULES[to].label == "UNKNOWN_REF") then
INFO.HAS_UNKNOWN_REF = 1
end
tinsert(MODULES[from].dofiles, MODULES[to])
end
function exportGraph(out_file)
local hndOUT
if (out_file == "-") then
hndOUT = _STDOUT
else
hndOUT = openfile(out_file, "w")
if (hndOUT == nil) then
error("Cannot write to "..out_file)
end
end
write(hndOUT, "graph: {manhattanedges: yes\n")
for i, module in MODULES do
local module_label = module.label
local module_title = module.title
if not (((module_label == "MODULE_NOT_FOUND") and (INFO.HAS_MODULE_NOT_FOUND == nil)) or
((module_label == "UNKNOWN_REF") and (INFO.HAS_UNKNOWN_REF == nil))) then
write(hndOUT, 'node: {label: "'..module_label..'" title: "'..module_title..'"}\n')
end
end
for i, module in MODULES do
for i = 1, getn(module.dofiles) do
local dofile = module.dofiles[i]
write(hndOUT, 'edge: {sourcename: "'..module.label..'" targetname: "'..dofile.label..'"}\n')
end
end
write(hndOUT, "}\n")
closefile(hndOUT)
end
function getBases(filename)
return gsub(filename, "(.*/)(.*)", "%2") or filename,
gsub(filename, "(.*/)(.*)", "%1")
end
function getReferenceForModules(hnd)
local filename
local modules = {}
local dirname
local filename
local basedir
local basename
newModule("UNKNOWN_REF", "UNKNOWN_REF")
newModule("MODULE_NOT_FOUND", "MODULE_NOT_FOUND")
filename = read(hnd)
while (filename) do
if (filename ~= '') then
basename, basedir = getBases(filename)
tinsert(modules, filename)
newModule(basename, basename)
end
filename = read(hnd)
end
for i = 1, getn(modules) do
processFile(modules[i])
end
end
function processFile(filename)
local wholefile, result
local hndIN
local basename, basedir = getBases(filename)
local execute_function_param = function (param)
local n
local module_name
local module_basename
module_name, n = gsub(param, ".*([\"'])([^%1]*)%1.*", "%2")
if (n == 0) then
module_name, n = gsub(param, ".*%[%[[^%]*]%]%].*", "%1")
end
if (n ~= 1) then
module_basename = "UNKNOWN_REF"
else
module_name = gsub(module_name, "%."..CONFIG.luac_extension, ".lua")
module_basename = getBases(module_name)
end
connectModules(%basename, module_basename)
end
print("processing file "..filename)
hndIN = openfile(filename, "r")
if (hndIN == nil) then
print("** error opening file "..fname)
end
wholefile = read(hndIN, "*a")
wholefile = gsub(wholefile, "%-%-[^\n\r]*", "")
for i, execute_function in CONFIG.lua_file_execute_functions do
gsub(wholefile, "^"..execute_function.."[^0-9A-Za-z_]*(%b())", execute_function_param)
gsub(wholefile, "[^0-9A-Za-z_]"..execute_function.."[^0-9A-Za-z_]*(%b())", execute_function_param)
end
closefile(hndIN)
end
CONFIG = {
output = "-",
exclude_from_path = "",
luac_extension = "luac",
lua_file_execute_functions = {"dofile"},
}
if (not list) then
print('Usage: lua <list="luafiles.list"|"-"> [output="file"|"-"]')
print(' [luac_extension="ext"]')
print(' [lua_file_execute_functions="{\'func1\', ..., \'funcn\'}"]')
print(' ListDeps.lua"')
print()
print()
print(' list: A file with the list of lua files to process. One file per line.')
print(' Use "-" to read from standard input')
print(' example: find project/lua -name "*.lua" | lua list="-" ListDeps.lua')
print()
print(' output: The GDL output file, or "-" to write to the standard output.')
print()
print(' exclude_from_path: A gsub pattern to exclude from each lua file path')
print()
print(' luac_extension: The lua compiled files extension used in dofiles')
print()
print(' lua_file_execute_functions: a table with the functions that do files')
print()
print()
print('Note: it only works over source code files!')
exit()
end
if (output) then
CONFIG.output = output
output = nil
end
if (luac_extension ~= nil) then
CONFIG.luac_extension = luac_extension
luac_extension = nil
end
if (lua_file_execute_functions ~= nil) then
CONFIG.lua_file_execute_functions = dostring('return '..lua_file_execute_functions)
lua_file_execute_functions = nil
end
if (list == "-") then
getReferenceForModules(_STDIN)
else
hnd = openfile(list, "r")
if (hnd == nil) then
print("File "..list.." not found!")
exit()
end
getReferenceForModules(hnd)
closefile(hnd)
end
exportGraph(CONFIG.output)