• Как в 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.
    Ответ написан
    Комментировать
  • Как исправить при запуске docker из gitlab-runner ошибку поиска gitlab-runner-cache?

    @klirichek
    Есть небольшая путаница с пакетами. Из "официального" репозитория дистрибутива очень часто не подходит.
    Проблема с ними в том, что они ссылаются на внешний тэг образов докера, который может "протухнуть". Тогда у раннера нет доступа к образам, где находится нужные ему для работы вспомогательные бинари, и всё падает.

    sudo apt-get remove gitlab-runner
    sudo curl -L https://packages.gitlab.com/install/repositories/r... | sudo bash
    sudo apt-get install gitlab-runner
    Ответ написан
  • Как при сборке указать компилятору не создавать проект для 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 комментария
  • Как правильно делать сборку для snap (snapcraft.yaml)?

    @klirichek
    Полагаю, там должно быть достаточно просто прописать extension (если программа в итоге "общается" с gtk/qt, а не прямо висит на голых иксах). Ну и прописать соответствующий лаунчер (без него обычно бывают разные странные спец. эффекты, вроде невозможности авторизоваться). Что-то вроде:
    apps:
      my-part:
        command: desktop-launch $SNAP/bin/passgen2
        extensions:
          - gnome-3-28
    ...


    Ну и собирать такие вещи лучше итеративно; в процессе придёт понимание, чего не хватает.
    Скорее всего на базе гнома из кучи ваших либ больше ничего не понадобится (т.е. stage-packages может "похудеть" или вообще исчезнуть)
    Запускаем `snapcraft try`. Он проходит по всем стадиям, и готовит папку prime. Дальше подключаем её с помощью `snap try` - получаем гибкий снап, который одновременно и приложение, и папка, куда можно лазить и что-то менять (добавлять/настраивать) прямо на лету. Потом обкатанные настройки прописываем в сам скрипт снапкрафта. Confinement, кстати, изначально лучше поставить devmode. Тогда приложению открыт доступ повсюду. И потом с помощью snappy-debug в параллельной консоли смотреть, куда оно на самом деле лазает, и, соответственно, какие plugs ему надо прописать, чтобы оно успешно взлетело в strict.

    (например, с вашим файлом приложение надо будет вызывать как 'passgen2.my-part' - подозреваю, что вы не этого хотели добиться, и поэтому эта часть будет меняться. А если запускать полный ребилд snapcraft - по каждому чиху - это медленно и грустно).
    Ответ написан
    1 комментарий
  • OpenWRT: dnsmasq не добавляет хосты в /etc/hosts. Как побороть?

    @klirichek
    Вроде /etc/hosts это вообще файл для ручной правки; туда никакой демон автоматом ничего не пишет.

    А что мешает добавить путь к нужному файлу в config dnsmasq как строчку list addnhosts '/path/to/file'? Он подгрузится и будет использоваться вместо /etc/hosts
    Ответ написан
    Комментировать
  • Как в сфинксе получить морфологические варианты слова?

    @klirichek
    Получить производные формы слова никак нельзя; aot изначально работает в прямо противоположном направлении (получает ИЗ производных исходные леммы).
    Вот леммы получить можно, но чуть муторно.
    Для этого добавляем в конфиге индекс с нужным лемматизатором (индексировать его необязательно, т.е. можно сказать что он rt и тогда демон взлетит с ним и без файлов индекса).
    типа:
    index fake_rt
    {
      ...
      rt_field = fake
      morphology = lemmatize_ru_all, lemmatize_en_all, lemmatize_de_all
    }


    Затем включаем профайлинг и делаем запрос к этому индексу с нужным словом. После смотрим план запроса.
    mysql> set profiling=1;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select * from rt_full where match ('стали');
    Empty set (0.00 sec)
    
    mysql> show plan;
    +------------------+--------------------------------------------------------------------------------------------------------+
    | Variable         | Value                                                                                                  |
    +------------------+--------------------------------------------------------------------------------------------------------+
    | transformed_tree | OR(
      AND(KEYWORD(сталь, querypos=1, morphed)), 
      AND(KEYWORD(стать, querypos=1, morphed)))           |
    +------------------+--------------------------------------------------------------------------------------------------------+
    1 row in set (0.00 sec)

    Из крайнего значения плана можно выкусить нужные формы внешними регэкспами
    Ответ написан
    Комментировать
  • Как сгенерировать sphinx индекс программно?

    @klirichek
    Можно сделать так, чтобы своя программа генерила xml либо csv/tsv - и скормить получившийся поток сфинксу (через pipe).
    Ответ написан
    Комментировать
  • Как получить из сфинкса уникальные свойства товаров и количество товаров с этими свойствами?

    @klirichek
    max_matches законфигурирован какой?

    (см. в доке: WARNING: grouping is done in fixed memory and thus its results are only approximate; so there might be more groups reported in total_found than actually present. @count might also be underestimated. To reduce inaccuracy, one should raise max_matches. If max_matches allows to store all found groups, results will be 100% correct. )
    Ответ написан
  • Как установить нужную версию sphinxapi?

    @klirichek
    Просто посмотрите версию.
    (запуск любой тулзы сфинкса без параметров выдаст баннер с версией).
    Потом тащите соответствующий тарболл с исходниками.

    Более верный способ - посмотрите, может мейнтейнер пакета выделил в отдельный пакет всякую сопроводиловку (какой-нибудь sphinx-dev например). Поищите в /usr/share/doc.
    Если нет вообще нигде - тащите таки из сорцов. И не забудьте написать "Кю!" мейнтейнеру пакета, покуда вообще-то api - это часть сфинкса.

    Ну и вообще универсальный совет - воспользуйтесь sphinxql. Тогда вообще никакое api не нужно, хватит стандартных функций из mysql/mysqli
    Ответ написан
    2 комментария
  • Как настроить RT индекс shpinx для поддержки кириллицы?

    @klirichek
    charset_type всего лишь определял, какую кодировку (однобайтную или юникод) пихать в сфинкс. Очевидно, что однобайтная - пережиток прошлого (нужно возиться с кодовыми страницами и т.д.), потому её устранили. А поскольку их было всего две, то устранили и опцию.

    Тем не менее, charset_table никто не трогал, она по-прежнему работает. "аналогичного результата" с ней быть не может, проверяйте внимательнее!
    Собственно, это и есть ключ.

    charset_table определяет, как входной поток символов преобразуется перед тем, как его подхватит сфинкс. И ещё тут может повлиять ваш консольный клиент - потому что он тоже шлёт данные сфинксу в определённой кодировке.
    Вообще есть "волшебная команда" show meta. Её можно запустить сразу после запроса, и она покажет, какие именно слова и как искал сфинкс.
    Насколько я помню, раньше типичной проблемой было, когда как раз таки charset_type стоял в sbcs, а индексировали utf-8. Сейчас подобного уже быть не должно, но возможно что другое возникло.
    Ответ написан
    Комментировать
  • Почему Sphinx не находит исходное слово?

    @klirichek
    Смотрите show meta сразу после запроса. Там будет видно, какие именно сущности искал сфинкс.
    (возможно, "кварталу" не осилила морфология, к примеру).
    Ответ написан
    Комментировать
  • Можно ли раздать интернет на 2 ПК если у провайдера привязка к MAC-адресу?

    @klirichek
    Если физический провод (от провайдера) один, то и подключить к нему можно только одно устройство. То есть, чтобы подключить ДВА - в любом случае потребуется "разветвитель" - свитч, хаб или роутер.
    Привязка к MAC говорит о том, что свитч или хаб отпадают.
    Итог: нужен либо аппаратный роутер, либо вторая сетевуха (проводная или беспроводная) в основной комп и программный роутинг на компе
    Ответ написан