Add a utility checking uses of require()s
Currently, "everything can require everything". It's an unstructured mess which sometimes causes problems. This commit adds a tool that enforces a white-list of require() uses. It uses depgraph to scan the source code and then each use of require() that is found is checked. If any violations are found, the tool returns a failure. This tool is wired up to a new target "make check-requires" which is included in "make check". Thus, Travis will run this. Reference: https://github.com/awesomeWM/awesome/issues/1400 Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
fe4d6404a6
commit
0afed66ccf
|
@ -88,6 +88,8 @@ install:
|
|||
- travis_retry sudo luarocks install busted
|
||||
# Install luacheck for "make check-qa".
|
||||
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install luacheck; fi
|
||||
# Install depgraph for "make check-qa".
|
||||
- if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install depgraph; fi
|
||||
|
||||
# Install ldoc for building docs.
|
||||
- |
|
||||
|
|
|
@ -381,6 +381,13 @@ add_custom_target(check-integration
|
|||
COMMENT "Running integration tests"
|
||||
USES_TERMINAL
|
||||
VERBATIM)
|
||||
add_custom_target(check-requires
|
||||
lua "${CMAKE_SOURCE_DIR}/build-utils/check_for_invalid_requires.lua"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMENT "Checking use of require()"
|
||||
USES_TERMINAL
|
||||
VERBATIM)
|
||||
list(APPEND CHECK_QA_TARGETS check-requires)
|
||||
a_find_program(BUSTED_EXECUTABLE busted FALSE)
|
||||
if(BUSTED_EXECUTABLE)
|
||||
# Keep the arguments in sync with the version below!
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
#!/usr/bin/env lua
|
||||
|
||||
-- This uses the "depgraph" package to parse all source code and look for calls
|
||||
-- to require(). For all these uses, we make sure that it is allowed by the
|
||||
-- white-list below. This is done to mitigate the risk of cyclic dependencies
|
||||
-- between modules and to (hopefully) enforce a bit of sane code structure.
|
||||
|
||||
local have_depgraph, depgraph = pcall(require, "depgraph")
|
||||
|
||||
if not have_depgraph then
|
||||
print("depgraph not found")
|
||||
print(depgraph)
|
||||
print("(this error is non-fatal; we just skip its use)")
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
local allowed_deps = {
|
||||
gears = {
|
||||
lgi = true,
|
||||
},
|
||||
beautiful = {
|
||||
gears = true,
|
||||
lgi = true,
|
||||
},
|
||||
wibox = {
|
||||
beautiful = true,
|
||||
gears = true,
|
||||
lgi = true,
|
||||
},
|
||||
awful = {
|
||||
beautiful = true,
|
||||
gears = true,
|
||||
lgi = true,
|
||||
wibox = true,
|
||||
},
|
||||
naughty = {
|
||||
awful = true,
|
||||
beautiful = true,
|
||||
gears = true,
|
||||
lgi = true,
|
||||
wibox = true,
|
||||
},
|
||||
menubar = {
|
||||
awful = true,
|
||||
beautiful = true,
|
||||
gears = true,
|
||||
lgi = true,
|
||||
wibox = true,
|
||||
},
|
||||
-- TODO: Get rid of these
|
||||
["beautiful.xresources"] = { ["awful.util"] = true },
|
||||
["gears.object"] = { ["awful.util"] = true },
|
||||
["wibox.container"] = { ["awful.util"] = true },
|
||||
["wibox.layout"] = { ["awful.util"] = true },
|
||||
["wibox.widget"] = { ["awful.util"] = true },
|
||||
["gears.surface"] = { ["wibox.hierarchy"] = true },
|
||||
}
|
||||
|
||||
-- Turn "foo.bar.baz" into "foo.bar". Returns nil if there is nothing more to
|
||||
-- remove.
|
||||
local function get_supermodule(module)
|
||||
return string.match(module, "(.+)%.%a+")
|
||||
end
|
||||
|
||||
-- Check if "module" (or one of its parents) is allowed to depend on
|
||||
-- "dependency" (or one of its parents).
|
||||
local function is_allowed(module, dependency)
|
||||
assert(module ~= nil)
|
||||
-- If both have a common parent, the dependency is allowed
|
||||
do
|
||||
local last_mod, mod = module, module
|
||||
while mod do
|
||||
last_mod, mod = mod, get_supermodule(mod)
|
||||
end
|
||||
local last_dep, dep = dependency, dependency
|
||||
while dep do
|
||||
last_dep, dep = dep, get_supermodule(dep)
|
||||
end
|
||||
if last_mod == last_dep then
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- Check the allowed_deps table
|
||||
while module ~= nil do
|
||||
local allowed = allowed_deps[module]
|
||||
if allowed then
|
||||
local dep = dependency
|
||||
while dep ~= nil do
|
||||
if allowed[dep] then
|
||||
return true
|
||||
end
|
||||
dep = get_supermodule(dep)
|
||||
end
|
||||
end
|
||||
module = get_supermodule(module)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Generate the dependency graph
|
||||
local graph = assert(depgraph.make_graph({"lib"}, {}, "lib", nil, nil))
|
||||
|
||||
-- Check if all require's are allowed by the above table
|
||||
local had_violation = false
|
||||
for _, module in ipairs(graph.modules) do
|
||||
for _, dep in ipairs(module.deps) do
|
||||
if not is_allowed(module.name, dep.name) then
|
||||
had_violation = true
|
||||
print(string.format("Dependency from %s onto %s is not allowed",
|
||||
module.name, dep.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if had_violation then
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue