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
3 changes: 2 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ Before you begin, please:
### Development Environment

- **Language**: C++20 with the Qt version defined in [`build-config.json`](build-config.json)
- **Build System**: CMake (minimum version defined in [`build-config.json`](build-config.json))
- **Build System**: CMake (minimum version defined in [`build-config.json`](build-config.json)); see
the [CMake development guide](../cmake/README.md) for modules, presets, and focused tests
- **Platforms**: Windows, macOS, Linux, Android, iOS
- **IDE**: Qt Creator (recommended), VS Code, or your preferred IDE
- **Build/test/lint commands**: see [tools/README.md](../tools/README.md) for the `just configure` /
Expand Down
17 changes: 9 additions & 8 deletions .github/ci-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ via composite actions and reusable workflows. Python helpers in `scripts/` are i
├── build-config.json # Centralized version numbers and build settings
├── build-config.schema.json # JSON Schema for build-config.json
├── dependabot.yml # Dependabot config (GitHub Actions only)
└── renovate.json # Renovate config (npm, python, pre-commit)
└── renovate.json # Renovate config (code, tooling, and dev-environment dependencies)
```

## Workflows
Expand Down Expand Up @@ -181,10 +181,11 @@ Python helpers in `.github/scripts/` invoked by workflows and composite actions.

Dependency updates are split between two bots to avoid overlapping PRs:

- **Dependabot** (`.github/dependabot.yml`) owns `github-actions` updates only, grouped weekly.
Merge with `@dependabot merge`.
- **Renovate** (`.github/renovate.json`) owns `npm`, `python` (pep621/uv), and `pre-commit`
updates, grouped into a single weekly PR. GitHub Actions paths are excluded via `ignorePaths`.
- **Dependabot** (`.github/dependabot.yml`) owns action references in `.github/workflows`, grouped
weekly. Merge with `@dependabot merge`.
- **Renovate** (`.github/renovate.json`) owns `npm`, Python (pep621/uv), pre-commit, devcontainer,
Dockerfile, Gradle Wrapper, and composite-action dependencies. Workflow paths are excluded so
the bots do not open overlapping action updates.

## CI Conventions

Expand All @@ -209,11 +210,11 @@ linting) and a pytest job covering both `tools/tests` and `.github/scripts/tests
Run the CI script tests locally:

```bash
pytest -q .github/scripts/tests/
uv run --project tools --extra scripts --extra test pytest -q .github/scripts/tests
```

Run the full set the same way CI does (also covers `tools/`):
Run the full set locally with the same locked dependency groups CI installs (also covers `tools/`):

```bash
pytest -q tools/tests .github/scripts/tests
uv run --project tools --extra scripts --extra test pytest -q tools/tests .github/scripts/tests
```
5 changes: 2 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Dependabot owns GitHub Actions only. npm, python (uv, /tools), and pre-commit are
# handled by Renovate (see .github/renovate.json). Merge bot PRs with: @dependabot merge.
# Docs: https://docs.github.com/en/code-security/dependabot
# Dependabot owns workflow actions. Renovate owns npm, Python, pre-commit, development
# environments, and composite actions (see .github/renovate.json).
version: 2
updates:
- package-ecosystem: "github-actions"
Expand Down
32 changes: 30 additions & 2 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"extends": [
"config:recommended",
":dependencyDashboard",
":enablePreCommit",
":semanticCommitTypeAll(deps)"
],
"labels": [
Expand All @@ -11,16 +12,27 @@
"ignorePaths": [
".github/workflows/**"
],
"description": "Active manager for npm, python (pep621/uv), and pre-commit. GitHub Actions are handled by Dependabot (see dependabot.yml); excluded here via ignorePaths.",
"description": "Manages npm, Python, pre-commit, development environments, Gradle Wrapper, Dockerfiles, and composite actions. Dependabot owns .github/workflows, which are excluded here.",
"enabledManagers": [
"devcontainer",
"dockerfile",
"github-actions",
"gradle-wrapper",
"npm",
"pep621",
"pre-commit"
],
"packageRules": [
{
"description": "Auto-merge patch updates after CI passes",
"matchUpdateTypes": ["patch"],
"matchManagers": [
"npm",
"pep621",
"pre-commit"
],
"matchUpdateTypes": [
"patch"
],
"automerge": true
},
{
Expand All @@ -34,6 +46,22 @@
"schedule": [
"before 6am on monday"
]
},
{
"description": "Group composite GitHub Action updates not covered by Dependabot",
"groupName": "composite action dependencies",
"matchManagers": [
"github-actions"
]
},
{
"description": "Group development environment and build bootstrap updates",
"groupName": "development environment dependencies",
"matchManagers": [
"devcontainer",
"dockerfile",
"gradle-wrapper"
]
}
],
"prConcurrentLimit": 5,
Expand Down
61 changes: 46 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ repos:

# Secret detection
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.95.7
rev: v3.97.1
hooks:
- id: trufflehog
args: ['--only-verified', '--exclude-paths=tools/configs/trufflehog-exclude.txt']

# C++ formatting (check-only, use 'clang-format -i' to fix)
# Note: Install matching clang-format version locally to avoid formatting differences
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.5
rev: v23.1.0
hooks:
- id: clang-format
args: ['--dry-run', '--Werror']
Expand All @@ -52,7 +52,7 @@ repos:

# Python formatting (report-only)
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.20
rev: v0.16.5
hooks:
- id: ruff
- id: ruff-format
Expand All @@ -77,25 +77,26 @@ repos:

# uv lockfile sync (keeps tools/uv.lock current with tools/pyproject.toml)
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.26
rev: 0.12.8
hooks:
- id: uv-lock
files: ^tools/(pyproject\.toml|uv\.lock)$
args: ['--project', 'tools']

# Shell scripts
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
rev: v0.11.0.1-1
hooks:
- id: shellcheck
args: ['-e', 'SC1091'] # Don't warn about not following sourced files

# Dockerfile linting
- repo: https://github.com/AleksaC/hadolint-py
rev: v2.14.0
rev: v2.15.1
hooks:
- id: hadolint
files: ^deploy/docker/Dockerfile-.*$
args: ['--failure-threshold', 'warning']
files: (^|/)Dockerfile([.-].*)?$
types: [file]

# GitHub Actions linting
Expand Down Expand Up @@ -123,23 +124,53 @@ repos:

# JSON Schema validation for build-config.json
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.4
rev: 0.38.0
hooks:
- id: check-jsonschema
name: Validate .github/build-config.json
files: ^\.github/build-config\.json$
args: ['--schemafile', '.github/build-config.schema.json']

- id: check-jsonschema
alias: validate-dependabot-config
name: Validate Dependabot config
files: ^\.github/dependabot\.ya?ml$
args: ['--builtin-schema', 'vendor.dependabot']

- id: check-jsonschema
alias: validate-codecov-config
name: Validate Codecov config
files: ^\.github/codecov\.ya?ml$
args: ['--builtin-schema', 'vendor.codecov']

- id: check-jsonschema
alias: validate-issue-template-config
name: Validate GitHub issue template config
files: ^\.github/ISSUE_TEMPLATE/config\.ya?ml$
args: ['--builtin-schema', 'vendor.github-issue-config']

- id: check-jsonschema
alias: validate-issue-forms
name: Validate GitHub issue forms
files: ^\.github/ISSUE_TEMPLATE/(?!config\.ya?ml$).*\.ya?ml$
args: ['--builtin-schema', 'vendor.github-issue-forms']

- id: check-jsonschema
alias: validate-compose-config
name: Validate Docker Compose config
files: ^deploy/docker/(?:docker-)?compose\.ya?ml$
args: ['--builtin-schema', 'vendor.compose-spec']

# Renovate config validation (schema-checks the active renovate.json)
- repo: https://github.com/renovatebot/pre-commit-hooks
rev: 43.249.6
rev: 44.56.1
hooks:
- id: renovate-config-validator
files: ^\.github/renovate\.json$

# Markdown linting (report-only, doesn't auto-fix)
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.49.0
rev: v0.49.1
hooks:
- id: markdownlint
exclude: ^(translations/|tools/skills/|docs/(zh|ko|tr|ru|ja|de|es|fa|fr|he|uk)/|CHANGELOG\.md)
Expand All @@ -156,20 +187,20 @@ repos:
# install of the bare module fails and aborts the whole pre-commit run before any hook
# fires.
additional_dependencies:
- 'github.com/errata-ai/vale/v3/cmd/vale@v3.15.1'
- 'github.com/errata-ai/vale/v3/cmd/vale@v3.19.0'
entry: vale sync
files: ^(docs/|\.vale\.ini$)
pass_filenames: false
- repo: https://github.com/errata-ai/vale
rev: v3.15.1
rev: v3.19.0
hooks:
- id: vale
files: ^docs/
args: ['--no-exit'] # Don't fail on warnings, just report

# Spell checking (report-only, doesn't auto-fix)
- repo: https://github.com/crate-ci/typos
rev: v1.48.0
rev: v1.50.1
hooks:
- id: typos
args: [] # Empty args disables auto-fix mode
Expand Down Expand Up @@ -199,15 +230,15 @@ repos:
language: python
additional_dependencies: ['cmakelang==0.6.13', 'pyyaml==6.0.3']
files: (CMakeLists\.txt|\.cmake)$
exclude: ^(build/|libs/)
exclude: ^(build/|libs/|cmake/modules/CPM\.cmake$)

- id: cmake-lint
name: cmake-lint
entry: cmake-lint
language: python
additional_dependencies: ['cmakelang==0.6.13', 'pyyaml==6.0.3']
files: (CMakeLists\.txt|\.cmake)$
exclude: ^(build/|libs/)
exclude: ^(build/|libs/|cmake/modules/CPM\.cmake$)

- id: clazy
name: Qt static analysis (clazy)
Expand Down
107 changes: 107 additions & 0 deletions cmake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# CMake Development Guide

This directory contains QGroundControl's reusable build modules, platform configuration, package
assembly, dependency finders, and pure-CMake regression tests. The root `CMakeLists.txt` remains the
entry point for normal builds.

## Layout

| Path | Purpose |
| --- | --- |
| `GStreamer/` | GStreamer discovery, download, validation, installation, and pure-CMake tests |
| `install/` | Runtime deployment and package assembly |
| `modules/` | Reusable project modules and dependency helpers |
| `platform/` | Android, Apple, Linux, and Windows configuration |
| `presets/` | Platform configure, build, test, package, and workflow presets |
| `tests/` | Configure-time contracts for shared CMake helpers |
| `find-modules/` | CMake find modules for optional dependencies |

## Conventions

- Add `include_guard(GLOBAL)` to reusable modules that define functions, macros, options, or global
state.
- Prefix project-owned functions, macros, targets, and cache variables with `qgc` or `QGC`.
- Validate required and unknown arguments in public helper functions. Fail during configure rather
than allowing a misspelled option to be ignored.
- Prefer target-scoped properties and commands over directory-global compile or link flags.
- Account for single- and multi-config generators. Custom test targets should pass their active
configuration to CTest.
- Keep host and target paths distinct in cross-builds, especially for Android and iOS.
- In GStreamer modules with repeated basenames, include the intended file by absolute path.
- When changing an existing file, format only the edited region. Whole-file formatting is reserved
for new files or an explicit formatting task.

`cmake/modules/CPM.cmake` is vendored. Do not modify or reformat it as part of project CMake
maintenance; the pre-commit CMake hooks intentionally exclude it.

Run the CMake lint hooks on the files you change:

```bash
pre-commit run cmake-format --files path/to/file.cmake
pre-commit run cmake-lint --files path/to/file.cmake
```

## Tests

After configuring a build with tests enabled, run the shared-module and finder tests with:

```bash
ctest --test-dir build --output-on-failure -L CMake
ctest --test-dir build --output-on-failure -L FindModule
```

Use `just configure`, `just build`, and the relevant CTest labels for the final validation described
in the root [AGENTS.md](../AGENTS.md).

## Presets

The root [`CMakePresets.json`](../CMakePresets.json) aggregates the platform definitions in
`presets/`. CI selects one of those configure presets and limits workflow overrides to dynamic
values such as signing credentials, sanitizer selection, and target-specific paths. Keep build
type, generator, testing, coverage, toolchain, and cache defaults in the presets so local and CI
configuration cannot drift.

The root file owns `cmakeMinimumRequired`; included fragments declare only their required preset
schema `version`. Preset build directories stay under `${sourceDir}/build` so separate source
checkouts cannot share or overwrite one another's build trees.

List every configure, build, test, package, and workflow preset with:

```bash
cmake --list-presets=all
```

Each visible configure preset has a same-name build preset and workflow preset. Workflows run tests
only for configurations that set `QGC_BUILD_TESTING=ON`; mobile and ARM64 cross-build debug presets
intentionally keep tests disabled because their target binaries cannot be assumed runnable on the
host. For example:

```bash
cmake --workflow --preset Linux-debug
cmake --workflow --preset Linux-deb
```

The `just configure` and `just build` recipes use the matching `default*` configure and build
presets for `BUILD_TYPE`. `just test` always uses the `default` test preset and passes `BUILD_TYPE`
through `--build-config`. `tools/configure.py` selects a configure preset by build type and accepts
an explicit `--preset`; `--no-preset` is reserved for unsupported custom toolchains. Coverage uses
`Linux-coverage`. Docker and Multipass builders invoke `qt-cmake` directly because their mounted
paths and cross-toolchains are assembled at runtime; Vagrant uses the `Linux` preset with a build
directory override.

| Preset family | Required environment | Notes |
| --- | --- | --- |
| `default*` | A discoverable Qt installation | Generic Ninja build in the source tree |
| `Linux*`, `macOS*`, `Windows*` | `QT_ROOT_DIR` | Qt target installation containing `qt.toolchain.cmake` |
| `Linux-arm64*`, `Windows-arm64*` | `QT_ROOT_DIR`, `QT_HOST_PATH` | Target and host Qt installations for ARM64 or cross-builds |
| `Android*` | `QT_TARGET_ROOT_DIR`, `QT_HOST_PATH`, `ANDROID_NDK`, `ANDROID_MIN_SDK` | Qt for Android plus its matching host Qt and Android toolchain |
| `iOS*` | `QT_ROOT_DIR`, `QT_HOST_PATH` | Qt for iOS plus its matching host Qt |

Package presets exist only for CPack-backed artifacts: Linux `.deb`/`.rpm` packages and Windows
NSIS installers. The normal Linux AppImage and macOS DMG are install-time artifacts, so configure
and build their release workflow and then run `cmake --install <build-dir> --config Release`.
Android APK signing/deployment and iOS IPA assembly remain platform workflow steps rather than CPack
operations.

Use `CMakeUserPresets.json` for machine-local paths or derived presets; it remains ignored by Git
and `tools/clean.py` preserves it.
Loading
Loading