List Dep

lua-users home
wiki

[!] VersionNotice: The below code pertains to an older Lua version, Lua 4. It does not run as is under Lua 5.

This program outputs a list of dependencies between Lua files, such as when one Lua file calls dofile on another Lua file. (This is a CASE tool.)

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 
        -- only show MODULE_NOT_FOUD and UNKNOWN_REF if they are connected to someone
        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 


-- returns the 'basename' and the 'basedir' 
function getBases(filename) 
    return  gsub(filename, "(.*/)(.*)", "%2") or filename, 
            gsub(filename, "(.*/)(.*)", "%1") 
end 


-- processes the modules listed in the 'hnd' file
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 


-- processes a module
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 
        
        -- detect '' and "" strings
        module_name, n  = gsub(param, ".*([\"'])([^%1]*)%1.*", "%2")
        if (n == 0) then
            -- detect [[]] strings
            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")

    -- remove comments (don't work if -- is inside a string)
    wholefile = gsub(wholefile, "%-%-[^\n\r]*", "")

    -- find "dofile"s 
    for i, execute_function in CONFIG.lua_file_execute_functions do 
        -- for beginning of the file 
        gsub(wholefile, "^"..execute_function.."[^0-9A-Za-z_]*(%b())", execute_function_param) 
        -- for the middle and the end of the file 
        gsub(wholefile, "[^0-9A-Za-z_]"..execute_function.."[^0-9A-Za-z_]*(%b())", execute_function_param) 
    end 

   closefile(hndIN) 

end 


-- main 


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)

RecentChanges · preferences
edit · history
Last edited January 2, 2007 1:45 am GMT (diff)