fix(gamepad): build libevdev from a vendored copy

The Linux gamepad backend included <libevdev/libevdev.h> from
/usr/include/libevdev-1.0 and linked the system libevdev, but the
Flatpak SDK crafter-build runs in (org.freedesktop.Sdk 26.08) ships no
libevdev headers, so Crafter.Graphics stopped building there.
lib/libevdev now carries libevdev 1.14.0 (MIT), built once as its own
static library that Crafter.Graphics depends on. As part of cfg it would
be compiled again in every test configuration, since they copy
cfg.cFiles.

libevdev's generated event-names.h names every input code by its macro,
so the library compiles against the kernel input headers that table was
generated from (lib/libevdev/kernel-headers), not the target's. That
directory is deliberately not called include: Crafter.Build puts every
include directory of a dependency on its consumers' include path, where
those headers would shadow each consumer's own <linux/input.h>. The
paths are absolute because Crafter.Build only resolves the returned
configuration's paths against the project root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-19 03:03:16 +02:00
commit dfb70bc088
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
14 changed files with 8515 additions and 5 deletions

View file

@ -80,11 +80,42 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
cfg.linkFlags.push_back("-lwayland-client");
cfg.linkFlags.push_back("-lxkbcommon");
// Gamepad: libudev for hot-plug + device enumeration; libevdev
// for event parsing + axis calibration. libevdev ships its headers
// under a versioned dir (libevdev-1.0/) so the -I is mandatory.
// for event parsing + axis calibration.
cfg.linkFlags.push_back("-ludev");
cfg.linkFlags.push_back("-levdev");
cfg.compileFlags.push_back("-I/usr/include/libevdev-1.0");
// libevdev is vendored in lib/libevdev rather than linked from the
// system: the Flatpak SDK crafter-build runs in ships no libevdev
// headers, and a vendored copy cross-builds like everything else. It
// is its own static library so it compiles once, not again in every
// test configuration (they copy cfg.cFiles). One per call, since each
// distinct argument list loads this project separately; it lives for
// the whole build like a GitProject's configuration. Its paths are
// absolute because Crafter.Build only resolves the returned
// configuration's paths against this project's root, and this
// project.cpp runs with that root as cwd.
static std::deque<Configuration> libevdevConfigs;
Configuration& libevdev = libevdevConfigs.emplace_back();
libevdev.path = fs::absolute(cfg.path);
libevdev.name = "Crafter.Graphics-libevdev";
libevdev.outputName = "Crafter.Graphics-libevdev";
libevdev.type = ConfigurationType::LibraryStatic;
libevdev.target = cfg.target;
libevdev.march = cfg.march;
libevdev.mtune = cfg.mtune;
libevdev.debug = cfg.debug;
libevdev.sysroot = cfg.sysroot;
libevdev.cFiles = {
fs::absolute("lib/libevdev/libevdev/libevdev"),
fs::absolute("lib/libevdev/libevdev/libevdev-names"),
};
// Its generated event-names.h names every input code by macro, so it
// compiles against the kernel headers that table was generated from
// rather than the target's.
libevdev.compileFlags.push_back(std::format("-I{}", fs::absolute("lib/libevdev/kernel-headers").string()));
cfg.dependencies.push_back(&libevdev);
// For Gamepad's #include <libevdev/libevdev.h>.
cfg.compileFlags.push_back(std::format("-I{}", fs::absolute("lib/libevdev").string()));
cfg.cFiles.push_back("lib/xdg-shell-protocol");
cfg.cFiles.push_back("lib/wayland-xdg-decoration-unstable-v1-client-protocol");
cfg.cFiles.push_back("lib/fractional-scale-v1");