Ответы пользователя по тегу CMake
  • Как в cmake слинковать статично одну библиотеку?

    @klirichek
    Можно линковать по явному имени файла.
    Например, вместо target_link_libraries(foo PRIVATE GLU) можно target_link_libraries(foo PRIVATE libGLU.so), либо target_link_libraries(foo PRIVATE libGLU.a)
    Ответ написан
    Комментировать
  • Превратить команду gcc в cmake?

    @klirichek
    Создаёте CMakeLists.txt

    Пишете в нём наверху:
     
    PROJECT ( esl1demo C )
    Потом добавляете цель -
    add_executable (esl1_demo util/esl1_demo.c)

    И дальше последовательно, опцию за опцией, переносите их внутрь
    target_include_directories (esl1_demo PRIVATE ./Include)
    target_link_directories(esl1_demo  PRIVATE ./lib)
    target_link_libraries(esl1_demo PRIVATE esl1image)
    ...


    И далее по списку.
    Опции компилятора - через target_compile_options, опции либы - через target_link_libraries и т.д.

    Прямо ВСЕ прописывать, скорее всего, не нужно (например, -O2 -g - это часть стандартных флагов конфига RelWithDebInfo).

    Проверяется попыткой сбилдить с флагом verbose. Там будет видно, какие именно опции передались компилятору; и будет ясно, какие надо добавить в проект.
    Ответ написан
    Комментировать
  • Почему возникает ошибка при выполнении команды $Cmake -G "Visual Studio 16 2019 Win64"?

    @klirichek
    Запустите `cmake -G`. Получите список возможных генераторов, дефолтный помечен звёздочкой. Если он уже верный - то в рабочем запуске вам -G вообще не нужен. Если нет - выбираете нужный, копируете имя, заключаете в кавычки.
    Насколько я помню, начиная с 16-й (2019-й) студии архитектуру указывать в имени генератора не надо.
    Т.е. скорее всего там cmake -G"Visual Studio 16 2019", без Win64.
    Ответ написан
    Комментировать
  • Как при сборке указать компилятору не создавать проект для Visual Studio(cmake, mcvs2019)?

    @klirichek
    cmake --build . --config release
    cmake --build . --config debug

    проект в данном случае - это как раз сгенерированный билд-файл. cmake --build умеет билдить любые сконфигурированные проекты, вызывая правильную тулзу (что именно там будет - make, ninja или msbuild - неважно, синтаксис одинаковый)
    Ответ написан
    Комментировать
  • MSBuild.exe to get the value of VCTargetsPath: Не удается найти указанный файл почему?

    @klirichek
    Посмотрите на установку студии
    Обычно она оставляет где-то на видном месте (может даже в меню) пару батников, которые устанавливают нужное окружение. Можно попробовать звать их перед вызовом cmake. Они просто ставят в текущей консоли разные переменные окружения, чтобы потом нужное для компиляции/сборки из командной строки легко находилось.

    И да, к слову - `-DCMAKE_BUILD_TYPE=Debug` - не сработает! Пока собираете что-то на студии или любом мультиконфиге - лучше вообще забудьте про эту переменную, она только внесёт кучу путаницы, которую потом когда-нибудь придётся разгребать.
    Ответ написан
    Комментировать
  • Как собрать проект с cmake вместе со сторонней библиотекой?

    @klirichek
    А зачем?
    Если вы с ней линкуетесь - то просто зовите её по имени. Включение через add_subdirectory подразумевает, что за всеми связями и зависимостями следит cmake. Вы просто пишете у себя target_link_libraries для своего проекта с этой либой.

    Если же "просто собрать" - тогда и включать незачем вообще. Вместо make нынче принято запускать cmake --build (потому что кроме make ещё всякие ninja и msbuild бывают)
    Ответ написан
  • CMake. Как подключить библиотеку из файла?

    @klirichek
    Так у вас либа из двух частей - собственно, архив .a, и хедер (или хедеры).

    install(TARGETS approx_lib LIBRARY DESTINATION lib) - ставит только собранный target (т.е. непосредственно файл статик либы), но не хедер!

    Если не производить установку - можно либу подключить как add_subdirectory с путём к исходникам. Тогда она будет частью основного проекта, будет собираться вместе с ним и всё нужное найдётся (покуда target_include_directories прописан и знает, где искать).

    Если устанавливать - то, очевидно, надо установить и хедеры тоже. И ещё как-то сообщить проекту, где что лежит.

    Вот кусочек из нашей кастомной сборки icu (пусть имя пакета будет foo, а не icu. А namespace - baz. Так интереснее!)
    add_library(icu .....)
    target_include_directories(icu PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/source/common>$<INSTALL_INTERFACE:include/icu>)
    install ( TARGETS icu EXPORT icuExport RUNTIME DESTINATION lib LIBRARY DESTINATION lib  )
    install ( DIRECTORY source/common/unicode DESTINATION include/icu FILES_MATCHING PATTERN "*.h")
    
    # install exports
    set ( CMAKE_CMAKE_DIR "lib/cmake/foo" )
    install ( EXPORT icuExport FILE foo-targets.cmake DESTINATION "${CMAKE_CMAKE_DIR}" NAMESPACE baz:: )
    
    file ( WRITE foo-config.cmake 
    "if(NOT TARGET baz::icu)
        include(\"\${CMAKE_CURRENT_LIST_DIR}/foo-targets.cmake\")
    endif()")
    install ( FILES foo-config.cmake DESTINATION "${CMAKE_CMAKE_DIR}" )


    Здесь несколько моментов.
    Во-первых, обратите внимание на target_include_directories. Там два пути. Один будет работать при сборке, второй - при инсталляции. Т.е. когда собирается либа, либо подключается через add_subdirectory - найдётся нужное по первому пути. После установки - по второму, и этот второй - относительный.

    Во-вторых, при установке задано имя export. Это что-то вроде именованного набора разных свойств, которые cmake накапливает из добавленных с этим именем целей и потом может оформить в файл экспорта. В данном случае туда попадает явно прописанная библиотека, и неявно - include-path от неё, последний потому что он изначально включён как public.

    В-третьих, собственно, ставим помимо таргета и сами хедеры!

    И в-последних, ставим то, что "накопил" экспорт. Первый файл foo-targets.cmake. Второй - который будет работать при поиске пакета (я его осознанно упростил, на самом деле он длиннее) foo-config.cmake.

    Всё это куда-то ставится. Например, в /root/bar (для определённости) - как `cmake --install . --prefix /root/bar`. У вас оно прямо в систему, но тут спецом левый путь, чтобы глазами было видно, как он в конфиге пробрасывается и используется.

    Потом в самом проекте - не надо никаких find_library, всё же известно, где!
    LIST (APPEND CMAKE_PREFIX_PATH "/root/bar")
    find_package(foo)
    
    add_executable(my_prog main.cpp)
    target_link_libraries(my_prog PRIVATE baz::icu)


    Собственно, всё.

    find_package сперва пытается найти модуль FindFoo.cmake. Такого нет. Потом пытается найти конфиг FooConfig.cmake или foo-config.cmake. Он разыскивается по указанному префиксу (там весьма развесистый алгоритм, который умеет заглядывать в разные папки, в доках всё есть) и включается. Он, в свою очередь, включает. foo-targets.cmake, сгенерированный из экспорта. И вот там и находится вся магия.

    ... # это сгенерированный код!
    # Create imported target baz::icu
    add_library(baz::icu STATIC IMPORTED)
    
    set_target_properties(baz::icu PROPERTIES
      INTERFACE_COMPILE_DEFINITIONS "U_STATIC_IMPLEMENTATION;U_CHARSET_IS_UTF8=1;U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1"
      INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/icu"
    )
    ...
    # Load information for each installed configuration.
    get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
    file(GLOB CONFIG_FILES "${_DIR}/foo-targets-*.cmake")
    foreach(f ${CONFIG_FILES})
      include(${f})
    endforeach()
    ...
    ... а это файл foo-targets-relwithdebinfo.cmake
    # Import target "baz::icu" for configuration "RelWithDebInfo"
    set_property(TARGET baz::icu APPEND PROPERTY IMPORTED_CONFIGURATIONS RELWITHDEBINFO)
    set_target_properties(baz::icu PROPERTIES
      IMPORTED_LINK_INTERFACE_LANGUAGES_RELWITHDEBINFO "CXX"
      IMPORTED_LOCATION_RELWITHDEBINFO "${_IMPORT_PREFIX}/lib/libicu.a"
      )


    а вообще в доках cmake (не надо никаких левых вики/SO!) этой теме посвящён большой и подробный раздел.
    Ответ написан
    Комментировать
  • Как исправить ошибку сборки зависимости(ExternalProject) в cmake?

    @klirichek
    Так в доках же об этом есть, не?

    https://github.com/google/benchmark#building-with-gcc
    Ответ написан
    Комментировать
  • Как установить зависимость boost::program_options вместе со своим проектом с помощью ExternalProject_Add в cmake?

    @klirichek
    А зачем эта мышиная возня с ручным импортом?

    Я примерно так делал для boost::context


    cmake_minimum_required(VERSION 2.8.2)

    project(boost-context-download NONE)

    include(ExternalProject)

    ExternalProject_Add(boost-context
    SOURCE_DIR "@BOOST_CONTEXT_SRC@"
    BUILD_IN_SOURCE ON
    CONFIGURE_COMMAND bootstrap.bat
    BUILD_COMMAND b2 --with-context
    INSTALL_COMMAND b2 install --with-context --build-type=complete --prefix=@BOOST_CONTEXT_BUILD@
    )


    Параметры чуть другие, чем у вас (в конфигуратор список либ необязательно. А при билде уже надо. И ещё отдельно установка, где они тоже нужны).
    Ещё при сборке желательно удалить (если есть) переменную окружения CXX, если её вдруг определена в проекте выше (у b2 собственная интроспекция, которая этого пугается).

    Просто дальше, после сборки external_project - и идёт обычный find_package(Boost 1.61.0 COMPONENTS context), который создаёт все нужные импортированные таргеты. А последующий target_link_libraries(boost::context) их использует.

    Но в целом сам подход не совсем хорош. Под винду можно найти готовые сборки буста. Качать примерно столько же, но возиться с установкой уже совершенно не нужно. Распаковал архив, натравил в него BOOST_ROOT, вызвал find_package(boost...) - и вуаля!
    Ответ написан
  • Как правильно построить структуру проекта используя cmake?

    @klirichek
    либа - это обычно файл (.a, .lib, .so, .dll, .dylib - зависит от статики/динамики) + хедеры, которые должно включить приложение, использующее либу.

    При разработке одновременно либы и приложения add_subdirectory достаточно очевидный вариант, покуда объединяет их оба в один проект. Желательно позаботиться о модульности (раз уж вы поделили проект на две части - значит, это зачем-то было нужно!).

    В папке с либой должен быть свой собственный CMakeLists.txt, собирающий либу; он должен быть самодостаточным для сборки. Т.е. если ей нужны какие-то зависимости - чтобы они искались именно для проекта либы, а не "где-то выше". Т.е какие-то исходники, какие-то find(), потом add_library(name). И нужно определить, что из либы должно увидеть использующее её приложение. Это делается через задание свойств target_XXX(). Например, чтобы показать консумеру путь к нужным хедерам - target_include_directories, чтобы добавить ключи компиляции - target_compile_definitions(). В современном cmake это можно настроить как в настоящем ООП - свойства, нужные для сборки самой либы (например, зависимости, с которыми она статически линкуется. Ей нужны, но как слинковались - консумеру они уже невидимы и ненужны); свойства, нужные консумеру, но не нужные для сборки либы (например, хедеры, специфичные для включения либы, но не нужные для её сборки), и общие свойства, нужные как для либы, так и для её пользователей. Смысл в том, что все эти "настройки для внешних пользователей" не кладутся в некие глобальные переменные cmake (так делали многие годы, но это далеко не best practices), а определяются как свойства самой библиотеки. И вишенкой на торте, в конце проекта либы можно дописать для неё экспорт и алиас (зачем это нужно - чуть ниже).

    export(TARGETS mylib FILE "mylib-targets.cmake" NAMESPACE mylib::)
    add_library(mylib::mylib ALIAS mylib)

    Приложение, использующее либу, должно, во-первых, найти её. И во-вторых, слинковать. Про add_subdirectory - понятно. Добавляете папку и либа доступна. Другой вариант - если либа уже устоялась и НЕ хочется каждый раз её пересобирать. Тогда вместо add_subdirectory включаете (через include) файлик 'mylib-targets.cmake', который будет в сборочной папке либы. На машине разработчика это вполне рабочий вариант! Ну и крайний вариант - это делать полноценный экспорт, тогда пользователи смогут находить либу через штатный find_package().
    После нахождения либа добавляется единственной строчкой:

    target_link_libraries(myprog PUBLIC mylib::mylib)

    Благодаря прописанным свойствам либы эта строчка автоматически добавляет всё необходимое - пути к хедерам, либу для линковки, дополнительные параметры компиляции и т.д. (раньше во время глобальных переменных подразумевалось, что после нахождения либы с помощью find_package у вас появлялось несколько переменных, вида mylib_FOUND - что оно нашлось, mylib_INCLUDE_DIRECTORIES - путь к хедерам, mylib_LIBRARIES - что линковать. А если либа появлялась через add_subdirectory, там вообще фантазия на тему "как сообщить о себе" ничем не ограничивалась, что, в свою очередь, приводило к лапшевидному коду, который использовал библиотеку. Со свойствами целей это всё уже не нужно!). Ну а имя mylib::mylib - оно работает как для импортированной либы (через включение mylib-targets.cmake), так и для add_subdirectory(), именно для этого оно было добавлено там как алиас.

    Итого, в основном проекте у вас будет что-то вроде

    add_subdirectory(mylib)
    target_link_libraries(myprog PUBLIC mylib::mylib)

    или

    include (mylib/build/mylib-targets.cmake)
    target_link_libraries(myprog PUBLIC mylib::mylib)

    или

    find_package(mylib REQUIRED)
    target_link_libraries(myprog PUBLIC mylib::mylib)

    Первый вариант - для одновременной разработки. В IDE будут исходники и основного проекта, и либы сразу. Тёмная сторона - когда сделаете clean all, либа тоже сотрётся :) (что очевидно)
    Второй - для приложения. Либа будет всегда под рукой, но уже как бинарь. Линковаться можно сколько угодно, при очистке ничего не сотрётся, путь привязан к папке (двинуть в другую папку нельзя, потому что в файле прописаны абсолютные пути).
    Третий - для широкой разработки. Когда либа уже отдельно, и ставится отдельно, и кто-то с ней что-то разрабатывает. Это самый цивилизованный вариант, папку можно двигать куда угодно, главное сообщить cmake, где её искать. Но настраивать это чуть сложнее и не всегда оправдано.

    И да - если ещё поизучать cmake, то либа - это не обязательно библиотека для линковки "в общепринятом смысле". Но это просто набор свойств (include-папки, опции компилятора, опции линковки и т.д.), инкапсулированные в нечто под названием "библиотека". Например, можно определить либу для задания варнингов компилятора -wall, где не будет ни исходников, ни библиотек, а только один параметр компилятора. И это вполне нормальный и рабочий способ! Помимо статических либ для чуть-чуть ускорения сборки можно использовать либу типа object. Это просто набор скомпилированных объектников, которые не пакуются в отдельный файл .a или .lib. а прямо так передаются в зависимые проекты. На одну операцию упаковки/распаковки меньше ).
    Ответ написан
    2 комментария