LINUX.ORG.RU

[cmake] Режимы сборки


0

0

В этом треде я изучаю CMake (cmake 2.6.4, Ubuntu 9.10/cmake 2.8.2, Windows 7). Скажу сразу, что все очень плохо. Как эта система работает, я не понимаю «в приниципе» (т.к. бака), а подходящая для полного новичка документация по ней заключается в грепании архива списка рассылки и паре хаутушек в гугле. Однако, ладно. Есть один CMakeLists.txt:

CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
SET (CMAKE_BUILD_TYPE "Debug")
PROJECT (hello)
ADD_EXECUTABLE (hello main.cpp)

Я делаю out-of-source build:

$ mkdir build
$ cd build
$ cmake ..
$ make VERBOSE=1

Программа при этом компилируется явно с CMAKE_BUILD_TYPE=«None», который по дефолту, Флаг "-g" не передается, варнинги не выдаются. Режим «Release» соответственно, тоже не включается. Свои кастомные режимы пока делать не пробовал. Обнаружил между делом, что если прогнать cmake два раза подряд, то нужные режимы включаются. Это баг или фича? Это, наверное, как-то связано с «кэшем»? Чтобы это ни было, как от этого избавиться?

Кроме того, как можно получить «обычный» вывод make вместо этого цветного пижонства, скрывающего все самое интересное, без утомительного добавления VERBOSE=1 в командную строку? В импровизированной доке упомянута переменная CMAKE_VERBOSE_MAKEFILE, но она не работает (команда «SET (CMAKE_VERBOSE_MAKEFILE TRUE)» просто ничего не делает).

Ах да, учитывая, что unix makefile generator одноконфигурационный (makefile генерируется либо для Debug, либо для Release, либо для любого другого типа сборки, в т.ч. кастомного), какие кто знает костыли для создания многоконфигурационного makefile'а?

Во-первых, не надо в CMakeLists менять то, что предназначено для указания при вызове cmake. За распространение проектов, которые форсят BUILD_TYPE или VERBOSE_MAKEFILE, удобные автору, а не мне, вообще надо руки вырывать.

Во-вторых, просто указываете переменные после PROJECT. Не знаю с чем это связяно.

какие кто знает костыли для создания многоконфигурационного makefile

Зачем? Вы, надеюсь, не собираетесь распространять сгенеренные Makefile?

slovazap ★★★★★ ()

Чтобы это ни было, как от этого избавиться?

CMAKE_BUILD_TYPE - внутренняя переменная, пользуйтесь просто BUILD_TYPE или опять же - подставляйте значение CMAKE_BUILD_TYPE из командной строки (вариант: cmake-gui/ccmake).

«обычный» вывод make вместо этого цветного пижонства

$ cmake -LA .
...
CMAKE_COLOR_MAKEFILE:BOOL=ON
...

Ставите в OFF при первой конфигурации:

cmake -DCMAKE_COLOR_MAKEFILE=OFF

какие кто знает костыли для создания многоконфигурационного makefile'а?

Можно написать собственный стрипт на том же CMake (чтобы не было лишних зависимостей), который будет делать несколько сборок с различными параметрами. А можно как-то так:

CMakeLists.txt

if ( CMAKE_BUILD_TYPE STREQUAL "Debug" )
	add_definitions( -DAPP_DEBUG )
endif()
add_executable( app_${CMAKE_BUILD_TYPE} main.cpp )

main.cpp

#include <stdio.h>

int main( int argc, char ** argv )
{
#ifdef APP_DEBUG
	printf( "Me DEBUG\n" );
#else
	printf( "Me RELEASE\n" );
#endif
	return 0;
}

app_debug_and_release/CMakeLists.txt

set( CMAKE_BUILD_TYPE "Debug" )
add_subdirectory( ".." "app_debug" )

set( CMAKE_BUILD_TYPE "Release" )
add_subdirectory( ".." "app_release" )

Проверяем:

$ mkdir build && cd build
$ cmake ../app_debug_and_release
$ make
Scanning dependencies of target app_Debug
[ 50%] Building CXX object app_debug/CMakeFiles/app_Debug.dir/main.o
Linking CXX executable app_Debug
[ 50%] Built target app_Debug
Scanning dependencies of target app_Release
[100%] Building CXX object app_release/CMakeFiles/app_Release.dir/main.o
Linking CXX executable app_Release
[100%] Built target app_Release
$
$ ./app_debug/app_Debug 
Me DEBUG
$ ./app_release/app_Release 
Me RELEASE
Dendy ★★★★★ ()
Ответ на: комментарий от slovazap

