LINUX.ORG.RU

Конспект по GNU Make

 


9

3

Привет, я штудировал доки по Make (уже второй раз за несколько лет) и пожалел о том, что в прошлый раз не составил небольшой конспект. Оставлю здесь на случай, когда потребуется освежить память, может и ещё кто сочтёт полезным.

1.  include / -include - включить Makefile, дупускаются shell
    шаблоны. С / без ошибки при отсутствии файла. После прочтения всего
    Makefile, имена файлов, которые указаны  директиве, проверяются на
    возможность обновления рецептами. В случае обновления включаются
    заново (основа для автоматической генерации зависимостей).

2.  Secondary expansion - после чтения Makefile и перед фазой
    обновления целей, производится второй проход по списку
    пререквизитов. Доступны автоматические переменные.
        .SECONDEXPANSION:
        main_OBJS := main.o try.o test.o
        lib_OBJS := lib.o api.o
        main lib: $$($$@_OBJS)

3.  Order-only пререквизиты - цель не будет считаться устаревшей при
    выполнении рецепта такого пререквизита
        targets : normal_prereq | order_only_prereq.

4.  Переменная VPATH - пути поиска всего. Директива vpath - пути поиска
    для определённого класса файлов.

5.  .PHONY : target - цель target всегда устаревшая. Для target не
    производится поиск неявных правил.

6.  Если в качестве пререквизита -lname, то поиск библиотеки в
    следующих местах: в текущей директории, vpath и VPATH, /lib,
    /usr/lib, prefix/lib.

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

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

9.  target : ; пустой рецепт, не будет производиться поиск неявного
    правила.

10. Static pattern rule
       targets : target-pattern : prereq-pattern
            recipe
       ${objects} : %.o : %.c
    Часть имени соответствующая % - stem.

11. Каждая строка рецепта исполняется в своей shell (в одной есле
    .ONESHELL). Shell, которая используется, хранится в SHELL, опции в
    .SHELLFLAGS. Значение SHELL не наследуется из окружения make.
    Exit-status shell, выполняющей команду, содержится в .SHELLSTATUS.

12. В рецепте (в начале строки):
       @ - отключает печать команды (-n и --just-print всё равно
           печатают такие команды);
       + - выполнить даже если заданы -n или --just-print;
       - - игнорировать ошибки при исполнении команды.

13. В рецепте: обращение к make переменной ${var}, к shell переменной
    $${var}.

14. При рекурсивном вызове make использовать переменную ${MAKE}
       $(MAKE) -C subdir

15. [override] undefine var - удалить переменную.

16. export / unexport var [=val] - экспортировать / не экспортировать
    переменную в sub-make. Одинокий export / unexport - экспортировать
    / не экспортировать все переменные.

17. var = val - ссылка;
    var := val - обычная переменная;
    var += val - ссылка ли, зависит от типа val;
    var ?= val - присвоить значение если var не объявлена;
    var != cmd - выполнить cmd в shell;
    ${foo:.o=.c} - заменить .o в конце слов на .c;
    ${foo:%.o=%.c}

18. Иерархия переменных:
       1. объявлянные внутри Makefile с override (дальнейшая
          модификация возможно только с override);
       2. переданные через командную строку;
       3. объявленные внутри Makefile без override;
       4. из окружения, где запускается make.
    Экспортируются переменные в sub-make:
       1. из окружения, где запускается make (редактирование внутри
          Makefile не влияет);
       2. переданные через командную строку (если Makefile редактирует
          с override, то в sub-make приходит значение из командной
          строки);
       3. объявленные внутри Makefile с export.

19. MAKEFLAGS - содержит флаги переданные при запуске make +
    переменные со значениями переданными через командрую строку, но не
    содержит цели. В переменную можно добавить флаги внутри Makefile
    или в окружении, в котором запускается make).

20. MAKELEVEL - содержит целое число, которое указывает на глубину
    рекурсии. 0 для главного make.

21. MAKECMDGOALS - содержит цели заданные при запуске make.

22. Target-specific переменные
       prog : [private] CFLAGS=-g
       prog : prog.o foo.o bar.o
           recipe
     задаёт CFLAGS в рецептах для prog, prog.o, foo.o, bar.o. Если
     private стоит, то пререквизиты не наследуют CFLAGS. Если
     переменная объявлена private на глобальном уровне, то она не
     будет видна ни в одно рецепте.

23. Pattern-specific переменные
       %.o : CFLAGS=-g

24. Match-anything pattern rule (цель одинокий %). Бывают:
      1. терминальные (%::pregeq) - prereq должен существовать, не
         может быть создан посредством другого правила.
      2. иначе - правило будет рассмотрено только если нет других
         неявных правил , чья цель совпадает с искомой.

25. Last-resort default rule
      1. %::
      2. .DEFAULT:      # если рецепт не указан, то текущее правило
                          будет удалено

26. Отмена неявного правила
       %.o:%.s
    указать ту же цель и те же пререквизиты, рецепт не указывать.
    
27. Архивы:
      1. при использовании нотации со скобками ( libname(objs) ) не
         допускать параллельного запуска make (-j опция).
      2. внутри архива содержится таблица символов (__.SYMDEF), ranlib
         lib.a её обновляет. GNU ar не требует запуска вручную,
         запускает её автоматически.

28. Договорённости:
      * Каждый Makefile должен содержать
          SHELL = /bin/sh
        для избежания проблем на системах, где SHELL может быть
        унаследована из окружения.
        
      * Не плохо задать суффиксы для которых будет производиться поиск
        неявных правил
           .SUFFIXES:             # удаляет все раннее установленные
           .SUFFIXES: .c .o
           
      * ar bison cc flex install ld ldconfig lex make makeinfo ranlib
        texi2dvi yacc chgrp chmod chown mknod - вызывать через
        переменные ${AR}, ... . Для каждой переменной-команды
        переменная с опциями образуется добавлением FLAGS к имени
        (исключение - CFLAGS, YFLAGS, LFLAGS).

       * команды, которые следует использовать напраямую (без
         переменных): awk cat cmp cp diff echo egrep expr false grep
         install-info ln ls mkdir mv printf pwd rm rmdir sed sleep
         sort tar test touch tr true gzip.

       * каждый Makefile должен задавать переменные: INSTALL,
         INSTALL_PROGRAM (дефолтно ${INSTALL}), INSTALL_DATA (дефолтно
         ${INSTALL} -m 644).

       * staged install - для целей install и uninstall поддерживать
         переменную DESTDIR
            $(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo
            $(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

       * установка info и man страниц в цели install.

       * Разбивка install на $(PRE_INSTALL), $(POST_INSTALL),
         $(NORMAL_INSTALL). Разбивка uninstall на $(PRE_UNINSTALL),
         $(POST_UNINSTALL), $(NORMAL_UNINSTALL).

Господа, у меня вот ещё какой вопрос имеется: вот написал я какую-нибудь софтину, как мне определиться с именами файлов и всего проекта? Т.е. чтобы небыло конфликтов имён с другим софтом при make install. К тому же если сегодня конфликтов нет, то завтра у меня нет никаких гарантий, что такой софт не появится. Как тут правильно поступать? Каждый проект должен писать какие-то проверки на конфликты имён? Если так, то делает ли это автоматом autoconf+automake? cmake?

Как тут правильно поступать?

Кто первый встал, того и тапки.
У кого больше популярность, того в дистрибутиве и оставят (а другого - нет).
Ты сперва напиши.

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

Ну я не претендую на известность и что меня куда-нибудь включат как emacs, например. Пусть это будет мелкая софтинка, которую я дам Васе и Пете, как софтинка выживет в их системе, когда её спокойно перезапишут какие-нибудь большие и важные xserver'ы (о которых я могу не знать в момент распространения программы, вот напишут что-то завтра)?
Я понимаю, что можно поставить в /opt, но что я должен сделать, чтобы иметь возможность спокойно устанавливаться в /usr/local?

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

Каждый проект должен писать какие-то проверки на конфликты имён?

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

Если так, то делает ли это автоматом autoconf+automake?

Там install используется, который, вроде, всегда перезаписывает существующий файл.

Как тут правильно поступать?

Пакетные менеджеры должны ругаться на конфликт. Разработчик просто не может заботиться о конфликтах в будущем. Файлы/проекты можно переименовывать, если уж совсем неудобный конфликт получился.

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

Ребята, ведь пакетный менеджер кидает всё в /usr, за уникальностью имён/совместимостью софта следят те, кто поддерживает дистрибутив, с этим всё понятно, вопросов нет. Я же про /usr/local, т.е. то место, где пользователь ручками устанавливает софт, который качает откуда угодно. Кто там следит за уникальностью, мне не понятно. В целом ничего сложного то нет, например: make DESTDIR=tmpdir install, дальше проверка уникальности tmpdir/... и /usr/local/..., если всё хорошо, то копируем. Но занимается ли этим autotools. Если нет, то это надо делать самому, я то справлюсь, думаю, на справятся ли с этим простые пользователи, которым я дам свою софтинку? Очевидно, что нет.

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

за уникальностью имён/совместимостью софта следят те, кто поддерживает дистрибутив

Пакетный менеджер знает списки файлов и может их проверять.

Но занимается ли этим autotools.

Я же сказал, что он просто вызывает install, т.е. не занимается.

Это не проблема, так как конфликт возникает редко, да и в /usr/local мало чего обычно имеется. У autotools есть:

Program names:
  --program-prefix=PREFIX            prepend PREFIX to installed program names
  --program-suffix=SUFFIX            append SUFFIX to installed program names
  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
И можно изменить имена, чтобы убрать конфликты у исполняемых файлов.

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

Пакетный менеджер знает списки файлов и может их проверять.

Разве пакетный менеджер суётся в usr/local ? Если только собрать пакет для дистрибутива, но я ведь не про такой случай, а про стандартную установку make install.

Это не проблема, так как конфликт возникает редко, да и в /usr/local мало >>чего обычно имеется.

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

У autotools есть:

Да, я такое встречал, но это требует достаточно высокой квалификации пользователя, чтобы во-первых понять, что конфликт есть, а во-вторых справится с configure. Это не решение, когда его осилит 1 из 10 человек.

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

Разве пакетный менеджер суётся в usr/local ?

Если собирать пакет, то да. А их собирать обычно нетрудно и рекомендуется это делать.

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

Если Вася первую ставил, то он наверное заметит, что что-то не то произошло.

И кол-во таких малоизвестных софтин неограничено.

А количество с одинаковыми именами, которые будут установлены на одну и ту же машину, пренебрежимо мало (имён много, установок мало).

это требует достаточно высокой квалификации пользователя

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

xaizek ★★★★★ ()

Вроде придумал довольно дешёвый способ, который может помочь ловить совпадения имён. Но есть условие: Makefile должен быть написан правильно, придерживаясь договорённостей. Например, имеем цель install:

INSTALL ::= install -D
INSTALL_PROGRAM ::= ${INSTALL}
INSTALL_DATA ::= ${INSTALL} -m 0644

install : main
        ${INSTALL_PROGRAM} main ${DESTDIR}${bindir}
далее запускаем make не от root'a (без права записи) следующим образом:

make INSTALL_PROGRAM='cp -i' INSTALL_DATA='cp -i' install

Далее, если получаем интерактивное сообщение с предложением перезаписать, то совпадения имеются:

pavlick@pc ~/ud/test/q/build $ make INSTALL_PROGRAM='cp -i' install
cp -i main /usr/local/bin
cp: недоступен для записи '/usr/local/bin/main' (права 0755, rwxr-xr-x); попробовать всё равно?

Конечно лучше написать какую-нибудь спец утилиту. Может такая уже есть?

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

лучше написать какую-нибудь спец утилиту

и распространять её вместе с основным продуктом? Ну хз, на вкус и цвет, конечно... А вообще, перед установкой проверяй все конфликты и, в случае наличия таковых, выход с ошибкой без внесения изменений в систему. Как уже выше писали - все случаи не предвидеть, с некоторыми придётся разбираться кому-то другому. К тому же, если хочешь сделать идеально, нужна ещё и деинсталляция. И вот здесь совпадения отследить, чтобы не удалить чужое..., ну ты понел:)

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

и распространять её вместе с основным продуктом

на это я уже не надеюсь, если autotools из коробки не отслеживают совпадения, то как это сделать не очень продвинутому пользователю я не знаю. Эти изыскания для сохранения собственной системы в нормальной форме. Странно, но у сp, install, mv нет ключа --pretend, было бы удобно.

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

как это сделать не очень продвинутому пользователю я не знаю

.PHONY: install safe-install

install: $(PREFIX)/bin/1 $(PREFIX)/bin/2

$(PREFIX)/bin/%: build-dir/% | safe-install ; $(CP) $(CPFLAGS) $< $@

#check here all(!) installation targets
safe-install: ; $(if $(wildcard $(PREFIX)/bin/1 $(PREFIX)/bin/2),$(error deal with it, user!))

Копипастить не надо, я не проверял. Но как один из вариантов

Deleted ()
Ответ на: комментарий от pavlick

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

Deleted ()

По поводу совпадений: чисто для личного использования написал небольшой скрипт:

#!/bin/bash
# instcol
path1=$(cd $1; pwd)
path2=$(cd $2; pwd)
echo -e "path1 = ${path1}\npath2 = ${path2}"
pushd ${path1}
find -L -true | sort > /tmp/instcol_1
pushd ${path2}
find -L -true | sort > /tmp/instcol_2
popd && popd
echo "NAME COLLISIONS"
comm -12 /tmp/instcol_1 /tmp/instcol_2

Ну и пример:
#Makefile
main: ${objects}
libm.a: ${objects}
install : main libm.a
        ${INSTALL_PROGRAM} -t ${DESTDIR}${bindir} main
        ${INSTALL_DATA} -t ${DESTDIR}${libdir} libm.a

# в /usr/local ешё ничего не ставили
$ make DESTDIR=./temp install
$ instcol ./temp/usr/local/ /usr/local/
path1 = /home/pavlick/ud/test/q/build/temp/usr/local
path2 = /usr/local
~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
/usr/local ~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
~/ud/test/q/build
NAME COLLISIONS
.
./bin
./lib

$ sudo make install
$ instcol ./temp/usr/local/ /usr/local/
path1 = /home/pavlick/ud/test/q/build/temp/usr/local
path2 = /usr/local
~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
/usr/local ~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
~/ud/test/q/build/temp/usr/local ~/ud/test/q/build
~/ud/test/q/build
NAME COLLISIONS
.
./bin
./bin/main
./lib
./lib/libm.a
А кто не хочет/не может отслеживать совпадения имён, то выход наверное один - ставить в /opt

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

Такой скрипт намного шустрее:

#!/bin/bash

# $1 - source tree (tree that you want to install)
# $2 - destination tree.

path_src=$(cd $1; pwd)
path_dest=$(cd $2; pwd)
echo -e "source tree root = ${path_src}\ndestination tree root = ${path_dest}"
pushd ${path_src}
find -true > /tmp/instcol
popd

while IFS= read -r var
do
    if [[ "$var" == "." ]]; then
	continue
    fi
    if [[ -a "${path_dest}/${var}" ]]; then
	echo "COLLISION IS FOUND, FILE = $var"
    fi
done < "/tmp/instcol"
rm "/tmp/instcol"

pavlick ()

Кстати, тут говорили, что совпадения имён - это же так маловероятно. Даже в рамках своего теста, сравнив свой проект с /usr нашлись совпадения имён. Ну понятно, что ставил бы в /usr/local, но всё же, могу ведь и из исходников пол системы собрать.

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

Я говорил

пренебрежимо мало

Т.е. не стоит того, чтобы напрягаться.

Когда у меня был конфликт, то переименовал скрипт, добавив название проекта как префикс. Просто на практике пакетов много тысяч и конфликтов практически нет. И если всё класть по пути, который включает имя проекта, то их возникновение — редкость.

xaizek ★★★★★ ()

чтобы небыло конфликтов имён с другим софтом при make install

Не твоя задача такие вещи определять. Да и мейк инсталл лучше не юзать. Разве что у тебя LFS :D

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

Не твоя задача такие вещи определять.

Посмеялся. Где-нибудь в сети, никому неизвестный товарищ, выложил свою софтину, которая имеет конфликты с другими не более известными поделками (ну ведь может сложиться так, что нужно поставить софт, не из репозитория дистрибутива. Да это подраздел «Development», в конце то концов, здесь свой софт пишут). Наверное Линус Торвальдс сразу же свяжется с этим товарищем и скажет: «ты зачем так назвал файлы в своей софтине? Переименуй немедленно».

Да и мейк инсталл лучше не юзать.

Если человек не умеет пользоваться, то да. Но тогда и вилкой лучше не кушать, мало ли чего. Если всё по уму делать - отслеживать конфликты имён перед установкой (простейший скрипт выше), сохранять исходники в /usr/local/src, то никаких проблем не будет, даже если это кривой Makefile с отсутствующей целью uninstall.

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

Если всё по уму делать - отслеживать конфликты имён перед установкой (простейший скрипт выше), сохранять исходники в /usr/local/src, то никаких проблем не будет, даже если это кривой Makefile с отсутствующей целью uninstall.

Как раз попался кривой Makefile без uninstall (cmake автоматом не создаёт данную цель), набросал скрипт для удаления, кину сюда. Технология простая - делаем

$ install DESTDIR=~/temp_dir install    # Для кривой софтины
$ make_uninstall ~/temp_dir /usr/local

make_uninstall скрипт:
#!/bin/bash

# $1 - DESTDIR installation root.
# $2 - root of installed files.

destdir_path=$(cd $1; pwd)
installed_path=$(cd $2; pwd)
echo -e "DESTDIR installation root = ${destdir_path}\nroot of installed files = ${installed_path}\ncontinue ?"
read cont
if [[ ${cont} != "y" ]]; then
    exit 0
fi

pushd ${destdir_path}
IFS=$'\n'
files=($(find . -regex '.*/.*'))
unset IFS

pushd ${installed_path}
for var in "${files[@]}"; do
        rm "${var}"
done
popd
popd

pavlick ()

Ещё кину сюда свою универсальную «build system» на основе make.
1. состоит из двух файлов
2. настройка под конкретный проект элементарна.
3. автоматом отслеживает зависимости от заголовочных файлов.
3. простая интеграция с autoconf
4. система генерирует compile_commands.json базу для clang based штуковин (и автоматом добавляет обновляет при появлении новых файлов), я использую rtags+company (нормально работает как autocomplete, так и go to defenition. Без всяких там irony). На борту должен стоять bear https://github.com/rizsotto/Bear. Нет необходимости делать make clean (для обновления compile_commands.json).
5. работает с C/C++. Формат исходников .c/.cc

Напрмер, создаём проект prg:

$ mkdir prg && cd prg
$ echo "int main() {}" > main.cc
$ umake       # создаём ./build/Makefile
$ umake       # компилируем, линкуем ..
$ ls .
build  compile_commands.json  main.cc
$ ls build
main  main.d  main.o  Makefile


$ cat /usr/local/src/my/unimake/Makefile
# universal makefile, version 1.0.0
srcdir ::= ..
compdb ::= ${srcdir}/compile_commands.json
SHELL ::= /bin/bash
INSTALL ::= install -D
INSTALL_PROGRAM ::= ${INSTALL}
INSTALL_DATA ::= ${INSTALL} -m 0644

override ARFLAGS +=
override ASFLAGS +=
override CXXFLAGS += -std=c++14 -Wall
override CPPFLAGS +=
override LDFLAGS +=
override LDLIBS += -lstdc++

prefix ::= /usr/local
exec_prefix ::= ${prefix}
bindir ::= ${exec_prefix}/bin
sbindir ::= ${exec_prefix}/sbin
libexecdir ::= ${exec_prefix}/libexec
datarootdir ::= ${prefix}/share
datadir ::= ${datarootdir}
sysconfdir ::= ${prefix}/etc
sharedstatedir ::= ${prefix}/com
localstatedir ::= ${prefix}/var
runstatedir ::= ${localstatedir}/run
includedir ::= ${prefix}/include
libdir ::= ${exec_prefix}/lib
infodir ::= ${datarootdir}/info
#-------------------------------------------------#
objects ::= $(patsubst %.cc,%.o,${notdir ${wildcard ${srcdir}/*.cc}})\
  $(patsubst %.c,%.o,${notdir ${wildcard ${srcdir}/*.c}})
VPATH ::= ${srcdir}
#-------------------------------------------------^
.PHONY: install uninstall clean
#--- Creating executable or shared library.
#--- One of the sources should have the same base part as target.
main: ${objects}
clean::
	${RM} main
#--- Creating static library
#libm.a: ${objects}
#install: main libm.a
#	${INSTALL_PROGRAM} -t ${DESTDIR}${bindir} main
#	${INSTALL_DATA} -t ${DESTDIR}${libdir} libm.a
#uninstall:
#	${RM} ${DESTDIR}${bindir}/main
#	${RM} ${DESTDIR}${libdir}/libm.a
#-------------------------------------------------------------#
clean::
	${RM} *.o *.d
	${RM} ${compdb}
%.a:
	$(AR) $(ARFLAGS) $@ $^
define deprec =
	@set -e; rm -f $@; \
	$(CXX) -MM $(CPPFLAGS) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$
endef
%.d: %.c
	${deprec}
%.d: %.cc
	${deprec}
include ${objects:.o=.d}
#-------------------------------------------------------------^

$ cat /usr/local/bin/umake
#!/bin/bash
# universal makefile, version 1.0.0

if [[ ! -d build  &&  ! -a Makefile ]]; then
    if [[ -z $(find . -maxdepth 1 -name '*.c' -o -name '*.cc') ]]; then
	echo "source files are not found, exit"
	exit 1
    fi
    mkdir build
    cp -t build /usr/local/src/my/unimake/Makefile
    exit 0
fi

if [[ -a Makefile ]]; then
    bdir=${PWD}
    pushd $(grep -m 1 '^[[:space:]]*srcdir[[:space:]:]*=' Makefile | sed 's/srcdir[[:space:]:]*= *//') &> /dev/null
    if (( $? != 0 )); then
	echo 'Makefile does not contain srcdir, exit'
	exit 2
    fi
    bear -a make "$@" -C ${bdir}
    popd &> /dev/null
else
    bear -a make "$@" -C build
fi

pavlick ()