¿Es mejor especificar archivos fuente con GLOB o cada archivo individualmente en CMake?

157

CMake ofrece varias formas de especificar los archivos de origen para un destino. Una es usar globbing ( documentación ), por ejemplo:

FILE(GLOB MY_SRCS dir/*)

Otro método es especificar cada archivo individualmente.

¿Qué camino se prefiere? Globbing parece fácil, pero escuché que tiene algunas desventajas.

Marenz
fuente

Respuestas:

185

Revelación completa: Originalmente preferí el enfoque global por su simplicidad, pero a lo largo de los años he llegado a reconocer que enumerar explícitamente los archivos es menos propenso a errores para proyectos grandes de múltiples desarrolladores.

Respuesta original:


Las ventajas del globbing son:

  • Es fácil agregar nuevos archivos, ya que solo se enumeran en un lugar: en el disco. No globbing crea duplicación.

  • Su archivo CMakeLists.txt será más corto. Esta es una gran ventaja si tiene muchos archivos. No globular te hace perder la lógica CMake entre grandes listas de archivos.

Las ventajas de usar listas de archivos codificados son:

  • CMake hará un seguimiento de las dependencias de un nuevo archivo en el disco correctamente: si usamos glob, los archivos que no se hayan bloqueado la primera vez cuando ejecutaste CMake no serán detectados

  • Se asegura de que solo se agreguen los archivos que desea. Globbing puede recoger archivos perdidos que no desea.

Para evitar el primer problema, simplemente puede "tocar" el CMakeLists.txt que hace el glob, ya sea utilizando el comando táctil o escribiendo el archivo sin cambios. Esto obligará a CMake a volver a ejecutar y recoger el nuevo archivo.

Para solucionar el segundo problema, puede organizar su código cuidadosamente en directorios, que es lo que probablemente haga de todos modos. En el peor de los casos, puede usar el list(REMOVE_ITEM)comando para limpiar la lista global de archivos:

file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})

La única situación real en la que esto puede morderte es si estás usando algo como git-bisect para probar versiones anteriores de tu código en el mismo directorio de compilación. En ese caso, es posible que deba limpiar y compilar más de lo necesario para asegurarse de obtener los archivos correctos en la lista. Este es un caso de esquina, y uno en el que ya estás alerta, que en realidad no es un problema.

richq
fuente
1
También es malo con el globbing: los archivos difftool de git se almacenan como $ basename. $ Ext. $ Type. $ Pid. $ Ext que pueden causar errores divertidos al intentar compilar después de una única resolución de fusión.
Mathstuf
9
Creo que esta respuesta pasa por alto los inconvenientes de cmake que faltan nuevos archivos, Simply "touch" the CMakeLists.txtestá bien si usted es el desarrollador, pero para otros que construyen su software, realmente puede ser un problema que su compilación falla después de la actualización y la carga recae en ellos para investigar por qué.
ideasman42
36
¿Sabes que? Desde que escribí esta respuesta hace 6 años , he cambiado de opinión un poco y ahora prefiero enumerar explícitamente los archivos. Su única desventaja es que "es un poco más trabajo agregar un archivo", pero le ahorra todo tipo de dolores de cabeza. Y en muchos sentidos, explícito es mejor que implícito.
richq
1
@richq ¿ Este git hook te haría reconsiderar tu posición actual? :)
Antonio
8
Bueno, como dice Antonio, los votos fueron dados por abogar por el enfoque "global". Cambiar la naturaleza de la respuesta es algo que los cebos deben cambiar. Como compromiso, he agregado una edición para reflejar mi opinión modificada. Pido disculpas a Internet por causar tal tormenta en una taza de té
:-P
113

La mejor manera de especificar archivos fuente en CMake es enumerándolos explícitamente .

Los propios creadores de CMake aconsejan no usar globbing.

Ver: https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(No recomendamos usar GLOB para recopilar una lista de archivos fuente de su árbol fuente. Si no cambia el archivo CMakeLists.txt cuando se agrega o elimina una fuente, el sistema de compilación generado no puede saber cuándo pedirle a CMake que se regenere).

Por supuesto, es posible que desee saber cuáles son las desventajas: ¡siga leyendo!


Cuando falla Globbing:

La gran desventaja de globbing es que crear / eliminar archivos no actualizará automáticamente el sistema de compilación.

Si usted es la persona que agrega los archivos, esto puede parecer una compensación aceptable, sin embargo, esto causa problemas a otras personas que construyen su código, actualizan el proyecto desde el control de versiones, ejecutan la compilación y luego se contactan con usted, quejándose de que
"la compilación es roto".

Para empeorar las cosas, la falla generalmente produce algún error de vinculación que no da ninguna pista sobre la causa del problema y se pierde tiempo para solucionarlo.

En un proyecto en el que trabajé, comenzamos a englobar, pero recibimos tantas quejas cuando se agregaron nuevos archivos, que fue una razón suficiente para enumerar explícitamente los archivos en lugar de glob.

Esto también rompe los flujos de trabajo comunes de git
( git bisecty el cambio entre ramas de características).

Por lo tanto, no podría recomendar esto, los problemas que causa superan con creces la conveniencia, cuando alguien no puede construir su software debido a esto, puede perder mucho tiempo para rastrear el problema o simplemente darse por vencido.

Y otra nota, solo recordar tocar CMakeLists.txtno siempre es suficiente, con las compilaciones automatizadas que usan globbing, tuve que ejecutar cmakeantes de cada compilación ya que los archivos podrían haberse agregado / eliminado desde la última compilación *.

Excepciones a la regla:

Hay momentos en que es preferible el pegadizo:

  • Para configurar CMakeLists.txtarchivos para proyectos existentes que no usan CMake.
    Es una forma rápida de obtener toda la fuente referenciada (una vez que el sistema de compilación se está ejecutando, reemplace globbing con listas de archivos explícitas).
  • Cuando CMake no se utiliza como el sistema de compilación principal , si, por ejemplo, está utilizando un proyecto que no está utilizando CMake y desea mantener su propio sistema de compilación para él.
  • Para cualquier situación en la que la lista de archivos cambia con tanta frecuencia que resulta poco práctico mantenerla. En este caso, podría ser útil, pero luego debe aceptar la ejecución cmakepara generar archivos de compilación cada vez para obtener una compilación confiable / correcta (lo que va en contra de la intención de CMake: la capacidad de dividir la configuración de la compilación ) .

* Sí, podría haber escrito un código para comparar el árbol de archivos en el disco antes y después de una actualización, pero esta no es una buena solución y algo mejor le queda al sistema de compilación.

ideasman42
fuente
9
"La gran desventaja de globbing es que crear nuevos archivos no actualizará automáticamente el sistema de compilación". Pero, ¿no es cierto que si no se pega, todavía tiene que actualizar manualmente CMakeLists.txt, lo que significa que cmake todavía no actualiza automáticamente el sistema de compilación? Parece que de cualquier manera debe recordar hacer algo manualmente para que se creen los nuevos archivos. Tocar CMakeLists.txt parece más fácil que abrirlo y editarlo para agregar el nuevo archivo.
Dan
17
@Dan, para su sistema, claro, si solo se desarrolla solo, está bien, pero ¿qué pasa con todos los demás que construyen su proyecto? ¿Vas a enviarles un correo electrónico para que toquen manualmente el archivo CMake? cada vez que se agrega o elimina un archivo? - Almacenar la lista de archivos en CMake asegura que la compilación siempre use los mismos archivos que vcs conoce. Créame, esto no es solo un detalle sutil: cuando su compilación falla para muchos desarrolladores, envían listas de correo y preguntan en IRC que el código está roto. Nota: (Incluso en su propio sistema, puede volver al historial de git, por ejemplo, y no pensar en entrar y tocar archivos CMake)
ideasman42
2
Ah, no había pensado en ese caso. Esa es la mejor razón por la que he escuchado contra el globbing. Deseo que los documentos de cmake se expandan sobre por qué recomiendan que las personas eviten el engorde.
Dan
1
He estado pensando en la solución de escribir la marca de tiempo de la última ejecución de cmake en el archivo. Los únicos problemas son: 1) es probable que tenga que hacerlo cmake para que sea multiplataforma, por lo que debemos evitar que cmake se ejecute por segunda vez de alguna manera. 2) Posiblemente más conflictos de fusión (que aún suceden con la lista de archivos por cierto). En realidad, en este caso podrían resolverse de manera trivial tomando una marca de tiempo posterior.
Predelnik
2
@ tim-mb, "Pero sería bueno que CMake creara un archivo filetree_updated que pudieras registrar, que cambiaría automáticamente cada vez que se actualizaran los archivos". - Acabas de describir exactamente lo que hace mi respuesta.
Glen Knowles
22

En CMake 3.12, los comandos file(GLOB ...)yfile(GLOB_RECURSE ...) obtuvieron una CONFIGURE_DEPENDSopción que vuelve a ejecutar cmake si el valor del globo cambia. Como esa era la principal desventaja de buscar archivos fuente, ahora está bien hacerlo:

# Whenever this glob's value changes, cmake will rerun and update the build with the
# new/removed files.
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")

add_executable(my_target ${sources})

Sin embargo, algunas personas aún recomiendan evitar la búsqueda de fuentes. De hecho, la documentación establece:

No recomendamos usar GLOB para recopilar una lista de archivos fuente de su árbol fuente. ... La CONFIGURE_DEPENDSbandera puede no funcionar de manera confiable en todos los generadores, o si se agrega un nuevo generador en el futuro que no pueda soportarlo, los proyectos que lo usen se bloquearán. Incluso si CONFIGURE_DEPENDSfunciona de manera confiable, todavía hay un costo para realizar la verificación en cada reconstrucción.

Personalmente, considero los beneficios de no tener que administrar manualmente la lista de archivos de origen para superar los posibles inconvenientes. Si tiene que volver a cambiar a los archivos de la lista manual, esto se puede lograr fácilmente simplemente imprimiendo la lista de origen global y pegándola nuevamente.

Justin
fuente
Si su sistema de compilación realiza un ciclo completo de compilación y compilación (elimine el directorio de compilación, ejecute cmake desde allí y luego invoque el archivo MAKE), siempre que no extraigan archivos no deseados, ¿seguramente no hay inconvenientes en el uso de fuentes GLOBbed? En mi experiencia, la parte cmake se ejecuta mucho más rápido que la compilación, por lo que de todos modos no es una gran sobrecarga
Den-Jason
9

Puede glob con seguridad (y probablemente debería) a costa de un archivo adicional para contener las dependencias.

Agregue funciones como estas en alguna parte:

# Compare the new contents with the existing file, if it exists and is the 
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
    set(old_content "")
    if(EXISTS "${path}")
        file(READ "${path}" old_content)
    endif()
    if(NOT old_content STREQUAL content)
        file(WRITE "${path}" "${content}")
    endif()
endfunction(update_file)

# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of 
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
    set(deps_file "CMakeDeps.cmake")
    # Normalize the list so it's the same on every machine
    list(REMOVE_DUPLICATES deps)
    foreach(dep IN LISTS deps)
        file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
        list(APPEND rel_deps ${rel_dep})
    endforeach(dep)
    list(SORT rel_deps)
    # Update the deps file
    set(content "# generated by make process\nset(sources ${rel_deps})\n")
    update_file(${deps_file} "${content}")
    # Include the file so it's tracked as a generation dependency we don't
    # need the content.
    include(${deps_file})
endfunction(update_deps_file)

Y luego ve a pelear:

file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})

Todavía está analizando las dependencias explícitas (¡y activando todas las compilaciones automatizadas!) Como antes, solo está en dos archivos en lugar de uno.

El único cambio en el procedimiento es después de haber creado un nuevo archivo. Si no se bloquea, el flujo de trabajo consiste en modificar CMakeLists.txt desde Visual Studio y reconstruir, si lo hace, ejecuta cmake explícitamente, o simplemente toca CMakeLists.txt.

Glen Knowles
fuente
Al principio pensé que era una herramienta que actualizaría automáticamente los Makefiles cuando se agrega un archivo fuente, pero ahora veo cuál es su valor. ¡Agradable! Esto resuelve la preocupación de que alguien actualice desde el repositorio y tenga makeerrores extraños en el enlazador.
Cris Luengo
1
Creo que este podría ser un buen método. Por supuesto, uno aún debe recordar activar cmake después de agregar o eliminar un archivo, y también es necesario confirmar este archivo de dependencia, por lo que es necesaria cierta educación del lado del usuario. El principal inconveniente podría ser que este archivo de dependencia podría originar desagradables conflictos de fusión que podrían ser difíciles de resolver sin requerir nuevamente que el desarrollador comprenda algo del mecanismo.
Antonio
1
Esto no funcionará si su proyecto ha incluido archivos condicionalmente (por ejemplo, algunos archivos que solo se usan cuando una función está habilitada, o solo se usa para un sistema operativo en particular). Es bastante común para el software portátil que algunos archivos solo se usen para plataformas específicas.
ideasman42
0

¡Especifique cada archivo individualmente!

Utilizo un CMakeLists.txt convencional y un script de Python para actualizarlo. Ejecuto el script de Python manualmente después de agregar archivos.

Vea mi respuesta aquí: https://stackoverflow.com/a/48318388/3929196

palfi
fuente