Merge branch 'master' into virtual-keyboard-mouse

virtual-keyboard-mouse
nirenjan 2026-03-26 12:54:50 -07:00
commit 67c7cc7548
22 changed files with 492 additions and 43 deletions

View File

@ -2,10 +2,20 @@
# Run the build and tests
set -e
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled build
cd build
ninja
ninja test
BUILDDIR="${1:-build}"
rm -rf "$BUILDDIR"
# Handle the meson dist failure in CI
if [[ "$GITHUB_ACTIONS" == "true" ]]
then
git config --global --add safe.directory '*'
fi
meson setup -Dprefix=/usr -Dsysconfdir=/etc -Dlocalstatedir=/var -Dnls=enabled "$BUILDDIR"
cd "$BUILDDIR"
meson compile
meson test
meson dist
# Print bugreport output

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Generate the list of distros as a build matrix"""
import pathlib
import json
import sys
class BuildMatrix:
"""Generate a build matrix for Github actions"""
COMPILER = 'gcc'
OS = 'ubuntu-latest'
EXPERIMENTAL = False
def __init__(self, image_prefix):
self.matrix = []
self.image_prefix = image_prefix
self.get_distros()
self.add_extra_builds()
self.generate_output()
def build_matrix_obj(self, distro, experimental, os=None, compiler=None, image=None):
"""Build the matrix object for the given distro"""
matrix_obj = {
'distro': distro,
'experimental': experimental,
'os': os or self.OS,
'compiler': compiler or self.COMPILER,
}
if image is None:
image = f"{self.image_prefix}/ci-build-{distro}:latest"
matrix_obj['image'] = image
return matrix_obj
def get_distros(self):
"""Get the list of distros from the Dockerfiles"""
for dockerfile in pathlib.Path('.').glob('docker/Dockerfile.*'):
distro = dockerfile.suffix[1:]
with open(dockerfile, encoding='utf-8') as dfd:
experimental = 'experimental="true"' in dfd.read()
self.matrix.append(self.build_matrix_obj(distro, experimental))
def add_extra_builds(self):
"""Add manual canary builds that don't have a corresponding dockerfile"""
canary_build = self.build_matrix_obj('ubuntu22', False, compiler='clang')
self.matrix.append(canary_build)
macos_build = self.build_matrix_obj('macos', True, os='macos-latest',
compiler='clang', image='')
self.matrix.append(macos_build)
def generate_output(self):
"""Generate the output for github actions"""
matrix_data = json.dumps(self.matrix)
print(f"matrix={matrix_data}")
if __name__ == '__main__':
BuildMatrix(sys.argv[1])

View File

@ -0,0 +1,46 @@
#!/bin/bash
# Get the list of changed Dockerfiles
# Usage: get-changed-dockerfiles.sh <before-SHA> <head-SHA>
set -euxo pipefail
mapfile -t ALL_DOCKERFILES < <(git ls-files 'docker/Dockerfile.*')
if [[ -n "${BUILD_DOCKER_MANUAL_ENV:-}" ]]
then
if [[ "${BUILD_DOCKER_MANUAL_ENV}" == "ALL" ]]
then
DOCKERFILES=("${ALL_DOCKERFILES[@]}")
else
BUILD_LIST=($BUILD_DOCKER_MANUAL_ENV)
DOCKERFILES=()
for item in "${BUILD_LIST[@]}"
do
if [[ -f "docker/Dockerfile.${item}" ]]
then
DOCKERFILES+=("$item")
fi
done
fi
else
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR HEAD^..HEAD)
mapfile -t DOCKERFILES < <(echo "$CHANGED_FILES" | grep 'docker/Dockerfile')
mapfile -t SCRIPT_CHANGES < <(echo "$CHANGED_FILES" | grep 'docker/' | grep '\.sh$')
for file in "${SCRIPT_CHANGES[@]}"
do
for dockerfile in "${ALL_DOCKERFILES[@]}"
do
if grep -q "$(basename "$file")" "$dockerfile"
then
DOCKERFILES+=("$dockerfile")
fi
done
done
fi
echo -n "matrix="
echo "${DOCKERFILES[@]}" | \
tr ' ' '\n' | sort -u | sed 's,docker/Dockerfile\.,,' | \
jq -Rsc 'split("\n") | map(select(length > 0)) | unique'

View File

