Как запустить проект в clion
Перейти к содержимому

Как запустить проект в clion

  • автор:

Как правильно собирать проект в clion?

6068725283548093416057.png

Clion работает нормально и запускает приложения в режиме отладки без проблем. Также, он создаёт файл типа «Разделяемая библиотека», который запускается из терминала, но не запускается по клику и выдаёт ошибку:

Код Cmake:

cmake_minimum_required(VERSION 3.17) project(test4) set(CMAKE_CXX_STANDARD 14) add_executable(test4 main.cpp)

Так вот — как сформировать exe/elf файл, который будет запускаться без посторнних средств?

  • Вопрос задан более двух лет назад
  • 495 просмотров

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

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

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

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

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

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

и вот таким элементарным 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:

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

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

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.

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 :

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 :

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: 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
foo.o: foo.c cc -c -o $@ $

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

foo.o: foo.c $(CC) -c -o $@ $
GNUMAKEFLAGS += --no-print-directory

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

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

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

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

Избегайте параллелизма на уровне процессов ( 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" — флаги в этом поле используются для анализа проектной модели, но не для сборки):

Аналогичным образом, при рекурсивных вызовах 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'
target: cd subdir && $(MAKE) target

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

target: $(MAKE) -C subdir target
  • Makefile
  • foo/
    • Makefile
    • Makefile
    • 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;)
    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
    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 $@ $

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

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

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

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

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

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

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

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

    и вот таким элементарным 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, –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: 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 
    foo.o: foo.c cc -c -o $@ $ 

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

    foo.o: foo.c $(CC) -c -o $@ $ 
    GNUMAKEFLAGS += --no-print-directory 

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

    -e, –environment-overrides Give variables taken from the environment precedence over variables from makefiles.

    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' 
    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' 
    target: cd subdir && $(MAKE) target 

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

    target: $(MAKE) -C subdir target 
    • Makefile
    • foo/
      • Makefile
      • Makefile
      • 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;) 
      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 
      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 $@ $ 

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

      Bass ★★★★★
      17.05.22 03:34:25 MSK
      Проверено: hobbit ( 17.05.22 09:11:48 MSK )
      1 2 →

      Спасибо за проделанную работу.

      Формат не совсем новостной, обычно в новостях у нас публикуются не сами статьи, а их анонсы. Но бывают и исключения.

      hobbit ★★★★★
      ( 17.05.22 09:14:56 MSK )

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

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

      ox55ff ★★★★★
      ( 17.05.22 09:26:41 MSK )

      «мой проект не открывается в среде CLion»

      Что такое CLion?

      skvitek ★★★
      ( 17.05.22 10:04:51 MSK )

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

      filosofia ☆
      ( 17.05.22 10:48:29 MSK )
      Ответ на: комментарий от ox55ff 17.05.22 09:26:41 MSK

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

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

      mx__ ★★★★★
      ( 17.05.22 11:01:55 MSK )
      Последнее исправление: mx__ 17.05.22 11:14:26 MSK (всего исправлений: 1)

      Ответ на: комментарий от ox55ff 17.05.22 09:26:41 MSK

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

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

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

      Crocodoom ★★★★★
      ( 17.05.22 11:18:05 MSK )
      Последнее исправление: Crocodoom 17.05.22 11:19:08 MSK (всего исправлений: 1)

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

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

      Oldboy
      ( 17.05.22 11:36:22 MSK )
      Ответ на: комментарий от Oldboy 17.05.22 11:36:22 MSK

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

      hobbit ★★★★★
      ( 17.05.22 11:45:43 MSK )
      Ответ на: комментарий от hobbit 17.05.22 11:45:43 MSK

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

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

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

      Oldboy
      ( 17.05.22 11:52:16 MSK )
      Последнее исправление: Oldboy 17.05.22 11:53:39 MSK (всего исправлений: 2)

      Ответ на: комментарий от hobbit 17.05.22 11:45:43 MSK

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

      А IDE вообще не нужны ��

      Crocodoom ★★★★★
      ( 17.05.22 11:54:36 MSK )
      Последнее исправление: Crocodoom 17.05.22 11:55:02 MSK (всего исправлений: 1)

      Ответ на: комментарий от hobbit 17.05.22 09:14:56 MSK

      Почему бы не перенести в Development и не закрепить?

      hateWin ★★
      ( 17.05.22 12:11:09 MSK )

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

      Ford_Focus ★★★★★
      ( 17.05.22 12:27:29 MSK )
      Ответ на: комментарий от skvitek 17.05.22 10:04:51 MSK

      А вам гугол бойкот объявил?

      xt1zer ★
      ( 17.05.22 13:11:48 MSK )
      Ответ на: комментарий от Oldboy 17.05.22 11:52:16 MSK

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

      xt1zer ★
      ( 17.05.22 13:13:58 MSK )

      Вот ещё хорошая статья про JetBrains

      fluorite ★★★★★
      ( 17.05.22 13:35:35 MSK )
      Ответ на: комментарий от hobbit 17.05.22 11:45:43 MSK

      Напиши аналогичную статью про любую свободную 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 ★★★★★
      ( 17.05.22 13:52:17 MSK ) автор топика
      Ответ на: комментарий от hateWin 17.05.22 12:11:09 MSK

      Да, наверное, имеет смысл. Плюсую.

      Bass ★★★★★
      ( 17.05.22 13:52:50 MSK ) автор топика
      Ответ на: комментарий от fluorite 17.05.22 13:35:35 MSK

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

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

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

      Bass ★★★★★
      ( 17.05.22 14:06:38 MSK ) автор топика

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

      Bass ★★★★★
      ( 17.05.22 14:08:51 MSK ) автор топика
      Ответ на: комментарий от ox55ff 17.05.22 09:26:41 MSK

      Я использую. До определённого момента конечно.

      Dark_SavanT ★★★★★
      ( 17.05.22 14:16:25 MSK )
      Ответ на: комментарий от skvitek 17.05.22 10:04:51 MSK

      +1, Что такое CLion?

      crypt ★★★★★
      ( 17.05.22 14:20:44 MSK )

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

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

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

      Odalist ★★★★★
      ( 17.05.22 14:46:14 MSK )
      Ответ на: комментарий от crypt 17.05.22 14:20:44 MSK

      A cross-platform IDE for C and C++

      За этот продукт, первый год, ты будешь платить €199.00.

      Odalist ★★★★★
      ( 17.05.22 14:47:56 MSK )
      Последнее исправление: Odalist 17.05.22 14:48:25 MSK (всего исправлений: 1)

      Ответ на: комментарий от Odalist 17.05.22 14:46:14 MSK

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

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

      hobbit ★★★★★
      ( 17.05.22 14:50:40 MSK )
      Последнее исправление: hobbit 17.05.22 14:52:21 MSK (всего исправлений: 1)

      Ответ на: комментарий от Odalist 17.05.22 14:46:14 MSK

      Добавил в статью соответствующий абзац, надеюсь, Bass не против. :)

      hobbit ★★★★★
      ( 17.05.22 14:58:02 MSK )
      Ответ на: комментарий от hobbit 17.05.22 14:58:02 MSK

      Всё нормально. Не против. Спасибо.

      Bass ★★★★★
      ( 17.05.22 15:02:57 MSK ) автор топика
      Ответ на: комментарий от hobbit 17.05.22 14:58:02 MSK

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

      Odalist ★★★★★
      ( 17.05.22 15:09:14 MSK )
      Ответ на: комментарий от Bass 17.05.22 15:02:57 MSK

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

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

      hobbit ★★★★★
      ( 17.05.22 15:10:06 MSK )
      Ответ на: комментарий от Odalist 17.05.22 14:47:56 MSK

      А с меня взяли 80$ + tax. Последующие продления дешевле. Плюс на пол года продлили на халяву

      SR_team ★★★★★
      ( 17.05.22 15:13:25 MSK )
      Ответ на: комментарий от SR_team 17.05.22 15:13:25 MSK

      Интересно, почему так. На сайте пишут, что годовая подписка (на год), стоит €199.00, а месячная- €19.90

      Odalist ★★★★★
      ( 17.05.22 15:19:49 MSK )
      Ответ на: комментарий от Odalist 17.05.22 15:19:49 MSK

      это для организаций. За «For Individual Use» просят $89

      SR_team ★★★★★
      ( 17.05.22 15:25:35 MSK )
      Ответ на: комментарий от SR_team 17.05.22 15:25:35 MSK
      Odalist ★★★★★
      ( 17.05.22 15:32:43 MSK )
      Ответ на: комментарий от Bass 17.05.22 13:52:17 MSK

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

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

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

      Ответ на: комментарий от Dark_SavanT 17.05.22 14:16:25 MSK

      Любите вы в своём embed извращаться.

      ox55ff ★★★★★
      ( 17.05.22 16:24:06 MSK )

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

      xwicked ★★☆
      ( 17.05.22 17:03:54 MSK )
      Ответ на: комментарий от Bass 17.05.22 14:08:51 MSK

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

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

      EXL ★★★★★
      ( 17.05.22 19:36:38 MSK )
      Ответ на: комментарий от hobbit 17.05.22 09:14:56 MSK

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

      Aceler ★★★★★
      ( 17.05.22 20:30:05 MSK )
      Ответ на: комментарий от Aceler 17.05.22 20:30:05 MSK

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

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

      hobbit ★★★★★
      ( 17.05.22 20:57:25 MSK )
      Ответ на: комментарий от hobbit 17.05.22 20:57:25 MSK

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

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

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

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

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

      Aceler ★★★★★
      ( 17.05.22 21:00:11 MSK )
      Ответ на: комментарий от Aceler 17.05.22 21:00:11 MSK

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

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

      hobbit ★★★★★
      ( 17.05.22 22:00:59 MSK )

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

      vitalif ★★★★★
      ( 17.05.22 23:57:02 MSK )
      Ответ на: комментарий от hobbit 17.05.22 15:10:06 MSK

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

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

      Bass ★★★★★
      ( 18.05.22 01:32:21 MSK ) автор топика
      Ответ на: комментарий от Crocodoom 17.05.22 16:22:22 MSK

      А что умеют 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 

      Как запустить проект в clion

      На первом шаге проект нужно сконфигурировать, то есть создать финальный скрипт сборки, запустив cmake в будущем каталоге сборки.

      # Сейчас мы в каталоге `myapp` с файлом CMakeLists.txt # Создадим каталог `myapp-release` и перейдём в него. mkdir --parents ../myapp-release cd ../myapp-release # Сконфигурируем проект для сборки в Release. # Флаг установит опцию CMAKE_BUILD_TYPE в значение "Release", # интерпретатор cmake считает это переключением на Release конфигурацию. cmake -DCMAKE_BUILD_TYPE=Release ../myapp 

      На втором шаге нужно запустить финальный скрипт. Не вызывайте make ! Утилита cmake сделает это сама:

      # Просим CMake запустить сборку в каталоге `myapp-release` # Можно добавить флаг `--target mylibrary` для сборки только mylibrary # Можно добавить флаг `--clean-first`, чтобы в начале новой сборки # стирать остатки предыдущей. cmake --build . # Аналогичный способ для GNU/Linux. Его по привычке советуют в сети, хотя # make доступен не всегда, а cmake --build работает на всех платформах. make 

      Структура CMakeLists.txt

      В начале главного файла CMakeLists.txt ставят метаинформацию о минимальной версии CMake и названии проекта:

      # Указывайте последнюю доступную вам версию CMake. cmake_minimum_required(VERSION 3.8) # Синтаксис: project( VERSION LANGUAGES CXX), # теги VERSION и LANGUAGES необязательные. project(libmodel3d) 

      Затем следует список инструкций, служащих для вычисления различных переменных, создания целей сборки, подключения проектов из подкаталогов и так далее. Например, подключить дополнительный CMakeLists.txt из подкаталога можно так:

      # Простая версия: подключает скрипт по пути /CMakeLists.txt add_subdirectory() # Расширенная версия: дополнительно установит подкаталог сборки подпроекта add_subdirectory( ) 

      Целью может стать исполняемый файл, собираемый из исходного кода

      # Синтаксис: add_executable( ) # Добавлять `.h` необязательно, но лучше для работы из IDE: # - IDE определит заголовок как часть проекта # - cmake будет отслеживать изменения в заголовке и пересобирать # проект при изменениях. add_executable(pngconverter main.cpp PngReader.h PngReader.cpp) 

      Целью также может быть библиотека, статическая или динамическая.

      # Синтаксис: add_library( [STATIC|SHARED|INTERFACE] ) # Тип библиотеки (staic или shared) зависит от параметров сборки add_library(libpngutils PngUtils.h PngUtils.cpp) # Тип библиотеки: static add_library(libpngtools STATIC PngTools.h PngTools.cpp) 

      Автогенерация проекта для Visual Studio (Windows)

      Если используется Visual C++, то путь немного другой: на шаге конфигурирования создаётся проект для Visual Studio, который затем можно собрать из IDE либо так же из командной строки.

      Созданный проект Visual Studio нельзя изменять и использовать постоянно, потому что при генерации проекта используются абсолютные пути и другие неприемлемые для постоянной работы вещи.

      # Сейчас мы в каталоге `myapp` с файлом CMakeLists.txt # Сгенерируем проект Visual Studio для сборки. mkdir --parents ../myapp-build cd ../myapp-build # Конфигурируем для сборки с Visual Studio 2017, версия тулчейна v140 cmake -G "Visual Studio 2017" 

      Если проект был сконфигурирован успешно, то в каталоге ../myapp-build появятся автоматически сгенерированный BUILD_ALL.sln и проекты для Visual Studio. Их можно открыть к IDE, либо собрать из командной строки с помощью cmake. Названия опций говорят сами за себя:

      cmake --build . \ --target myapp \ --config Release \ --clean-first 

      Зависимости между библиотеками и приложениями

      Не используйте директивы include_directories , add_definitions , add_compile_options ! Они меняют глобальные настройки для всех целей, это создаёт проблемы при масштабировании.

      • Используйте target_link_libraries для добавления статических и динамических библиотек, от которых зависит цель
      • Используйте target_include_directories вместо include_directories для добавления путей поиска заголовков, от которых зависит цель
      • Используйте target_compile_definitions вместо add_definitions для добавления макросов, с которыми собирается цель
      • Используйте target_compile_options для добавления специфичных флагов компилятора, с которыми собирается цель
      # Добавляем цель - статическую библиотеку add_library(mylibrary STATIC \ ColorDialog.h ColorDialog.cpp \ ColorPanel.h ColorPanel.cpp) # ! Осторожно - непереносимый код ! # Добавляем к цели путь поиска заголовков /usr/include/wx-3.0 # Лучше использовать find_package для получения пути к заголовкам. target_include_directories(mylibrary /usr/include/wx-3.0) 

      Вы можете выбирать область видимости настройки:

      • PUBLIC делает настройку видимой для текущей цели и для всех зависящих от неё целей
      • PRIVATE делает настройку видимой только для текущей цели
      • INTERFACE делает настройку видимой только для всех зависящих от неё целей

      Пример использования областей видимости:

      # Каталог include будет добавлен к путям поиска заголовков в текущей цели и во всех зависимых целях target_include_directories(myTarget PUBLIC ./include) # Каталог src будет добавлен к путям поиска заголовков только в текущей цели target_include_directories(myTarget PUBLIC ./src) 

      Схема зависимостей условного проекта:

      Схема

      Выбор стандарта и диалекта C++

      Для настройки стандарта и флагов языка C++ не добавляйте флаги напрямую!

      # ! Устаревший метод - прямое указание флага ! target_compile_options(hello PRIVATE -std=c++11) 

      В CMake версии 3.8+ вы можете прямо потребовать включить нужный стандарт:

      # Источник: https://cmake.org/cmake/help/latest/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html # Включаем C++ 2017 target_compile_features(myapp cxx_std_17) # Альтернатива: включаем C++ 2014 target_compile_features(myapp cxx_std_14) # Альтернатива: включаем C++ 2011 target_compile_features(myapp cxx_std_11) 

      В CMake версии до 3.7 включительно можно использовать set_target_properties (если не работает, то у вас слишком старый CMake):

      # Стандарт: C++ 2014, расширения языка от производителя компилятора отключены set_target_properties(myapp PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO ) 

      Для разработчиков библиотек есть более тонкий контроль над возможностями языка:

      # API библиотеки (т.е. заголовки) требуют лямбда-функций и override, # реализация библиотеки требует ещё и range-based for. target_compile_features(mylibrary PUBLIC cxx_override cxx_lambdas PRIVATE cxx_range_for) 

      Функции в CMake

      CMake позволяет объявлять функции командами function(name) / endfunction() и макросы командами macro(name) / endmacro() . Предпочитайте функции, а не макросы, т.к. у функций есть своя область видимости переменных, а у макросов - нет.

      function(hello_get_something var_name) . # Установить переменную в области видимости вызывающей стороны # можно с помощью тега PARENT_SCOPE set($var_name> $ret> PARENT_SCOPE) endfunction() 

      Добавление исходников к цели с target_sources

      Лучше добавлять специфичные исходники с помощью target_sources, а не с помощью дополнительных переменных.

      add_library(hello hello.cxx) if(WIN32) target_sources(hello PRIVATE system_win.cxx) elseif(UNIX) target_sources(hello PRIVATE system_posix.cxx) else() target_sources(hello PRIVATE system_generic.cxx) endif() 

      Интерфейс к утилитам командной строки

      # Создать каталог debug-build cmake -E make_directory debug-build # Перейти в каталог debug-build cmake -E chdir debug-build 

      Функция find_package

      Функция find_package принимает имя библиотеки как аргумент и обращается к CMake, чтобы найти скрипт для настройки переменных данной библиотеки. В итоге при сборке либо возникает ошибка из-за того что пакет не найден, либо добавляются переменные, хранящие пути поиска заголовков, имена библиотек для компоновщика и другие параметры.

      Пример подключения Boost, вызывающего встроенный в CMake скрипт FindBoost:

      # Весь Boost без указания конкретных компонентов find_package(Boost REQUIRED) # Теперь доступны переменные # - Boost_INCLUDE_DIRS: пути к заголовочным файлам # - Boost_LIBRARY_DIRS: пути к статическим/динамическим библиотекам # - Boost_LIBRARIES: список библиотек для компоновщика # - Boost__LIBRARY: библиотека для компоновки с компонентом библиотек Boost 

      Пример подключения библиотеки Bullet с помощью встроенного скрипта FindBullet и компоновки с приложением my_app:

      # Вызываем встроенный скрипт FindBullet.cmake find_package(Bullet REQUIRED) # Добавляем пути поиска заголовков к цели my_app target_include_directories(my_app $BULLET_INCLUDE_DIRS>) # Добавляем список библиотек для компоновки с целью my_app target_link_libraries(my_app $BULLET_LIBRARIES>) 

      PS-Group

      • PS-Group
      • sshambir@gmail.com
      • ps-group
      • image/svg+xml sshambir

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *