Изменить значение по умолчанию CMAKE_CXX_FLAGS_DEBUG и друзей в CMake

Я хотел бы изменить значения по умолчанию для CMAKE_CXX_FLAGS_RELEASE или же CMAKE_CXX_FLAGS_DEBUG в CMake. По сути, у меня есть некоторые значения по умолчанию для проекта, которые немного отличаются от CMake (например, для выпуска), и мне не нужно спрашивать себя: "О, их -O3 или наш -O2 имеют преимущество при добавлении с add_compile_options".

Теперь я знаю, как установить эти значения, но я не знаю, как сделать их редактируемыми пользователем двумя обычными способами: используя -DCMAKE_CXX_FLAGS_DEBUG=yourflags в командной строке или путем настройки его с помощью ccmake или CMakeSetup.

Проблема в том, что CMAKE устанавливает и кэширует свои собственные значения по умолчанию для них, и если вы попытаетесь перезаписать переменные без использования FORCE, "значения по умолчанию" никогда не изменятся. Если я использую FORCE в моей команде set: set(CMAKE_CXX_FLAGS_DEBUG blah CACHE STRING "" FORCE), он будет перезаписывать его каждый раз при запуске скрипта, исключая возможность для пользователя изменить его, если он пожелает.

Мне удалось взломать его для работы с CCMAKE, выполнив следующее, но это по-прежнему не работает с cmake -DCMAKE_CXX_FLAGS_DEBUG поскольку он перезаписывает изменение пользователя ПОСЛЕ того, как он это сделал:

set(DEFAULTS_SET FALSE CACHE BOOL "")
set(CMAKE_CXX_FLAGS_DEBUG "-this -that" CACHE STRING "" FORCE)
set(DEFAULTS_SET TRUE CACHE BOOL "" FORCE)

Очевидно, что это неприятный хак и не работает полностью (в случае cmake -Dwhwhat =thisorthat). Я мог бы добавить и другие типы сборки, но я не понимаю, почему это необходимо, чтобы просто изменить несколько простых вещей.

Изменить 1 марта 2015 г.:

Я создал решение, которое работает, хотя я все еще не в восторге от того, что я должен сделать. Я видел другие комментарии, которые решают проблему установки CMAKE_CXX_FLAGS_DEBUG и друзья, не имея их, получают удар, но это первоначально не работало для меня, потому что я пытался также выбрать их на основе используемого компилятора. Однако компилятор не определен, пока он не заполнит переменные для меня. Хитрость, которую я использовал, заключается в следующем. Вы должны установить переменные флагов на что-то особенное перед командой проекта.

set(CMAKE_CXX_FLAGS_DEBUG "_UNSET" CACHE STRING "")
project(your_project C CXX)

if(${CMAKE_CXX_FLAGS_DEBUG} STREQUAL "_UNSET")
    # Do some compiler switching here and then set your flags with FORCE.
    set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
endif()

Теперь это позволяет мне выбирать значения по умолчанию, которые полностью переопределяются через командную строку с -D или в cmake-gui.

2 ответа

Решение

Я просто хотел добавить четыре варианта, которые я вижу:

  1. Наличие ваших собственных файлов инструментов, содержащих пресеты для каждого поддерживаемого вами компилятора, например:

    GNUToolchain.cmake

    set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "")
    

    А затем использовать его с

    cmake -DCMAKE_TOOLCHAIN_FILE:string=GNUToolchain.cmake ...
    
  2. Вы можете попытаться определить компилятор, проверив CMAKE_GENERATOR (который действителен до project() команды):

    CMakeLists.txt

    if("${CMAKE_GENERATOR}" MATCHES "Makefiles" OR 
       ("${CMAKE_GENERATOR}" MATCHES "Ninja" AND NOT WIN32))
        set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "")
    endif()
    
    project(your_project C CXX)
    
  3. Ты можешь использовать CMAKE_USER_MAKE_RULES_OVERRIDE дать сценарий со своим собственным ..._INIT ценности:

    Он загружается после загрузки встроенного компилятора CMake и информационных модулей платформы, но до использования информации. Файл может установить переменные информации о платформе, чтобы переопределить значения по умолчанию CMake.

    MyInitFlags.cmake

    # Overwrite the init values choosen by CMake
    if (CMAKE_COMPILER_IS_GNUCXX)
        set(CMAKE_CXX_FLAGS_DEBUG_INIT "-ggdb3 -O0")
    endif()
    

    CMakeLists.txt

    set(CMAKE_USER_MAKE_RULES_OVERRIDE "MyInitFlags.cmake")
    
    project(your_project C CXX)
    
  4. Вы можете упростить свое решение с 1 марта, проверив ..._INIT варианты переменных флага компилятора:

    CMakeLists.txt

    project(your_project C CXX)
    
    if (DEFINED CMAKE_CXX_FLAGS_DEBUG_INIT AND  
        "${CMAKE_CXX_FLAGS_DEBUG_INIT}" STREQUAL "${CMAKE_CXX_FLAGS_DEBUG}")
        # Overwrite the init values choosen by CMake
        if (CMAKE_COMPILER_IS_GNUCXX)
            set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
        endif()
    endif()
    