@ -10,34 +10,46 @@ on:
- '!gh-pages'
paths-ignore:
- 'kernel_module/**'
- 'docker/**'
pull_request:
branches: [ master ]
jobs:
build:
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
name: ${{ join(matrix.*, '/') }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ startsWith(matrix.os, 'macos-') }}
env:
CC: ${{ matrix.cc }}
list-distros:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v6
- id: set-matrix
run: .github/scripts/generate_build_matrix.py ghcr.io/${{ github.repository }} >> $GITHUB_OUTPUT
build:
needs: list-distros
if: "!(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
strategy:
fail-fast: false
matrix:
os: ['ubuntu-22.04', 'ubuntu-24.04', 'macos-latest']
cc: ['gcc', 'clang']
include: ${{ fromJson(needs.list-distros.outputs.matrix) }}
name: ${{ format('{0}/{1}{2}', matrix.distro, matrix.compiler, matrix.experimental == true && ' (experimental)' || '') }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
container:
image: ${{ matrix.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install dependencies (Ubuntu)
run: ./.github/scripts/install-dependencies-ubuntu.sh
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
- name: Install dependencies (MacOS)
run: ./.github/scripts/install-dependencies-macos.sh
if: ${{ startsWith(matrix.os, 'macos-') }}
- name: Build and Test
env:
CC: ${{ matrix.compiler }}
run: ./.github/scripts/build-and-test.sh

66
.github/workflows/docker.yml vendored 100644
View File

@ -0,0 +1,66 @@
name: Build Docker CI Images
on:
workflow_dispatch:
inputs:
distros:
description: "List distros to build (space separated)"
type: string
default: "ALL"
push:
paths:
- "docker/Dockerfile.*"
- "docker/*.sh"
jobs:
detect-changes:
runs-on: ubuntu-latest
env:
BUILD_DOCKER_MANUAL_ENV: ${{ inputs.distros || '' }}
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0 # Needed for detecting changes
- name: Set build matrix
id: set-matrix
run: .github/scripts/get-changed-dockerfiles.sh >> $GITHUB_OUTPUT
build-and-push:
needs: detect-changes
if: ${{ needs.detect-changes.outputs.matrix != '[]' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false # Don't cancel other distros if this one fails
matrix:
distro: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
permissions:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: docker
file: ./docker/Dockerfile.${{ matrix.distro }}
push: true
tags: ghcr.io/${{ github.repository }}/ci-build-${{ matrix.distro }}:latest
- name: Cleanup old builds
uses: actions/delete-package-versions@v5
with:
package-name: libx52/ci-build-${{ matrix.distro }}
package-type: container
delete-only-untagged-versions: true

View File

@ -38,4 +38,4 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5

View File

@ -6,6 +6,10 @@ The format is based upon [Keep a Changelog].
## [Unreleased]
### Changed
- Migrated CI builds to run in multiple distro containers.
### Fixed
- Addressed meson build bugs found in v0.3.3

View File

@ -5,22 +5,22 @@ Build has been tested on the following operating systems (x86-64 only):
* Ubuntu 22.04 LTS
* Ubuntu 24.04 LTS
* macOS Big Sur 11
* macOS Monterey 12
* Fedora latest (Fedora 42, as of this commit)
* Archlinux
* Alpine Linux (Experimental)
* Ubuntu 26.04 LTS (Experimental as of this commit)
* macOS (latest tag on Github, ARM)
# Prerequisites
## Required Packages
* automake
* autoconf
* autopoint
* meson
* ninja
* gettext
* hidapi + headers
* inih
* libtool
* libusb-1.0 + headers
* libevdev + headers (on Linux)
* pkg-config
* python3 (3.6 or greater)
* git (not required for builds, but necessary to clone the repository)
@ -29,15 +29,16 @@ Build has been tested on the following operating systems (x86-64 only):
| Platform | Install instructions |
| -------- | -------------------- |
| Ubuntu | `sudo apt-get install automake autoconf gettext autopoint libhidapi-dev libevdev-dev libtool libusb-1.0-0-dev libinih-dev pkg-config python3 git` |
| MacOS + Homebrew | `brew install automake autoconf gettext hidapi libtool libusb pkg-config python3 git` |
| Arch Linux | `pacman -S base-devel libusb hidapi libevdev libinih python git` |
| Fedora | `sudo dnf install autoconf automake gettext-devel findutils libtool hidapi-devel libusb-devel libevdev-devel inih-devel pkg-config python3 git` |
| Ubuntu | `sudo apt-get install meson gettext libhidapi-dev libevdev-dev libusb-1.0-0-dev libinih-dev pkg-config python3 git` |
| MacOS + Homebrew | `brew install meson gettext hidapi libtool libusb pkg-config python3 git` |
| Arch Linux | `pacman -S base-devel meson libusb hidapi libevdev libinih python git` |
| Fedora | `sudo dnf install meson gettext-devel findutils hidapi-devel libusb-devel libevdev-devel inih-devel pkg-config python3 git` |
## Optional Packages
* doxygen - to generate HTML documentation and man pages
* libcmocka (1.1 or greater) + headers - to run unit tests
* libevdev + headers (on Linux) - to add virtual keyboard/mouse support
# Installation Instructions
@ -49,37 +50,36 @@ git clone https://github.com/nirenjan/libx52.git
2. Run autogen.sh
```
cd ./libx52
./autogen.sh
meson setup build -Dprefix=/usr
```
3. Run the following commands:
```
./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc
make && sudo make install
meson compile -C build && meson install -C build
```
You may want to remove or edit the `--prefix=/usr` option, most users prefer
non-distro binaries in `/usr/local` (default without `--prefix`) or `/opt`.
You may want to remove or edit the `-Dprefix=/usr` option, most users prefer
non-distro binaries in `/usr/local` (default without `-Dprefix`) or `/opt`.
## Configuration options
### udev
The configuration system should automatically detect the udev rules directory,
but you can override it by using the following argument to `configure`:
but you can override it by using the following argument to `meson setup`:
```
--with-udevrulesdir=/path/to/udev/rules.d
-Dudev-rules-dir=/path/to/udev/rules.d
```
### Input group
The udev rules that are installed provide read/write access to members of the
input devices group. This defaults to `plugdev`, but can be modified using
the following argument to `configure`:
the following argument to `meson setup`:
```
--with-input-group=group
-Dinput-group=group
```
### Systemd support
@ -89,13 +89,13 @@ itself to run in the background. Typical deployments with systemd will have it
run in the foreground, and disable timestamps in the logs, since those are
inserted automatically by journald.
Systemd support is enabled by default, but can be disabled with the
`--disable-systemd` argument to `configure`
Systemd support is enabled by default, which disables timestamps in the program
logs, but you can re-enable timestamps by passing `-Dsystemd-logs=disabled`
argument to `meson setup`
It is also possible to configure the directory in which the service file is
installed with the following option. This is ignored if you have specified
`--disable-systemd`.
installed with the following option. This is ignored if systemd is not found.
```
--with-systemdsystemunitdir=/path/to/systemd/system
-Dsystemd-unit-dir=/path/to/systemd/system
```

View File

@ -0,0 +1,8 @@
FROM archlinux:base-devel
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
COPY ./install-dependencies-archlinux.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-archlinux.sh && /tmp/ci-setup.sh

View File

@ -0,0 +1,8 @@
FROM fedora:latest
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
COPY ./install-dependencies-fedora.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-fedora.sh && dnf clean all && /tmp/ci-setup.sh

View File

@ -0,0 +1,12 @@
FROM ubuntu:22.04
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
ENV DEBIAN_FRONTEND=noninteractive
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-ubuntu.sh && \
rm -rf /var/lib/apt/lists/* && \
/tmp/ci-setup.sh

View File

@ -0,0 +1,12 @@
FROM ubuntu:24.04
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
ENV DEBIAN_FRONTEND=noninteractive
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-ubuntu.sh && \
rm -rf /var/lib/apt/lists/* && \
/tmp/ci-setup.sh

View File

@ -0,0 +1,13 @@
FROM ubuntu:26.04
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
LABEL com.project.ci.experimental="true"
ENV DEBIAN_FRONTEND=noninteractive
COPY ./install-dependencies-ubuntu.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-ubuntu.sh && \
rm -rf /var/lib/apt/lists/* && \
/tmp/ci-setup.sh

37
docker/README.md 100644
View File

@ -0,0 +1,37 @@
Dockerfiles for CI builds
=========================
This repo contains a number of Dockerfiles for building libx52 against multiple
distros. Github only supports Ubuntu LTS for it's Linux runners, but libx52
users run multiple distros, including Arch Linux, Gentoo and Fedora.
For this reason, it's better to get a prebuilt Docker image that builds the CI
environment, pulling in all the relevant dependencies, and staging the CI build
environment as a container within Github. The regular build pipeline can then
run on these containers, and this will help ensure that the breakages are
reduced to a minimum.
Build Instructions
==================
In order to run the CI build on all the supported platforms, first run the
`build-containers.sh` script, it will find the Dockerfiles for all the
suppported platforms in the `docker` directory, and build the container images
for them with the necessary dependencies included.
Once the container images are built, you can build against one or all of the
container images by running `build-repo.sh`. If you don't specify the container
distro, which is basically the same as the extension after `Dockerfile.`, it
will run everything. If you specify a distro that doesn't exist, or has not been
built, the script will silently exit.
Extending to a new distro
=========================
To extend the builds to a new distro, create a `Dockerfile.<distro>` with the
necessary instructions to build a container image for that distro. Make sure you
install the necessary dependencies for the distro. It is strongly recommended to
add a `install-dependencies-<distro>.sh` so that you can build against multiple
versions of that distro, eg. Ubuntu 22.04 and Ubuntu 24.04. Make sure you copy
`ci-setup.sh` to allow setting up the environment, since it is highly likely
that the `meson dist` command will fail if you do not run that script.

View File

@ -0,0 +1,25 @@
#!/bin/bash
# Build containers for all the target environments
set -euo pipefail
SCRIPT_DIR=$(dirname "$0")
if command -v realpath &>/dev/null
then
SCRIPT_DIR=$(realpath "$SCRIPT_DIR")
fi
GITHUB_SCRIPTS_DIR="$SCRIPT_DIR/../.github/scripts"
if command -v realpath &>/dev/null
then
GITHUB_SCRIPTS_DIR=$(realpath "$GITHUB_SCRIPTS_DIR")
fi
for dockerfile in "$SCRIPT_DIR"/[Dd]ockerfile.*
do
if [[ -e "$dockerfile" ]]
then
SUFFIX="$(basename "$dockerfile" | cut -d. -f2)"
TAG="ghcr.io/nirenjan/libx52/ci-build-${SUFFIX}:latest"
docker build --tag "$TAG" -f "$dockerfile" "${SCRIPT_DIR}"
fi
done

View File

@ -0,0 +1,40 @@
#!/bin/bash
# Build the repository for all found directories
# Build containers for all the target environments
set -euo pipefail
GIT_ROOT=$(git rev-parse --show-toplevel)
CC=${CC:-gcc}
IMAGE="${1:-*}"
for image in $(docker images --filter "reference=ghcr.io/nirenjan/libx52/ci-build-${IMAGE}" --format '{{ .Repository}}')
do
distro=${image##*/ci-build-}
container_name="libx52-runner-${distro}"
if [[ "$(docker ps -aq -f name=$container_name)" ]]
then
echo "Cleaning up old container for '$distro'"
docker rm -f $container_name >/dev/null
fi
experimental=$(docker inspect --format='{{.Config.Labels}}' $image | \
grep -q 'experimental:true' && echo " (experimental)" || true)
if docker run --rm --name $container_name \
--device /dev/bus/usb:/dev/bus/usb \
-v "$GIT_ROOT":/code \
-w /code \
-e CC="${CC}" \
$image \
/bin/bash -c ".github/scripts/build-and-test.sh builddir/${distro}" \
&> "$GIT_ROOT/build-${distro}.log"
then
echo "=== ${distro}${experimental} OK ==="
else
echo "=== ${distro}${experimental} !!! FAIL !!! ==="
tail -20 "$GIT_ROOT/build-${distro}.log"
fi
done

10
docker/ci-setup.sh 100755
View File

@ -0,0 +1,10 @@
#!/bin/sh
# Common CI setup
git config --global --add safe.directory /code
git config --global --add safe.directory '*'
cat >> /root/.bashrc <<"EOF"
echo 'WARNING: This is an internal CI container'
echo 'Do not use for production'
EOF

View File

@ -0,0 +1,15 @@
# This file is deliberately named with a lowercase d, in order to avoid the
# Github action logic from picking this up as a supported distro. The Alpine
# image fails in the Github actions, because it needs the /dev/bus/usb device
# mounted inside the container, however, attempting to do so causes every
# build to fail. Therefore, we disable the Alpine image in CI, but keep it
# local, so that we can test the build against Alpine locally if necessary.
FROM alpine:latest
LABEL org.opencontainers.image.description="INTERNAL CI USE ONLY - Not intended for production"
LABEL org.opencontainers.image.authors="Nirenjan Krishnan"
LABEL com.project.ci.experimental="true"
COPY ./install-dependencies-alpine.sh ./ci-setup.sh /tmp/
RUN /tmp/install-dependencies-alpine.sh
RUN /tmp/ci-setup.sh

View File

@ -0,0 +1,19 @@
#!/bin/sh
# Setup for alpine
set -eux
apk update
apk add --no-cache \
build-base \
meson \
bash \
git \
gettext \
libusb-dev \
hidapi-dev \
libevdev-dev \
inih-dev \
cmocka-dev \
tzdata \
musl-libintl \
doxygen

View File

@ -0,0 +1,14 @@
#!/bin/bash
# Install dependencies on Archlinux container
# Assumes that it's running off a base-devel tag
set -euo pipefail
pacman -Syu --noconfirm \
git \
meson \
libusb \
hidapi \
libinih \
libevdev \
python \
gettext

View File

@ -0,0 +1,16 @@
#!/bin/bash
# Install dependencies on Fedora container
set -euo pipefail
dnf update -y
dnf install -y \
gcc \
git \
meson \
libusb1-devel \
hidapi-devel \
inih-devel \
libevdev-devel \
pkg-config \
python3 \
gettext-devel

View File

@ -0,0 +1,19 @@
#!/bin/bash
# Install dependencies to build and test on Ubuntu runners
apt-get update && apt-get upgrade -y
apt-get install -y \
git \
gcc clang \
meson \
gettext \
pkg-config \
python3 \
libusb-1.0-0-dev \
libhidapi-dev \
libevdev-dev \
libinih-dev \
doxygen \
libcmocka-dev \
tzdata
exit 0