Cómo estructurar proyectos en C: estas mejores prácticas me funcionaron
Estructurar proyectos en C y elegir herramientas de compilación se vuelve rápidamente polémico cuando entra en juego la complejidad del mundo real. Los comentaristas comparan los diseños tradicionales basados en Make (`src/`, `include/` y Makefiles escritos a mano) con sistemas modernos como Meson, CMake e incluso las herramientas de Rust con Cargo o las de Go, discutiendo las compensaciones entre simplicidad, flexibilidad, portabilidad y comodidad de “cero configuración”. A lo largo del debate también tratan la organización de encabezados y fuentes, las estrategias de pruebas, la generación de código, la compilación cruzada y si el ecosistema fragmentado de herramientas de C es un precio aceptable por su ubicuidad o una razón para preferir lenguajes más nuevos.
Estructura general del proyecto
- La estructura propuesta se considera muy similar al diseño “pitchfork” de C++ (
src/,include/, etc.). - Algunos sostienen que separar
.cy.hen directorios de nivel superior distintos es innecesario para encabezados internos; otros lo encuentran útil para implementaciones específicas de plataforma (por ejemplo,platform_windows.cfrente aplatform_linux.c). - Varios prefieren poner todos los subsistemas dentro de
src/y reservarinclude/solo para encabezados públicos/de biblioteca. - La distinción entre encabezados instalables y encabezados internos se considera crucial para las bibliotecas.
- A algunos no les gusta
bin/ylib/dentro del repositorio, y prefieren unPREFIXconfigurable (build/o$HOME/.local) y archivos de entorno que ajustenPATH.
Make, CMake y herramientas de compilación alternativas
- Múltiples recetas para Makefiles flexibles: reglas de patrón,
wildcard+patsubst, y listas de objetos generadas automáticamente para evitar actualizar los Makefiles manualmente. - Aclaración de que las reglas integradas de
%.osolo funcionan cuando los स्रोतes y los objetos comparten directorio; las soluciones incluyen VPATH o reglas explícitas. - Crítica al Makefile del artículo: depende de un comportamiento frágil del orden de compilación bajo
-j, guardacompile_commands.jsony mezcla directorios de editor/CI como “basura”. - Algunos abogan por compilaciones muy minimalistas (un único
all.cque#includetodo) para proyectos pequeños; los críticos señalan que eso no escala a sanitizers, pruebas ni CI. - Las opiniones sobre Make divergen: para algunos es simple, componible y escalable; para otros está anticuado y es demasiado delicado frente a Meson, Buck2, Xmake, el sistema de compilación de Zig, etc.
- CMake se ve como potente pero incómodo; hay debate sobre listar archivos explícitamente frente a usar globs (con compensaciones entre regeneración y corrección).
Herramientas, pruebas y generación de código
- Sugerencias: MinUnit, Clang-Tidy (por ejemplo, el perfil CERT), Clang-Format,
-Weverything, sanitizers (ASan/UBSan) y advertencias estrictas (-Werror=missing-declarations, etc.). - Visiones mixtas sobre las pruebas unitarias en C: algunos enfatizan aserciones y procedimientos sensibles al contexto; otros señalan proyectos de C muy probados como contraejemplos.
- Varios describen la colocación conjunta de pruebas unitarias con la implementación, además de convenciones como
*_test.cdetectadas automáticamente por Make. - Fuerte incentivo para usar generadores de código (Lua, Python, motores de plantillas, incluso C) y tratar
src/como generadores,gen/como C emitido yobj/como salida de compilación. - Discusión sobre compilar encabezados por separado y el intercambio entre
#pragma oncey los guardas de inclusión.
Paquetes, compilación cruzada y debate meta
- Deseo de simplicidad al estilo “Cargo”: una única herramienta estándar, configuración cero o mínima, disposición consistente.
- Otros argumentan que la antigüedad de C, la portabilidad y la diversidad de plataformas hacen irreal una sola herramienta estándar; en su lugar coexisten varios sistemas de paquetes/compilación (Conan, vcpkg, pkg-config, vendoring).
- Compilación cruzada: se menciona
zig ccy toolchains de estilo embebido; énfasis en separar la capa de plataforma detrás de interfaces. - Largo hilo meta sobre Rust frente a C: se elogian Cargo de Rust y las herramientas de Go; algunos se quejan de los desvíos de “reescribir en Rust”, mientras que otros dicen que comparar herramientas es justo y esperable.