>За распространение проектов, которые форсят BUILD_TYPE или VERBOSE_MAKEFILE, удобные автору, а не мне, вообще надо руки вырывать.

можно использовать следующую конструкцию:

if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif (NOT CMAKE_BUILD_TYPE)

annulen ★★★★★ ()

Спасибо всем ответившим. Постараемся разобраться, что получилось.

Установка значения CMAKE_BUILD_TYPE чудесным образом заработала «как надо» дома на cmake 2.8, Ubuntu 10.04. Ладно, нервы дороги, спишем необходимость двойного запуска cmake на славную «фичу» версии 2.6. Назначение же переменной CMAKE_VERBOSE_MAKEFILE по-прежнему остается загадочным.

anonymous ()
Ответ на: комментарий от slovazap

>За распространение проектов ... вообще надо руки вырывать.

распространять сгенеренные Makefile?

Никто ничего распространять не собирается. Программа вообще проприетарная. Ваши политические мотивы мне понятны. Приводя примеры бредовых файлов, я пытаюсь методом тыка (а как еще прикажете изучать это?) разобраться в том, как же этот чертов cmake работает.

anonymous ()
Ответ на: комментарий от Dendy

>BUILD_TYPE

Странно. cmake 2.8 в упор не признает такую переменную. В справке я тоже ничего не нашёл.

CMAKE_COLOR_MAKEFILE

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

Можно написать собственный стрипт на том же CMake

Прошу прощения, написание скриптов на птичьем языке j-го парогенератора не вдохновляет совершенно, тем более, что информацию о нём надо собирать по крупицам.

А можно как-то так

Провалиться мне на этом месте, если это не то, что нужно! На самом деле, мне хотелось бы за один присест собирать еще больше конфигураций (как минимум, 4 - debug/release+пара своих кастомных с юниттестами), но мне кажется, что этот способ без проблем можно проапгрейдить и на мой случай. Еще раз спасибо.

anonymous ()
Ответ на: комментарий от anonymous

BUILD_TYPE

Странно. cmake 2.8 в упор не признает такую переменную. В справке я тоже ничего не нашёл.

Значит это у меня остаточная каша в голове после переезда с 2.6 на 2.8.

Прошу прощения, написание скриптов на птичьем языке j-го парогенератора не вдохновляет совершенно...

Имелось в виду, что вместо идущих подряд add_subdirectory(), предварённых параметрами сборки будет что-то вида:

app.cmake

macro( build_app WHERE )

  if ( NOT EXISTS "${WHERE}/CMakeCache.txt" )
    file( MAKE_DIRECTORY "${WHERE}" )
    execute_process( COMMAND "${CMAKE_COMMAND}" WORKING_DIRECTORY "${WHERE}" lots of configure variables "/path/to/app/dir" )
  endif()

  get_property( make_program CACHE "${WHERE}/CMakeCache.txt" CMAKE_MAKE_PROGRAM )
  execute_propess( COMMAND "${make_program}" WORKING_DIRECTORY "${WHERE}" )

endmacro()




build_app( "release" )
build_app( "debug" )

Запускаться которое будет так:

$ cmake -P app.cmake
Dendy ★★★★★ ()
Ответ на: комментарий от Dendy

>app.cmake

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

anonymous ()
Ответ на: комментарий от anonymous

app.cmake

Да, кстати, как многие уже наверно догадались, можно просто добавить в начало:

#!/usr/bin/cmake -P

сделать файл исполняемым:

$ chmod +x app.cmake

и запускать скрипт как:

$ ./app.cmake
Dendy ★★★★★ ()

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

<src>/build/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)
project(hello CXX)

set (CMAKE_BUILD_TYPE "Debug") 
add_subdirectory (".." "Debug")
 
set (CMAKE_BUILD_TYPE "Release") 
add_subdirectory (".." "Release")

set (CMAKE_BUILD_TYPE "Debug_Tests")
add_subdirectory (".." "Debug_Tests")

set (CMAKE_BUILD_TYPE "Release_Tests")
add_subdirectory (".." "Release_Tests")

<src>/CMakeLists.txt:

set(EXECBL_NAME "app")

if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
	add_executable(${EXECBL_NAME}-d main.cpp)
endif ()

