c++ - online - descarga cmake
CMake: elimina una marca de compilación para una sola unidad de traducción (3)
Me gustaría eliminar una marca de compilación establecida para una unidad de traducción única. ¿Hay alguna forma de hacer esto? (por ejemplo, usando set_property
?)
Nota: el indicador de compilación no tiene ninguna negación -fno-name
(por cualquier motivo).
He intentado:
get_property(FLAGS TARGET target PROPERTY COMPILE_FLAGS)
string(REPLACE "-fname" "" FLAGS ${FLAGS})
set_property(TARGET target PROPERTY COMPILE_FLAGS ${FLAGS})
sin suerte La propiedad que quiero eliminar es parte de CMAKE_CXX_FLAGS
y, por lo tanto, esto no funciona.
Esta solución particular (?) .cpp
tanto para objetivos como para unidades de traducción individuales (un solo .cpp
.cpp). Requerirá CMake 3.7
o más reciente, ya que debemos recorrer todas las targes existentes utilizando la propiedad BUILDSYSTEM_TARGETS
, que se agregó en 3.7
. Si no puede usar 3.7
o más reciente, puede adaptar apply_global_cxx_flags_to_all_targets()
para tomar una lista de objetivos y especificarlos manualmente. Tenga en cuenta que debe considerar las siguientes macros como prueba de concepto. Probablemente necesitarán algunos ajustes y mejoras para cualquier proyecto que no sea muy pequeño.
El primer paso es recorrer todos los destinos existentes y aplicar CMAKE_CXX_FLAGS
a cada uno de ellos. Cuando se hace esto, CMAKE_CXX_FLAGS
. A continuación encontrarás una macro que hará esto. Probablemente esta macro debería llamarse en algún lugar al final de su CMakeLists.txt
nivel CMakeLists.txt
, cuando se hayan creado todos los objetivos, se hayan establecido todos los indicadores, etc. Es importante tener en cuenta que el comando get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
funciona en el directorio nivel, por lo que si usa add_subdirectory()
, debe llamar a esta macro en el nivel superior CMakeLists.txt
de ese subdirectorio.
#
# Applies CMAKE_CXX_FLAGS to all targets in the current CMake directory.
# After this operation, CMAKE_CXX_FLAGS is cleared.
#
macro(apply_global_cxx_flags_to_all_targets)
separate_arguments(_global_cxx_flags_list UNIX_COMMAND ${CMAKE_CXX_FLAGS})
get_property(_targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach(_target ${_targets})
target_compile_options(${_target} PUBLIC ${_global_cxx_flags_list})
endforeach()
unset(CMAKE_CXX_FLAGS)
set(_flag_sync_required TRUE)
endmacro()
Esta macro primero crea una lista de CMAKE_CXX_FLAGS
, luego obtiene una lista de todos los destinos y aplica CMAKE_CXX_FLAGS
a cada uno de los objetivos. Finalmente, se borra CMAKE_CXX_FLAGS
. _flag_sync_required
se usa para indicar si necesitamos forzar una reescritura de las variables en caché.
El siguiente paso depende de si desea eliminar una bandera de un objetivo o de una unidad de traducción en particular. Si desea eliminar una bandera de un destino, puede utilizar una macro similar a esta:
#
# Removes the specified compile flag from the specified target.
# _target - The target to remove the compile flag from
# _flag - The compile flag to remove
#
# Pre: apply_global_cxx_flags_to_all_targets() must be invoked.
#
macro(remove_flag_from_target _target _flag)
get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS)
if(_target_cxx_flags)
list(REMOVE_ITEM _target_cxx_flags ${_flag})
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_target_cxx_flags}")
endif()
endmacro()
Eliminar una bandera de una unidad de traducción en particular es un poco más complicado (a menos que haya pasado algo por alto). De todos modos, la idea es obtener primero las opciones de compilación del destino al que pertenece el archivo, y luego aplicar dichas opciones a todos los archivos de origen en ese objetivo, lo que nos permite manipular los indicadores de compilación para archivos individuales. Hacemos esto manteniendo una lista en caché de banderas de compilación para cada archivo del que queremos eliminarlas, y cuando se solicita una eliminación, la eliminamos de la lista en caché y luego volvemos a aplicar las banderas restantes. Las opciones de compilación para el objetivo en sí están desactivadas.
#
# Removes the specified compiler flag from the specified file.
# _target - The target that _file belongs to
# _file - The file to remove the compiler flag from
# _flag - The compiler flag to remove.
#
# Pre: apply_global_cxx_flags_to_all_targets() must be invoked.
#
macro(remove_flag_from_file _target _file _flag)
get_target_property(_target_sources ${_target} SOURCES)
# Check if a sync is required, in which case we''ll force a rewrite of the cache variables.
if(_flag_sync_required)
unset(_cached_${_target}_cxx_flags CACHE)
unset(_cached_${_target}_${_file}_cxx_flags CACHE)
endif()
get_target_property(_${_target}_cxx_flags ${_target} COMPILE_OPTIONS)
# On first entry, cache the target compile flags and apply them to each source file
# in the target.
if(NOT _cached_${_target}_cxx_flags)
# Obtain and cache the target compiler options, then clear them.
get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS)
set(_cached_${_target}_cxx_flags "${_target_cxx_flags}" CACHE INTERNAL "")
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "")
# Apply the target compile flags to each source file.
foreach(_source_file ${_target_sources})
# Check for pre-existing flags set by set_source_files_properties().
get_source_file_property(_source_file_cxx_flags ${_source_file} COMPILE_FLAGS)
if(_source_file_cxx_flags)
separate_arguments(_source_file_cxx_flags UNIX_COMMAND ${_source_file_cxx_flags})
list(APPEND _source_file_cxx_flags "${_target_cxx_flags}")
else()
set(_source_file_cxx_flags "${_target_cxx_flags}")
endif()
# Apply the compile flags to the current source file.
string(REPLACE ";" " " _source_file_cxx_flags_string "${_source_file_cxx_flags}")
set_source_files_properties(${_source_file} PROPERTIES COMPILE_FLAGS "${_source_file_cxx_flags_string}")
endforeach()
endif()
list(FIND _target_sources ${_file} _file_found_at)
if(_file_found_at GREATER -1)
if(NOT _cached_${_target}_${_file}_cxx_flags)
# Cache the compile flags for the specified file.
# This is the list that we''ll be removing flags from.
get_source_file_property(_source_file_cxx_flags ${_file} COMPILE_FLAGS)
separate_arguments(_source_file_cxx_flags UNIX_COMMAND ${_source_file_cxx_flags})
set(_cached_${_target}_${_file}_cxx_flags ${_source_file_cxx_flags} CACHE INTERNAL "")
endif()
# Remove the specified flag, then re-apply the rest.
list(REMOVE_ITEM _cached_${_target}_${_file}_cxx_flags ${_flag})
string(REPLACE ";" " " _cached_${_target}_${_file}_cxx_flags_string "${_cached_${_target}_${_file}_cxx_flags}")
set_source_files_properties(${_file} PROPERTIES COMPILE_FLAGS "${_cached_${_target}_${_file}_cxx_flags_string}")
endif()
endmacro()
Ejemplo
Tenemos esta estructura de proyecto muy simple:
source/
CMakeLists.txt
foo.cpp
bar.cpp
main.cpp
Supongamos que foo.cpp
viola -Wunused-variable
, y queremos deshabilitar esa bandera en ese archivo en particular. Para bar.cpp
, queremos deshabilitar -Werror
. Para main.cpp
, queremos eliminar -O2
, por el motivo que sea.
CMakeLists.txt
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
project(MyProject)
# Macros omitted to save space.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wunused-variable")
# CMake will apply this to all targets, so this will work too.
add_compile_options("-Werror")
add_executable(MyTarget foo.cpp bar.cpp main.cpp)
apply_global_cxx_flags_to_all_targets()
remove_flag_from_file(MyTarget foo.cpp -Wunused-variable)
remove_flag_from_file(MyTarget bar.cpp -Werror)
remove_flag_from_file(MyTarget main.cpp -O2)
Si, en cambio, desea eliminar un indicador de un destino, debe usar remove_flag_from_target()
, por ejemplo: remove_flag_from_target(MyTarget -O2)
Resultado
Salida antes de aplicar macros.
clang++ -Werror -O2 -Wunused-variable -o foo.cpp.o -c foo.cpp
clang++ -Werror -O2 -Wunused-variable -o bar.cpp.o -c bar.cpp
clang++ -Werror -O2 -Wunused-variable -o main.cpp.o -c main.cpp
Salida después de aplicar macros.
clang++ -Werror -O2 -o foo.cpp.o -c foo.cpp
clang++ -O2 -Wunused-variable -o bar.cpp.o -c bar.cpp
clang++ -Werror -Wunused-variable -o main.cpp.o -c main.cpp
Notas finales
Como se mencionó, esto debería verse como una prueba de concepto. Hay algunos problemas inmediatos a considerar:
- Solo considera
CMAKE_CXX_FLAGS
(común a todos los tipos de compilación), noCMAKE_CXX_FLAGS_<DEBUG|RELEASE>
etc. - Las macros solo pueden manejar una bandera a la vez
-
remove_flag_from_file()
solo puede manejar un archivo de entrada y destino como entrada a la vez. -
remove_flag_from_file()
espera un nombre de archivo, no una ruta. Pasar, digamos,source/foobar/foobar.cpp
no funcionará, ya quesource/foobar/foobar.cpp
se comparará confoobar.cpp
. Esto se puede solucionar utilizandoget_source_file_property()
y la propiedadLOCATION
y colocando una condición previa para que_file
sea una ruta completa.- ¿Qué sucede si un objetivo tiene dos o más archivos con el mismo nombre?
-
remove_flag_from_file()
probablemente se puede optimizar y mejorar mucho. - La llamada a
separate_arguments()
asume Unix.
La mayoría de estos deberían ser bastante fáciles de arreglar.
Espero que esto al menos lo empuje en la dirección correcta para resolver este problema. Déjame saber si necesito agregar algo a la respuesta.
Si no hay una opción de negación de bandera de compilación (que sería la forma estándar de cómo CMake maneja esto para los configuradores que no son IDE), debe eliminar esta bandera de compilación de CMAKE_CXX_FLAGS
.
Posibles enfoques
Convierta a
COMPILE_FLAGS
una propiedad de archivo fuente que seINHERITED
de una propiedad de directorio con el mismo nombre. Ahora puede modificar la propiedadCOMPILE_FLAGS
de cada directorio y archivo de origen individualmente:# Remove ''-fname'' from global flags string(REPLACE "-fname" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") define_property( SOURCE PROPERTY COMPILE_FLAGS INHERITED BRIEF_DOCS "brief-doc" FULL_DOCS "full-doc" ) # Add ''-fname'' for directory set_directory_properties(PROPERTIES COMPILE_FLAGS "-fname") add_executable(${PROJECT_NAME} "main.cpp") # Remove ''-fname'' from source file set_source_files_properties("main.cpp" PROPERTIES COMPILE_FLAGS "")
Si alguna propiedad, como
COMPILE_OPTIONS
, solo está disponible en el nivel de destino, tiene que mover los archivos para los que desea tener configuraciones diferentes a un nuevo destino independiente.Esto se puede hacer mediante el uso de bibliotecas OBJECT :
# Remove ''-fname'' from global flags string(REPLACE "-fname" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # Set ''-fname'' for directory, but if you also use add_compile_options() be aware that # this will overwrite your previous setting. Append/remove properties instead. set_directory_properties( PROPERTIES COMPILE_OPTIONS "-fname" ) # Remove ''-fname'' from source file(s) in separate object library target add_library(${PROJECT_NAME}_no_name OBJECT "main.cpp") set_target_properties( ${PROJECT_NAME}_no_name PROPERTIES COMPILE_OPTIONS "" ) # Use object library target in e.g. executable target add_executable(${PROJECT_NAME} $<TARGET_OBJECTS:${PROJECT_NAME}_no_name>)
Referencias
Una solución (no escalable y no portátil) está creando un comando personalizado con marcas modificadas. Un ejemplo mínimo que conseguí trabajando es el siguiente:
cmake_minimum_required(VERSION 2.8)
project(test)
# Some flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall")
# Remove unwanted flag
string(REPLACE "-Wall" "" CUSTOM_FLAGS ${CMAKE_CXX_FLAGS})
# Split the flags into a cmake list (; separated)
separate_arguments(CUSTOM_FLAGS UNIX_COMMAND ${CUSTOM_FLAGS})
# Custom command to build your one file.
add_custom_command(
OUTPUT out.o
COMMAND ${CMAKE_CXX_COMPILER}
ARGS ${CUSTOM_FLAGS} -c ${CMAKE_SOURCE_DIR}/out.cpp
-o ${CMAKE_BINARY_DIR}/out.o
MAIN_DEPENDENCY out.cpp)
add_executable(test test.cpp out.o)
Aunque está lejos de ser perfecto, funciona, que es lo que cuenta. Los CUSTOM_FLAGS
eran necesarios porque, de lo contrario, todos los espacios en CUSTOM_FLAGS
se escaparon (no sé cómo resolverlos rápidamente).