Получение cmake для создания из исходного кода без обертывания скриптов?

20

Я пытаюсь получить cmake для сборки в директорию 'build', как в project/build , где CMakeLists.txt находится в project/ .

Я знаю, что могу:

mkdir build
cd build
cmake ../

, но это громоздко. Я мог бы поместить его в скрипт и назвать его, но тогда неприятно давать разные аргументы cmake (например, -G «MSYS MakeFiles»), или мне нужно будет отредактировать этот файл на каждой платформе.

Я хотел бы сделать что-то вроде SET (CMAKE_OUTPUT_DIR build) в главном файле CMakeLists.txt. Скажите, пожалуйста, что это возможно, и если да, то как? Или какой-то другой метод сборки источника, который упрощает указание разных аргументов?

    
задан Max 21.06.2012 в 18:57
источник
  • У меня такая же архитектура проекта, как и вы. Мой «build» dir всегда здесь. Я лично считаю, что набрав cmake .. не так уж и много. –  Offirmo 25.06.2012 в 15:56

3 ответа

31

Вы можете использовать недокументированные опции CMake -H и -B , чтобы указать исходный и двоичный каталог при вызове cmake :

cmake -H. -Bbuild -G "MSYS Makefiles"

Это будет искать CMakeLists.txt в текущей папке и создать папку build (если она еще не существует).

    
ответ дан sakra 21.06.2012 в 20:05
  • Хорошие варианты. Но Макс сказал: «Неприятно давать разные аргументы cmake». Он хочет его в CMakeList. –  Offirmo 25.06.2012 в 14:56
5

Решение, которое я нашел недавно, состоит в том, чтобы объединить концепцию сборки вне исходного кода с оболочкой Makefile.

В моем файле CMakeLists.txt верхнего уровня я включаю следующее, чтобы предотвратить встроенные сборки:

if ( ${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} )
    message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt." )
endif()

Затем я создаю Makefile верхнего уровня и включаю следующее:

# -----------------------------------------------------------------------------
# CMake project wrapper Makefile ----------------------------------------------
# -----------------------------------------------------------------------------

SHELL := /bin/bash
RM    := rm -rf
MKDIR := mkdir -p

all: ./build/Makefile
    @ $(MAKE) -C build

./build/Makefile:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake ..)

distclean:
    @  ($(MKDIR) build > /dev/null)
    @  (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
    @- $(MAKE) --silent -C build clean || true
    @- $(RM) ./build/Makefile
    @- $(RM) ./build/src
    @- $(RM) ./build/test
    @- $(RM) ./build/CMake*
    @- $(RM) ./build/cmake.*
    @- $(RM) ./build/*.cmake
    @- $(RM) ./build/*.txt

ifeq ($(findstring distclean,$(MAKECMDGOALS)),)
    $(MAKECMDGOALS): ./build/Makefile
    @ $(MAKE) -C build $(MAKECMDGOALS)
endif

Заданный по умолчанию all вызывается путем ввода make и вызывает целевой ./build/Makefile .

Первое, на что нацелен ./build/Makefile - создать каталог build , используя $(MKDIR) , который является переменной для mkdir -p . Каталог build - это то, где мы будем выполнять нашу сборку вне источника. Мы предоставляем аргумент -p , чтобы убедиться, что mkdir не кричит на нас, пытаясь создать каталог, который может уже существовать.

Во-вторых, цель ./build/Makefile заключается в изменении каталогов в каталоге build и вызове cmake .

Возвращаясь к цели all , мы вызываем $(MAKE) -C build , где $(MAKE) - переменная Makefile, автоматически созданная для make . make -C изменяет каталог перед тем, как что-либо сделать. Поэтому использование $(MAKE) -C build эквивалентно выполнению cd build; make .

Подводя итог, вызов этой оболочки Makefile с make all или make эквивалентен выполнению:

mkdir build
cd build
cmake ..
make 

Целевой distclean вызывает cmake .. , затем make -C build clean и, наконец, удаляет все содержимое из каталога build . Я считаю, что это именно то, что вы просили в своем вопросе.

Последняя часть Makefile оценивает, является ли заданный пользователем целевой объект или не является distclean . Если нет, это приведет к изменению каталогов до build перед его вызовом. Это очень мощно, потому что пользователь может напечатать, например, make clean , и Makefile преобразует это в эквивалент cd build; make clean .

В заключение, эта оболочка Makefile в сочетании с обязательной конфигурацией CMake для сборки вне исходного кода делает так, чтобы пользователь никогда не должен взаимодействовать с командой cmake . Это решение также обеспечивает элегантный способ удаления всех выходных файлов CMake из каталога build .

P.S. В Makefile мы используем префикс @ для подавления вывода из команды оболочки и префикс @- для игнорирования ошибок из команды оболочки. При использовании rm как части distclean , команда возвращает ошибку, если файлы не существуют (они, возможно, были удалены уже с использованием командной строки с rm -rf build или они никогда не были сгенерированы в первом место). Эта обратная ошибка заставит наш Makefile выйти. Для предотвращения этого используется префикс @- . Это приемлемо, если файл уже был удален; мы хотим, чтобы наш Makefile продолжал идти и удалял остальные.

Еще одно замечание: этот Makefile может не работать, если вы используете переменное число переменных CMake для создания вашего проекта, например, cmake .. -DSOMEBUILDSUSETHIS:STRING="foo" -DSOMEOTHERBUILDSUSETHISTOO:STRING="bar" . Этот Makefile предполагает, что вы последовательно ссылаетесь на CMake, набрав cmake .. или предоставив cmake согласованное количество аргументов (которые вы можете включить в свой Makefile).

Наконец, кредит, в котором должен быть кредит. Эта оболочка Makefile была адаптирована из Makefile, предоставленной Шаблоном проектов приложений C ++ .

Этот ответ был первоначально опубликован ответ дан Hernan Villanueva 16.04.2015 в 17:10

1

Основываясь на предыдущих ответах, я написал следующий модуль, который вы можете включить для принудительной сборки внекорпоративной сборки.

set(DEFAULT_OUT_OF_SOURCE_FOLDER "cmake_output")

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(WARNING "In-source builds not allowed. CMake will now be run with arguments:
        cmake -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
")

    # Run CMake with out of source flag
    execute_process(
            COMMAND ${CMAKE_COMMAND} -H. -B${DEFAULT_OUT_OF_SOURCE_FOLDER}
            WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})

    # Cause fatal error to stop the script from further execution
    message(FATAL_ERROR "CMake has been ran to create an out of source build.
This error prevents CMake from running an in-source build.")
endif ()

Это работает, однако я уже заметил два недостатка:

  • Когда пользователь ленив и просто запускает cmake . , они всегда будут видеть FATAL_ERROR . Я не мог найти другого способа предотвратить использование CMake другими операциями и выйти раньше.
  • Любые аргументы командной строки, переданные исходному вызову cmake , не будут переданы в «вызов сборки из-источника».

Предложения по улучшению этого модуля приветствуются.

    
ответ дан Arno Moonen 31.05.2016 в 10:35