make power

Пока я работал с Неназываемым — столкнулся с необходимостью многократно прогонять комплект действий с питоновскими скриптами. Потратив примерно полчаса (чуть меньше на самом деле — учитывайте, что я не профессиональный программист, а скромный power user) разобрался в GNU make и написал себе такой вот файлик:

all: struc func

clean:
    rm *.csv
    rm *.dot
    rm *.png

struc:
    python st.py 
    find . -name '*.dot' -exec dot {} -Tpng -o {}.png \;
func:
    python fun.py 
    find . -name '*.dot' -exec dot {} -Tpng -o {}.png \;
one:
    python st.py 
    dot struct_kond_B1_01.dot -Tpng -o poligon.png
    geeqie poligon.png

В данном случае make struc считает одни аспекты Неназываемого и с помощью graphviz (точнее dot) рисует к ним диаграммы, make func делает тоже самое для других аспектов Неназываемого, make one — отладочный «единичный сценарий», make clean чистит папку от лишних файлов (храните в версионном контроле только то, что редактируется руками, ага). make или make all прогоняет сценарии всех аспектов Неназываемого.

Удобно. Когда освоил — понял, что именно make мне не хватало для автоматической обработки данных. Вообще говоря make предназначен совсем-совсем для другого, но с коммандлайновыми утилитами он прекрасно автоматизирует действия и в тоже время делает это очень прозрачно, избавляя от необходимости плодить где-то по окрестностям скрипты и алиасы.

Например.

Подкасты у меня качаются через newsbeuter (точнее через podbeuter) попадают в ~/av/pod, откуда через unison синхронизируются с папкой pod в плейере. Я люблю, чтобы подкасты лежали по папкам — на случай, если у меня будет настроение слушать что-то конкретное. Но. С другой стороны иногда хочется слушать их «в порядке поступления» — отсортированными по дате. Для чего достаточно иметь плейлист.

В ~/av/pod/ кладем makefile следующего содержания:

# Генерация плейлистов в прямом и обратном порядке дат для плейера 

all: m3u back

m3u:
    echo '#EXTM3U' > aktual.m3u8
    find . -name "*.mp3" -type f -printf '%T@ %p\n' | sort -k 1nr | sed 's/^[^ ]* //' | head -n 50 >> aktual.m3u8

back:
    echo '#EXTM3U' > back.m3u8
    find . -name "*.mp3" -type f -printf '%T@ %p\n' | sort -k 1n | sed 's/^[^ ]* //' | tail -n 10 >> back.m3u8

Теперь по make в папке будут созданы два файла aktual.m3u8 и back.m3u8 в которых файлы перечислены «в порядке поступления». У меня забит такой алиас:

alias podbeuter='podnorm && podbeuter && make -C ~/av/pod'

podnorm — это предобработка очереди закачки, о которой речь пойдет в другой раз. Видно, что каждый раз когда я вызываю podbeuter, после выхода из него выполняется makefile для ~/av/pod, который генерит нужные плейлисты. А они уже автоматом через unison попадают в плейер.

Еще один пример. Иногда мне удобно комитить и пушить все репозитории, которые у меня есть — я использую битбакет, как бэкап и историю.

Раньше у меня в ~/bin/ лежали монструозного вида hgci и hgpush, которые проходили по папкам и выполняли все требуемое. Теперь в корневике лежит Makefile такого вида:

# Посмотреть, что есть
show:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \;

# Commites
ci:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg ci -m 'autocommit by makefile' \;

commit:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg commit -m 'autocommit by makefile' \;    

ak:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg ak  \;   

# Push 
push:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg push  \; 

# Pull
pull:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg pull  \; 

# Update

up:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir hg up  \;
update:
    find . -maxdepth 2 -name ".hg" -type d -execdir pwd \; -execdir pwd \; -execdir hg update  \;

Следующий пример. Я храню различные rc-файлы в ~/bin/rc, причем основные соответствующие им файлы завязаны на оные через soft-линки.

Например:

vik@firefly:~$ ls -al ~/.config/openbox/