if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
	add_executable(${EXECBL_NAME} main.cpp)
endif ()

if (${CMAKE_BUILD_TYPE} STREQUAL "Debug_Tests")
	set(CMAKE_BUILD_TYPE Debug)
	add_definitions(-DRUN_THESE_STUPID_UNITTESTS) 
	add_executable(${EXECBL_NAME}-dt main.cpp)
endif ()

if (${CMAKE_BUILD_TYPE} STREQUAL "Release_Tests")
	set(CMAKE_BUILD_TYPE Release)
	add_definitions(-DRUN_THESE_STUPID_UNITTESTS) 
	add_executable(${EXECBL_NAME}-t main.cpp)
endif ()

Его, видимо, можно незначительно улучшить, поубирать лишнюю пасту и добавить плюшек, но это мелочи. Непонятно главное - зачем здесь два файла CMakeLists.txt и вот эти вот иерархические инклюды? Разве технически эти два файла нельзя совместить в один? Или я опять что-то не так понимаю?

anonymous ()
Ответ на: комментарий от anonymous

то, что сверху - полный бред. снизу разумно, но add_executable вынеси в конец после всех ифов

annulen ★★★★★ ()
Ответ на: комментарий от anonymous

Поскольку цели у вас отличаются по имени, то конечно можно:

cmake_minimum_required(VERSION 2.6) 

project(hello CXX) 
 
set(EXECBL_NAME "app") 

macro( add_app_executable APP_BUILD_TYPE )

if (${APP_BUILD_TYPE} STREQUAL "Debug") 
   set(CMAKE_BUILD_TYPE Debug) 
   add_executable(${EXECBL_NAME}-d main.cpp) 
endif () 
 
if (${APP_BUILD_TYPE} STREQUAL "Release") 
   set(CMAKE_BUILD_TYPE Release) 
   add_executable(${EXECBL_NAME} main.cpp) 
endif () 
 
