App: Miscellaneous fixes and improvements based on AI code analysis #44
2 changed files with 40 additions and 21 deletions
|
|
@ -114,7 +114,7 @@ class App(Base):
|
||||||
if pretty:
|
if pretty:
|
||||||
return self.__pretty_topdir
|
return self.__pretty_topdir
|
||||||
return self.__topdir
|
return self.__topdir
|
||||||
for d in [self.__projs_root, '/opt']:
|
for d in [self.__projs_root, self.___opt_root]:
|
||||||
ret = d + '/' + name
|
ret = d + '/' + name
|
||||||
if os.path.exists(ret):
|
if os.path.exists(ret):
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -126,10 +126,14 @@ class App(Base):
|
||||||
def __find_dir(
|
def __find_dir(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
search_subdirs: list[str] = [],
|
search_subdirs: list[str] | None = None,
|
||||||
search_absdirs: list[str] = [],
|
search_absdirs: list[str] | None = None,
|
||||||
pretty: bool = True,
|
pretty: bool = True,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
|
if search_subdirs is None:
|
||||||
|
search_subdirs = []
|
||||||
|
if search_absdirs is None:
|
||||||
|
search_absdirs = []
|
||||||
|
|
||||||
def __format_relpath(path: str):
|
def __format_relpath(path: str):
|
||||||
if path.startswith('./'):
|
if path.startswith('./'):
|
||||||
|
|
@ -181,16 +185,18 @@ class App(Base):
|
||||||
return ret
|
return ret
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@cache
|
|
||||||
def __read_project_conf(self, project_dir: str) -> ProjectConf:
|
def __read_project_conf(self, project_dir: str) -> ProjectConf:
|
||||||
return ProjectConf.read(project_dir + '/make/project.conf')
|
return ProjectConf.read(project_dir + '/make/project.conf')
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def __get_project_conf(self, project: str) -> ProjectConf:
|
def __get_project_conf(self, project: str) -> ProjectConf | None:
|
||||||
pd = self.__proj_dir(project, False)
|
pd = self.__proj_dir(project, False)
|
||||||
if pd is None:
|
if pd is None:
|
||||||
raise Exception(f'Failed to find directory of project {project}')
|
raise Exception(f'Failed to find directory of project {project}')
|
||||||
return self.__read_project_conf(pd)
|
try:
|
||||||
|
return self.__read_project_conf(pd)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
|
||||||
def __get_project_refs_cached(
|
def __get_project_refs_cached(
|
||||||
self, buf, visited, spec, section, key, add_self, scope, names_only
|
self, buf, visited, spec, section, key, add_self, scope, names_only
|
||||||
|
|
@ -272,8 +278,6 @@ class App(Base):
|
||||||
add_self = False,
|
add_self = False,
|
||||||
names_only = True,
|
names_only = True,
|
||||||
)
|
)
|
||||||
if deps is None:
|
|
||||||
continue
|
|
||||||
graph[project] = set(deps)
|
graph[project] = set(deps)
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
self.__read_dep_graph([dep], sections, graph)
|
self.__read_dep_graph([dep], sections, graph)
|
||||||
|
|
@ -313,22 +317,25 @@ class App(Base):
|
||||||
temp.remove(project)
|
temp.remove(project)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __find_circular_deps(self, projects: list[str], flavours: list[str]) -> bool:
|
def __find_circular_deps(self, projects: list[str],
|
||||||
|
flavours: list[str]) -> list[str]:
|
||||||
graph: Graph = {}
|
graph: Graph = {}
|
||||||
ret: list[str] = []
|
ret: list[str] = []
|
||||||
self.__read_dep_graph(projects, flavours, graph)
|
self.__read_dep_graph(projects, flavours, graph)
|
||||||
unvisited = list(graph.keys())
|
unvisited = list(graph.keys())
|
||||||
temp: set[str] = set()
|
temp: set[str] = set()
|
||||||
|
flipped = self.__flip_dep_graph(graph)
|
||||||
while unvisited:
|
while unvisited:
|
||||||
project = unvisited[0]
|
project = unvisited[0]
|
||||||
log(DEBUG, 'Checking circular dependency of', project)
|
log(DEBUG, 'Checking circular dependency of', project)
|
||||||
last = self.__find_circular_deps_recursive(
|
last = self.__find_circular_deps_recursive(
|
||||||
project, self.__flip_dep_graph(graph), unvisited, temp, ret
|
project, flipped, unvisited, temp, ret
|
||||||
)
|
)
|
||||||
if last is not None:
|
if last is not None:
|
||||||
log(DEBUG, f'Found circular dependency below {project}, last is {last}')
|
log(DEBUG, f'Found circular dependency below {project}, last is {last}')
|
||||||
return True
|
ret.append(last)
|
||||||
return False
|
return ret
|
||||||
|
return []
|
||||||
|
|
||||||
def __init__(self, distro: Distro | None = None) -> None:
|
def __init__(self, distro: Distro | None = None) -> None:
|
||||||
|
|
||||||
|
|
@ -347,6 +354,7 @@ class App(Base):
|
||||||
# -- Members with default values
|
# -- Members with default values
|
||||||
self.__topdir_fmt = 'absolute'
|
self.__topdir_fmt = 'absolute'
|
||||||
self.__projs_root = pwd.getpwuid(os.getuid()).pw_dir + '/local/src/jw.dev/proj'
|
self.__projs_root = pwd.getpwuid(os.getuid()).pw_dir + '/local/src/jw.dev/proj'
|
||||||
|
self.___opt_root = '/opt'
|
||||||
self.__pretty_projs_root = None
|
self.__pretty_projs_root = None
|
||||||
|
|
||||||
async def __init_async(self) -> None:
|
async def __init_async(self) -> None:
|
||||||
|
|
@ -489,8 +497,8 @@ class App(Base):
|
||||||
def find_dir(
|
def find_dir(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
search_subdirs: list[str] = [],
|
search_subdirs: list[str] | None = None,
|
||||||
search_absdirs: list[str] = [],
|
search_absdirs: list[str] | None = None,
|
||||||
pretty: bool = True,
|
pretty: bool = True,
|
||||||
throw: bool = False,
|
throw: bool = False,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
|
|
@ -541,13 +549,15 @@ class App(Base):
|
||||||
try:
|
try:
|
||||||
with open(version_path) as fd:
|
with open(version_path) as fd:
|
||||||
ret = fd.read().replace('\n', '').replace('-dev', '')
|
ret = fd.read().replace('\n', '').replace('-dev', '')
|
||||||
fd.close()
|
|
||||||
return ret
|
return ret
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
log(DEBUG, f'Ignoring unreadable file "{version_path}"')
|
log(DEBUG, f'Ignoring unreadable file "{version_path}"')
|
||||||
continue
|
continue
|
||||||
raise Exception(f'No version file found for project "{project}"')
|
raise Exception(f'No version file found for project "{project}"')
|
||||||
ret = self.__get_project_conf(project).get_str_or_none(section, key)
|
proj_conf = self.__get_project_conf(project)
|
||||||
|
if proj_conf is None:
|
||||||
|
return None
|
||||||
|
ret = proj_conf.get_str_or_none(section, key)
|
||||||
log(
|
log(
|
||||||
DEBUG,
|
DEBUG,
|
||||||
'Lookup %s -> %s / [%s%s] -> "%s"' %
|
'Lookup %s -> %s / [%s%s] -> "%s"' %
|
||||||
|
|
@ -574,7 +584,10 @@ class App(Base):
|
||||||
for key in keys:
|
for key in keys:
|
||||||
vals = self.get_value(p, section, key)
|
vals = self.get_value(p, section, key)
|
||||||
if vals:
|
if vals:
|
||||||
ret += [val.strip() for val in vals.split(',')]
|
for val in vals.split(','):
|
||||||
|
stripped = val.strip()
|
||||||
|
if stripped:
|
||||||
|
ret.append(stripped)
|
||||||
return list(dict.fromkeys(ret)) # Remove duplicates, keep ordering
|
return list(dict.fromkeys(ret)) # Remove duplicates, keep ordering
|
||||||
|
|
||||||
def get_project_refs(
|
def get_project_refs(
|
||||||
|
|
@ -624,5 +637,5 @@ class App(Base):
|
||||||
return ', '.join(intersection)
|
return ', '.join(intersection)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def find_circular_deps(self, projects: list[str], flavours: list[str]) -> bool:
|
def find_circular_deps(self, projects: list[str], flavours: list[str]) -> list[str]:
|
||||||
return self.__find_circular_deps(projects, flavours)
|
return self.__find_circular_deps(projects, flavours)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from ....lib.log import NOTICE, log
|
from ....lib.log import NOTICE, log
|
||||||
from .Cmd import Cmd, Parent
|
from .Cmd import Cmd, Parent
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from argparse import ArgumentParser, Namespace
|
from argparse import ArgumentParser, Namespace
|
||||||
|
|
@ -22,8 +23,13 @@ class CmdDep(Cmd): # export
|
||||||
parser.add_argument('-f', '--flavour', nargs = '?', default = 'build')
|
parser.add_argument('-f', '--flavour', nargs = '?', default = 'build')
|
||||||
|
|
||||||
async def _run(self, args: Namespace) -> None:
|
async def _run(self, args: Namespace) -> None:
|
||||||
if self.app.find_circular_deps(args.module, args.flavour):
|
cycle = self.app.find_circular_deps(args.module, args.flavour)
|
||||||
log(NOTICE, f'Found circular dependency in flavour {args.flavour}')
|
if cycle:
|
||||||
|
log(
|
||||||
|
NOTICE,
|
||||||
|
f'Found circular dependency in flavour {args.flavour}: '
|
||||||
|
' -> '.join(cycle)
|
||||||
|
)
|
||||||
exit(1)
|
exit(1)
|
||||||
log(
|
log(
|
||||||
NOTICE,
|
NOTICE,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue