LINUX.ORG.RU

Как работать с Makefile-проектами в среде CLion

 , , , ,


4

1

За последние несколько лет мне пришлось столкнуться с множеством вопросов, которые были сформулированы примерно так: «мой проект не открывается в среде CLion». В свою очередь, это приводило к необходимости из раза в раз объяснять разным людям примерно одно и то же. Статья имеет целью сохранить тот опыт, который был накоплен в процессе анализа десятков разных проектов.

CLion — проприетарная кроссплатформенная среда разработки для языков C и C++ от компании JetBrains. Предполагается, что официальная документация вам уже известна и не вызывает вопросов.

Если вам лень вникать в скучные технические детали, можете перейти прямо к разделу «Рекомендации».

Постановка задачи

Основная проблема с проектами, использующими в качестве системы сборки Make, состоит в том, что эта система не предоставляет ровным счётом никакой информации о проектной модели, т. е. о том, какие файлы исходного кода попадут на вход компилятора, какие ключи компилятора, директивы препроцессора и заголовочные файлы будут использованы, и какие бинарные файлы мы получим на выходе. Эта информация остаётся неизвестной до тех пор, пока проект не будет собран. В этом состоит сложность задачи интеграции сред разработки (IDE) и системы сборки Make.

Рассмотрим, например, проект с вот такой плоской структурой:

  • Makefile
  • foo.c
  • bar.c

и вот таким элементарным Makefile:

.PHONY: all
all: foo

.PHONY: clean
clean:
	$(RM) foo

Здесь видно, что файл foo.c является частью проектной модели, т. к. будет участвовать в процессе компиляции (посредством встроенного правила $(CC) foo.c -o foo), а файл bar.c — не является.

Подходы к анализу проектной модели

База данных компиляции

Можно решить задачу «в лоб» и сначала собрать проект, а уж затем выяснить, какие файлы и с какими флагами были скомпилированы. Для создания файла compile_commands.json (собственно базы данных компиляции) будем использовать любой из доступных генераторов — bear или compiledb. bear работает посредством перехвата динамических вызовов (LD_PRELOAD) и потому выдаёт достаточно точный результат, не зависит от системы сборки (т. е. может использоваться совместно с любой системой сборки, хоть с чёртом в ступе, а не только с Make), но имеет ограничения на Mac OS X и вообще не работает на Windows. С другой стороны, compiledb анализирует исключительно вывод команды make и потому нередко совершает ошибки, но, с другой стороны, работает везде. Если вы используете Linux, я предлагаю вам остановить свой выбор именно на bear. По крайней мере, у вас не будет ошибок, связанных с неверной интерпретацией двойных кавычек, апострофов и путей, содержащих пробелы.

Итак, мы собрали наш проект, «обернув» команду сборки и выполнив что-то вроде bear make или bear make all, и теперь имеем на выходе заветный compile_commands.json. CLion вполне в состоянии открыть этот файл как проект, но у такого подхода есть по меньшей мере 2 недостатка:

  1. Сборка всего проекта может занимать десятки минут (ядро Linux) или даже часы, а открыть проект в IDE хочется «почти мгновенно».

  2. Если вы не просто открыли проект с целью «посмотреть код», а собираетесь добавлять/удалять/переименовывать файлы, переписывать сам Makefile или его прототип (как в системах сборки типа GNU Autotools, работающих поверх Make), то после каждой такой операции вам придётся заново генерировать базу данных компиляции. И это муторно, скажу я вам.

Поэтому стоит задаться вопросом, нет ли способа проанализировать структуру проекта, не выполняя сборку.

Переопределение переменных окружения

Переопределение переменной $(MAKE)

Этот подход не может использоваться как самостоятельное решение, однако, если проект использует рекурсивные вызовы Make (см. 5.7 Recursive Use of make), можно переопределить переменную MAKE и, подставив в значение путь до собственной «обёртки» над Make (MAKE=my-custom-make-wrapper), отслеживать все такие вызовы и, при необходимости, менять или дополнять флаги Make, редактируя аргументы перехватываемой командной строки.

Переопределение компиляторов

Переопределяя переменные CC и CXX (и имея «обёртку», которая может имитировать поведение компилятора), можно перехватывать вызовы компилятора и, таким образом, точно знать, в каком каталоге и с какими аргументами компилятор был вызван.

Переопределение оболочки

Переопределяя переменную SHELL (при наличии «обёртки», опять же), можно получить информацию обо всех вызываемых командах (а не только о командах компиляции).

Разумеется, вышеупомянутые техники можно произвольным образом комбинировать.

Перехват системных вызовов

На Linux, Solaris и, с оговорками, Mac OS X информацию о системных вызовах execve() (и интересующих нас аналогах) можно получить через механизм LD_PRELOAD или (Linux) запуская утилиту strace. Впрочем, полученное решение уже не будет кроссплатформенным.

Мне не известен ни один инструмент, где бы хотя бы частично были реализованы эти техники, кроме, быть может, NetBeans CND и Oracle Solaris Studio. Однако, насколько мне известно, поддержка Makefile-проектов в упомянутых продуктах не развивалась с 2016 года.

Запуск Make в режиме «dry run»

На русский язык переводится как «репетиция», но подобного русскоязычного термина попросту нет, поэтому впредь будем называть этот режим именно «dry run», как в англоязычной документации. Вот описание ключа командной строки GNU Make:

-n, –just-print, –dry-run, –recon

Print the commands that would be executed, but do not execute them (except in certain circumstances).

Помимо этого флага, нам ещё понадобится флаг w:

-w, –print-directory

Print a message containing the working directory before and after other processing. This may be useful for tracking down errors from complicated nests of recursive make commands.

и флаг k:

-k, –keep-going

Continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.

Полная командная строка, таким образом, будет make -wnk, и вывод команды Make в большинстве случаев позволяет нам проанализировать структуру проекта. Этот способ не столь точен, как переопределение переменных или перехват системных вызовов, но он относительно прост в реализации, и в CLion используется именно этот подход.

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

Выделение списка целей

Помимо анализа проектной модели, CLion умеет собирать информацию о том, какие цели объявлены в Makefile. Каждая выявленная цель автоматически становится конфигурацией типа Makefile Application: снимок.

GNU Make

Для GNU Make информация о целях собирается так же, как это сделано в соответствующем сценарии из проекта bash-completion, достаточно воспользоваться флагом p:

-p, –print-data-base

Print the data base (rules and variable values) that results from reading the makefiles; then execute as usual or as otherwise specified. This also prints the version information given by the -v switch. To print the data base without trying to remake any files, use make -p -f/dev/null.

и выполнить make -npq.

BSD Make

Здесь нужен другой подход: BSD Make ничего не знает о флаге p, это расширение GNU.

В настоящее время CLion не поддерживает BSD Make, но, чисто теоретически, «научить» работать с целями BSD Make достаточно просто, используя (опять же, нестандартный) флаг V:

-V variable

Print bmake’s idea of the value of variable, in the global context. Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or undefined variable. If variable contains a ‘$’ then the value will be expanded before printing.

Таким образом, список целей можно легко получить, выполнив команду

bmake V '$(.ALLTARGETS)'

Если хочется исключить из этого списка синтетические «псевдоцели» (.WAIT), команду надо привести к следующему виду:

bmake -V '$(.ALLTARGETS:N.WAIT_*:O)'

Рекомендации

Теперь, собственно, то, ради чего вся статья и была написана. Следование этим рекомендациям не даст стопроцентной гарантии, что ваш проект без ошибок откроется в CLion, но, во всяком случае, существенно снизит количество этих ошибок.

Часть советов касается рекурсивных вызовов Make. Внимательный читатель, вероятно, скажет, что это абсолютное зло, сославшись на статью Питера Миллера «Recursive Make Considered Harmful» (HTML, PDF). На что можно возразить, что подавляющее большинство проектов, основанных на GNU Autotools, таки использует рекурсию Make, так что зло это хоть и абсолютное, но, увы, неизбежное. К тому же, как выяснилось в процессе подготовки статьи, есть и альтернативный взгляд на вещи.

Начнём.

  1. Убедитесь, что проект таки собирается (в том же окружении и тем же компилятором, какие выбраны в настройках проекта CLion). Условно говоря, если в настройках выбраны WSL и GCC, то код должен собираться в выбранной гостевой виртуальной машине компилятором GCC. Отсутствующие заголовочные файлы, недостающие зависимости, завершающийся с ошибкой сценарий configure (для GNU Autotools) — все эти проблемы стоит разрешить заранее, до того, как вы попытаетесь открыть проект в CLion.

  2. Используйте GNU Make. Убедитесь, что путь именно к этому инструменту выбран у вас в настройках. POSIX Make, BSD Make, Borland Make и Microsoft NMake пока не поддерживаются.

  3. Если ваш Makefile использует рекурсивные вызовы Make, в коде рецепта (т. е. интерпретируемого оболочкой набора команд для сборки цели) всегда вызывайте $(MAKE) вместо make. Тому есть две причины.

    Первая причина никак не связана собственно с CLion: у кого-то из пользователей инструмент Make может отсутствовать в переменной PATH или быть установлен как gmake или bmake. Рекурсивно вызывая $(MAKE), вы можете быть уверены, что для родительского и дочернего процессов Make будет использован один и тот же исполняемый файл (напр., /usr/bin/make), т. е., скажем, GNU Make никогда не породит BSD Make, и наоборот.

    Во-вторых, в пресловутом режиме «dry run», используемом для анализа проектной модели, первая форма записи будет распознана как рекурсивный вызов с печатью соответствующих команд, а вторая — нет. Рассмотрим проект с такой структурой:

    • Makefile
    • foo/
      • Makefile
      • foo.c
    • bar/
      • Makefile
      • bar.c

    А теперь сравним два варианта вывода Make. Первый, через $(MAKE):

    make: Entering directory '/home/alice'
    make -C foo all
    make[1]: Entering directory '/home/alice/foo'
    echo "Making all in foo..."
    gcc -c -o foo.o foo.c
    make[1]: Leaving directory '/home/alice/foo'
    make -C bar all
    make[1]: Entering directory '/home/alice/bar'
    echo "Making all in bar..."
    gcc -c -o bar.o bar.c
    make[1]: Leaving directory '/home/alice/bar'
    make: Leaving directory '/home/alice'
    

    А теперь make:

    make: Entering directory '/home/alice'
    make -C foo all
    make -C bar all
    make: Leaving directory '/home/alice'
    

    Во втором случае, если в каком-то из дочерних (рекурсивно вызываемых) Makefile’ов был столь нужный нам вызов компилятора, мы этого просто не увидим.

    Кстати, ровно в вышеописанном нюансе состоит сложность реализации поддержки средой CLion инструмента BSD Make: с точки зрения конечного пользователя, bmake -wnk никогда не распознаёт рекурсивные вызовы, независимо от формы записи. Связано это с тем, что GNU Make в режиме «dry-run» (-n) для каждого рекурсивного исполнения $(MAKE) производит системный вызов execve() (тоже с флагом -n, разумеется), а вот BSD Make — как раз нет (разница легко обнаруживается при запуске утилиты strace с ключом -f):

    $ strace -f -e execve make -wnk 2>&1 >/dev/null | grep -vF ENOENT | grep -F execve
    execve("/usr/bin/make", ["make", "-wnk"], 0x7ffe8a5a35a0 /* 80 vars */) = 0
    [pid 15729] execve("/usr/bin/make", ["make", "-C", "foo", "all"], 0x5608f4544a30 /* 84 vars */) = 0
    [pid 15730] execve("/usr/bin/make", ["make", "-C", "bar", "all"], 0x5608f4544a30 /* 84 vars */) = 0
    
    $ strace -f -e execve bmake -wnk 2>&1 >/dev/null | grep -vF ENOENT | grep -F execve
    execve("/usr/bin/bmake", ["bmake", "-wnk"], 0x7ffc10221bb0 /* 80 vars */) = 0
    
  4. Не «зашивайте» в ваш Makefile короткое имя компилятора или полный путь к нему, как, напр., gcc или /usr/bin/clang++. Вместо этого используйте стандартные переменные $(CC) и $(CXX). При запуске внешних процессов CLion устанавливает одноимённые переменные окружения так, чтобы они соответствовали набору инструментов («toolchain»), выбранному в настройках проекта. В результате и в процессе анализа проектной модели, и в процессе сборки будут использованы ровно те компиляторы C и C++, какие выбраны у вас в настройках проекта (поле «Toolchain»). Соответственно, и переключиться с GCC на Clang или наоборот легко и просто — с той лишь оговоркой, что потребуется повторить и фазу анализа проектной модели, и сборку.

    Вот так плохо:

    foo.o: foo.c
    	cc -c -o $@ $<
    

    Вот так хорошо:

    foo.o: foo.c
    	$(CC) -c -o $@ $<
    
  5. Поскольку флаги w, n и k являются принципиально важными для корректного анализа проектной модели, не меняйте значений этих флагов посредством MFLAGS, MAKEFLAGS или GNUMAKEFLAGS. Плохой пример, выключение флага w:

    GNUMAKEFLAGS += --no-print-directory
    

    В качестве альтернативы, если вы работаете с «чужим» проектом, куда у вас нет прав на запись (пусть, напр., Node.js), и не хотите менять файлы, находящиеся под контролем системы VCS, можно для фазы анализа включить флаг e:

    -e, –environment-overrides

    Give variables taken from the environment precedence over variables from makefiles.

    Вот так могут выглядеть настройки проекта для Node.js: снимок.

    Включение флага e через поле «Arguments» может быть альтернативным решением и в иных случаях, когда на уровне Makefile переопределены флаги Make или другие стандартные переменные окружения (см. ниже).

  6. Избегайте параллелизма на уровне процессов (make -jN при N > 1), «зашитого» в Makefile через переопределение переменных MFLAGS, MAKEFLAGS или GNUMAKEFLAGS (подробнее в 5.7.3 Communicating Options to a Sub-make):

    MAKEFLAGS += j8
    
    .PHONY: all
    all: foo-all bar-all
    
    .PHONY: foo-all
    foo-all:
    	$(MAKE) -C foo all
    
    .PHONY: bar-all
    bar-all:
    	$(MAKE) -C bar all
    

    В таких условиях Make будет использовать более одного (в примере выше — 8) параллельного процесса при рекурсивных вызовах, в результате чего в выводе команды сообщения вида «Entering directory ‘…’» и «Leaving directory ‘…’» будут перемешаны между собой, команды компиляции — произвольным образом разбросаны между этими сообщениями, и CLion не сможет отследить ни смену каталога, ни принадлежность команды тому или иному каталогу:

    make: Entering directory '/home/alice'
    make -C foo all
    make -C bar all
    make[1]: Entering directory '/home/alice/foo'
    make[1]: Entering directory '/home/alice/bar'
    echo "Making all in foo..."
    make[1]: Leaving directory '/home/alice/foo'
    echo "Making all in bar..."
    make[1]: Leaving directory '/home/alice/bar'
    make: Leaving directory '/home/alice'
    

    С учётом вышесказанного, если вы хотите иметь возможность собирать проект, используя несколько параллельных процессов Make, передайте флаг -j через поле «Build options» в настройках проекта (но ни в коем случае не через поле «Arguments» — флаги в этом поле используются для анализа проектной модели, но не для сборки): снимок.

  7. Аналогичным образом, при рекурсивных вызовах Make, не переопределяйте региональные настройки (LC_*, LANG, LANGUAGE) внутри ваших Makefile’ов и/или сценариев сборки. Дело в том, что CLion, отслеживая сообщения о смене каталога, ожидает эти сообщения именно на английском языке (и заботливо устанавливает нужное окружение для родительского процесса Make). Вот что будет, если вмешается пользователь:

    export LC_ALL = ru_RU.UTF-8
    export LANG = ru_RU.UTF-8
    
    .PHONY: all
    all: foo-all bar-all
    
    .PHONY: foo-all
    foo-all:
    	$(MAKE) -C foo all
    
    .PHONY: bar-all
    bar-all:
    	$(MAKE) -C bar all
    

    Вывод команды make -wnk:

    make: Entering directory '/home/alice'
    make -C foo all
    make[1]: вход в каталог «/home/alice/foo»
    echo "Making all in foo..."
    make[1]: выход из каталога «/home/alice/foo»
    make -C bar all
    make[1]: вход в каталог «/home/alice/bar»
    echo "Making all in bar..."
    make[1]: выход из каталога «/home/alice/bar»
    make: Leaving directory '/home/alice'
    
  8. Далее, при рекурсивных вызовах Make, старайтесь вместо формы

    target:
    	cd subdir && $(MAKE) target
    

    использовать форму

    target:
    	$(MAKE) -C subdir target
    

    Помимо служебных сообщений Make («Entering directory», «Leaving directory»), CLion, безусловно, обработает и команду cd и отследит смену текущего каталога, но второй вариант записи кажется надёжнее.

  9. Используйте встроенную функцию $(foreach) вместо циклов оболочки POSIX (for ... do ... done или while ... do ... done). Рассмотрим проект со следующей структурой:

    • Makefile
    • foo/
      • Makefile
    • bar/
      • Makefile
    • baz/
      • Makefile

    и вот таким Makefile’ом в корневом каталоге проекта:

    all-recursive:
    	for subdir in foo bar baz; \
    	do (cd $$subdir; $(MAKE) all) || exit 1; done
    

    Здесь рецепт цели all-recursive — это цикл оболочки POSIX, который, скорее всего, не будет выполнен в режиме «dry run». Если воспользоваться функцией $(foreach), то можно переписать так:

    all-recursive:
    	$(foreach subdir,$(SUBDIRS),$(MAKE) -C $(subdir) all;)
    
  10. Если есть необходимость скомпилировать сразу несколько файлов исходного кода одной командной строкой, не используйте маски (wildcards) оболочки. Вместо этого используйте встроенную функцию $(wildcard). Вот так плохо:

    all:
    	$(CC) -c *.c
    

    Если в одном с Makefile’ом каталоге находятся, например, файлы foo.c и bar.c, то на стадии анализа команда make -wnk по-прежнему выведет

    cc -c *.c
    

    а CLion не умеет вычислять маски оболочки (к тому же, на UNIX и Windows оболочки разные и, соответственно, синтаксис масок слегка различен). Вот так хорошо:

    all:
    	$(CC) -c $(wildcard *.c)
    

    В этом случае значение маски будет вычислено средствами Make:

    cc -c foo.c bar.c
    

    Файлы исходного кода должны существовать в дереве проекта на момент анализа проектной модели.

  11. Если вам необходимо выполнить фрагмент кода рецепта цели во вложенной оболочке (по-английски это называется «subshell»), и этот фрагмент одновременно является частью команды компиляции (которая, как вы ожидаете, должна быть распознана средой CLion), то дочерние процессы оболочки POSIX лучше создавать средствами Make, используя встроенную функцию $(shell), а не средствами оболочки ($(команда) или `команда`). Иными словами, вот такой подход будет работать:

    main.o: main.cpp
    	$(CXX) -I../ -g -Wall $(shell pkg-config some-library) -c -o $@ $<
    

    в то время как такой — нет:

    main.o: main.cpp
    	$(CXX) -I../ -g -Wall `pkg-config some-library` -c -o $@ $<
    	$(CXX) -I../ -g -Wall $$(pkg-config some-library) -c -o $@ $<
    

    Это связано с тем, что CLion, за вычетом некоторых исключений (напр., VPATH-сборки в GNU Autotools), не умеет анализировать командные строки, использующие вложенную оболочку.

На этом всё. Надеюсь, описанный опыт был кому-то полезен. Есть ещё некоторые нюансы, которые проще всего проиллюстрировать на конкретном Makefile-проекте (а именно, ядре Linux), но об этом — в следующей статье.

★★★★★

Проверено: hobbit ()

советы по написанию make файлов

Как будто кто-то использует самописные make файлы для проектов, состоящих из более чем двух файлов. А если файлов два, то соскочить на богомерзкий cmake можно за пол часа с учётом гуглежа синтаксиса и попыток найти зависимости.

ox55ff ★★★★★ ()

Во, у хабра дизайн поменялся. Наконец-то!

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

Стоп. А зачем тогда Jenkins и подобные штуки ?

И если что CLion не фрии а за деньги …

mx__ ★★★★★ ()
Последнее исправление: mx__ (всего исправлений: 1)
Ответ на: комментарий от ox55ff

Как будто кто-то использует самописные make файлы для проектов, состоящих из более чем двух файлов

🤚

Правда, я не пользуюсь CLion..

Насчёт «двух файлов» не особо в тему, Makefile же в шаблонные цели умеет

Crocodoom ★★★★★ ()
Последнее исправление: Crocodoom (всего исправлений: 1)

На этом всё. Надеюсь, описанный опыт был кому-то полезен.

Проприетарное ПО не нужно. Зачем ты это приволок?

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

Makefile хватит всем ))

А если так - любой редактор и вперде

Даже нет, VS очень хорошая IDE. Надо срочно в мининовость опыт клацанья!

Oldboy ()
Последнее исправление: Oldboy (всего исправлений: 2)

пользуясь случаем, дружно передаём привет @maxcom, закопавшему wiki-раздел

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

Какой VS в линух? Да и к тому же сами один коммент назад проклинали проприетарщину.

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

Напиши аналогичную статью про любую свободную IDE, которую можно сопрячь с мейкфайлами :)

Михаил, спасибо за предложение.

На деле, статья-то не только про CLion. Т. е., если следовать перечисленным рекомендациям по написанию новых Makefile’ов (если ты – автор проекта) либо уже править существующие Makefile’ы в соответствии с рекомендациями (если ты хочешь разобраться в чужом проекте) – то улучшить качество проектной модели можно при использовании любого инструмента, который анализирует вывод Make, будь это CLion, NetBeans CND, Oracle Studio или compiledb.

А выхлоп compiledb, compile_commands.json, в свою очередь, – эти lingua franca в современном мире разнообразных систем сборки, этот формат понимают ещё очень многие инструменты.

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

Я мог бы написать об NetBeans CND, но это, по факту, тоже несвободная среда. «Ядро» открыто, кажется, под Apache 2.0 license, а код модуля CND компания Oracle так за 8 лет (с версии 8.2) так и не открыла. Ну и, соответственно, инструмент отстал на 8 лет.

В Eclipse CDT всё печально – там либо managed Makefiles, либо восход Солнца вручную, когда пользователь должен самостоятельно указать пути к заголовочным файлам и макроопределения препроцессора. Т. е. в больших проектах, основанных на Make (ядро Linux), без множественных псевдо-ошибок («красного кода») в редакторе кода не обойтись.

Вообще, не очень понятно, почему сообщество, с учётом накопленного опыта, до сих пор не реализовало в виде библиотеки то, что умеют CLion или NetBeans CND. Необходимость в libmake.so давно назрела.

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

Да известно это всё. Не трави душу.

Но, перефразируя одного любимого многими киногероя, политические метания руководителей бизнеса преходящи, а стремление человека к прогрессу вечно.

А какой прогресс возможен без поддержки Make в IDE?

Bass ★★★★★ ()

Кстати, @EXL с полгода назад обещал написать статью про ещё одну коммерческую IDE. Сергей, где ты?

Bass ★★★★★ ()

CLion. A cross-platform IDE for C and C++

Is the trial free?

Yes, the trial of CLion is free. You have access to a fully featured product for 30 days.

Ты приволок свою статью сюда, и даже не заикнулся про то, что это проприетарный продукт. Да, лор уже не торт…

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

Ну вообще-то, автор сначала поместил свою статью в группу «Проприетарное ПО», это я при подтверждении решил, что ей место в группе «Документация».

В тексте самой статьи проприетарность IDE отметить бы не мешало, но прикол в том, что про сам CLion в статье почти ничего нет, всё больше про мейкфайлы и как с ними бороться.

hobbit ★★★★★ ()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

Добавил в статью соответствующий абзац

Спасибо.

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

Есть ещё некоторые нюансы, которые проще всего проиллюстрировать на конкретном Makefile-проекте (а именно, ядре Linux), но об этом — в следующей статье.

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

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

(V)

Вообще, не очень понятно, почему сообщество, с учётом накопленного опыта, до сих пор не реализовало в виде библиотеки то, что умеют CLion или NetBeans CND. Необходимость в libmake.so давно назрела.

А что умеют CLion или NetBeans CND такого, что имеет смысл выносить в libmake.so? Я в IDE не спец, правда интересно

Crocodoom ★★★★★ ()
Последнее исправление: Crocodoom (всего исправлений: 1)

Благодарю! Думаю в будущем пригодится.

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

Кстати, @EXL с полгода назад обещал написать статью про ещё одну коммерческую IDE. Сергей, где ты?

Я про Solaris вкупе с Sun Workshop обещал написать. И надеюсь смогу сдержать это обещание, статья готова наполовину и пылится в черновиках. За эти полгода много чего произошло из-за чего к сожалению пришлось отложить в долгий ящик подобные эксперименты :(

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

Завести на форуме отдельный раздел статей, запретить туда постить. Люди, которые хотят запостить статью, постят её в форум, модераторы вручную перетаскивают топики в нужный раздел, начисляют скор, корректоры корректируют. Можно грабить корованы создавать новости на главной со ссылкой на страницу поста.

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

Я в принципе за, но если статьи будут появляться раз в два года… Ты же помнишь, на ЛОРе во время оно была не только вики, но и раздел с доками.

Только в форум постить, наверное, смысла нет. Можно было бы сделать для статей такие же «Неподтверждённые», как для новостей, галереи и опросов. Скор вручную начислять всё равно нельзя…

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

Я в принципе за, но если статьи будут появляться раз в два года… Ты же помнишь, на ЛОРе во время оно была не только вики, но и раздел с доками.

Я туда даже что-то писал, да. Но вики это не вариант с нашей тролль-публикой, а вот статьи редактировать нельзя.

И в чём проблема постинга раз в два года? Вон в клубе вообще ничего нет)

Только в форум постить, наверное, смысла нет. Можно было бы сделать для статей такие же «Неподтверждённые», как для новостей, галереи и опросов. Скор вручную начислять всё равно нельзя…

Это потребует модификации движка, а ещё один раздел на форуме — нет. Но дело ваше.

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

а ещё один раздел на форуме — нет.

Я ж говорю, за постинг на форум скор руками не начислишь. Изменения всё равно потребуются.

hobbit ★★★★★ ()

В начале хотел задать вопрос, нафига использовать CLion, потом, нафига использовать голые мейкфайлы, но потом прочитал про ядро и тут вопрос, нафига голые мейкфайлы, отпал, вопрос про платные IDE правда остался

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

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

Михаил, спасибо за напутствие. Статья будет.

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

А что умеют CLion или NetBeans CND такого, что имеет смысл выносить в libmake.so?

В первую очередь – сбор информации, аналогичной compile_commands.json, но без долгой сборки проекта. Компилятор, директивы препроцессора, заголовочные файлы, файлы исходного кода. То, без чего полноценная навигация по коду (символы, «инклюды» и т. д.) будет невозможна.

Глянь, я про Eclipse CDT писал два года назад. Вот это навигация курильщика.

А теперь сравним с CLion. Есть такое действие («action»), называется Show Compiler Info. И вот какую информацию вызов этого действия выдаёт мне для одного из файлов исходного кода проекта u-boot. Здесь «compiler switches» – полный набор аргументов командной строки GCC для данного конкретного файла:

Compiler info for 'cache.c' in configuration 'cache.c' for language C

Compiler kind: GCC path: /usr/bin/gcc

Compiler switches:
	-Wp,-MD,arch/sandbox/cpu/.cache.o.d
	-nostdinc
	-isystem
	/usr/lib/gcc/x86_64-linux-gnu/8/include
	-Iinclude
	-I./arch/sandbox/include
	-include
	./include/linux/kconfig.h
	-D__KERNEL__
	-D__UBOOT__
	-Wall
	-Wstrict-prototypes
	-Wno-format-security
	-fno-builtin
	-ffreestanding
	-std=gnu11
	-fshort-wchar
	-fno-strict-aliasing
	-fno-PIE
	-Os
	-flto=8
	-fstack-protector-strong
	-fno-delete-null-pointer-checks
	-Wno-stringop-truncation
	-Wno-maybe-uninitialized
	-fmacro-prefix-map=./=
	-g
	-fstack-usage
	-Wno-format-nonliteral
	-Wno-unused-but-set-variable
	-D__SANDBOX__
	-U_FORTIFY_SOURCE
	-DCONFIG_ARCH_MAP_SYSMEM
	-fPIC
	-I/usr/include/SDL2
	-D_REENTRANT
	-pipe
	-DKBUILD_BASENAME="cache"
	-DKBUILD_MODNAME="cache"
	-c

Compiler info:

