Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: unit-tests

on:
pull_request:
push:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install coverage pytest
python -m pip install .

- name: Run rests
run: |
coverage run -m pytest -v -s

- name: Generate coverage report
run: |
coverage report -m
18 changes: 13 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-json # checks that all json files have proper syntax
- id: check-toml # checks that all toml files have proper syntax
- id: debug-statements # check for debug statements
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
exclude: '^(src/aedifix/main.py|src/aedifix/templates/.*)$' # intentional
- id: end-of-file-fixer # check all files end in a newline
- id: end-of-file-fixer
- id: pretty-format-json
args: ['--autofix', '--indent=4']
- id: trailing-whitespace # remove trailing whitespace
- id: trailing-whitespace
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
Expand Down Expand Up @@ -41,6 +42,13 @@ repos:
pass_filenames: false
args: ['src', 'tests']
additional_dependencies: [pytest, rich]
- repo: https://github.com/rhysd/actionlint
rev: v1.7.7
hooks:
- id: actionlint
types: [yaml]
files: ^\.github/workflows/
entry: env SHELLCHECK_OPTS="--enable=all --severity=style" actionlint

ci:
autoupdate_schedule: quarterly
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ classifiers = [
]
dependencies = [
"rich",
"typing_extensions",
]
dynamic = ["version"]

Expand Down
30 changes: 26 additions & 4 deletions src/aedifix/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from .cmake.cmaker import CMaker
from .config import ConfigFile
from .logger import Logger
from .package import packages
from .reconfigure import Reconfigure
from .util.argument_parser import ConfigArgument
from .util.callables import classify_callable, get_calling_function
Expand Down Expand Up @@ -495,6 +494,29 @@ def gen_summary() -> Iterator[str]:
align="left",
)

def _collect_deps(self) -> set[type[Package]]:
Comment thread
Jacobfaib marked this conversation as resolved.
"""Collect all transitive dependencies of the main package.

Returns
-------
dependencies: set[type[Package]]
The collected dependencies.

"""
# perform an iterative Depth-First Search to collect all dependencies
# that are reachable starting from the main package dependencies.
# ref: https://en.wikipedia.org/wiki/Depth-first_search#Pseudocode

seen: set[type[Package]] = set()
stack: list[type[Package]] = list(self._main_package.dependencies)

while stack:
if (dep := stack.pop()) not in seen:
seen.add(dep)
stack.extend(x for x in dep.dependencies if x not in seen)
Comment thread
Jacobfaib marked this conversation as resolved.

return seen

# Member variable access
@property
def argv(self) -> tuple[str, ...]:
Expand Down Expand Up @@ -954,9 +976,9 @@ def setup(self) -> None:
"""
self._setup_log()
self.log_execute_func(self._log_git_info)
self._modules.extend(
self.log_execute_func(packages.load_packages, self)
)

packages = self.log_execute_func(self._collect_deps)
self._modules.extend(cls(self) for cls in packages)

# Sort the modules alphabetically for the parsing of arguments, but
# keep the main package on top.
Expand Down
15 changes: 5 additions & 10 deletions src/aedifix/package/main_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ class DebugConfigureValue(IntEnum):
TRACE = 2
TRACE_EXPAND = 3

# Enum formatatting changed in 3.11, this is only needed for 3.10
def __str__(self) -> str:
return str(int(self.value))

@classmethod
def from_string(cls, str_val: str) -> DebugConfigureValue:
return cls(int(str_val))
Expand Down Expand Up @@ -364,24 +368,20 @@ class MainPackage(Package, ABC):
def __init__( # noqa: PLR0913
self,
manager: ConfigurationManager,
name: str,
argv: Sequence[str],
arch_name: str,
project_dir_name: str,
project_dir_value: Path,
project_config_file_template: Path,
project_src_dir: Path | None = None,
default_arch_file_path: Path | None = None,
dependencies: tuple[type[Package], ...] = (),
) -> None:
r"""Construct the MainPackage.

