Tengo un proyecto que se está volviendo lo suficientemente grande como para cambiar de un Makefile simple a CMake. Mi proyecto contiene solo una aplicación, pero admite dos tableros diferentes (cada tablero tiene un conjunto de archivos fuente y algunas definiciones específicas para el tablero). La estructura del proyecto (simplificada) actualmente se ve así:
CMakeLists.txt src/ ├─ board/ │ ├─ board_a/ │ │ ├─ board.c │ ├─ board_b/ │ │ ├─ board.c ├─ app/ │ ├─ main.c CMakeLists.txt Podría (y actualmente lo he hecho) simplemente poner todo en el archivo raíz CMakeLists.txt, pero no quiero que ese archivo se vuelva enorme a medida que se expande el proyecto. Esencialmente, me pregunto cómo puedo crear un proyecto que me permita construir un ejecutable para cualquiera de los tableros donde cada ejecutable puede tener un conjunto separado de definiciones (que se usan en los archivos board.c y main.c ), archivos fuente ( board.c en este caso, mientras comparte main.c ), y puede compartir opciones de compilación/vinculador (quiero compartir algunas configuraciones de compilación comunes entre ambos ejecutables) sin tener un CMakeLists.txt enorme.
Ya tiene un src/CMakeLists.txt , por lo que es parte del camino hacia allí. Ponga su configuración de compilación general (dependencias, versión estándar de C, indicadores globales del compilador) en el CMakeLists.txt de nivel superior. En los subdirectorios, coloque solo sus comandos CMake para los ejecutables, o cualquier destino que tenga sentido localmente.
(Aparte, ¿defina "enorme"? Aquí tengo CMakeLists de alto nivel que tienen entre 200 y 950 líneas, pero también he visto monstruosidades de 3000 líneas)
Personalmente, desde el boceto mínimo del diseño de origen aquí, haría:
src/CMakeLists.txt un comando add_subdirectory() para cada tablero , por ejemplo, add_subdirectory(board/board_a) . Si lo desea, set() una variable en una lista de nombres de tableros y puede iterar sobre ella.OBJECT ) con el nombre del tablero, con las fuentes de ese tablero. Por ejemplo add_library(board_a OBJECT board.c)src/CMakeLists.txt nuevamente, para cada tablero, agregue un ejecutable con la fuente de app/ y enlace a la biblioteca definida para el tablero, como add_executable(exe_board_a app/source.c) target_link_library(exe_board_a PRIVATE board_a) Si hay consideraciones especiales para ese ejecutable, configúrelas allí también. Los indicadores de compilación se pueden obtener de los objetivos de la biblioteca (entonces use STATIC, no OBJECT). Esto mueve la mayoría de las listas largas de fuentes y potencialmente compila indicadores al CMakeLists.txt por placa y convierte el nivel intermedio en una larga lista de "hacer que esto sea ejecutable".
cómo puedo crear un proyecto que me permita construir un ejecutable para cualquiera de los tableros donde cada ejecutable puede tener un conjunto separado de definiciones
Coloque CMakeLists.txt dentro board_a que hace add_library(board_a board.c) .
Coloque CMakeLists.txt dentro board_b que hace add_library(board_b board.c) .
Si los tableros son algo similares:
CMakeLists.txt cree un add_executable(exe_board_a main.c) que target_link_libraries(exe_board_a PUBLIC board_a) .exe_board_bSi los tableros no están relacionados, se necesitan muchos indicadores de compilador diferentes o archivos de cadena de herramientas separados o incluso compiladores diferentes:
En la raíz CMakeLists.txt add_executable(exe main.c) que hace target_link_libraries(exe PUBLIC ${USE_BOARD}) .
Compile su proyecto dos veces con cmake -DUSE_BOARD=board_a y cmake -DUSE_BOARD=board_b . Use dos directorios de compilación separados. La compilación doble se puede programar con un script personalizado, un Makefile personalizado raíz, o puede investigar cmake-presets.
Recuerde usar los comandos target_* , no los comandos específicos del directorio.
Esto no es tan malo con las bibliotecas de objetos:
cmake_minimum_required(VERSION 3.22) project(boards LANGUAGES C) add_library(board_a OBJECT src/board_a/board.c) target_compile_definitions( board_a PUBLIC "defines for board_a.c and main.c") add_library(board_b OBJECT src/board_b/board.c) target_compile_definitions( board_b PUBLIC "defines for board_b.c and main.c") add_executable(main_a src/app/main.c) target_link_libraries(main_a PRIVATE board_a) add_executable(main_b src/app/main.c) target_link_libraries(main_b PRIVATE board_b) Por supuesto, podría mover las llamadas add_library a subdirectorios (a través add_subdirectory ), pero el truco que estoy usando aquí es poner cosas como defines como requisitos de uso PÚBLICO para las bibliotecas de objetos, de modo que se propaguen a main.c cuando la aplicación asociada compila.
Tendría una llamada add_executable por tablero en el nivel superior que, por supuesto, podría colocar en un bucle si termina con muchos tableros.