Defines:
#define __STDC__ 1
#define __STDC_VERSION__ 201112L
#define __STDC_UTF_16__ 1
#define __STDC_UTF_32__ 1
#define __STDC_HOSTED__ 0
#define __GNUC__ 8
#define __GNUC_MINOR__ 3
#define __GNUC_PATCHLEVEL__ 0
#define __VERSION__ "8.3.0"
#define __ATOMIC_RELAXED 0
#define __ATOMIC_SEQ_CST 5
#define __ATOMIC_ACQUIRE 2
#define __ATOMIC_RELEASE 3
#define __ATOMIC_ACQ_REL 4
#define __ATOMIC_CONSUME 1
#define __pic__ 2
#define __PIC__ 2
#define __OPTIMIZE_SIZE__ 1
#define __OPTIMIZE__ 1
#define __FINITE_MATH_ONLY__ 0
#define _LP64 1
#define __LP64__ 1
#define __SIZEOF_INT__ 4
#define __SIZEOF_LONG__ 8
#define __SIZEOF_LONG_LONG__ 8
#define __SIZEOF_SHORT__ 2
#define __SIZEOF_FLOAT__ 4
#define __SIZEOF_DOUBLE__ 8
#define __SIZEOF_LONG_DOUBLE__ 16
#define __SIZEOF_SIZE_T__ 8
#define __CHAR_BIT__ 8
#define __BIGGEST_ALIGNMENT__ 16
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __ORDER_BIG_ENDIAN__ 4321
#define __ORDER_PDP_ENDIAN__ 3412
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __SIZEOF_POINTER__ 8
#define __SIZE_TYPE__ long unsigned int
#define __PTRDIFF_TYPE__ long int
#define __WCHAR_TYPE__ short unsigned int
#define __WINT_TYPE__ unsigned int
#define __INTMAX_TYPE__ long int
#define __UINTMAX_TYPE__ long unsigned int
#define __CHAR16_TYPE__ short unsigned int
#define __CHAR32_TYPE__ unsigned int
#define __SIG_ATOMIC_TYPE__ int
#define __INT8_TYPE__ signed char
...
#define __REGISTER_PREFIX__
#define __USER_LABEL_PREFIX__
#define __GNUC_STDC_INLINE__ 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1
#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 1
#define __GCC_ATOMIC_BOOL_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
#define __GCC_ATOMIC_SHORT_LOCK_FREE 2
#define __GCC_ATOMIC_INT_LOCK_FREE 2
#define __GCC_ATOMIC_LONG_LOCK_FREE 2
#define __GCC_ATOMIC_LLONG_LOCK_FREE 2
#define __GCC_ATOMIC_TEST_AND_SET_TRUEVAL 1
#define __GCC_ATOMIC_POINTER_LOCK_FREE 2
#define __GCC_HAVE_DWARF2_CFI_ASM 1
#define __PRAGMA_REDEFINE_EXTNAME 1
#define __SSP_STRONG__ 3
#define __SIZEOF_INT128__ 16
#define __SIZEOF_WCHAR_T__ 2
#define __SIZEOF_WINT_T__ 4
#define __SIZEOF_PTRDIFF_T__ 8
#define __amd64 1
#define __amd64__ 1
#define __x86_64 1
#define __x86_64__ 1
#define __SIZEOF_FLOAT80__ 16
#define __SIZEOF_FLOAT128__ 16
#define __ATOMIC_HLE_ACQUIRE 65536
#define __ATOMIC_HLE_RELEASE 131072
#define __GCC_ASM_FLAG_OUTPUTS__ 1
#define __k8 1
#define __k8__ 1
#define __code_model_small__ 1
#define __MMX__ 1
#define __SSE__ 1
#define __SSE2__ 1
#define __FXSR__ 1
#define __SSE_MATH__ 1
#define __SSE2_MATH__ 1
#define __SEG_FS 1
#define __SEG_GS 1
#define __gnu_linux__ 1
#define __linux 1
#define __linux__ 1
#define linux 1
#define __unix 1
#define __unix__ 1
#define unix 1
#define __ELF__ 1
#define __DECIMAL_BID_FORMAT__ 1
#define __KERNEL__ 1
#define __UBOOT__ 1
#define __SANDBOX__ 1
#define CONFIG_ARCH_MAP_SYSMEM 1
#define _REENTRANT 1
#define KBUILD_BASENAME "cache"
#define KBUILD_MODNAME "cache"
#define __has_include(x) __has_include(x)
#define __has_include_next(x) __has_include_next(x)
#define __has_cpp_attribute(x) __has_cpp_attribute(x)
#define __extension__
#define __builtin_va_start(list, paramN) ((void)(list = sizeof(paramN)))
#define __builtin_va_arg(list, type) ((type)list)
#define __builtin_va_end(list) ((void)list)
#define __builtin_va_copy(dest, src) ((void)(dest = src))
#define __builtin_offsetof(type, member) ((size_t)(&(((type *)0)->member)))
#define __builtin_types_compatible_p(X,Y) 1
#define __builtin_choose_expr(C,T,E) T

Features:
	SHORT=2
	LANGUAGE_STANDARD=c11
	CXX_RAW_STRING_LITERALS=false
	INT128_T=16
	FLOAT=4
	CXX_CONSTEXPR=false
	CXX_ATTRIBUTES=false
	C_STATIC_ASSERT=true
	LONG_DOUBLE=16
	LONG=8
	CXX_GENERIC_LAMBDAS=false
	CXX_RETURN_TYPE_DEDUCTION=false
	CXX_BINARY_LITERALS=false
	CXX_GENERALIZED_INITIALIZERS=false
	CXX_EXCEPTIONS=false
	WCHAR_T=2
	POINTER=8
	MISSING_RETURN_FROM_NON_VOID=WARNING
	CXX_NULLPTR=false
	CXX_USER_LITERALS=false
	INT=4
	SIZE_T=8
	CXX_NONSTATIC_MEMBER_INIT=false
	PTRDIFF_T=8
	DOUBLE=8
	CXX_AUTO_TYPE=false
	GCC_AUTO_TYPE=true
	LONG_LONG=8
	CXX_OVERRIDE_CONTROL=false

Implicit includes:
	/home/bass/docs/programming/c/tests/make-output-parsing/pre-configure/kbuild/u-boot/include/linux/kconfig.h

Header Search paths:
	Pointer: file:///home/bass/docs/programming/c/tests/make-output-parsing/pre-configure/kbuild/u-boot/include
	Pointer: file:///home/bass/docs/programming/c/tests/make-output-parsing/pre-configure/kbuild/u-boot/arch/sandbox/include
	Pointer: file:///usr/include/SDL2
	Pointer: file:///usr/lib/gcc/x86_64-linux-gnu/8/include
