Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modularize build system #1143

Merged
merged 114 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
68920fd
Move file versioning.py into subdirectory "versioning".
michael-rapp Nov 19, 2024
2c65e21
Add functions "parse" and "parse_version_number" to class Version.
michael-rapp Nov 20, 2024
8ab8140
Add utility functions "read_file" and "write_file".
michael-rapp Nov 19, 2024
524c834
Move file changelog.py into subdirectory "versioning".
michael-rapp Nov 19, 2024
53c2631
Add utility function "import_source_file".
michael-rapp Nov 19, 2024
3a486cf
Add class DirectorySearch.
michael-rapp Nov 19, 2024
58d27d5
Add class PhonyTarget.
michael-rapp Nov 19, 2024
36b7115
Dynamically register targets for updating the project's version and c…
michael-rapp Nov 19, 2024
916ad9e
Move file command_line.py into subdirectory "util".
michael-rapp Nov 19, 2024
5517ef0
Move file environment.py into subdirectory "util".
michael-rapp Nov 19, 2024
a98d802
Add utility function "format_iterable".
michael-rapp Nov 20, 2024
16f5760
Add utility function "in_virtual_environment".
michael-rapp Nov 20, 2024
68eb2db
Add class Command.
michael-rapp Nov 20, 2024
6b4dfb3
Add class Pip.
michael-rapp Nov 20, 2024
92fc0b1
Add classes Program and PythonModule.
michael-rapp Nov 20, 2024
aba685c
Add class BuildUnit.
michael-rapp Nov 21, 2024
2fc1866
Remove function "install_build_dependencies".
michael-rapp Nov 21, 2024
84db22d
Move class BuildOptions into a separate file.
michael-rapp Nov 21, 2024
8c55dad
Move classes MesonSetup, MesonConfigure, MesonCompile and MesonInstal…
michael-rapp Nov 21, 2024
58b412e
Split file compilation.py into files cpp.py and cython.py.
michael-rapp Nov 21, 2024
c9a132e
Move file build_options.py into subdirectory "compilation".
michael-rapp Nov 21, 2024
da355ee
Move file meson.py into subdirectory "compilation".
michael-rapp Nov 21, 2024
754a69c
Add enum Language.
michael-rapp Nov 22, 2024
4cec8f8
Add classes Module and ModuleRegistry.
michael-rapp Nov 21, 2024
a82b03e
Add class FileSearch.
michael-rapp Nov 23, 2024
ba5d478
Add class CodeModule.
michael-rapp Nov 21, 2024
4eadc0d
Add class CompilationModule.
michael-rapp Nov 21, 2024
6bc5292
Add class PhonyTarget.Builder.
michael-rapp Nov 22, 2024
7f16083
Add class TargetBuilder.
michael-rapp Nov 22, 2024
82c93a5
Split file code_style.py into files code_style_cpp.py, code_style_pyt…
michael-rapp Nov 23, 2024
85f7607
Rename file modules.py to modules_old.py.
michael-rapp Nov 23, 2024
8aea45d
Dynamically register modules.
michael-rapp Nov 23, 2024
10d8bc2
Move classes ClangFormat and CppLint into separate files.
michael-rapp Nov 23, 2024
677104e
Move class MdFormat into a separate file.
michael-rapp Nov 23, 2024
8feb7bd
Move class YamlFix into a separate file.
michael-rapp Nov 23, 2024
841d9c2
Move classes Yapf, ISort and PyLint into separate files.
michael-rapp Nov 23, 2024
8cccf14
Allow to set multiple functions or runnables via a PhonyTarget.Builder.
michael-rapp Nov 24, 2024
24daa60
Allow targets to depend on others.
michael-rapp Nov 25, 2024
6e52e59
Dynamically register targets and modules for checking and enforcing t…
michael-rapp Nov 23, 2024
c7666ad
Rename file dependencies.py to python_dependencies.py.
michael-rapp Nov 26, 2024
ee7c4c5
Add class Table.
michael-rapp Nov 26, 2024
7f5cade
Add class TextFile.
michael-rapp Nov 26, 2024
87d5a81
Add class YamlFile.
michael-rapp Nov 26, 2024
df7362b
Add class GithubApi.
michael-rapp Nov 26, 2024
4b7ef4f
Dynamically register targets for checking and updating GitHub Actions.
michael-rapp Nov 26, 2024
dfe0362
Remove function "by_name" from class BuildUnit.
michael-rapp Nov 27, 2024
8036d09
Add function "for_build_unit" to class Pip.
michael-rapp Nov 27, 2024
026f3ae
Add function "install_all_packages" to class Pip.
michael-rapp Nov 27, 2024
c1fb353
Add function "filter_by_name" to class FileSearch.
michael-rapp Nov 27, 2024
88f88cf
Add class PythonDependencyModule.
michael-rapp Nov 27, 2024
e237e7d
Dynamically register targets and modules for installing Python runtim…
michael-rapp Nov 27, 2024
b75dc1d
Move __init__.py file.
michael-rapp Nov 27, 2024
5c4bde0
Add function "list_outdated_dependencies" to class Pip.
michael-rapp Nov 28, 2024
4227924
Dynamically register target for checking dependency versions.
michael-rapp Nov 30, 2024
372434d
Add class BuildTarget.
michael-rapp Nov 30, 2024
bdabfd9
Allow to add custom filters to a FileSearch.
michael-rapp Nov 30, 2024
ee2ce06
Add classes Requirements and RequirementsFiles.
michael-rapp Dec 2, 2024
2e41016
Add classes VersionFile and DevelopmentVersionFile.
michael-rapp Dec 2, 2024
267d9ab
Add classes ChangesetFile and ChangelogFile.
michael-rapp Dec 2, 2024
f53108a
Replace property "requirements_file" of class BuildUnit with function…
michael-rapp Dec 2, 2024
6def0e6
Add property "target_names" to class TargetRegistry.
michael-rapp Dec 2, 2024
d5b86d4
Dynamically register targets for cleaning up output files.
michael-rapp Dec 2, 2024
6190c7d
Remove log statement from class Pip.
michael-rapp Dec 2, 2024
769f126
Allow to exclude subdirectories by substrings when using the classes …
michael-rapp Dec 2, 2024
b1a2647
Dynamically register targets and modules for compiling code.
michael-rapp Dec 2, 2024
913b9dc
Dynamically register targets and modules for testing C++ code.
michael-rapp Dec 2, 2024
2464f34
Allow to add filters to a DirectorySearch.
michael-rapp Dec 3, 2024
31f861b
Dynamically register targets and modules for testing Python code.
michael-rapp Dec 3, 2024
a72cc16
Move module definitions into appropriate subpackages.
michael-rapp Dec 3, 2024
9f7daa4
Add class PipList.
michael-rapp Dec 4, 2024
e15ff24
Add class Project.
michael-rapp Dec 4, 2024
1c2a18e
Allow specifying the input files required by a BuildTarget.
michael-rapp Dec 5, 2024
02ceef4
Add function "set_symlinks" to class FileSearch.
michael-rapp Dec 5, 2024
9da4a2a
Dynamically register targets and modules for building and installing …
michael-rapp Dec 5, 2024
5ded431
Replace enum Language with class FileType.
michael-rapp Dec 5, 2024
f89be9d
Add class Log.
michael-rapp Dec 6, 2024
17ebb44
Override "__str__" function in subclasses of the class Module.
michael-rapp Dec 6, 2024
57c84cf
Override "__str__" function in subclasses of the class Target.
michael-rapp Dec 6, 2024
c019318
Add utility function "delete_files".
michael-rapp Dec 8, 2024
5bada72
Replace scons with custom implementation.
michael-rapp Dec 9, 2024
5c54d11
Update file paths mentioned in the documentation.
michael-rapp Dec 10, 2024
8a0db5e
Fix name of environment variable mentioned in the documentation.
michael-rapp Dec 10, 2024
cf4e325
Pass individual modules to functions of the class PhonyTarget.Runnabl…
michael-rapp Dec 11, 2024
02446e9
Move files into new package "core".
michael-rapp Dec 11, 2024
b88e609
Remove file venv.py.
michael-rapp Dec 11, 2024
7be5ce4
Remove file reflection.py.
michael-rapp Dec 11, 2024
046cd43
Move files into new package "targets".
michael-rapp Dec 11, 2024
a8e5af1
Only run build targets if output files are missing or input files hav…
michael-rapp Dec 11, 2024
cfaab75
Add function "for_file" to class BuildUnit.
michael-rapp Dec 11, 2024
e43f796
Remove obsolete functions from class Target.Builder.
michael-rapp Dec 11, 2024
7340add
Add type hints to function arguments.
michael-rapp Dec 11, 2024
7b17117
Add utility function "create_directories".
michael-rapp Dec 11, 2024
89db992
Dynamically register targets and modules for generating C++ API docum…
michael-rapp Dec 11, 2024
d8177be
Add constructor argument "suffixes" to class FileType.
michael-rapp Dec 12, 2024
809317b
Dynamically register targets and modules for generating Python API do…
michael-rapp Dec 12, 2024
e1c52ff
Allow to exclude files by name when using the class FileSearch.
michael-rapp Dec 12, 2024
6e80455
Catch exceptions.
michael-rapp Dec 12, 2024
4495ad6
Fix filtering files by name when using a FileSearch.
michael-rapp Dec 12, 2024
9cb6fdd
Add function "run_all" to class BuildTarget.Runnable.
michael-rapp Dec 12, 2024
7b465e9
Dynamically register targets for generating index files referencing A…
michael-rapp Dec 12, 2024
c720b9e
Dynamically register targets and modules for generating Sphinx docume…
michael-rapp Dec 12, 2024
42f12f4
Define default target.
michael-rapp Dec 13, 2024
a309d56
Edit comments.
michael-rapp Dec 13, 2024
b06b079
Fix pylint errors.
michael-rapp Dec 13, 2024
3f4c0ed
Format template files.
michael-rapp Dec 13, 2024
6204aad
Make filters for running workflow steps more fine-grained.
michael-rapp Dec 13, 2024
a986164
Update changelog.
michael-rapp Dec 13, 2024
291b16b
Automatically create build directory if it does not exist.
michael-rapp Dec 13, 2024
268120c
Fix search for test directories.
michael-rapp Dec 13, 2024
c07ece2
Apply "mdformat" to files with suffix ".md.template".
michael-rapp Dec 14, 2024
56b3f17
Fix installation of external programs on Windows.
michael-rapp Dec 14, 2024
fe37092
Remove build directories when cleaning target "compile".
michael-rapp Dec 14, 2024
65f8dba
Specify build options when setting up meson for compiling Cython code.
michael-rapp Dec 14, 2024
dbe5442
Clean all targets if the default target should be cleaned.
michael-rapp Dec 14, 2024
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
1 change: 1 addition & 0 deletions .changelog-bugfix.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Quality-of-Life Improvements

