Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8cf9bde
ci: add GitHub Actions workflow for multi-platform testing
pproenca Jan 11, 2026
1e71853
fix(ci): use npm install instead of npm ci
pproenca Jan 11, 2026
55e40a0
fix(ci): use pip cpplint on Linux, fix Rocky FFmpeg install
pproenca Jan 11, 2026
a0bf678
fix(ci): don't skip optional deps for lint (biome needs them)
pproenca Jan 11, 2026
dc8c6cf
fix(ci): build TS for lint:types, use Python 3.9 on Rocky
pproenca Jan 11, 2026
48816a5
fix(ci): use GCC Toolset 12 on Rocky, install Alpine FFmpeg deps
pproenca Jan 11, 2026
e303f8a
fix(ci): use Rocky 9 for FFmpeg 6+, simplify Alpine FFmpeg
pproenca Jan 11, 2026
0c722b4
fix: add FFmpeg 5.0 compatibility for AVFrame::duration
pproenca Jan 11, 2026
19dcdf8
chore: commit cmake
pproenca Jan 11, 2026
817c9b2
feat(build): harden native build configuration
pproenca Jan 11, 2026
7b038e5
fix(build): use dynamic linking for Linux pkg-config fallback
pproenca Jan 11, 2026
7d38fbe
Revert "fix(build): use dynamic linking for Linux pkg-config fallback"
pproenca Jan 11, 2026
ecd0c54
feat(ci): use static FFmpeg musl package for Alpine builds
pproenca Jan 11, 2026
3da6fc6
feat: add musl FFmpeg package to optionalDependencies
pproenca Jan 11, 2026
c9f5e98
chore: re-trigger CI after musl FFmpeg package published
pproenca Jan 11, 2026
e33e68a
feat(build): use npm packages for FFmpeg headers and link flags
pproenca Jan 11, 2026
ee674ca
fix(ci): add zlib-dev and bzip2-dev for Alpine musl builds
pproenca Jan 11, 2026
53648a2
fix(ci): disable LTO for Alpine musl builds
pproenca Jan 11, 2026
0b966c9
fix(ci): use npm FFmpeg packages for Linux glibc builds
pproenca Jan 12, 2026
85dc955
feat(ci): align build matrix with ffmpeg-prebuilds packages
pproenca Jan 12, 2026
3e4b571
fix(ci): update ffmpeg-prebuilds to 0.1.7 and add zlib/bzip2 deps
pproenca Jan 12, 2026
a24041e
fix(ci): clean npm cache before installing FFmpeg packages
pproenca Jan 12, 2026
0b946ef
fix(ci): add libatomic for Linux glibc builds
pproenca Jan 12, 2026
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
180 changes: 180 additions & 0 deletions .claude/skills/dev-cmake/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
name: dev-cmake
description: Write CMake and Make files like a principal engineer with decades of experience. Use when creating, refactoring, reviewing, or debugging CMakeLists.txt, Makefiles, or build system configurations for C/C++ projects. Produces elegant, minimal, modern build files that reflect deep understanding of build systems.
---

# CMake & Make Expert

Write build files that are elegant because the understanding is deep. Every line should have a reason. Simplicity comes from mastery, not shortcuts.

## Core Philosophy

**Targets are everything.** Modern CMake is about targets and properties, not variables and directories. Think of targets as objects with member functions and properties.

**Explicit over implicit.** Always specify `PRIVATE`, `PUBLIC`, or `INTERFACE`. Never rely on inherited directory-level settings.

**Minimal surface area.** Expose only what consumers need. Default to `PRIVATE`; use `PUBLIC` only when downstream targets genuinely require it.

## CMake: The Principal Engineer Approach

### Project Structure
```cmake
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)

# Set standards at target level, not globally
# Use compile features, not flags
```

### Target Definition Pattern
```cmake
add_library(mylib)
add_library(MyProject::mylib ALIAS mylib)

target_sources(mylib
PRIVATE
src/impl.cpp
PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/mylib/api.h
)

target_compile_features(mylib PUBLIC cxx_std_17)

target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
```

### What to Never Do
- `include_directories()` — use `target_include_directories()`
- `link_directories()` — use full paths or targets
- `add_definitions()` — use `target_compile_definitions()`
- `link_libraries()` — use `target_link_libraries()`
- `CMAKE_CXX_FLAGS` manipulation — use `target_compile_options()` or features
- `file(GLOB)` for sources — list files explicitly
- Bare library names in `target_link_libraries()` — use namespaced targets