Bass ★★★★★ ()
Ответ на: комментарий от vitalif

вопрос про платные IDE правда остался

Ну, это личный выбор каждого.

Я не знаю бесплатных аналогов, кроме NetBeans CND, но, повторяю, инструмент остановился в своём развитии 8 лет назад.

В бесплатном VSCode всё сделано на эвристиках, и нередко эти эвристики не справляются. И инструмент превращается в элегантные шорты простой текстовый редактор с подсветкой синтаксиса. Т. е., я хочу сказать, что VSCode – инструмент априори неточный.

Базу данных компиляции (пресловутый compile_commands.json) тебе может открыть бесплатный QtCreator, но это способ читать код, но никак не писать.

Поддержка GNU Autotools (напомню, огромное количество легаси-проектов использует Autotools) есть в Anjuta и, кажется, ещё её пилят в Eclipse CDT, но у меня нет уверенности, что Anjuta или Eclipse проглотят проект, использующий libtool/dolt или собранный вне дерева исходников (VPATH). А у CLion с этим проблем не будет.

Наконец, Kbuild (поверх Make) – это не только про ядро Linux. Есть новые проекты (напр., u-boot), которые «садятся» именно на эту систему сборки. А вовсе не на стандартный CMake, при всех его достоинствах.

Есть всякая embedded-разработка, где нередко инструменты крайне убоги. Ну т. е.б напр., архаичный Arm Keil Studio плюс голый Make для сборки.

Наконец, есть непознанный мир BSD-систем, где вся базовая система – это один большой проект на мэйкфайлах. Правда, там BSD Make и немного другой синтаксис.

CLion тестировали на десятках проектов, и за несколько лет лишь один или два из списка «переползли» на CMake или Meson. Стоит просто раз взглянуть на таблицу, чтобы осознать масштабы бедствия.

Bass ★★★★★ ()
Последнее исправление: Bass (всего исправлений: 3)
Ответ на: комментарий от Bass

Ну я аргументы в теории понимаю, но в моей системе координат они неактуальны, да.

А так KDevelop ещё есть вроде, смотрели? :-) хотя не знаю чего он там умеет, а чего нет, т.к. сам вообще не пользуюсь IDE и не собираюсь :-) мне консоли и mcedit достаточно :-))

Ну и платность для меня блокер, насколько бы оно ни было лучше, ни копейки не отдам из принципа :-) (тем более умникам из РФ, которые «ушли из РФ»)

Голый мейк я не знаю… архаизм всё-таки какой-то. Ну в ядре и ембеддед понятно, там свой мир, а так всё-таки увидеть проект на голом мейке - странно. Но автотулсы тоже г..но конечно

vitalif ★★★★★ ()
Последнее исправление: vitalif (всего исправлений: 2)
Ответ на: комментарий от vitalif

Ну я аргументы в теории понимаю, но в моей системе координат они неактуальны, да.

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

А так KDevelop ещё есть вроде

Формально и KDevelop, и Anjuta поддерживают импорт проектов на Autotools. Но здесь шаг вправо, шаг влево – и всё ломается. VPATH-сборки тому пример. Я только что специально проверил.

тем более умникам из РФ, которые «ушли из РФ»

Ну вот ваш покорный слуга – умник, который предпочёл остаться. И он такой не один. А бизнес… У бизнеса никогда не будет ни убеждений, ни принципов, ни родины. Все прочие официально озвучиваемые позиции (за или против – даже неважно) – лишь затем, чтобы этот бизнес сохранить. И это нормально, молиться на условную «корпорацию добра» или обижаться на неё – так же глупо, как молиться богу дождя или обижаться на погоду. Другое дело, что отношения между государством и обществом нужно выстраивать, имея в виду упомянутое свойство бизнеса. И что-то мне подсказывает, что в некоторых ведущих мировых экономиках оно именно так.

Но автотулсы тоже г..но конечно

Всё в сравнении. Через 20 лет какой-нибудь бодрый молодой человек наверняка будет уверенно утверждать, что CMake, Meson и Ninja – всё дрянь и анахронизм =)

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

Поддержка GNU Autotools […]

Так в QtCreator же тоже есть плагин для этого. И в отличии от плагина для compile_commands.json не экспериментальный. Но сам не пробовал как оно работает.

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

А что умеют CLion или NetBeans CND такого, что имеет смысл выносить в libmake.so?

В первую очередь – сбор информации, аналогичной compile_commands.json, но без долгой сборки проекта.

make --always-make --dry-run \
 | grep -wE 'gcc|g\+\+' \
 | grep -w '\-c' \
 | jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' \
 > compile_commands.json

(https://stackoverflow.com/a/60806295)

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

Так в QtCreator же тоже есть плагин для этого.

Я уже написал выше и привёл пример двух других IDE: этот модуль расширения есть, но, скорее всего, он имеет ограничения и годится только для стандартных проектов.

Впрочем, QtCreator – прекрасный инструмент, и, если вам его хватает, было бы сущим безумием платить за CLion.

Bass ★★★★★ ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.