From a28a4ead9a2848a59230b1999bc3a1e7156a9aed Mon Sep 17 00:00:00 2001 From: Zapta Date: Thu, 4 Apr 2024 12:33:34 -0700 Subject: [PATCH 1/3] First step in deprecating the config command. Moved the exe mode attribute from "profile.json" to a new apio.ini attribute called exe_mode = default | native. Will document it in a later commit. --- apio/commands/config.py | 11 ---- apio/managers/project.py | 113 +++++++++++++++++++++++++-------------- apio/managers/scons.py | 11 ++-- apio/profile.py | 22 +++----- 4 files changed, 87 insertions(+), 70 deletions(-) diff --git a/apio/commands/config.py b/apio/commands/config.py index e26ea6cf..0a05a3b1 100644 --- a/apio/commands/config.py +++ b/apio/commands/config.py @@ -34,20 +34,12 @@ type=click.Choice(["0", "1"]), help="Verbose mode: `0` General, `1` Information.", ) -@click.option( - "-e", - f"--{EXE}", - type=click.Choice(["default", "native"]), - help="Configure executables: `default` selects apio packages, " - + "`native` selects system binaries.", -) def cli(ctx, **kwargs): """Apio configuration.""" # -- Extract the arguments _list = kwargs[LIST] verbose = kwargs[VERBOSE] - exe = kwargs[EXE] # -- Access to the profile file profile = Profile() @@ -60,9 +52,6 @@ def cli(ctx, **kwargs): elif verbose: profile.add_config("verbose", verbose) - # -- Configure executable mode - elif exe: - profile.add_config("exe", exe) # -- No paratemers: Show the help else: diff --git a/apio/managers/project.py b/apio/managers/project.py index 3d5e07f8..e9e285cf 100644 --- a/apio/managers/project.py +++ b/apio/managers/project.py @@ -7,13 +7,18 @@ # -- Licence GPLv2 """Utilities for accesing the apio.ini projects""" +# TODO(zapta): Deprecate the mutations of existing api.ini file. + +# TODO(zapta): Deprecate the copying of the sconstruct file. This is a developer only +# feature and developers can copy it manually as needed. + import sys from os.path import isfile from pathlib import Path # -- Config Parser: Use INI config files with easy # https://docs.python.org/3/library/configparser.html -import configparser +from configparser import ConfigParser import click from apio import util from apio.resources import Resources @@ -26,10 +31,10 @@ class Project: """Class for managing apio projects""" def __init__(self): + # TODO(zapta): Make these __private and provide getter methods. self.board = None - - # -- Top module by default: main - self.top_module = "main" + self.top_module = None + self.exe_mode = None def create_sconstruct(self, project_dir: Path, arch=None, sayyes=False): """Creates a default SConstruct file""" @@ -105,6 +110,7 @@ def create_ini(self, board, top_module, project_dir="", sayyes=False): # -- Create the apio.ini from scratch self._create_ini_file(board, top_module, ini_path, PROJECT_FILENAME) + # TODO: Deprecate prgramatic mutations of apio.ini def update_ini(self, top_module, project_dir): """Update the current init file with the given top-module""" @@ -123,7 +129,7 @@ def update_ini(self, top_module, project_dir): return # -- Read the current apio.ini file - config = configparser.ConfigParser() + config = ConfigParser() config.read(ini_path) # -- Set the new top-mddule @@ -143,11 +149,11 @@ def update_ini(self, top_module, project_dir): def _create_ini_file(board, top_module, ini_path, ini_name): click.secho(f"Creating {ini_name} file ...") with open(ini_path, "w", encoding="utf8") as file: - config = configparser.ConfigParser() + config = ConfigParser() config.add_section("env") - config.set("env", "board", board) - # -- Set the top module + # Set the required attributes. + config.set("env", "board", board) config.set("env", "top-module", top_module) # -- Write the apio ini file @@ -185,53 +191,78 @@ def read(self): print(f"Info: No {PROJECT_FILENAME} file") return - # -- Read stored board - board = self._read_board() + # Load the project file. + config_parser = ConfigParser() + config_parser.read(PROJECT_FILENAME) - # -- Read stored top-module - top_module = self._read_top_module() + for section in config_parser.sections(): + if section != "env": + print(f"Project file {PROJECT_FILENAME} has an invalid section named [{section}].") + sys.exit(1) - # -- Update board - self.board = board - if not board: - print("Error: invalid {PROJECT_FILENAME} project file") - print("No 'board' field defined in project file") + if "env" not in config_parser.sections(): + print(f"Project file {PROJECT_FILENAME} does not have an [env] section.") sys.exit(1) - # -- Update top-module - self.top_module = top_module + # Parse attributes in the env section. + parsed_attributes = set() + self.board = self._parse_board(config_parser, parsed_attributes) + self.top_module = self._parse_top_module(config_parser, parsed_attributes) + self.exe_mode = self._parse_exe_mode(config_parser, parsed_attributes) - # -- Warn the user the top module has not been set in the apio.ini - # -- file - if not top_module: - click.secho("Warning! No TOP-MODULE in apio.ini", fg="yellow") + # Verify that the project file (api.ini) doesn't contain additional (illegal) keys that + # where not parsed. + for attribute in config_parser.options("env"): + if attribute not in parsed_attributes: + print(f"Project file {PROJECT_FILENAME} contains an unknown attribute '{attribute}'.") + sys.exit(1) @staticmethod - def _read_board() -> str: - """Read the configured board from the project file + def _parse_board(config_parser: ConfigParser, parsed_attributes: set[str]) -> str: + """Parse the configured board from the project file parser and add the keys used + to parsed_attributes. RETURN: * A string with the name of the board """ - - config = configparser.ConfigParser() - config.read(PROJECT_FILENAME) - board = config.get("env", "board") - + parsed_attributes.add("board") + board = config_parser.get("env", "board") + if not board: + print(f"Error: invalid {PROJECT_FILENAME} project file") + print("No 'board' field defined in [env] section") + sys.exit(1) return board @staticmethod - def _read_top_module() -> str: - """Read the configured top-module from the project file + def _parse_top_module(config_parser: ConfigParser, parsed_attributes: set[str]) -> str: + """Read the configured top-module from the project file parser and add the keys used + to parsed_attributes. RETURN: * A string with the name of the top-module """ - - config = configparser.ConfigParser() - config.read(PROJECT_FILENAME) - - try: - top_module = config.get("env", "top-module") - except configparser.NoOptionError: - top_module = None - + parsed_attributes.add("top-module") + top_module = config_parser.get("env", "top-module") + if not top_module: + click.secho(f"Warning! invalid {PROJECT_FILENAME} project file", fg="yellow") + click.secho(f"No 'top-module' in [env] section. Assuming 'main'.") + return 'main' return top_module + + + @staticmethod + def _parse_exe_mode(config_parser: ConfigParser, parsed_attributes: set[str]) -> str: + """Read the configured exe mode from the project file parser and add the keys used + to parsed_attributes. + RETURN: + * A string with "default" (default) or "native" + """ + # print(f"*** project.py: reading exe mode") + parsed_attributes.add("exe_mode") + exe_mode = config_parser.get("env", "exe_mode", fallback="default") + # print(f"*** {exe_mode =}") + if exe_mode not in {"default", "native"}: + print(f"Error: invalid {PROJECT_FILENAME} project file") + print("Optional attribute 'exe_mode' should have the value 'default' or 'native'.") + sys.exit(1) + return exe_mode + + diff --git a/apio/managers/scons.py b/apio/managers/scons.py index 1919c413..4005d35d 100644 --- a/apio/managers/scons.py +++ b/apio/managers/scons.py @@ -26,6 +26,7 @@ from apio.managers.system import System from apio.profile import Profile from apio.resources import Resources +from apio.managers.project import Project # -- Constant for the dictionary PROG, which contains # -- the programming configuration @@ -48,6 +49,11 @@ def __init__(self, project_dir: Path): If not given, the curent working dir is used """ + # -- Read the project file (apio.ini) + self.proj = Project() + # print(f"Reading proj file....") + self.proj.read() + # -- Read the apio profile file self.profile = Profile() @@ -884,9 +890,8 @@ def run(self, command, variables, packages, board=None, arch=None): click.secho("Info: use custom SConstruct file") # -- Check the configuration mode - if self.profile.check_exe_default(): + if self.proj.exe_mode == "default": # Run on `default` config mode - # -- Check if the necessary packages are installed if not util.resolve_packages( packages, @@ -896,7 +901,7 @@ def run(self, command, variables, packages, board=None, arch=None): # Exit if a package is not installed raise AttributeError("Package not installed") else: - click.secho("Info: native config mode") + click.secho("Info: native exe mode") # -- Execute scons return self._execute_scons(command, variables, board) diff --git a/apio/profile.py b/apio/profile.py index 70200a77..f838d6fd 100644 --- a/apio/profile.py +++ b/apio/profile.py @@ -21,9 +21,9 @@ def __init__(self): # ---- Set the default parameters # -- Apio default config mode - self.config = {"exe": "default", "verbose": 0} + self.config = {"verbose": 0} - self.labels = {"exe": "Executable", "verbose": "Verbose"} + self.labels = {"verbose": "Verbose"} # Apio settings self.settings = {} @@ -73,13 +73,6 @@ def installed_version(self, name: str, version: str): # -- Package not installed return False - def check_exe_default(self) -> bool: - """Check if the exe mode is 'default'""" - - is_exe_default = self.config["exe"] == "default" - - return is_exe_default - def add_package(self, name: str, version: str): """Add a package to the profile class""" @@ -164,8 +157,6 @@ def _load_profile(self, profile: Path): if "config" in data.keys(): self.config = data["config"] - if "exe" not in self.config.keys(): - self.config["exe"] = "default" if "verbose" not in self.config.keys(): self.config["verbose"] = 0 @@ -204,8 +195,9 @@ def list(self): for key in self.config: # -- Print the parameter - click.secho( - f"{self.labels[key]} mode: {self.config[key]}", - fg="yellow", - ) + if key in self.labels: + click.secho( + f"{self.labels[key]} mode: {self.config[key]}", + fg="yellow", + ) print() From a59051d199f03563fae3bf36a9636643fc4ff474 Mon Sep 17 00:00:00 2001 From: Zapta Date: Thu, 4 Apr 2024 12:37:53 -0700 Subject: [PATCH 2/3] Removed a left over print statement. --- apio/managers/scons.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apio/managers/scons.py b/apio/managers/scons.py index 4005d35d..86fe6452 100644 --- a/apio/managers/scons.py +++ b/apio/managers/scons.py @@ -51,7 +51,6 @@ def __init__(self, project_dir: Path): # -- Read the project file (apio.ini) self.proj = Project() - # print(f"Reading proj file....") self.proj.read() # -- Read the apio profile file From 8ea213f99f2b185aa30dd0090f3058cea56e4f72 Mon Sep 17 00:00:00 2001 From: Zapta Date: Thu, 4 Apr 2024 12:46:18 -0700 Subject: [PATCH 3/3] Renamed the attribute 'exe_mode' to 'exe-mode' for consistency with 'top-module'. --- apio/managers/project.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apio/managers/project.py b/apio/managers/project.py index e9e285cf..14548090 100644 --- a/apio/managers/project.py +++ b/apio/managers/project.py @@ -256,12 +256,11 @@ def _parse_exe_mode(config_parser: ConfigParser, parsed_attributes: set[str]) -> * A string with "default" (default) or "native" """ # print(f"*** project.py: reading exe mode") - parsed_attributes.add("exe_mode") - exe_mode = config_parser.get("env", "exe_mode", fallback="default") - # print(f"*** {exe_mode =}") + parsed_attributes.add("exe-mode") + exe_mode = config_parser.get("env", "exe-mode", fallback="default") if exe_mode not in {"default", "native"}: print(f"Error: invalid {PROJECT_FILENAME} project file") - print("Optional attribute 'exe_mode' should have the value 'default' or 'native'.") + print("Optional attribute 'exe-mode' should have the value 'default' or 'native'.") sys.exit(1) return exe_mode