### Dependency Handling

For find_package dependencies:
```cmake
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem)
target_link_libraries(mylib PRIVATE Boost::filesystem)
```

For FetchContent (prefer over ExternalProject for CMake deps):
```cmake
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.1.0
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(mylib PRIVATE fmt::fmt)
```

### Generator Expressions

Use for build/install path differences:
```cmake
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
```

Use for conditional compilation:
```cmake
target_compile_definitions(mylib PRIVATE
$<$<CONFIG:Debug>:DEBUG_MODE>
)
```

## Make: The Principal Engineer Approach

### Essential Structure
```makefile
# Immediately expanded defaults
CC ?= gcc
CXX ?= g++
CFLAGS ?= -Wall -Wextra -pedantic
LDFLAGS ?=

# Preserve user-provided flags
CFLAGS += -MMD -MP

# Automatic dependency tracking
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:src/%.c=build/%.o)
DEPS := $(OBJS:.o=.d)

.PHONY: all clean

all: bin/program

bin/program: $(OBJS) | bin
$(CC) $(LDFLAGS) -o $@ $^

build/%.o: src/%.c | build
$(CC) $(CFLAGS) -c -o $@ $<

bin build:
mkdir -p $@

clean:
rm -rf build bin

-include $(DEPS)
```

### Pattern Rules — The Elegant Way
```makefile
# Single pattern rule replaces N explicit rules
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
```

### Automatic Variables (memorize these)
- `$@` — target
- `$<` — first prerequisite
- `$^` — all prerequisites (no duplicates)
- `$+` — all prerequisites (with duplicates, for libs)
- `$*` — stem (matched by %)

### What Makes Makefiles Elegant
1. Order-only prerequisites (`| dir`) for directory creation
2. `-include` for optional dependency files
3. `?=` for overridable defaults, `+=` to append
4. `.PHONY` for non-file targets
5. `.DELETE_ON_ERROR` to clean failed builds
6. Consistent variable naming (UPPERCASE for user-facing)

## Reference Files

- **references/cmake-patterns.md** — Complete modern CMake patterns (library export, install, presets, toolchains)
- **references/make-patterns.md** — Advanced Make patterns (multi-directory, cross-compilation, dependencies)
- **references/antipatterns.md** — Common mistakes and their fixes

## Quality Checklist

Before finalizing any build file:

### CMake
- [ ] Every target has a namespaced alias
- [ ] All `target_*` calls specify scope (`PRIVATE`/`PUBLIC`/`INTERFACE`)
- [ ] No directory-level commands (`include_directories`, etc.)
- [ ] Generator expressions for build/install differences
- [ ] Version requirements on `find_package`
- [ ] `cmake_minimum_required` reflects actual features used

### Make
- [ ] All variables use `?=` or `+=` appropriately
- [ ] Automatic dependency generation (`-MMD -MP`)
- [ ] Pattern rules instead of repeated explicit rules
- [ ] `.PHONY` declared for non-file targets
- [ ] Clean target removes all generated files
- [ ] Build artifacts in separate directory from source
101 changes: 101 additions & 0 deletions .claude/skills/dev-cmake/assets/CMakeLists-library.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
cmake_minimum_required(VERSION 3.16)
project(MyLibrary
VERSION 1.0.0
DESCRIPTION "A well-structured C++ library"
LANGUAGES CXX
)

# ============================================================================
# Options
# ============================================================================
option(MYLIB_BUILD_TESTS "Build unit tests" OFF)
option(MYLIB_BUILD_EXAMPLES "Build examples" OFF)

# ============================================================================
# Library Target
# ============================================================================
add_library(mylib)
add_library(${PROJECT_NAME}::mylib ALIAS mylib)

target_sources(mylib
PRIVATE
src/mylib.cpp
# PUBLIC headers via FILE_SET (CMake 3.23+) or target_include_directories
)

target_compile_features(mylib PUBLIC cxx_std_17)

target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)

# Uncomment and modify for dependencies:
# find_package(fmt REQUIRED)
# target_link_libraries(mylib PUBLIC fmt::fmt)

target_compile_options(mylib
PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)

set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)

# ============================================================================
# Installation
# ============================================================================
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

install(TARGETS mylib
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)

install(FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

# ============================================================================
# Testing
# ============================================================================
if(MYLIB_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
Loading
Loading