if (${APP_BUILD_TYPE} STREQUAL "Debug_Tests") 
   set(CMAKE_BUILD_TYPE Debug) 
   add_executable(${EXECBL_NAME}-dt main.cpp) 
   set_target_properties(${EXECBL_NAME}-dt PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS")
endif () 
 
if (${APP_BUILD_TYPE} STREQUAL "Release_Tests") 
   set(CMAKE_BUILD_TYPE Release) 
   add_executable(${EXECBL_NAME}-t main.cpp) 
   set_target_properties(${EXECBL_NAME}-t PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS")
endif () 

endmacro()


add_app_executable("Debug")
add_app_executable("Release")
add_app_executable("Debug_Tests") 
add_app_executable("Release_Tests") 

Поскольку директория сборки теперь одна, то чтобы не мешались макросы в разных целях - их нужно перетащить из глобального add_definitions() в локальные set_target_properties(). Теперь они будут собираться (к примеру) не в Release_Tests/CMakeFiles/app-t, а в CMakeFiles/app-t (и так далее по именам целей).

Dendy ★★★★★ ()
Ответ на: комментарий от Dendy

Это действительно круто. Но всё равно у глупого анонимуса осталась куча вопросов.

Номер раз: если можно склеить так 2 CMakeList'а в один, то почему тогда нельзя писать сразу что-то типа:

cmake_minimum_required(VERSION 2.6)  
project(hello CXX)  
set(EXECBL_NAME "app")  

set(CMAKE_BUILD_TYPE Debug)  
add_executable(${EXECBL_NAME}-d main.cpp)

set(CMAKE_BUILD_TYPE Release)  
add_executable(${EXECBL_NAME} main.cpp)  

set(CMAKE_BUILD_TYPE Debug)  
set_target_properties(${EXECBL_NAME}-dt PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS") 
add_executable(${EXECBL_NAME}-dt main.cpp)  

set(CMAKE_BUILD_TYPE Release)  
set_target_properties(${EXECBL_NAME}-t PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS") 
add_executable(${EXECBL_NAME}-t main.cpp)  
По-моему, это то же самое.

Номер два: и ваш код, и мой имеют одну и ту же ошибку - они оба собирают четыре релизных бинарника с -O3 (два обычных, два с тестами). В чем дело - понять не могу. Куда же подевались дебажные?

anonymous ()
Ответ на: комментарий от anonymous

Потому что в кеше всего одна переменая для всего проекта, моя вина, прохлопал. Лечится так, вместо:

set(CMAKE_BUILD_TYPE "Debug")

вставляем:

set_target_properties(${EXECBL_NAME}-d PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_DEBUG}")

Соответственно для других целей CMAKE_C_FLAGS_RELEASE, CMAKE_C_FLAGS_RELWITHDEBINFO и так далее.

И да, set_target_properties() нужно вызывать после создания цели, то-есть после add_executable().

Dendy ★★★★★ ()
Ответ на: комментарий от Dendy

Доброе утро (если теперь, конечно, оно ещё доброе), я снова выхожу на связь. Сделал вот так:

cmake_minimum_required(VERSION 2.6)  
project(hello CXX)  
set(EXECBL_NAME "app")  

add_executable(${EXECBL_NAME}-d main.cpp)
set_target_properties(${EXECBL_NAME}-d PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_DEBUG}")

add_executable(${EXECBL_NAME} main.cpp)
set_target_properties(${EXECBL_NAME} PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_RELEASE}")

add_executable(${EXECBL_NAME}-dt main.cpp)  
set_target_properties(${EXECBL_NAME}-dt PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_DEBUG}")
set_target_properties(${EXECBL_NAME}-dt PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS") 

add_executable(${EXECBL_NAME}-t main.cpp)  
set_target_properties(${EXECBL_NAME}-t PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_RELEASE}")
set_target_properties(${EXECBL_NAME}-t PROPERTIES COMPILE_DEFINITIONS "RUN_THESE_STUPID_UNITTESTS") 

Теперь это собирает четыре бинарника в режиме CMAKE_BUILD_TYPE=None (без флага -g, и вообще без каких-либо флагов), впрочем, -DRUN_THESE_STUPID_UNITTESTS передаётся. По-моему, команда set(CMAKE_BUILD_TYPE «Debug») и вот то вот сложное ругательство с set_target_properties не совсем равносильны. Скажу более, я вычитал в справке, что если переменная CMAKE_BUILD_TYPE не установлена, то переменные CMAKE_C_FLAGS_DEBUG/CMAKE_C_FLAGS_RELEASE будут пустыми.

anonymous ()
Ответ на: комментарий от Dendy

>Потому что в кеше всего одна переменая для всего проекта

Вы не могли бы объяснить эту фразу? Для чего нужен этот глупый кэш, и почему скрипт не работает просто так, как написан? Если я это пойму, то может быть, выдумаю, как это обойти.

anonymous ()

Делать надо так

mkdir gcc-release
cd gcc-release
cmake -DCMAKE_BUILD_TYPE=Release path_to_source
make

mkdir gcc-debug
cd gcc-debug
cmake -DCMAKE_BUILD_TYPE=Debug path_to_source
make

mkdir icc-release
cd icc-release
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=`which icpc` -DCMAKE_C_COMPILER=`which icc` path_to_source
make

...
Reset ★★★★★ ()

вообще это всё есть в официальной документации, никаких хаутушек и рассылок мне ни разу не приходилось читать

Reset ★★★★★ ()
Ответ на: комментарий от anonymous

За задание BUILD_TYPE в CMakeLists.txt надо руки отрывать. И если cmake специально это игнорирует, то это правильно.

Reset ★★★★★ ()
Ответ на: комментарий от Reset

Что ж вы все мне руки-то оттяпать пытаетесь? Я действительно не понимаю, почему мне нельзя за один вызов make собрать сразу debug/release+тесты. Возможно, это сделано исходя из каких-то важных причин?

anonymous ()
Ответ на: комментарий от Reset

Ну в официальной документации-то, конечно, все переменные, команды, свойства, политика там, модули разные и все такие важные и сложные вещи написаны, а просто как это все работает, и что можно делать, за что сразу «руки», я не нашёл. По-моему, такая документация гогдится для тех, кто пользуется этим cmake с самых первых версий. А для чайников, калек и убогих - списки рассылки и хауту, там, бывает, что на пальцах объясняют.

in b4 порог вхождения и пр.

anonymous ()
Ответ на: комментарий от Reset

Вот вы говорите - дизайн такой у cmake, а, например, тот же MSVS project generator спокойно делает родной проект с debug/releae (кастомные конфигурации делать пока не пробовал), в студии ставишь ему batch build на все цели сразу и идешь пить чай, так почему unix makefile generator имеет столь досадное анальное ограничение?

anonymous ()
Ответ на: комментарий от anonymous

«Глупый» кеш нужен для:

1. Конфигурации в несколько этапов путём ответов на вопросы помощника как мы хотим подтюнить сборку. Если же сборка происходит заданием 100% параметров в командной строке - этот пункт безполезен.

2. Для инкрементальной сборки, когда на середине разработки вы, к примеру, добавляете в проект ещё один исходник. При этом не нужно дёргать вызов cmake самостоятельно - он сам возьмёт заданные ранее параметры из кеша и переконфигурирует Makefile с учётом изменений.

Не могу видеть что вы там делаете, но вот так на Windows у меня работает:

cmake_minimum_required( VERSION 2.8 )

set( SOURCES main.cpp )

macro( add_app_executable BUILD_TYPE )
	add_executable( app-${BUILD_TYPE} ${SOURCES} )
	if ( "${BUILD_TYPE}" STREQUAL "debug" )
		set_target_properties( app-${BUILD_TYPE} PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_DEBUG}" )
	elseif ( "${BUILD_TYPE}" STREQUAL "release" )
		set_target_properties( app-${BUILD_TYPE} PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS_RELEASE}" )
	endif()
endmacro()

add_app_executable( "debug" )
add_app_executable( "release" )

Проверяем:

$ mkdir build && cd build
$ cmake -G "MinGW Makefiles" -DCMAKE_VERBOSE_MAKEFILE=YES ..
$ make
...
[ 50%] Building CXX object CMakeFiles/app-debug.dir/main.cpp.obj
D:\dev\qtsdk-2010.06.23\mingw\bin\g++.exe     -g -o CMakeFiles\app-debug.dir\main.cpp.obj -c D:\projects\tmp\multi-cmake\main.cpp
...
[100%] Building CXX object CMakeFiles/app-release.dir/main.cpp.obj
D:\dev\qtsdk-2010.06.23\mingw\bin\g++.exe     -O3 -DNDEBUG -o CMakeFiles\app-release.dir\main.cpp.obj -c D:\projects\tmp\multi-cmake\main.cpp
...

Как видите - параметры компиляции замечательно подставились отдельно для «release» и «debug».

Dendy ★★★★★ ()
Ответ на: комментарий от Reset

> Дизайн такой у cmake.

Это просто поведение по умолчанию, чтобы файлы проектов получались более читабельными, поскольку команды влияют глобально на всю директорию и весь кеш. Но при желании большинство параметров можно задать per-target, даже другой компилятор, если поковыряться.

Dendy ★★★★★ ()
Ответ на: комментарий от anonymous

batch build последовательно запускает сборку release и debug. никто не запрещает сделать .sh'шник который тебе последовательно введет эти две команды, если руками так сложно набрать

Reset ★★★★★ ()
Ответ на: комментарий от Reset

Вряд ли, совместимость с генераторами под альтернативные системы сборки (MSVC, CodeBlocks, Eclipse) работает до определённой степени, по причине высокоуровневости последних. Гибкость CMake основана на гибкости Makefile-проектов. По правде говоря, сам смысл существования генератора того же vcproj стоит под большим вопросом. Другое дело если бы этот генератор создавал Makefile-based vcproj, с иерархией исходников, путями к заголовочникам, проставлеными макросами и настроеной отладкой. Хоть бери и сам дописывай.

Dendy ★★★★★ ()
Ответ на: комментарий от Reset

Попробую яснее озвучить свой предыдущий пост. Генераторы проектов для MSVC, CodeBlocks, Eclipse, etc - большая бесполезная глупость в CMake, ориентироваться на совместимость с которой лично я не вижу смысла.

Dendy ★★★★★ ()
Ответ на: комментарий от Dendy

>Но при желании большинство параметров можно задать per-target, даже другой компилятор, если поковыряться.

...и даже для каждого отдельного фала

annulen ★★★★★ ()
Ответ на: комментарий от Reset

> Приятно открыть проект в родной среде и редактировать и отлаживать его там.

Абсолютно с вами согласен. У меня претензии что они ещё и конвертируют систему сборки в чужую. В результате чтобы, к примеру, добавить файл в проект приходится закрывать всю IDE, перегенериовать файл проекта, открывать и потом компилировать. В Makefile-based проектах достаточно просто запустить сборку.

Dendy ★★★★★ ()
Ответ на: комментарий от Reset

Так а с Makefile-based проектами студии её вообще не нужно переоткрывать. Собственно у меня все студийные проекты такие.

Dendy ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.