mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-03 17:30:11 +00:00
110 lines
4.3 KiB
Python
Executable file
110 lines
4.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
# (C) 2013-21 - ntop.org
|
|
# Author: Simone Mainardi <mainardi@ntop.org>
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
import networkx as nx
|
|
|
|
__version__ = '1.0.0'
|
|
|
|
class CircularDeps(object):
|
|
def __init__(self):
|
|
self._G = None # The graph
|
|
self._requires = [] # All the dependencies read from files
|
|
|
|
def _build_requires(self):
|
|
# Iterate over all lua files, both community and pro
|
|
for available_path in ['./../scripts', './../pro/scripts']:
|
|
for path in Path(available_path).rglob('*.lua'):
|
|
# Exclude backup files (e.g., starting with #)
|
|
if path.name.startswith('.') or path.name.startswith('#') or path.name.startswith('~'):
|
|
continue
|
|
|
|
# Search for requires
|
|
with path.open('r', encoding="utf-8") as fid:
|
|
for line in fid:
|
|
res = [
|
|
# require without assignment
|
|
re.search(r'^require.*\"(.*?)\"' ,line),
|
|
re.search(r'^require.*\'(.*?)\'' ,line),
|
|
# require with assignment to local variables
|
|
re.search(r'^local.*require.*\"(.*?)\"' ,line),
|
|
re.search(r'^local.*require.*\'(.*?)\'' ,line),
|
|
# require with assignment to global variables (also ignore comments)
|
|
re.search(r'^[^\s\-].*require.*\"(.*?)\"' ,line),
|
|
re.search(r'^[^\s\-].*require.*\'(.*?)\'' ,line),
|
|
]
|
|
|
|
# Add requires
|
|
for r in res:
|
|
if r and r.group:
|
|
# Prepare node names
|
|
require_node_name = r.group(1)
|
|
|
|
# If the file is under the REST API, an extra folder is added to the node name
|
|
# to avoid clashes with names, .e.g., delete/endpoints.lua with get/endpoints.lua
|
|
file_node_name = '/'.join(path.parts['/rest/' in str(path) and -2 or -1:])
|
|
file_node_name = file_node_name.replace('.lua', '')
|
|
|
|
self._requires.append((file_node_name, require_node_name))
|
|
|
|
def _build_graph(self):
|
|
self._G = nx.DiGraph()
|
|
self._G.add_edges_from(self._requires)
|
|
|
|
def _find_cycles(self):
|
|
all_cycles = []
|
|
|
|
# For each node
|
|
for n in self._G.nodes():
|
|
try:
|
|
# Search a cycle with Depth-first traversal
|
|
cycle = list(nx.find_cycle(self._G, n))
|
|
|
|
# If cycle not already found, add it to the results list
|
|
found = False
|
|
for c in all_cycles:
|
|
if cycle == c:
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
all_cycles.append(cycle)
|
|
|
|
# When no cycles are found for the current node, an exception is thrown and it is safe to ignore it
|
|
except nx.exception.NetworkXNoCycle:
|
|
pass
|
|
|
|
print(all_cycles)
|
|
|
|
def check(self):
|
|
self._build_requires()
|
|
self._build_graph()
|
|
self._find_cycles()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='Find cycles in Lua dependencies. Re-run after removing cycles until no other cycle shows up.')
|
|
parser.add_argument('-V', '--version', action='version', version='%(prog)s v' + sys.modules[__name__].__version__)
|
|
|
|
tf = CircularDeps()
|
|
|
|
tf.check()
|