Parameters
----------
manager : ConfigurationManager
The configuration manager that will manage the main package.
name : str
The name of the main package, e.g. 'Legate'.
argv : Sequence[str]
The command line options.
arch_name : str
Expand Down Expand Up @@ -409,12 +409,7 @@ def __init__( # noqa: PLR0913
If the project arch value is set (either from command line or
environment variable) but empty.
"""
super().__init__(
manager=manager,
name=name,
always_enabled=True,
dependencies=dependencies,
)
super().__init__(manager=manager, always_enabled=True)
assert not arch_name.startswith("-")
assert not arch_name.endswith("_")
assert arch_name.isupper()
Expand Down
29 changes: 8 additions & 21 deletions src/aedifix/package/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def __getattr__(self, value: str) -> Package:


class Package(Configurable):
#: A name for the package, e.g. 'CUDA'.
name: str

# Other package (types) that this package depends on
dependencies: tuple[type[Package], ...] = ()

__slots__ = "_always_enabled", "_dep_types", "_deps", "_name", "_state"

@dataclass(slots=True, frozen=True)
Expand All @@ -56,21 +62,14 @@ def implicitly_disabled(self) -> bool:
return self.disabled() and (not self.explicit)

def __init__(
self,
manager: ConfigurationManager,
name: str,
*,
always_enabled: bool = False,
dependencies: tuple[type[Package], ...] = (),
self, manager: ConfigurationManager, *, always_enabled: bool = False
) -> None:
r"""Construct a Package.

Parameters
----------
manager : ConfigurationManager
The configuration manager to manage this package.
name : str
The name of the package, e.g. 'CUDA'.
always_enabled : bool, False
Whether this package should be considered unconditionally enabled.
"""
Expand All @@ -81,23 +80,11 @@ def __init__(
if isinstance(self, MainPackage):
always_enabled = True

self._name = name
self._state = Package.EnableState(value=always_enabled)
self._always_enabled = always_enabled
self._dep_types = dependencies
self._dep_types = self.dependencies
self._deps: Dependencies | None = None

@property
def name(self) -> str:
r"""Get the name of the package.

Returns
-------
name : str
The name of the package, e.g. 'Legion'.
"""
return self._name

@property
def state(self) -> EnableState:
r"""Get whether the package is enabled or disabled.
Expand Down
42 changes: 0 additions & 42 deletions src/aedifix/package/packages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,3 @@
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

import importlib
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from ...manager import ConfigurationManager
from ..package import Package


def load_packages(manager: ConfigurationManager) -> list[Package]:
r"""Load all package modules in the packages directory, and return the
constructed packages.

Parameters
----------
manager : ConfigurationManager
The configuration manager with which to construct the packages.

Returns
-------
packages : list[Package]
The list of loaded packages.
"""
assert __package__, "Cannot auto-load packages without relative imports!"
packages = []
cur_dir = Path(__file__).parent
manager.log(f"Using package directory: {cur_dir}")
for module_file in cur_dir.iterdir():
manager.log(f"Attempting to load package: {module_file}")
if module_file.is_dir() or module_file.name.startswith("_"):
# skip __init__.py, __pycache__ and any other directories
manager.log(
f"Skipping loading package: {module_file} is not a package!"
)
continue
module = importlib.import_module(f".{module_file.stem}", __package__)
manager.log(f"Loaded package: {module}")
conf_obj = module.create_package(manager)
manager.log(f"Adding package: {conf_obj}")
packages.append(conf_obj)
return packages
55 changes: 0 additions & 55 deletions src/aedifix/package/packages/cal.py

This file was deleted.

4 changes: 3 additions & 1 deletion src/aedifix/package/packages/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def _determine_default_generator() -> str | None:


class CMake(Package):
name = "CMake"

CMAKE_COMMAND: Final = ConfigArgument(
name="--cmake-executable",
spec=ArgSpec(
Expand Down Expand Up @@ -67,7 +69,7 @@ def __init__(self, manager: ConfigurationManager) -> None:
manager : ConfigurationManager
The configuration manager to manage this package.
"""
super().__init__(manager=manager, name="CMake", always_enabled=True)
super().__init__(manager=manager, always_enabled=True)

def configure_cmake_version(self) -> None:
r"""Determine the version of the cmake executable.
Expand Down
Loading
Loading