Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 228 lines (198 sloc) 6.703 kB
#!/usr/bin/env php
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 filetype=php: */
if (!extension_loaded('inotify')) throw new Exception("Please install inotify: pecl install inotify");
$inotifyR = inotify_init();
if ($inotifyR === false) throw new Exception("inotify_init failed");
if (!file_exists('watchngo.config')) throw new Exception("No watchngo.config");
include 'watchngo.config';
if (!isset($config)) throw new Exception('No $config in watchngo.config');
print "Parsing config...\n";
$manager = array();
$watchIdToConfigIdMap = array();
$watchIdToPathMap = array();
$i = 1;
$triggerFullMap = array();
foreach ($config as $id => $c) {
print " [{$i}] => {$id}\n";
$triggerFullMap["{$i}"] = $id;
$i++;
$manager[$id] = array(
'monitor' => is_array($c['monitor']) ? $c['monitor'] : array($c['monitor']),
'perform' => $c['perform'],
'debounce' => 500 / 1000,
'lastDts' => $c['triggerOnStart'] ? 0 : NULL,
);
}
print <<<EOL
Want to manually trigger a rebuild?
=> RETURN to rebuild all
=> ID from above to trigger individual module
EOL;
print PHP_EOL . "Installing watches...\n";
foreach ($manager as $id => $c) {
foreach ($c['monitor'] as $f) {
$watchId = inotify_add_watch($inotifyR, $f, IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM); // IN_ATTRIB gets php::touch()
$watchIdToConfigIdMap[$watchId] = $id;
$watchIdToPathMap[$watchId] = $f;
}
}
print "Staring monitoring...\n";
stream_set_blocking($inotifyR, 0);
// watch STDIN for input related to manual rebuilds
$stdin = fopen('php://stdin', 'r');
$oldStty = `stty -g`;
system("stty -icanon");
stream_set_blocking($stdin, 0);
while (true) {
// watch for numeric input to run specific IDs
$readId = fread($stdin, 10);
if ($readId !== false and !empty($readId))
{
$manualRecompileList = array();
if ($readId === PHP_EOL)
{
print PHP_EOL . "=> REBUILD ALL" . PHP_EOL;
$manualRecompileList = array_values($triggerFullMap);
}
else
{
$readId = trim($readId);
if (isset($triggerFullMap[$readId]))
{
print PHP_EOL . "=> [{$readId}] TRIGGER {$triggerFullMap[$readId]}" . PHP_EOL;
$manualRecompileList[] = $triggerFullMap[$readId];
}
else
{
print PHP_EOL . "=> NO MATCH {$readId}" . PHP_EOL;
}
}
foreach ($manualRecompileList as $managerId) {
// touch all to ensure rebuild doesn't just say "unchanged" (i'm looking at you, compass)
$res = array_map('touch', $manager[$managerId]['monitor']);
fire($manager, $managerId);
}
}
$events = inotify_read($inotifyR);
if ($events === false)
{
checkDebounce($manager);
sleep(1);
continue;
}
$now = microtime(true);
foreach ($events as $event) {
$watchId = $event['wd'];
$configId = $watchIdToConfigIdMap[$watchId];
$manager[$configId]['lastDts'] = $now;
logEvent($event, $watchId, $watchIdToPathMap);
}
checkDebounce($manager);
}
system("stty {$oldStty}");
function checkDebounce(&$manager)
{
print '.';
// check for debounce timeouts
$now = microtime(true);
foreach ($manager as $id => $c) {
if ($c['lastDts'] === NULL)
{
continue;
}
$tdiff = $now - $c['lastDts'];
if ($tdiff > $c['debounce'])
{
print "\n";
fire($manager, $id);
}
}
}
function fire(&$manager, $id)
{
print "[{$id}] {$manager[$id]['perform']}\n";
print `{$manager[$id]['perform']}`;
$manager[$id]['lastDts'] = NULL;
}
function logEvent($event, $watchId, $watchIdToPathMap)
{
return;
$in_events = array(
'IN_ACCESS' => IN_ACCESS,
'IN_MODIFY' => IN_MODIFY,
'IN_ATTRIB' => IN_ATTRIB,
'IN_CLOSE_WRITE' => IN_CLOSE_WRITE,
'IN_CLOSE_NOWRITE' => IN_CLOSE_NOWRITE,
'IN_OPEN' => IN_OPEN,
'IN_MOVED_TO' => IN_MOVED_TO,
'IN_MOVED_FROM' => IN_MOVED_FROM,
'IN_CREATE' => IN_CREATE,
'IN_DELETE' => IN_DELETE,
'IN_DELETE_SELF' => IN_DELETE_SELF,
'IN_MOVE_SELF' => IN_MOVE_SELF,
'IN_CLOSE' => IN_CLOSE,
'IN_MOVE' => IN_MOVE,
'IN_ALL_EVENTS' => IN_ALL_EVENTS,
'IN_UNMOUNT' => IN_UNMOUNT,
'IN_Q_OVERFLOW' => IN_Q_OVERFLOW,
'IN_IGNORED' => IN_IGNORED,
'IN_ISDIR' => IN_ISDIR,
'IN_ONLYDIR' => IN_ONLYDIR,
'IN_DONT_FOLLOW' => IN_DONT_FOLLOW,
'IN_MASK_ADD' => IN_MASK_ADD,
'IN_ONESHOT' => IN_ONESHOT,
);
print "{$watchIdToPathMap[$watchId]}:\n";
foreach ($in_events as $str => $val) {
if ($event['mask'] & $val) print " => {$str}\n";
}
}
/**
* @param mixed VARGS: any number of strings or arrays, each being a file path or a glob to watch.
* @return array The set of files matches the passed globs.
*/
function recursive_glob($requestedGlobs)
{
// expoode vargs
$requestedGlobs = array();
foreach (func_get_args() as $arg) {
if (!is_array($arg))
{
$arg = array($arg);
}
$requestedGlobs = array_merge($requestedGlobs, $arg);
}
$requestedGlobs = array_unique($requestedGlobs);
// expand all a/b/**/c/*.foo into globs
$globsToProcess = array();
foreach ($requestedGlobs as $glob) {
$hasRecursiveDirGlob = (strpos($glob, '**') !== false);
if ($hasRecursiveDirGlob)
{
list($baseDir, $subGlob) = explode('**', $glob, 2);
$baseDir = rtrim($baseDir, '/');
$subGlob = ltrim($subGlob, '/');
// root glob
$globsToProcess[] = "{$baseDir}/{$subGlob}";
$rDirIterator = new RecursiveDirectoryIterator($baseDir);
$itIterator = new RecursiveIteratorIterator($rDirIterator, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($itIterator as $path => $fileInfo) {
if ($itIterator->getInnerIterator()->isDot()) continue;
if ($itIterator->getInnerIterator()->isFile()) continue;
$globsToProcess[] = "{$path}/{$subGlob}";
}
}
else
{
$globsToProcess[] = $glob;
}
}
$matches = array();
$globsToProcess = array_unique($globsToProcess);
foreach ($globsToProcess as $glob) {
$matches = array_merge($matches, glob($glob));
}
$matches = array_unique($matches);
return $matches;
}
Jump to Line
Something went wrong with that request. Please try again.