Makefile: - Add reconciler and ingest to the `modules` target; they were only built as side-effects of `make node`, making `make modules` incomplete planning.md: - Add 4 missing CLI drivers: discovery_cli, config_cli, protocol_cli, query_cli (all existed in code and dev/cli/Makefile but were absent) - Add header-only utilities table: stream_stats.h, v4l2_fmt.h README.md: - Add transport_cli, discovery_cli, config_cli, protocol_cli, query_cli to CLI tools list conventions.md: - Add ERR_NOT_FOUND to Error_Code enum example - Replace placeholder Invalid_Error_Detail with actual fields (config_line, message) that have been in use since config module - Add missing error macros: APP_INVALID_ERROR, APP_INVALID_ERROR_MSG, APP_NOT_FOUND_ERROR - Update directory structure: node/ description (was "later"), add web/ and tools/ entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.5 KiB
Conventions and Preferences
Language
- C11 throughout (
-std=c11) _GNU_SOURCEdefined via-D_GNU_SOURCEin all Makefiles — enables the full GNU/Linux feature set (POSIX 2008, BSD, GNU and Linux-specific extensions); do not define this in source files- LTO enabled via
-fltoin all Makefiles — the compiler handles inlining across translation units at link time; do not usestaticon functions solely to enable inlining - Target platform: Linux (V4L2, epoll, etc. are Linux-specific)
Naming
| Kind | Convention | Example |
|---|---|---|
| Structs | Title_Snek_Case |
struct App_Error, struct Frame_Header |
| Enums (type) | Title_Snek_Case |
enum Error_Code |
| Enum values | CAPITAL_SNEK_CASE |
ERR_NONE, ERR_SYSCALL |
| Functions | lower_snek_case |
v4l2_ctrl_open(), app_error_print() |
| Local variables | lower_snek_case |
frame_count, device_fd |
Constants (#define, const) |
CAPITAL_SNEK_CASE |
MAX_FRAME_SIZE |
| Module-level singletons | lower_snek_case |
(treated as functions) |
Identifiers are prefixed with their module name where disambiguation is needed (e.g. v4l2_ctrl_, media_ctrl_, transport_).
Types
No typedefs for structs
Always use the struct keyword at the call site. This makes composite types unambiguous without inspecting declarations.
/* correct */
struct App_Error err = app_error_ok();
void process(struct Frame_Header *header);
/* wrong */
typedef struct { ... } App_Error;
typedefs are acceptable for
- Enums:
typedef enum Error_Code { ... } Error_Code;— allows using the name withoutenumkeyword - Scalar aliases where the underlying type is genuinely irrelevant to the caller (e.g.
typedef uint32_t Frame_Id;)
Formatting
- Indentation: tabs (display width 4)
- Braces: always use block braces, including single-statement bodies
/* correct */
if (err.code != ERR_NONE) {
return err;
}
/* wrong */
if (err.code != ERR_NONE)
return err;
- Quotes: double quotes for string literals (C standard)
Error Handling
Errors are returned as struct App_Error values. Functions that can fail return struct App_Error directly, or take an out-parameter for the result alongside it.
Structure
/* modules/common/error.h */
typedef enum Error_Code {
ERR_NONE = 0,
ERR_SYSCALL = 1, /* errno is meaningful */
ERR_INVALID = 2,
ERR_NOT_FOUND = 3,
} Error_Code;
struct Syscall_Error_Detail {
int err_no;
};
struct Invalid_Error_Detail {
int config_line; /* source line number, or 0 if not applicable */
const char *message; /* static string describing what was wrong */
};
struct App_Error {
Error_Code code;
const char *file; /* __FILE__ — future: replaced by generated location code */
int line; /* __LINE__ — future: replaced by generated location code */
union {
struct Syscall_Error_Detail syscall;
struct Invalid_Error_Detail invalid;
} detail;
};
Macros
#define APP_OK \
((struct App_Error){ .code = ERR_NONE })
#define APP_IS_OK(e) \
((e).code == ERR_NONE)
#define APP_SYSCALL_ERROR() \
/* sets ERR_SYSCALL + captures errno */
#define APP_INVALID_ERROR() \
/* sets ERR_INVALID, no message */
#define APP_INVALID_ERROR_MSG(cfg_line, msg) \
/* sets ERR_INVALID with config_line and message */
#define APP_NOT_FOUND_ERROR() \
/* sets ERR_NOT_FOUND */
Presentation
Each error kind is handled in app_error_print(), which writes a human-readable description to stderr. A JSON writer (app_error_write_json()) will be added later and will use the same structured fields.
Future upgrade
When the custom preprocessor is available, __FILE__ and __LINE__ will be replaced by a generated numeric location code that uniquely identifies the error site in the project. JSON consumers will see the same struct shape either way.
Directory Structure
video-setup/
src/ - all C source files
modules/ - translation units; each has a .c and a Makefile
common/ - shared types (error, base definitions); no external dependencies
<module>/
node/ - video node binary (source + display sink roles)
include/ - public headers (.h files)
dev/ - development aids; not part of the final deliverable
cli/ - exploratory CLI drivers, one per module
web/ - development web UI (Node.js/Express)
experiments/ - freeform experiments
tools/ - build-time code generators (e.g. gen_font_atlas)
tests/ - automated tests (later)
Makefile - top-level build
Source files live in src/. Public headers live in include/. CLI drivers and experiments live in dev/. Nothing in dev/ is a dependency of anything in src/.
Module Structure
Each module has its source under src/modules/<name>/ and its public header under include/:
src/modules/<name>/
<name>.c - implementation
Makefile - builds a static object
include/
<name>.h - public API; minimal includes, no implementation details
The corresponding CLI driver lives at dev/cli/<name>_cli.c.
Build
- GNU make with tabs for recipe indentation
- Each module builds to a static object (
.o) - CLI drivers link the module object(s) they need
- No CDN or vendored sources; dependencies are system libraries (libc, Linux kernel headers)