Немного о makefile
Среди программистов freebasic (да и других бейсиков), как правило не обсуждается тема makefile , в частности на платформе Windows. Наверно это связано с тем, что лень залезать в дебри документации по GNU утилитам. Конечно, когда дело касается одного, двух или даже 10 ".bas" файлов, проще создать проект в FbEdit или написать простенький bat файл. Но вот у меня проект из ~300 файлов . Да, да... моя библиотека насчитывает 290 отдельных файлов bas. И все их надо собрать в одну статическую библиотеку. Для такого кол-ва проект FbEdit не подойдет (автор просто не внес такую возможность и редактор вылетает). До какого-то времени я использовал bat файл . Но батник себя оправдывает, когда действительно нужно скомпилировать\перекомпилировать все файлы. Какого же приходится, когда нужно внести правки лишь в отдельные файлы... Ожидание компиляции становится тягостным! К примеру компиляция проходит так:
1) каждый из 300 файлов .bas сначала компилируется в
объектный файл .о
2) все 300 объектных файлов
.о собираются в единый файл библиотеки .а
Первый пункт выполняется долго (несколько минут). Второй пункт 2-3 секунды. Очевидно, что первый пункт желательно оптимизировать, в особенности при малых правках. И действительно, если мы внесли правки в один , два файла, зачем нам делать перекомпиляцию остальных 298 файлов? Нам просто нужно перекомпилировать эти два файла и затем выполнить 2 пункт. Конечно это работает, если объектные файлы после компиляции не удаляются!!!
И вот тут , как раз пригодится утилита make. Она умеет автоматически определять измененные файлы и выполнять определенные вами команды.
Но здесь встают два вопроса:
1) Надо специально ставить пакет MinGW+MSYS
2) Нужно
изучать нехилую
документацию по утилите MAKE.
На самом деле, 1 пункт никогда не будет лишним. Вам пригодится данный пакет , хотя бы для компиляции самого компилятора freebasic или любых других сишных библиотек.
Что же касается 2 пункта, то тут может и не стоит учить все? Мне например достаточно малой толики знаний по этой утилите. Нужно просто знать как внутри выглядит простейший makefile и основные правила.
Простейший формат записи примерно такой:
target: prerequisite
commands
target - это цель которую надо достигнуть, например в качестве цели можно указать конечный файл библиотеки. Например так: libmylib.a :
prerequisite - это файл\файлы , которые нужны для достижения target . Несколько файлов записываются через пробел. Пример: 1.bas 2.bas 3.bas
commands - это команда которую нужно выполнить для достижения target , например: fbc -lib 1.bas 2.bas 3.bas -x libmylib.a
Перед commands и для разделения нескольких команд , должен стоять разделитель в виде TAB (символа табуляции). Некоторые пишут, что должны стоять 2 символа TAB.
Но для того, чтобы выполнить нашу изначальную задачу (т.е. оптимизировать
компиляцию) , нужно одну цель разделить на несколько задач, от которых в
конечном итоге и будет зависеть конечная цель. Важно понимать, что утилита MAKE
выполняет задачи рекурсивно , то есть если утилита выполнит самодостаточную
команду с самого начала, то makefile считается выполненным и остальные команды к
выполнению не подлежат. Поэтому нужно образно сделать так:
Конечный продукт: первая_заготовка вторая_заготовка
третья_заготовка
собрать из заготовок
конечный_продукт
Первая заготовка:
первое_исходное_сырье
собрать из сырья
первую_заготовку
Вторая заготовка:
второе_исходное_сырье
собрать из сырья
вторую_заготовку
Третья заготовка:
третье_исходное_сырье
собрать из сырья
третью_заготовку
#######следующие строки по умолчанию выполняться не
будут#######
Другой продукт: четвертая_заготовка пятая_заготовка
шестая_заготовка
собрать из заготовок другой_продукт
Четвертая заготовка:
исходное_сырье
собрать из сырья
четвертую_заготовку
Пятая заготовка:
исходное_сырье
собрать из сырья
пятую_заготовку
Шестая заготовка: исходное_сырье
собрать из сырья шестую_заготовку
Утилита MAKE при таком подходе читает цель по умолчанию (Конечный продукт) и смотрит зависимости , которые нужно выполнить. Должно быть понятно, что заготовок пока не существует, поэтому утилита сначала выполнит работу по их созданию, а потом уже выполнит работу по их объедению в конечный продукт. Если мы первые 2 строчки:
Конечный продукт:
первая_заготовка вторая_заготовка третья_заготовка
собрать из заготовок
конечный_продукт
запишем в конец makefile , то тогда выполняются строчки:
Первая заготовка:
первое_исходное_сырье
собрать
из сырья первую_заготовку
и make завершит работу, потому что работа по данной цели самодостаточна и не требует никаких зависимостей.
Если нам нужно выполнить цель "Другой продукт" вместо цели по умолчанию, то в командной строке нужно после make ввести имя данной цели.
В таком варианте, утилита MAKE всегда проверяет сырье на предмет изменения. Если не найден какой нибудь объектный файл, то выполняется команда по его компиляции.
А теперь представьте, что у нас есть 3 файла: 1.bas , 2.bas , 3.bas и нам нужно создать makefile для создания 1.exe , 2.exe и удаления всех созданных файлов. Файл 1.exe компилируется сразу из трех модулей (1.bas , 2.bas , 3.bas) , а файл 2.exe только из двух (1.bas , 2.bas). Тогда makefile может выглядеть примерно так:
#цель по умолчанию 1.exe: 1.o 2.o 3.o fbc -x 1.exe 1.o 2.o 3.o #цель опционально (задается в командной строке, например так: make 2.exe) 2.exe: 2.o 1.o fbc -x 2.exe 2.o 1.o #цель опционально удаление файлов (задается в командной строке например так: make clean) clean: rm -f *.exe *.o #вспомогательная цель 1.o 1.o: 1.bas fbc -m 1 -c 1.bas #вспомогательная цель 2.o 2.o: 2.bas fbc -c 2.bas #вспомогательная цель 3.o 3.o: 3.bas fbc -c 3.bas
Как можно заметить, есть три главных цели (1.exe, 2.exe, clean) и три
вспомогательные (1.o, 2.o, 3.o). При чем цель 1.exe (по умолчанию) выполняется ,
когда в командной строке набирается make. Цель 1.exe зависит от 3
вспомогательных целей 1.o, 2.o, 3.o. Пока Make не создаст 1.o, 2.o, 3.o, нет
возможности выполнить цель 1.exe, поэтому вспомогательные цели выполняются в
первую очередь. Если мы наберем make clean , то выполнится цель clean , которая
кстати не имеет зависимостей (просто удаление имеющихся файлов по маске) и make
завершит работу. Если мы наберем make 2.exe , то выполнится цель 2.exe , которая
зависит от 2 вспомогательных целей 1.o, 2.o и make завершит работу.
Как видно символ # определяет комментарии.
Для того, чтобы напечатать какую-нибудь строку есть команда echo. Пример ее
использования:
#цель по умолчанию 1.exe: 1.o 2.o 3.o @echo Выполняется сборка fbc -x 1.exe 1.o 2.o 3.o
Символ @ запрещает печатать символьное представление команды, но конечно
же строка в параметре команды echo выведется.
Для переноса длинных строк есть символ \
И последнее, что реально может понадобится, это переменные. Объявляются переменные так:
имя = значение
или
имя := значение
В реальности это два разных присваивания, с разными тонкостями, но для простых makefile , я думаю не нужно придавать этому значение.
Обращение к переменной происходит так:
$(имя)
И прошлый пример с использованием переменной:
#переменная OBJ = 1.o 2.o 3.o #цель по умолчанию 1.exe: $(OBJ) @echo Выполняется сборка объектых файлов fbc -x 1.exe $(OBJ) #цель опционально (задается в командной строке, например так: make 2.exe) 2.exe: 2.o 1.o fbc -x 2.exe 2.o 1.o #цель опционально удаление файлов (задается в командной строке например так: make clean) clean: rm -f *.exe *.o #вспомогательная цель 1.o 1.o: 1.bas fbc -m 1 -c 1.bas #вспомогательная цель 2.o 2.o: 2.bas fbc -c 2.bas #вспомогательная цель 3.o 3.o: 3.bas fbc -c 3.bas
Конечно это лишь капля в море из возможностей утилиты make, но даже это может сильно сэкономить вам время.