Как стать автором
Обновить

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

Мне кажется сейчас заниматься написанием shell скриптов уже как-то не комильфо. Рассматривались ли какие-либо альтернативы? Тот-же ninja и системы его использующие? Типа cmake/ninja, gn/ninja и т.п.
А в чём проблем shell-а? В чём не комильфовость? Всё равно на нём же пишутся (в Unix-like системах, конечно же) все вызовы того как и что надо собирать. Но redo не обязывает писать на shell — многие реализации позволяют использовать любой язык или просто исполняемые файлы, лишь бы вызывали redo-* команды.
Ну конечно сильно зависито от того что вы делаете. Если у вас такая куча разнообразных целей, что вам на каждую нужно писать отдельный скрипт, то может так оно и лучше. Но когда речь идет об обычных проектах где кроме компиляции и возможно нескольких кастомных шагах особо инчего нет, то смысла каждый раз самому все это прописывать, вместо того чтобы декларативно описать структуру проекта я смысла не вижу…
А какая разница как описать структуру проекта? Декларативно или в виде набора команд с флагами разными? Грубо говоря, количество информации которое вам, как разработчику, нужно ввести в компьютер, чтобы объяснить ему правила для сборки — и в том и в другом случае одинаковое. Но для декларативного описания нужно изучать этот yet another декларативный формат/язык. Если все ваши .c файлы (например) собираются во всём проекте с одними и теми же флагами, то можно обойтись одним мизерным default.*.do файлом хоть в корне проекта. Описания опций/флагов/параметров/путей до зависимостей — что в CMake файлах, что в shell скрипте занимают одинаково места. С CMake у меня несколько месяцев назад был небольшой опыт в большом проекте — с ходу вот прям не вспомню где бы он мне ощутимо больше/лучше помог, чем redo-like подход.
Ну на мой взгляд это примерно как сравнивать C и prolog. Понятно что на C можно написать все что хочешь. Но задачи для которых «заточен» prolog удобней писать все-же на нем.
А какая разница как описать структуру проекта? Декларативно или в виде набора команд с флагами разными?

Вот тут я не соглашусь. Ну точней, если у вас уже есть какой-нибудь обширный «билд-фремворк» для redo в который вы прсто вставляете свою «структуру проекта». То наверно можно сказать что будет ± одинаково. Но если писать все с нуля, то на спецализированных системах/языках все будет гораздо компактней и скорее всего понятней.

К слову, CMake умеет такую штуку как INTERFACE_* свойства таргетов, которые подтягиваются из зависимостей. Умеет ли redo вытаскивание значений свойств из зависимостей, без замусоривания глобального контекста?

А какая разница как описать структуру проекта? Декларативно или в виде набора команд с флагами разными? Грубо говоря, количество информации которое вам, как разработчику, нужно ввести в компьютер, чтобы объяснить ему правила для сборки — и в том и в другом случае одинаковое

Ну конечно же нет, в том то и дело. Количество информации различается на порядки. Команды и флаги будут разными на разных системах, компиляторах, дистрибутивах и версиях одного дистрибутива. Например, чтобы подключить потоки может потребоваться -pthread, -lpthread, -lthr только на BSD. -ldl на FreeBSD не существует, а на Linux нужен. C++17 может включаться как -std=c++17, -std=c++1z, /std:c++17, где-то вообще быть по умолчанию. Банальная сборка статической библиотеки это два ни разу не очевидных вызова ar/runlib только на *nix, в windows вообще по другому. У install есть несовместимости в аргументах между GNU и BSD версиями. Как и у sed, awk, grep и что вы там ещё будете звать из ваших скриптов.


В CMake каждая из этих операций делается ровно одной строкой, которая описывает сама себя и работает везде, даже на solaris и haiku, даже если вы о них первый раз слышите.

Немного оффтопик. Можете пояснить, почему все так любят ninja? Мне его много кто советовал.
Мой стек: windows/cmake/ninja/clang (CLion) не может собрать gRPC, валится с непонятными ошибками.

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

Clang откуда стандартную библиотеку берет? Microsoft VS? MinGW?

Microsoft. Я не совсем понимаю, как виндовый clang можно скрестить с mingw.

mingw вроде в последнее время и так на clang-е живет, зачем их скрещивать. Другое дело что он не сможет MS-ную библиотеку съесть. Если нужно использовать MS-ные либы/инклуды используйте clang-cl.

Ну так я и использую clang-cl установленный через msvs. Вместе с CLion/cmake/ninja

ninja все любят, потому что он быстро работает. Прямо никто файлы для ninja не пишет, подразумевается, что они будут генерироваться чем-то более высокоуровневым: Autotools(гипотетически), Meson, CMake и т.д.


В действительно крупных проектах — типа Chrome, для которого и разрабатывался ninja — разница по скорости сборки бывает на порядки.


Рядовым же проектам все равно :-)

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


Если мы про сборку ПО, то уж извините, но никто кроме горстки радикалов которым не угодил размер CMake (на самом деле сборка — это сложно, и эта сложность так или иначе должна где-то обитать; очень недальновидно отказываться от замечательного инструмента который всю это сложность инкапсулирует и поддерживает за вас, тащить её себе в проект и потом нести бремя поддержки) не пишет руками россыпь императивных скриптов (и в особенности не парсит include sed'ом), когда можно декларативно написать "нужна библиотека A из исходников X и исполняемый файл B из исходников Y и этой библиотеки" на CMake, и из него получить сборку хоть на make, хоть на ninja, хоть на msbuild, хоть на том же redo (если кто-то напишет генератор) или проекты под любые IDE, и это будет нормально обрабатывать зависимости, работать с любыми компиляторами, уметь кросс-компиляцию, выполнять все требования к сборке (а не только те что вы не забыли) и ещё много много всего что вам придётся либо писать руками, либо вы написать забудете (сломав кому-то сборку), либо просто не сможете не продублировав весь набор скриптов и/или не превратив его в include лапшу. И это будет не требуя от вас вообще никаких затрат развиваться и поддерживать новые возможности существующего инструментария и новые инструменты.


Так что, вашими же словами, CMake настолько впечатлил life-changing простотой, гибкостью и куда лучшим выполнением задач сборки, что я во всех своих проектах им полностью заменил make (вместе с qmake, scons, autotools) и точно не поменяю на redo.


Помимо сборки софта, в простых случаях я однозначно предпочту make, только за то что его все знают (POLA) и за наглядность (один файл, удобное задание переменных для подстановки (=, ?=, += без лишних скобок и кавычек), далее все цели в одном месте). Для простых, повторюсь, случаев есть небольшое общее подмножество GNU/BSD синтаксисов с которым make по прежнему везде есть, и перечисленные вами минусы неактуальны, так что остаются только плюсы.


Вот примеры таких Makefile для наглядности:
https://github.com/repology/repology-logo/blob/master/Makefile
https://github.com/repology/repology-linkchecker/blob/master/Makefile


А вот прям сложные случаи сборки не-софта, с разветвлённым деревом зависимостей и разнообразными процессами, требующими вызова внешних утилит — тут да, можно и рассмотреть redo.

Здравствуйте. Позвольте заметить, что CMake я считаю говном. Это ужасный язык, это ужасная реализация.
А теперь перейду к аргументам. Опишу ту ситуацию, с которой я сам столкнулся. Копипащу свой старый коммент:

Жрал я тут недавно плюсовую куку под названием CMake.
Из свежего, с чем уже 3 дня маюсь. Задача простая — собрать с BOOST'ом проект на кластере. Что я нашел недавно обнаружил в попытках понять, почему сборка ломается:

So my current understanding is:

For Boost ≤ 1.69, we can specify the location with BOOST_ROOT as a command line parameter, set(), or environment variable.
The world is :)
But from ≥ Boost 1.70, it will start preferring to use «config mode», meaning it will silently ignore the BOOST_ROOT if we specify it via the command line or set().
But it will continue heeding it if we specify it via an environment variable.
But the new config mode will find Boost directories in new places (eg /opt) and these will silently override our BOOST_ROOT, even if specified via an environment variable (that it otherwise heeds).
To revert to the previous, non «config mode» behaviour, we must specify Boost_NO_BOOST_CMAKE=ON
But this must be specified via a command line parameter or set(); if we specify it via an environment variable, it will be silently ignored.
To specify the location under «config mode», we can specify Boost_DIR
But this must be specified as an environment variable or set( Boost_DIR CACHE ); if it is specified via the command line or the a plain set( Boost_DIR <value ), it will be silently ignored.
There is further complexity with Boost_ROOT (the same as BOOST_ROOT?) and the version of cmake we're using and the version of cmake we've required.
The world is :(

Здесь детальнее о проблеме.

Я не знаю, кто виноват. Я, желающий в этом разобраться и починить, авторы CMake, авторы Буста? Не знаю. Но факт остается фактом. Я не могу нормально использовать одну из самых популярных библиотек на кластере, потому что CMake превращает весь процесс в цирк.

Был бы он так приятен и понятен, не пилилось бы ему тогда 100 альтернатив и не писались бы статьи на хабре о том, как его заменить.
КМК логично там где cmake удобен — использовать его. Там где неудобен — не использовать.
По хорошему, вся эта магия с BOOST_ROOT и версиями boost должна быть реализована в модулях cmake.

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


Был бы он так приятен и понятен, не пилилось бы ему тогда 100 альтернатив и не писались бы статьи на хабре о том, как его заменить.

Альтернатива ему пилится ровно одна — meson. От второй, qbs, сами авторы отказались в сторону CMake — не был бы он так приятен и понятен, этого бы не произошло. Но я не настаиваю — используйте любую другую высокоуровневую систему сборки. Посыл был прежде всего в том чтобы не писать скрипты вручную.

Поиск модулей у CMake странный местами, тут согласен. Могу только посоветовать попробовать ключики PATHS и HINTS у find_package.

У современных (ну как, за последние 10 лет) проектов есть интересная ообенность — использования кучи утилит тулчейна в проекте. То есть ты приходишь на проект на языке не до конца тебе знакомомом. И тебе надо для сетапа запустить полдюжины утилит которые ты видишь в первые в жизни. И еще столько же перед пушем в гит.


Я понимаю все недостатки make. Но почему никто не говорит о достоинствах? Универесальная в всех смыслах точка хода в проект. Любой язык, система, окружения, тулчейн. Если ты пришел на новый проект — то единственное место куда надо смотреть на мейк файл. Не первое, а именно единственное. И тебе уже не важно где проект стоит, что надо ставить, какая структура папок и вообще что где лежит.


Наша индустрия полна парадоксов. Нам не хватает вещей, которые уже есть. Но которыми мы все меньше и меньше пользуемся. Которые нам все больше и больше нужны. И мы пишем все больше сломанных несовместимых велосипедов.

Но много ли людей умеют хорошо программировать на make? Чтобы не было проблем с недописанными файлами, не ломалась параллельная сборка, не пересобиралось слишком многое, были зависимости от нужных заголовочных файлов?
Почти все крупные проекты, использующие make, пишут довольно увесистую систему на нём (например Kconfig). С таким подходом единственным преимуществом make остаётся только то, что я буду знать, что для сборки проекта надо выполнить команду «make», а со всем остальным придётся разбираться отдельно для каждого проекта.
Если единственное, что нужно от make — одна универсальная команда для сборки проекта, то Dockerfile выглядит ещё лучше, ведь он с собой ещё и весь комплект разработчика притащит, ничего не надо будет устанавливать и настраивать.

Хорошо делать make — может почти никто. И я не спорил с недостатками.


Докер по всей видимости таки станет заменой make. Жаль только правда прбрасывать все между контейнерами раздражает. Но против большинства не попрешь.

Предположим, что нам необходимо собрать программу или отчуждаемый пакет для
работы на трех устройствах с именами ci20, bt01 и dm64. Первые два устройства
ci20 и bt01 основаны на архитектуре MIPS, третье устройство dm64 построенно
на базе процессора ARM. Toolchain-ы для сборки программ, для простоты, назовем
mips и arm, соответственно.

Сценарий сборки исходной программы одинаков для всех трех устройств и написан
на языке GNU Make.

Если представить все комбинации вызовов команды Make, необходимые для сборки
программы на наши устройства, получим:

$ TOOLCHAIN=mips HARDWARE=ci20 make
$ TOOLCHAIN=mips HARDWARE=bt01 make
$ TOOLCHAIN=arm HARDWARE=dm64 make

или, при передаче имен устройств и Toolchain-ов в качестве аргументов:

$ make TOOLCHAIN=mips HARDWARE=ci20
$ make TOOLCHAIN=mips HARDWARE=bt01
$ make TOOLCHAIN=arm HARDWARE=dm64

Таким образом, система сборки должна принимать пары TOOLCHAIN-HARDWARE,
которые определяют какой именно Toolchain необходимо использовать для того
или иного устройства.

Рассмотрим теперь, как, на уровне системы сборки, организовать последовательность
вызовов утилиты Make для нашего сценария таким образом, чтобы пользователь мог
осуществить данные действия с помощью лишь одного вызова:

$ make

без задания дополнительных аргументов, отвечающих за выбор целевого устройства
и связанного с ним Toolchain-а.

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

COMPONENT_TARGETS = $(HARDWARE_CI20)
COMPONENT_TARGETS += $(HARDWARE_BT01)
COMPONENT_TARGETS += $(HARDWARE_DM64)

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

targets = target_mips_ci20 target_mips_bt01 target_arm_dm64

Имея такой список, система сборки может восстановить аргументы, которые
необходимо передавать при каждом вызове утилиты Make, для нашего сценария.
Сделать это нетрудно, на языке GNU Make эти действия можно описать так:

target_%: TOOLCHAIN = $(shell echo $(word 2, $(subst _, , $@)))
target_%: HARDWARE = $(shell echo $(word 3, $(subst _, , $@)))
target_%:
$(MAKE) TOOLCHAIN=$(TOOLCHAIN) HARDWARE=$(HARDWARE)

Таким образом, при вызове команды Make без аргументов, переменные TOOLCHAIN и
HARDWARE будут не определены и, в этом случае, система сборки займется созданием
списка targets из числа допустимых комбинаций. Когда же список будет составлен,
система сборки сможет осуществить вызов

$(MAKE) TOOLCHAIN=$(TOOLCHAIN) HARDWARE=$(HARDWARE)

с действительными аргументами.

Когда же, при очередном вызове, система убедится в том, что переменные
TOOLCHAIN и HARDWARE определены, управление будет передано нашему сценарию
без дополнительных вычислений.

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

Все просто. Каждый выбирает средство исходя из поставленной задачи.

И еще. Современные системы сборки слишком перегружены, например тем, что пытаются анализировать флаги передаваемые через переменные окружения CFLAGS, CXXFLAGS,… вместо того, чтобы просто передать их компилятору. Посмотрите в какой ад превращалось портирование Qt на новую архитектуру, когда надо было редактировать *.pro файлы так, чтобы система сборки не калечила флаги, передаваемые компилятору code.qt.io/cgit/qt/qtwebengine.git/tree/src/core/gyp_run.pro?h=5.7.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.