- The build system now uses a lightweight custom implementation instead of [SCons](https://scons.org/) and is better modularized to avoid unnecessary runs of Continuous Integration jobs when only certain parts of it are modified.
- Releases are now automated via Continuous Integration, including the update of the project's changelog.
- The presentation of algorithmic parameters in the documentation has been improved.
- Outdated GitHub Actions can now be printed via the build target `check_github_actions`. Alternatively, the build target `update_github_actions` may be used to update them automatically.
11 changes: 10 additions & 1 deletion .github/workflows/test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ jobs:
- '.github/workflows/test_build.yml'
- 'build'
- 'build.bat'
- 'scons/**'
- 'build_system/main.py'
- 'build_system/core/**'
- 'build_system/util/**'
- 'build_system/targets/paths.py'
- 'build_system/targets/compilation/*'
- 'build_system/targets/testing/*'
cpp: &cpp
- *build_files
- 'build_system/targets/compilation/cpp/*'
- 'build_system/targets/testing/cpp/*'
- 'cpp/**/include/**'
- 'cpp/**/src/**'
- '**/*.pxd'
Expand All @@ -48,6 +55,8 @@ jobs:
- 'cpp/**/test/**'
python: &python
- *build_files
- 'build_system/targets/compilation/cython/*'
- 'build_system/targets/testing/python/*'
- 'python/requirements.txt'
- 'python/**/mlrl/**'
python_tests: &python_tests
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/test_changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ jobs:
- '.github/workflows/test_changelog.yml'
- 'build'
- 'build.bat'
- 'scons/**'
- 'build_system/main.py'
- 'build_system/core/**'
- 'build_system/util/**'
- 'build_system/targets/paths.py'
- 'build_system/targets/versioning/*'
bugfix:
- *build_files
- '.changelog-bugfix.md'
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/test_doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ jobs:
- '.github/workflows/test_doc.yml'
- 'build'
- 'build.bat'
- 'scons/**'
- 'build_system/main.py'
- 'build_system/core/**'
- 'build_system/util/**'
- 'build_system/targets/paths.py'
cpp: &cpp
- *build_files
- 'build_system/targets/documentation/cpp/*'
- 'cpp/**/include/**'
python: &python
- *build_files
- 'build_system/targets/documentation/python/*'
- 'python/**/mlrl/**'
doc: &doc
- *build_files
- 'build_system/targets/documentation/*'
- 'doc/**'
any:
- *cpp
Expand Down
18 changes: 13 additions & 5 deletions .github/workflows/test_format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,31 @@ jobs:
- '.github/workflows/test_format.yml'
- 'build'
- 'build.bat'
- 'scons/**'
- 'build_system/main.py'
- 'build_system/core/**'
- 'build_system/util/**'
- 'build_system/targets/paths.py'
cpp:
- *build_files
- 'build_system/targets/code_style/*'
- 'build_system/targets/code_style/cpp/*'
- '.cpplint.cfg'
- '**/*.hpp'
- '**/*.cpp'
- '.clang-format'
python:
- *build_files
- 'build_system/targets/code_style/*'
- 'build_system/targets/code_style/python/*'
- '**/*.py'
- '.isort.cfg'
- '.pylintrc'
- '.style.yapf'
md:
- *build_files
- 'build_system/targets/code_style/*'
- 'build_system/targets/code_style/markdown/*'
- '**/*.md'
yaml:
- *build_files
- 'build_system/targets/code_style/*'
- 'build_system/targets/code_style/yaml/*'
- '**/*.y*ml'
- name: Check C++ code style
if: steps.filter.outputs.cpp == 'true'
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/test_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ jobs:
- '.github/workflows/template_publish_pure.yml'
- 'build'
- 'build.bat'
- 'scons/**'
- 'build_system/main.py'
- 'build_system/core/**'
- 'build_system/util/**'
- 'build_system/targets/paths.py'
- 'build_system/targets/packaging/*'
- name: Read Python version
uses: juliangruber/read-file-action@v1
id: python_version
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build files
__pycache__/
scons/build/
build_system/build/
python/**/build/
python/**/dist/
python/**/*egg-info/
Expand Down
7 changes: 3 additions & 4 deletions build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh

VENV_DIR="venv"
SCONS_DIR="scons"
BUILD_SYSTEM_DIR="build_system"
CLEAN=false

set -e
Expand All @@ -22,13 +22,12 @@ fi

if [ -d "$VENV_DIR" ]; then
. $VENV_DIR/bin/activate
python3 -c "import sys; sys.path.append('$SCONS_DIR'); import dependencies; dependencies.install_build_dependencies('scons')"
scons --silent --file $SCONS_DIR/sconstruct.py $@
python3 $BUILD_SYSTEM_DIR/main.py $@
deactivate
fi

if [ $CLEAN = true ] && [ -d $VENV_DIR ]; then
echo "Removing virtual Python environment..."
rm -rf $VENV_DIR
rm -rf $SCONS_DIR/build
rm -rf $BUILD_SYSTEM_DIR/build
fi
9 changes: 4 additions & 5 deletions build.bat
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@echo off

set "VENV_DIR=venv"
set "SCONS_DIR=scons"
set "BUILD_SYSTEM_DIR=build_system"
set "CLEAN=false"

if not "%1"=="" if "%2"=="" (
Expand All @@ -20,16 +20,15 @@ if not exist "%VENV_DIR%" (

if exist "%VENV_DIR%" (
call %VENV_DIR%\Scripts\activate || exit
.\%VENV_DIR%\Scripts\python -c "import sys;sys.path.append('%SCONS_DIR%');import dependencies;dependencies.install_build_dependencies('scons')" || exit
.\%VENV_DIR%\Scripts\python -m SCons --silent --file %SCONS_DIR%\sconstruct.py %* || exit
.\%VENV_DIR%\Scripts\python %BUILD_SYSTEM_DIR%\main.py %* || exit
call deactivate || exit
)

if "%CLEAN%"=="true" if exist "%VENV_DIR%" (
echo Removing virtual Python environment...
rd /s /q "%VENV_DIR%" || exit

if exist "%SCONS_DIR%\build" (
rd /s /q "%SCONS_DIR%\build" || exit
if exist "%BUILD_SYSTEM_DIR%\build" (
rd /s /q "%BUILD_SYSTEM_DIR%\build" || exit
)
)
Empty file added build_system/core/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions build_system/core/build_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Author: Michael Rapp ([email protected])

Provides classes that provide information about independent units of the build system.
"""
from os import path
from typing import List


class BuildUnit:
"""
An independent unit of the build system that may come with its own built-time dependencies.
"""

BUILD_SYSTEM_DIRECTORY = 'build_system'

BUILD_DIRECTORY_NAME = 'build'

def __init__(self, root_directory: str = BUILD_SYSTEM_DIRECTORY):
"""
:param root_directory: The root directory of this unit
"""
self.root_directory = root_directory

@staticmethod
def for_file(file) -> 'BuildUnit':
"""
Creates and returns a `BuildUnit` for a given file.

:param file: The file for which a `BuildUnit` should be created
:return: The `BuildUnit` that has been created
"""
return BuildUnit(path.relpath(path.dirname(file), path.dirname(BuildUnit.BUILD_SYSTEM_DIRECTORY)))

@property
def build_directory(self) -> str:
"""
The path to the build directory of this unit.
"""
return path.join(self.root_directory, self.BUILD_DIRECTORY_NAME)

def find_requirements_files(self) -> List[str]:
"""
The path to the requirements file that specifies the build-time dependencies of this unit.
"""
requirements_files = []
current_directory = self.root_directory

while path.basename(current_directory) != self.BUILD_SYSTEM_DIRECTORY:
requirements_file = path.join(current_directory, 'requirements.txt')

if path.isfile(requirements_file):
requirements_files.append(requirements_file)

current_directory = path.dirname(current_directory)

return requirements_files
132 changes: 132 additions & 0 deletions build_system/core/changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
Author: Michael Rapp ([email protected])

Provides classes for detecting changes in files.
"""
import json

from functools import cached_property
from os import path
from typing import Dict, List, Set

from core.modules import Module
from util.io import TextFile, create_directories


class JsonFile(TextFile):
"""
Allows to read and write the content of a JSON file.
"""

@cached_property
def json(self) -> Dict:
"""
The content of the JSON file as a dictionary.
"""
lines = self.lines

if lines:
return json.loads('\n'.join(lines))

return {}

def write_json(self, dictionary: Dict):
"""
Writes a given dictionary to the JSON file.

:param dictionary: The dictionary to be written
"""
self.write_lines(json.dumps(dictionary, indent=4))

def write_lines(self, *lines: str):
super().write_lines(*lines)

try:
del self.json
except AttributeError:
pass


class ChangeDetection:
"""
Allows to detect changes in tracked files.
"""

class CacheFile(JsonFile):
"""
A JSON file that stores checksums for tracked files.
"""

@staticmethod
def __checksum(file: str) -> str:
return str(path.getmtime(file))

def __init__(self, file: str):
"""
:param file: The path to the JSON file
"""
super().__init__(file, accept_missing=True)
create_directories(path.dirname(file))

def update(self, module_name: str, files: Set[str]):
"""
Updates the checksums of given files.

:param module_name: The name of the module, the files belong to
:param files: A set that contains the paths of the files to be updated
"""
cache = self.json
module_cache = cache.setdefault(module_name, {})

for invalid_key in [file for file in module_cache.keys() if file not in files]:
del module_cache[invalid_key]

for file in files:
module_cache[file] = self.__checksum(file)

if module_cache:
cache[module_name] = module_cache
else:
del cache[module_name]

if cache:
self.write_json(cache)
else:
self.delete()

def has_changed(self, module_name: str, file: str) -> bool:
"""
Returns whether a file has changed according to the cache or not.

:param module_name: The name of the module, the file belongs to
:param file: The file to be checked
:return: True, if the file has changed, False otherwise
"""
module_cache = self.json.get(module_name, {})
return file not in module_cache or module_cache[file] != self.__checksum(file)

def __init__(self, cache_file: str):
"""
:param cache_file: The path to the file that should be used for tracking files
"""
self.cache_file = ChangeDetection.CacheFile(cache_file)

def track_files(self, module: Module, *files: str):
"""
Updates the cache to keep track of given files.

:param module: The module, the files belong to
:param files: The files to be tracked
"""
self.cache_file.update(str(module), set(files))

def get_changed_files(self, module: Module, *files: str) -> List[str]:
"""
Filters given files and returns only those that have changed.

:param module: The module, the files belong to
:param files: The files to be filtered
:return: A list that contains the files that have changed
"""
module_name = str(module)
return [file for file in files if self.cache_file.has_changed(module_name, file)]
Loading
Loading