lrwxrwxrwx  1 vik vik   37 окт 30 00:36 autostart.sh -> /home/vik/bin/rc/openbox/autostart.sh
lrwxrwxrwx  1 vik vik   33 окт 30 00:36 menu.xml -> /home/vik/bin/rc/openbox/menu.xml
lrwxrwxrwx  1 vik vik   31 окт 30 00:36 rc.xml -> /home/vik/bin/rc/openbox/rc.xml
lrwxrwxrwx  1 vik vik   37 окт 30 00:36 rootmenu.xml -> /home/vik/bin/rc/openbox/rootmenu.xml

~/bin, как я писал выше, синхронизируется с битбакетом. Чем это удобно? Любое изменение в управляющих rc-файлах попадает под контроль версий и синхронизируется с прочими компьютерами. Таким образом у меня не просто стандартно обеспечивается одинаковое рабочее место на всех моих машинах, но и одинаковое изменение, если я где-то что-то меняю.

Такая система хороша всем, за исключением того, что при первой инсталляции требуется ручками прописать везде эти софт-линки. Но зачем делать это ручками?

vik@firefly:~$ cat ~/bin/rc/Makefile 

all: pod main openbox

pod:
        ln -s --backup ~/bin/rc/makepod/makefile -t ~/av/pod

main:
        ln -s --backup ~/bin/rc/all_hg_Makefile/Makefile -t ~ 

openbox:
        ln -s --backup ~/bin/rc/openbox/* -t ~/.config/openbox/

Makefile на самом деле больше, но идея, думаю тоже ясна.

Ну и на закуску. Методички, книги и прочая литература обычно лежат каждая в своей папке, в которой book.txt — это сам текст, а makefile выглядит примерно так:

# makefile для конверсии из markdown в разные форматы и быстрого просмотра комментариев
# предполагается что директория под контролем hg (и версии извлекаются оттуда)
# комменты отбиваются либо через %% в начале строки либо выделены двойными %% %% в тексте
# в системе должны быть pandoc, hg, sed, head и grep

# комментаторство
com: outline inline

# внутренние комментарии
inline: book.txt
    grep '\(%%.\+%%\)' book.txt -n --color | sed 's/^/book.txt:/'

# внешние комментарии
outline: book.txt
    grep '^%%.\+' book.txt -n --color| sed 's/^/book.txt:/'

# конверсия 

# получаем версию книги
rev:
    echo "\tслужебная информация:" > rev.ed.txt
    hg log -f book.txt | sed -n -e '1,4s/^/\t/p' >> rev.ed.txt

# основа md-файла без комментов и с ревизией в голове
txt: rev
    sed '/^%%/ d' book.txt > book.ed.txt 
    echo '***' >> book.ed.txt
    cat rev.ed.txt >> book.ed.txt

# чистый md-файл с заэкранированными диалогами 
md: txt
    sed 's/^-[ ]/\\- /g' book.ed.txt > book.ed.ed.txt

# html-файл 
html: md
    pandoc -f markdown -t html --standalone book.ed.ed.txt -o book.ed.html

finehtml: md
    sed 's/^\\- /--- /g' book.ed.ed.txt > book.fine.ed.txt
    sed 's/ - / --- /g' book.fine.ed.txt > book.ed.ed.txt
    cp book.ed.ed.txt book.fine.ed.txt
    pandoc --smart -f markdown -t html --standalone  book.fine.ed.txt -o book.ed.html

# html-файл с содержанием
t_html: md
    pandoc -f markdown -t html --toc --standalone book.ed.ed.txt -o book.ed.html

odt: md finehtml
    libreoffice4.0 --headless --convert-to odt book.ed.html

doc: md finehtml
    libreoffice4.0 --headless --convert-to doc book.ed.html

pdf: md finehtml
    libreoffice4.0 --headless --convert-to pdf book.ed.html

epub: md
    pandoc -f markdown -t epub --toc --standalone book.ed.ed.txt -o book.ed.epub

# очистка от мусора
clean:
    rm -f *.ed.odt *.ed.txt *.ed.html *.ed.rtf *.ed.doc *.ed.epub *.ed.pdf

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

Почему make, а не комплект скриптов? Опять-таки из-за прозрачности с одной стороны, из-за емкости с другой стороны (в файл умещается очень много задач, кроме того задачи можно исполнять «каскадами», завязывая их друг на друга) и из-за стандарта с третьей стороны (не нужно ничего придумывать — все уже готовое и оптимальное.

Словом, пытливому уму есть, где развернуться.

Оставьте комментарий