void main(string[] args)
{
import std.stdio, std.getopt, std.process, std.c.stdlib; // несколько импортов одной строкой
import core.thread, std.file, std.range.primitives;
import colorize : fg, color, cwritef, cwritefln; // импорт конкретных имен из модуля
import std.datetime;
// объявление нескольких переменных:
auto deb=false, interval=1, icolr="cyan", wcolr="yellow", okcolr="green";
auto opts = getopt( args, // анализ опций из args
"i|interval", "interval in seconds between checking", &interval, // i|interval - значит или -i или --interval
"d|deb|debug", "show debug messages to stderr", &deb,
"icolor" , "info color", &icolr,
"wcolor" , "warnings color", &wcolr,
"okcolor", "ok color", &okcolr,
); // ссылается на значение bool, поэтому работает как флаг
// если пользователь использовал -h|--help:
// после if может не быть фигурных скобок:
if( opts.helpWanted ) defaultGetoptPrinter( "Usage: ", opts.options ), exit(0);
if ( args.length < 3 ){
stderr.writeln("Too few parameters"); // синтаксический сахар для: writeln( stderr, "Too few parameters");
exit(1); // выйти с плохим кодом возврата
}
// последний аргумент командной строки - команда, остальное - отслеживаемые файлы:
auto cmd = args[$-1], files = args[1..$-1];
auto path = environment.get("PATH", "not present"); // пример доступа к переменным окружения
// Ниже использован цветной вывод на stdout с использованием стороннего модуля colorize
// Кроме того что colorize импортирован, он указан как зависимость в dub.json такой записью: "colorize": "~>1.0.5"
// cwritef из colorize - аналог writef из std.stdio. Правила форматированного вывода те же.
deb && cwritef("watch files: %s;\ncmd: \"%s\";\ninterval: %d sec;\nPATH: %s;\n".color(icolr), files, cmd, interval, path);
auto cnt = 0;
SysTime[string] file_times; // создать х-м файл=>время. Тип SysTime подсмотрен в доке по std.file.
while(true){
string[] changed; // массив строк
foreach (f; files){
auto lm = timeLastModified(f); // функция из std.file
if ( get( file_times, f, SysTime() ) != lm ){
changed ~= f; // добавление в массив
file_times[f] = lm; // присвоение элементу массива
}
}
if ( !empty(changed) ){ // проверка массива на "не пусто"
cnt++;
cwritef("\n %d => ".color(icolr) , cnt);
deb && cwritef("%s changed\n\"%s\"".color(icolr), changed, cmd);
auto pid = spawnShell( cmd); // асинхронный запуск shell-процесса
auto status = wait(pid); // код возврата
auto c = okcolr;
if (status>0) c = wcolr; // одно выражение в блоке может быть без фигурных скобок
cwritefln( "status: %d\n".color(c), status );
}
Thread.sleep( dur!("seconds")( interval ) ); // спать секунду
}
}
Когда вы пишете какую-то программу, очень удобно, чтобы происходила автоматическая компиляция/проверка программы после каждого сохранения. Это позволяет сократить количество ручной работы и повышает продуктивность. Этим и будет заниматься программа auto-rerun
.
Программа принимает опции командной строки и два или более аргумента. Все, аргументы, кроме последнего - должны быть именами файлов (явная проверка на проводится). Последний аргумент - команда оболочки, которую нужно выполнить, если хоть у одного файла изменится время модификации. Опции включают отладочные сообщения, изменяют интервал проверки файлов.
Как только рабочая версия нижеприведенной программы скомпилируется и нормально заработает, ее можно использовать для отслеживания изменения собственных исходников (если продолжать над ней работать). То есть, ее вызов из корня проекта будет таким: ./auto-rerun -d ./source/app.d "dub build"
Создадим проект командой dub init auto-rerun
В интерактивном режиме на все вопросы ответим Enter, за исключением поля dependencyes, куда соответствующем вопросе введем colorize
После создания проекта, перейдем к редактированию файла ./source/app.d и напишем нижеприведенную программу. В другом окне в корне каталога auto-rerun периодически запускаем dub build , чтобы видеть допущенные ошибки, обнаруженные во время компиляции. При первом запуске, будет скачана указанная зависимость.
Написать такую программу можно лишь заглядывая в документацию по библиотекам D и гугля некоторые типичные вопросы. То есть, какой-то, прям, учебник, который я все время до этого пытался найти, не нужен.
Вышеприведенная программа написана по мотивам нижеописанной программы на Perl. За небольшими отличиями, она несет почти тот же смысл и логику работы. Можно убедиться, что, если убрать все комментарии из D-версии, перловая версия не намного короче и читается так же несложно.
#!/usr/bin/env perl
use strict;
use Data::Dumper;
use Term::ANSIColor;
use Getopt::Long;
my ($help, $verb);
my ($badsuffix, $goodsuffix) = ("","");
GetOptions(
"help" => \$help,
"verbose" => \$verb,
"badsuffix=s" => \$badsuffix,
"goodsuffix=s" => \$goodsuffix,
) or die "Bad opt!";
print "Usage auto-rebuild file1 [file2 ...] 'on-change-command'" and exit if $help;
my $cmd = pop @ARGV or die "Cmd!";
print STDERR "Command: '$cmd', watch files: @ARGV\n" if $verb;
my %files;
my $cnt = 0;
$|=1;
while (1){
my @changed;
for my $f ( @ARGV ){
my @stat = stat($f);
my $ts = $stat[9];
if ($files{$f}{ts} != $ts){
push @changed, $f;
}
$files{$f}{ts}=$ts;
}
if (@changed){
$cnt++;
print STDERR "\n$cnt). Changed: @changed\n";
print STDERR `$cmd`;
print STDERR $? ? color('red bold') : color('green bold');
print STDERR $? ? "<== $cnt - $? $badsuffix" : "<== $cnt - ok $goodsuffix";
print STDERR color("reset");
}
sleep 2;
}