// SPDX-License-Identifier: GPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // lint-disable-file no-char-pointer import std; import Crafter.Build; #include "lint-rules.h" namespace fs = std::filesystem; using namespace Crafter; // libqcomtee — Qualcomm's BSD-3 userspace client for QTEE, built by // packaging/make-libqcomtee.sh into a per-target cache dir. It is not vendored // here: it is upstream code we pin, and the script builds it WITHOUT QCBOR // (see the script for why that dependency is avoidable). static void ApplyQcomteeFlags(Configuration& cfg) { fs::path base = fs::path(std::getenv("HOME") ? std::getenv("HOME") : ".") / ".cache" / "fingerprintd"; // make-libqcomtee.sh names its output dir after --target, and plain // "libqcomtee" when built for the host. cfg.target is always populated // (it defaults to the host triple), so try the target-specific dir first // and fall back to the host one. std::error_code ec; fs::path root = base / ("libqcomtee-" + cfg.target); if (!fs::exists(root / "libqcomtee.a", ec)) root = base / "libqcomtee"; if (!fs::exists(root / "libqcomtee.a", ec)) { std::println(std::cerr, "libqcomtee not built for '{}'. Run:\n" " packaging/make-libqcomtee.sh{}{}", cfg.target.empty() ? std::string("native") : cfg.target, cfg.target.empty() ? std::string() : " --target=" + cfg.target, cfg.sysroot.empty() ? std::string() : " --sysroot=" + cfg.sysroot); } cfg.compileFlags.push_back("-I" + (root / "include").string()); cfg.linkFlags.push_back((root / "libqcomtee.a").string()); } extern "C" Configuration CrafterBuildProject(std::span args) { // fingerprintd-core — the wire formats and state machines as a static // library of pure C++ modules. Deliberately free of GLib, libqcomtee and // every system header: the byte-level decisions in here were the whole // cost of this project, so they are pinned by tests that run on a dev box // with no phone, no TEE and no sensor. static auto Core = std::make_unique(); Core->path = "./"; Core->name = "fingerprintd-core"; Core->outputName = "fingerprintd-core"; ApplyStandardArgs(*Core, args); Core->type = ConfigurationType::LibraryStatic; { std::array ifaces = { "interfaces/Fingerprintd", "interfaces/Fingerprintd-Sfs", "interfaces/Fingerprintd-Rpmb", "interfaces/Fingerprintd-Ta", "interfaces/Fingerprintd-Engine", "interfaces/Fingerprintd-Store", "interfaces/Fingerprintd-Tee", "interfaces/Fingerprintd-Sensor", }; std::array impls = {}; Core->GetInterfacesAndImplementations(ifaces, impls); } // fingerprintd — the daemon: sensor rail, QTEE session, the gpfile and // RPMB listeners, and the bus surface, wrapped around the core. Configuration cfg; cfg.path = "./"; cfg.name = "fingerprintd"; cfg.outputName = "fingerprintd"; ApplyStandardArgs(cfg, args); cfg.type = ConfigurationType::Executable; cfg.dependencies = { Core.get() }; { std::array ifaces = {}; std::array impls = { "implementations/main" }; cfg.GetInterfacesAndImplementations(ifaces, impls); } ApplyQcomteeFlags(cfg); cfg.linkFlags.push_back("-lpthread"); // the supplicant thread cfg.AddTest("Sfs").Dependencies({ Core.get() }); cfg.AddTest("Rpmb").Dependencies({ Core.get() }); cfg.AddTest("Ta").Dependencies({ Core.get() }); cfg.AddTest("Engine").Dependencies({ Core.get() }); cfg.AddTest("Store").Dependencies({ Core.get() }); cfg.AddTest("Tee").Dependencies({ Core.get() }); cfg.AddTest("Sensor").Dependencies({ Core.get() }); ProjectLint::AddProjectLintRules(cfg); return cfg; }