Комментарии:

Я предпочитаю и использую вариант toolchain. Но я признаю, что у него есть недостаток, заключающийся в том, что файл инструментария нужно давать вручную (если вы не звоните cmake через скрипт / пакетный файл).

Рекомендации:

Ответ Флориана с использованием файлов набора инструментов хорошо подходит для более ранних версий CMake. Но в CMake 3.19 добавлена ​​функция, называемая пресетами , которая помогает управлять общими наборами переменных кэша для вашего проекта. По сути, вы создаете как минимум один из двух файлов, CMakePresets.jsonа также CMakeUserPresets.json(обычно добавляют к .gitignoreили аналогичные), которые содержат спецификации того, как настроить проект.

Например, вы можете написать:

      {
  "version": 1,
  "cmakeMinimumRequired": {
    "major": 3,
    "minor": 19,
    "patch": 0
  },
  "configurePresets": [
    {
      "name": "default",
      "displayName": "Default",
      "description": "Build using Ninja and a GCC-like compiler",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/build",
      "cacheVariables": {
        "CMAKE_CXX_FLAGS_DEBUG": "-ggdb3 -O0"
      }
    },
    {
      "name": "default-vcpkg",
      "displayName": "Default (vcpkg)",
      "description": "Default build with vcpkg (from VCPKG_ROOT)",
      "inherits": "default",
      "cacheVariables": {
        "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
      }
    }
  ]
}

Затем из исходного каталога ваша командная строка CMake просто станет:

      $ cmake --preset=default

Этот подход имеет несколько преимуществ:

  1. Это делает командную строку намного проще
  2. Он совместим с другими файлами набора инструментов (например, vcpkg во втором пресете).
  3. Он может переопределять флаги, которые обычно безоговорочно добавляются в *_INITфлаги.
  4. Вам не нужно писать неуклюжую логику в вашем CMakeLists.txt.
  5. Предустановки доступны для пользователя, что важно, если вы распространяете библиотеку.

В дополнение к пунктам 4 и 5: добавление флагов — плохая идея, если только они не должны быть абсолютно необходимы для правильной компиляции, и нет встроенной функции для достижения этих флагов (например. CMAKE_CXX_STANDARD). Если кто-то попытается скомпилировать вашу библиотеку с помощью другого компилятора (или даже другой версии того же компилятора), он может столкнуться с проблемами, если, например, вы добавите слишком новый или неподдерживаемый флаг предупреждения. Вы можете обойти это с помощью выражений генератора и/или сложной логики (например, _UNDEFтрюк выше), но, как правило, проще и удобнее использовать набор инструментов или эти новые пресеты.

Например, чтобы правильно добавить -Wsuggest-override, вам нужно будет написать:

      target_compile_options(lib PRIVATE $<$<AND:$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5.1>,$<COMPILE_LANG_AND_ID:CXX,GNU>>:-Wsuggest-override>)

# ... or ...

# Note: only correct if using "PRIVATE". Must use a genex for INTERFACE/PUBLIC because the whole genex gets exported, whereas this flag will get exported verbatim.
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 5.1)
  target_compile_options(lib PRIVATE -Wsuggest-override)
endif ()

Или вы можете просто поместить флаг в цепочку инструментов/пресет, где вы уже знаете, какой компилятор вы используете.

Другие вопросы по тегам