App: Use ProjectConf

Refactor App to use the new ProjectConf module for parsing make/project.conf files.

This commit

- removes the inline ad-hoc read_value() method and its nested helper functions, replacing them with ProjectConf's get_str_or_none() API.
- introduces two cached helper methods (__read_project_conf() and __get_project_conf()) to read and cache ProjectConf instances per project.
- updates get_value() to delegate to ProjectConf and moves the proj_dir lookup to only run in the version branch where it is needed.
- updates the topdir init to use __read_project_conf() with a FileNotFoundError catch for optional config files.
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-06-09 13:17:14 +02:00
commit 9c8a09d696
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -16,6 +16,7 @@ from typing import TYPE_CHECKING
from .lib.App import App as Base from .lib.App import App as Base
from .lib.Distro import Distro from .lib.Distro import Distro
from .lib.log import DEBUG, ERR, log from .lib.log import DEBUG, ERR, log
from .lib.ProjectConf import ProjectConf
if TYPE_CHECKING: if TYPE_CHECKING:
import argparse import argparse
@ -180,6 +181,17 @@ class App(Base):
return ret return ret
return None return None
@cache
def __read_project_conf(self, project_dir: str) -> ProjectConf:
return ProjectConf.read(project_dir + '/make/project.conf')
@cache
def __get_project_conf(self, project: str) -> ProjectConf:
pd = self.__proj_dir(project, False)
if pd is None:
raise Exception(f'Failed to find directory of project {project}')
return self.__read_project_conf(pd)
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
): ):
@ -405,9 +417,11 @@ class App(Base):
self.___pretty_topdir = self.__format_topdir(self.___topdir, args.topdir_format) self.___pretty_topdir = self.__format_topdir(self.___topdir, args.topdir_format)
self.__topdir_fmt = args.topdir_format self.__topdir_fmt = args.topdir_format
if self.___topdir is not None: if self.___topdir is not None:
self.__top_name = self.read_value( try:
self.___topdir + '/make/project.conf', 'build', 'name' conf = self.__read_project_conf(self.__topdir)
) self.__top_name = conf.get_str_or_none('build', 'name')
except FileNotFoundError:
pass
if not self.__top_name: if not self.__top_name:
self.__top_name = re.sub( self.__top_name = re.sub(
'-[0-9.-]*$', '-[0-9.-]*$',
@ -529,66 +543,13 @@ class App(Base):
file.close() file.close()
return ret.rstrip() return ret.rstrip()
@cache
def read_value(self, path: str, section: str, key: str) -> str | None:
def scan_section(f, key: str) -> str | None:
if key is None:
ret = ''
for line in f:
if len(line) and line[0] == '[':
break
ret += line
return ret if len(ret) else None
lines: list[str] = []
cont_line = ''
for line in f:
if len(line) and line[0] == '[':
break
cont_line += line.rstrip()
if len(cont_line) and cont_line[-1] == '\\':
cont_line = cont_line[0:-1]
continue
lines.append(cont_line)
cont_line = ''
rx = re.compile(r'^\s*' + key + r'\s*=\s*(.*)\s*$')
for line in lines:
# log(DEBUG, ' looking for "%s" in line="%s"' % (key, line))
m = re.search(rx, line)
if m is not None:
return m.group(1)
return None
def scan_section_debug(f, key: str) -> str | None:
ret = scan_section(f, key)
# log(DEBUG, ' returning', rr)
return ret
try:
# log(DEBUG, 'looking for {}::[{}].{}'.format(path, section, key))
# TODO: Parse
with open(path, 'r') as f:
if not len(section):
return scan_section(f, key)
pat = '[' + section + ']'
for line in f:
if line.rstrip() == pat:
return scan_section(f, key)
return None
except Exception:
log(DEBUG, f'Not found: {path}')
# TODO: handle this special case cleaner somewhere up the stack
if section == 'build' and key == 'libname':
return 'none'
return None
@cache @cache
def get_value(self, project: str, section: str, key: str) -> str | None: def get_value(self, project: str, section: str, key: str) -> str | None:
ret: str | None ret: str | None
proj_dir = self.__proj_dir(project, pretty = False)
if proj_dir is None:
raise Exception(f"Can't get project directory for {project}")
if section == 'version': if section == 'version':
proj_dir = self.__proj_dir(project, pretty = False)
if proj_dir is None:
raise Exception(f"Can't get project directory for {project}")
proj_version_dirs = [proj_dir] proj_version_dirs = [proj_dir]
if proj_dir != self.___topdir: if proj_dir != self.___topdir:
proj_version_dirs.append('/usr/share/doc/packages/' + project) proj_version_dirs.append('/usr/share/doc/packages/' + project)
@ -603,8 +564,7 @@ class App(Base):
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}"')
path = proj_dir + '/make/project.conf' ret = self.__get_project_conf(project).get_str_or_none(section, key)
ret = self.read_value(path, section, key)
log( log(
DEBUG, DEBUG,
'Lookup %s -> %s / [%s%s] -> "%s"' % 'Lookup %s -> %s / [%s%s] -> "%s"' %