Starting to implement support for task artefacts and its dependencies
This commit is contained in:
parent
4cf34b0dba
commit
5cae1bf622
|
@ -0,0 +1,386 @@
|
||||||
|
<?php
|
||||||
|
namespace taskman\artefact;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class TaskmanArtefact
|
||||||
|
{
|
||||||
|
private string $path;
|
||||||
|
private array $sources_fn = array();
|
||||||
|
private array $sources_spec = array();
|
||||||
|
private iterable $sources = array();
|
||||||
|
private array $sources_changed = array();
|
||||||
|
private array $sources_changed_fn = array();
|
||||||
|
private array $sources_newer = array();
|
||||||
|
|
||||||
|
function __construct(string $path, array $sources_spec)
|
||||||
|
{
|
||||||
|
$this->path = $path;
|
||||||
|
$this->sources_spec = $sources_spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPath() : string
|
||||||
|
{
|
||||||
|
return $this->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSourcesSpec() : array
|
||||||
|
{
|
||||||
|
return $this->sources_spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSources(int $idx) : iterable
|
||||||
|
{
|
||||||
|
if(isset($this->sources[$idx]))
|
||||||
|
return $this->sources[$idx];
|
||||||
|
|
||||||
|
if(isset($this->sources_fn[$idx]))
|
||||||
|
{
|
||||||
|
$fn = $this->sources_fn[$idx];
|
||||||
|
$sources = $fn();
|
||||||
|
$this->sources[$idx] = $sources;
|
||||||
|
return $sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSourcesFn(int $idx, \Closure $fn)
|
||||||
|
{
|
||||||
|
$this->sources_fn[$idx] = $fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSourcesChangedFn(int $idx, \Closure $fn)
|
||||||
|
{
|
||||||
|
$this->sources_changed_fn[$idx] = $fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChangedSources(int $idx) : iterable
|
||||||
|
{
|
||||||
|
if(isset($this->sources_changed[$idx]))
|
||||||
|
return $this->sources_changed[$idx];
|
||||||
|
|
||||||
|
if(isset($this->sources_changed_fn[$idx]))
|
||||||
|
{
|
||||||
|
$fn = $this->sources_changed_fn[$idx];
|
||||||
|
$changed = $fn();
|
||||||
|
$this->sources_changed[$idx] = $changed;
|
||||||
|
return $changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSourcesNewer(int $idx) : bool
|
||||||
|
{
|
||||||
|
return isset($this->sources_newer[$idx]) && $this->sources_newer[$idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewerSourcesIndices() : array
|
||||||
|
{
|
||||||
|
return array_keys($this->sources_newer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStale() : bool
|
||||||
|
{
|
||||||
|
return count($this->sources_newer) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSources(?\taskman\TaskmanFileChanges $file_changes)
|
||||||
|
{
|
||||||
|
foreach($this->getSourcesSpec() as $src_idx => $src_spec)
|
||||||
|
{
|
||||||
|
//[[dir1, dir2, ..], [ext1, ext2, ..]]
|
||||||
|
if(is_array($src_spec) && count($src_spec) == 2 &&
|
||||||
|
is_array($src_spec[0]) && is_array($src_spec[1]))
|
||||||
|
{
|
||||||
|
$this->setSourcesFn($src_idx, function() use($src_spec) {
|
||||||
|
$dir2files = array();
|
||||||
|
foreach($src_spec[0] as $spec_dir)
|
||||||
|
$dir2files[$spec_dir] = scan_files([$spec_dir], $src_spec[1]);
|
||||||
|
return new TaskmanDirFiles($dir2files);
|
||||||
|
});
|
||||||
|
|
||||||
|
if($file_changes != null)
|
||||||
|
{
|
||||||
|
$this->setSourcesChangedFn($src_idx, function() use($src_spec, $file_changes) {
|
||||||
|
$changed = array();
|
||||||
|
foreach($src_spec[0] as $spec_dir)
|
||||||
|
{
|
||||||
|
$matches = $file_changes->matchDirectory($spec_dir);
|
||||||
|
$changed[$spec_dir] = $matches;
|
||||||
|
}
|
||||||
|
return new TaskmanDirFiles($changed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(is_array($src_spec) || $src_spec instanceof \Iterator)
|
||||||
|
{
|
||||||
|
$this->setSourcesFn($src_idx, fn() => $src_spec);
|
||||||
|
|
||||||
|
if($file_changes != null)
|
||||||
|
{
|
||||||
|
$this->setSourcesChangedFn($src_idx,
|
||||||
|
fn() => $file_changes->matchFiles($this->getSources($src_idx))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Exception("Unknown artefact '{$this->getPath()}' source type" . gettype($src_spec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNewerSources(?\taskman\TaskmanFileChanges $file_changes) : bool
|
||||||
|
{
|
||||||
|
foreach($this->getSourcesSpec() as $src_idx => $src_spec)
|
||||||
|
{
|
||||||
|
$sources = $file_changes != null ? $this->getChangedSources($src_idx) : $this->getSources($src_idx);
|
||||||
|
if(is_stale($this->getPath(), $sources))
|
||||||
|
{
|
||||||
|
$this->sources_newer[$src_idx] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaskmanDirFiles implements \ArrayAccess, \Countable, \Iterator
|
||||||
|
{
|
||||||
|
/*var array<string, string[]>*/
|
||||||
|
private array $dir2files = array();
|
||||||
|
|
||||||
|
private $iter_pos = 0;
|
||||||
|
|
||||||
|
function __construct(array $dir2files = array())
|
||||||
|
{
|
||||||
|
foreach($dir2files as $dir => $files)
|
||||||
|
$this->dir2files[$dir] = $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toMap() : array
|
||||||
|
{
|
||||||
|
return $this->dir2files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear()
|
||||||
|
{
|
||||||
|
$this->dir2files = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmpty() : bool
|
||||||
|
{
|
||||||
|
return empty($this->dir2files);
|
||||||
|
}
|
||||||
|
|
||||||
|
function count() : int
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
$total += count($files);
|
||||||
|
return $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply(callable $fn)
|
||||||
|
{
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
$this->dir2files[$base_dir] = $fn($base_dir, $files);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter(callable $filter)
|
||||||
|
{
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
$this->dir2files[$base_dir] = array_filter($files, $filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
function forEachFile(callable $fn)
|
||||||
|
{
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
{
|
||||||
|
foreach($files as $file)
|
||||||
|
$fn($base_dir, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(string $base_dir, string $file)
|
||||||
|
{
|
||||||
|
if(!isset($this->dir2files[$base_dir]))
|
||||||
|
$this->dir2files[$base_dir] = array();
|
||||||
|
|
||||||
|
$this->dir2files[$base_dir][] = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns [[base_dir, file1], [base_dir, file2], ...]
|
||||||
|
function getFlatArray() : array
|
||||||
|
{
|
||||||
|
$flat = [];
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
{
|
||||||
|
foreach($files as $file)
|
||||||
|
$flat[] = [$base_dir, $file];
|
||||||
|
}
|
||||||
|
return $flat;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllFiles() : array
|
||||||
|
{
|
||||||
|
$all_files = [];
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
$all_files = array_merge($all_files, $files);
|
||||||
|
|
||||||
|
return $all_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ArrayAccess interface
|
||||||
|
function offsetExists(mixed $offset) : bool
|
||||||
|
{
|
||||||
|
if(!is_int($offset))
|
||||||
|
throw new Exception("Invalid offset");
|
||||||
|
|
||||||
|
return $this->count() > $offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetGet(mixed $offset) : mixed
|
||||||
|
{
|
||||||
|
if(!is_int($offset))
|
||||||
|
throw new Exception("Invalid offset");
|
||||||
|
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
{
|
||||||
|
$n = count($files);
|
||||||
|
if($offset - $n < 0)
|
||||||
|
return $files[$offset];
|
||||||
|
$offset -= $n;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetSet(mixed $offset, mixed $value) : void
|
||||||
|
{
|
||||||
|
if(!is_int($offset))
|
||||||
|
throw new Exception("Invalid offset");
|
||||||
|
|
||||||
|
foreach($this->dir2files as $base_dir => &$files)
|
||||||
|
{
|
||||||
|
$n = count($files);
|
||||||
|
if($offset - $n < 0)
|
||||||
|
{
|
||||||
|
$files[$offset] = $value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$offset -= $n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetUnset(mixed $offset) : void
|
||||||
|
{
|
||||||
|
if(!is_int($offset))
|
||||||
|
throw new Exception("Invalid offset");
|
||||||
|
|
||||||
|
foreach($this->dir2files as $base_dir => $files)
|
||||||
|
{
|
||||||
|
$n = count($files);
|
||||||
|
if($offset - $n < 0)
|
||||||
|
{
|
||||||
|
unset($files[$offset]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$offset -= $n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Iterator interface
|
||||||
|
function rewind() : void
|
||||||
|
{
|
||||||
|
$this->iter_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function current() : mixed
|
||||||
|
{
|
||||||
|
return $this->offsetGet($this->iter_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function key() : mixed
|
||||||
|
{
|
||||||
|
return $this->iter_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() : void
|
||||||
|
{
|
||||||
|
++$this->iter_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function valid() : bool
|
||||||
|
{
|
||||||
|
return $this->offsetExists($this->iter_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_stale(string $file, iterable $deps) : bool
|
||||||
|
{
|
||||||
|
if(!is_file($file))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
$fmtime = filemtime($file);
|
||||||
|
|
||||||
|
foreach($deps as $dep)
|
||||||
|
{
|
||||||
|
if($dep && is_file($dep) && (filemtime($dep) > $fmtime))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scan_files(array $dirs, array $only_extensions = [], int $mode = 1) : array
|
||||||
|
{
|
||||||
|
$files = array();
|
||||||
|
foreach($dirs as $dir)
|
||||||
|
{
|
||||||
|
if(!is_dir($dir))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$dir = normalize_path($dir);
|
||||||
|
|
||||||
|
$iter_mode = $mode == 1 ? \RecursiveIteratorIterator::LEAVES_ONLY : \RecursiveIteratorIterator::SELF_FIRST;
|
||||||
|
$iter = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), $iter_mode);
|
||||||
|
|
||||||
|
foreach($iter as $filename => $cur)
|
||||||
|
{
|
||||||
|
if(($mode == 1 && !$cur->isDir()) ||
|
||||||
|
($mode == 2 && $cur->isDir()))
|
||||||
|
{
|
||||||
|
if(!$only_extensions)
|
||||||
|
$files[] = $filename;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$flen = strlen($filename);
|
||||||
|
foreach($only_extensions as $ext)
|
||||||
|
{
|
||||||
|
if(substr_compare($filename, $ext, $flen-strlen($ext)) === 0)
|
||||||
|
$files[] = $filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize_path(string $path) : string
|
||||||
|
{
|
||||||
|
$path = str_replace('\\', '/', $path);
|
||||||
|
$path = preg_replace('/\/+/', '/', $path);
|
||||||
|
$parts = explode('/', $path);
|
||||||
|
$absolutes = array();
|
||||||
|
foreach($parts as $part)
|
||||||
|
{
|
||||||
|
if('.' == $part)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if('..' == $part)
|
||||||
|
array_pop($absolutes);
|
||||||
|
else
|
||||||
|
$absolutes[] = $part;
|
||||||
|
}
|
||||||
|
return implode(DIRECTORY_SEPARATOR, $absolutes);
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
<?php
|
||||||
|
namespace taskman\internal;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
function _default_logger($msg)
|
||||||
|
{
|
||||||
|
echo $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _default_usage($script_name = "<taskman-script>")
|
||||||
|
{
|
||||||
|
echo "\nUsage:\n $script_name [OPTIONS] <task-name1>[,<task-name2>,..] [-D PROP1=value [-D PROP2]]\n\n";
|
||||||
|
echo "Available options:\n";
|
||||||
|
echo " -c specify PHP script to be included (handy for setting props,config options,etc)\n";
|
||||||
|
echo " -V be super verbose\n";
|
||||||
|
echo " -q be quite, only system messages\n";
|
||||||
|
echo " -b batch mode: be super quite, don't even output any system messages\n";
|
||||||
|
echo " -- pass all options verbatim after this mark\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function _collect_tasks()
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
global $TASKMAN_TASK_ALIASES;
|
||||||
|
global $TASKMAN_FILE_CHANGES;
|
||||||
|
|
||||||
|
$TASKMAN_TASKS = array();
|
||||||
|
$TASKMAN_TASK_ALIASES = array();
|
||||||
|
|
||||||
|
$cands = _get_task_candidates();
|
||||||
|
foreach($cands as $name => $args)
|
||||||
|
{
|
||||||
|
if(isset($TASKMAN_TASKS[$name]))
|
||||||
|
throw new \taskman\TaskmanException("Task '$name' is already defined");
|
||||||
|
|
||||||
|
if(is_array($args))
|
||||||
|
{
|
||||||
|
$props = array();
|
||||||
|
if(sizeof($args) > 2)
|
||||||
|
{
|
||||||
|
$props = $args[1];
|
||||||
|
$func = $args[2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$func = $args[1];
|
||||||
|
|
||||||
|
$task = new \taskman\TaskmanTask($func, $name, $props, $TASKMAN_FILE_CHANGES);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Exception("Task '$name' is invalid");
|
||||||
|
|
||||||
|
$TASKMAN_TASKS[$name] = $task;
|
||||||
|
|
||||||
|
foreach($task->getAliases() as $alias)
|
||||||
|
{
|
||||||
|
if(isset($TASKMAN_TASKS[$alias]) || isset($TASKMAN_TASK_ALIASES[$alias]))
|
||||||
|
throw new \taskman\TaskmanException("Alias '$alias' is already defined for task '$name'");
|
||||||
|
$TASKMAN_TASK_ALIASES[$alias] = $task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_validate_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _validate_tasks()
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
|
||||||
|
foreach($TASKMAN_TASKS as $task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$before = $task->getPropOr("before", "");
|
||||||
|
if($before)
|
||||||
|
\taskman\get_task($before)->addBeforeDep($task);
|
||||||
|
|
||||||
|
$after = $task->getPropOr("after", "");
|
||||||
|
if($after)
|
||||||
|
\taskman\get_task($after)->addAfterDep($task);
|
||||||
|
|
||||||
|
foreach($task->getDeps() as $dep_task)
|
||||||
|
\taskman\get_task($dep_task);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
throw new Exception("Task '{$task->getName()}' validation error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _get_task_candidates()
|
||||||
|
{
|
||||||
|
global $TASKMAN_CLOSURES;
|
||||||
|
|
||||||
|
$cands = array();
|
||||||
|
|
||||||
|
//get tasks defined as closures
|
||||||
|
foreach($TASKMAN_CLOSURES as $name => $args)
|
||||||
|
{
|
||||||
|
if(isset($cands[$name]))
|
||||||
|
throw new Exception("Task '$name' is already defined");
|
||||||
|
$cands[$name] = $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($cands);
|
||||||
|
|
||||||
|
return $cands;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _resolve_callable_prop($name)
|
||||||
|
{
|
||||||
|
$prop = $GLOBALS['TASKMAN_PROP_' . $name];
|
||||||
|
if(!($prop instanceof \Closure))
|
||||||
|
return;
|
||||||
|
$value = $prop();
|
||||||
|
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isset_task($task)
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
global $TASKMAN_TASK_ALIASES;
|
||||||
|
return isset($TASKMAN_TASKS[$task]) || isset($TASKMAN_TASK_ALIASES[$task]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _get_hints($task)
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
global $TASKMAN_TASK_ALIASES;
|
||||||
|
$tasks = array_merge(array_keys($TASKMAN_TASKS), array_keys($TASKMAN_TASK_ALIASES));
|
||||||
|
$found = array_filter($tasks, function($v) use($task) { return strpos($v, $task) === 0; });
|
||||||
|
$found = array_merge($found, array_filter($tasks, function($v) use($task) { $pos = strpos($v, $task); return $pos !== false && $pos > 0; }));
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
//e.g: run,build,zip
|
||||||
|
function _parse_taskstr($str)
|
||||||
|
{
|
||||||
|
$task_spec = array();
|
||||||
|
$items = explode(',', $str);
|
||||||
|
foreach($items as $item)
|
||||||
|
{
|
||||||
|
$args = null;
|
||||||
|
$task = $item;
|
||||||
|
if(strpos($item, ' ') !== false)
|
||||||
|
@list($task, $args) = explode(' ', $item, 2);
|
||||||
|
|
||||||
|
if($args)
|
||||||
|
$task_spec[] = array($task, explode(' ', $args));
|
||||||
|
else
|
||||||
|
$task_spec[] = $task;
|
||||||
|
}
|
||||||
|
return $task_spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _read_env_vars()
|
||||||
|
{
|
||||||
|
$envs = getenv();
|
||||||
|
|
||||||
|
foreach($envs as $k => $v)
|
||||||
|
{
|
||||||
|
if(strpos($k, 'TASKMAN_SET_') === 0)
|
||||||
|
{
|
||||||
|
$prop_name = substr($k, 12);
|
||||||
|
\taskman\log(0, "Setting prop '$prop_name' (with env '$k')\n");
|
||||||
|
\taskman\set($prop_name, $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _process_argv(&$argv)
|
||||||
|
{
|
||||||
|
global $TASKMAN_LOG_LEVEL;
|
||||||
|
global $TASKMAN_BATCH;
|
||||||
|
global $TASKMAN_NO_DEPS;
|
||||||
|
global $TASKMAN_FILE_CHANGES;
|
||||||
|
|
||||||
|
$filtered = array();
|
||||||
|
$process_defs = false;
|
||||||
|
|
||||||
|
for($i=0;$i<sizeof($argv);++$i)
|
||||||
|
{
|
||||||
|
$v = $argv[$i];
|
||||||
|
|
||||||
|
if($v == '--')
|
||||||
|
{
|
||||||
|
for($j=$i+1;$j<sizeof($argv);++$j)
|
||||||
|
$filtered[] = $argv[$j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if($v == '-D')
|
||||||
|
{
|
||||||
|
$process_defs = true;
|
||||||
|
}
|
||||||
|
else if($v == '-V')
|
||||||
|
{
|
||||||
|
$TASKMAN_LOG_LEVEL = 2;
|
||||||
|
}
|
||||||
|
else if($v == '-q')
|
||||||
|
{
|
||||||
|
$TASKMAN_LOG_LEVEL = 0;
|
||||||
|
}
|
||||||
|
else if($v == '-b')
|
||||||
|
{
|
||||||
|
$TASKMAN_LOG_LEVEL = -1;
|
||||||
|
}
|
||||||
|
else if($v == '-O')
|
||||||
|
{
|
||||||
|
$TASKMAN_NO_DEPS = true;
|
||||||
|
}
|
||||||
|
else if($v == '-c')
|
||||||
|
{
|
||||||
|
if(!isset($argv[$i+1]))
|
||||||
|
throw new \taskman\TaskmanException("Configuration file(-c option) is missing");
|
||||||
|
require_once($argv[$i+1]);
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
else if($v == '-F')
|
||||||
|
{
|
||||||
|
if(!isset($argv[$i+1]))
|
||||||
|
throw new \taskman\TaskmanException("Argument(-F option) is missing");
|
||||||
|
|
||||||
|
$TASKMAN_FILE_CHANGES = \taskman\TaskmanFileChanges::parse($argv[$i+1]);
|
||||||
|
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
else if($process_defs)
|
||||||
|
{
|
||||||
|
$eq_pos = strpos($v, '=');
|
||||||
|
if($eq_pos !== false)
|
||||||
|
{
|
||||||
|
$def_name = substr($v, 0, $eq_pos);
|
||||||
|
$def_value = substr($v, $eq_pos+1);
|
||||||
|
|
||||||
|
//TODO: this code must be more robust
|
||||||
|
if(strtolower($def_value) === 'true')
|
||||||
|
$def_value = true;
|
||||||
|
else if(strtolower($def_value) === 'false')
|
||||||
|
$def_value = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$def_name = $v;
|
||||||
|
$def_value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
\taskman\log(0, "Setting prop '$def_name'=" . var_export($def_value, true) . "\n");
|
||||||
|
\taskman\set($def_name, $def_value);
|
||||||
|
|
||||||
|
$process_defs = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$filtered[] = $v;
|
||||||
|
}
|
||||||
|
$argv = $filtered;
|
||||||
|
}
|
||||||
|
|
756
taskman.inc.php
756
taskman.inc.php
|
@ -1,5 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
namespace {
|
namespace taskman;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
include_once(__DIR__ . '/internal.inc.php');
|
||||||
|
include_once(__DIR__ . '/util.inc.php');
|
||||||
|
include_once(__DIR__ . '/tasks.inc.php');
|
||||||
|
include_once(__DIR__ . '/artefact.inc.php');
|
||||||
|
|
||||||
$GLOBALS['TASKMAN_TASKS'] = array();
|
$GLOBALS['TASKMAN_TASKS'] = array();
|
||||||
$GLOBALS['TASKMAN_CLOSURES'] = array();
|
$GLOBALS['TASKMAN_CLOSURES'] = array();
|
||||||
|
@ -9,51 +15,43 @@ $GLOBALS['TASKMAN_LOG_LEVEL'] = 1; //0 - important, 1 - normal, 2 - debug
|
||||||
$GLOBALS['TASKMAN_NO_DEPS'] = false;
|
$GLOBALS['TASKMAN_NO_DEPS'] = false;
|
||||||
$GLOBALS['TASKMAN_SCRIPT'] = '';
|
$GLOBALS['TASKMAN_SCRIPT'] = '';
|
||||||
$GLOBALS['TASKMAN_CURRENT_TASK'] = null;
|
$GLOBALS['TASKMAN_CURRENT_TASK'] = null;
|
||||||
$GLOBALS['TASKMAN_HELP_FUNC'] = '_taskman_default_usage';
|
$GLOBALS['TASKMAN_HELP_FUNC'] = '\taskman\internal\_default_usage';
|
||||||
$GLOBALS['TASKMAN_LOGGER'] = '_taskman_default_logger';
|
$GLOBALS['TASKMAN_LOGGER'] = '\taskman\internal\_default_logger';
|
||||||
$GLOBALS['TASKMAN_ERROR_HANDLER'] = null;
|
$GLOBALS['TASKMAN_ERROR_HANDLER'] = null;
|
||||||
$GLOBALS['TASKMAN_START_TIME'] = 0;
|
$GLOBALS['TASKMAN_START_TIME'] = 0;
|
||||||
|
$GLOBALS['TASKMAN_FILES_CHANGES'] = null;
|
||||||
function _taskman_default_logger($msg)
|
|
||||||
{
|
|
||||||
echo $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _taskman_default_usage($script_name = "<taskman-script>")
|
|
||||||
{
|
|
||||||
echo "\nUsage:\n $script_name [OPTIONS] <task-name1>[,<task-name2>,..] [-D PROP1=value [-D PROP2]]\n\n";
|
|
||||||
echo "Available options:\n";
|
|
||||||
echo " -c specify PHP script to be included (handy for setting props,config options,etc)\n";
|
|
||||||
echo " -V be super verbose\n";
|
|
||||||
echo " -q be quite, only system messages\n";
|
|
||||||
echo " -b batch mode: be super quite, don't even output any system messages\n";
|
|
||||||
echo " -- pass all options verbatim after this mark\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
} //namespace global
|
|
||||||
|
|
||||||
namespace taskman {
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
class TaskmanException extends Exception
|
class TaskmanException extends Exception
|
||||||
{}
|
{}
|
||||||
|
|
||||||
class TaskmanTask
|
class TaskmanTask
|
||||||
{
|
{
|
||||||
private $func;
|
private \Closure $func;
|
||||||
private $name;
|
|
||||||
private $file;
|
|
||||||
private $line;
|
|
||||||
private $props = array();
|
|
||||||
private $is_running = false;
|
|
||||||
private $has_run = array();
|
|
||||||
private $args = array();
|
|
||||||
private $deps = null;
|
|
||||||
private $aliases = null;
|
|
||||||
private $before_deps = array();
|
|
||||||
private $after_deps = array();
|
|
||||||
|
|
||||||
function __construct(\Closure $func, $name, $props = array())
|
private string $file;
|
||||||
|
private int $line;
|
||||||
|
|
||||||
|
private string $name;
|
||||||
|
//initialized lazily
|
||||||
|
private ?array $_aliases = null;
|
||||||
|
|
||||||
|
private $args = array();
|
||||||
|
|
||||||
|
private $props = array();
|
||||||
|
private bool $is_running = false;
|
||||||
|
private array $has_run = array();
|
||||||
|
|
||||||
|
private ?array $deps = null;
|
||||||
|
private array $before_deps = array();
|
||||||
|
private array $after_deps = array();
|
||||||
|
|
||||||
|
//initialized lazily
|
||||||
|
private ?array $_artefacts = null;
|
||||||
|
|
||||||
|
private ?TaskmanFileChanges $file_changes;
|
||||||
|
|
||||||
|
function __construct(\Closure $func, string $name,
|
||||||
|
array $props = array(), ?TaskmanFileChanges $file_changes = null)
|
||||||
{
|
{
|
||||||
$refl = new \ReflectionFunction($func);
|
$refl = new \ReflectionFunction($func);
|
||||||
$this->file = $refl->getFileName();
|
$this->file = $refl->getFileName();
|
||||||
|
@ -62,38 +60,40 @@ class TaskmanTask
|
||||||
$this->func = $func;
|
$this->func = $func;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->props = $props;
|
$this->props = $props;
|
||||||
|
|
||||||
|
$this->file_changes = $file_changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getName()
|
function getName() : string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFile()
|
function getFile() : string
|
||||||
{
|
{
|
||||||
return $this->file;
|
return $this->file;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLine()
|
function getLine() : int
|
||||||
{
|
{
|
||||||
return $this->line;
|
return $this->line;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFunc()
|
function getFunc() : \Closure
|
||||||
{
|
{
|
||||||
return $this->func;
|
return $this->func;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArgs()
|
function getArgs() : array
|
||||||
{
|
{
|
||||||
return $this->args;
|
return $this->args;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAliases() : array
|
function getAliases() : array
|
||||||
{
|
{
|
||||||
if($this->aliases === null)
|
if($this->_aliases === null)
|
||||||
$this->aliases = $this->_parseAliases();
|
$this->_aliases = $this->_parseAliases();
|
||||||
return $this->aliases;
|
return $this->_aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseAliases() : array
|
function _parseAliases() : array
|
||||||
|
@ -107,6 +107,44 @@ class TaskmanTask
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return artefact\TaskmanArtefact[]
|
||||||
|
*/
|
||||||
|
function getArtefacts() : array
|
||||||
|
{
|
||||||
|
if($this->_artefacts === null)
|
||||||
|
$this->_artefacts = $this->_parseArtefacts();
|
||||||
|
return $this->_artefacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return artefact\TaskmanArtefact[]
|
||||||
|
*/
|
||||||
|
function _parseArtefacts() : array
|
||||||
|
{
|
||||||
|
$artefacts = array();
|
||||||
|
$specs = $this->getPropOr("artefacts", array());
|
||||||
|
if(is_callable($specs))
|
||||||
|
$specs = $specs();
|
||||||
|
if(is_array($specs))
|
||||||
|
{
|
||||||
|
foreach($specs as $dst => $src_spec)
|
||||||
|
$artefacts[] = new artefact\TaskmanArtefact($dst, $src_spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $artefacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileChanges() : ?TaskmanFileChanges
|
||||||
|
{
|
||||||
|
return $this->file_changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIncrementalBuild() : bool
|
||||||
|
{
|
||||||
|
return $this->file_changes != null;
|
||||||
|
}
|
||||||
|
|
||||||
function run($args = array())
|
function run($args = array())
|
||||||
{
|
{
|
||||||
global $TASKMAN_CURRENT_TASK;
|
global $TASKMAN_CURRENT_TASK;
|
||||||
|
@ -120,13 +158,19 @@ class TaskmanTask
|
||||||
$this->is_running)
|
$this->is_running)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$this->is_running = true;
|
|
||||||
$this->args = $args;
|
|
||||||
|
|
||||||
$task_result = null;
|
$task_result = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if($this->getArtefacts() && !$this->_checkIfArtefactsStale())
|
||||||
|
{
|
||||||
|
$this->has_run[$args_str] = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->is_running = true;
|
||||||
|
$this->args = $args;
|
||||||
|
|
||||||
$TASKMAN_STACK[] = $this;
|
$TASKMAN_STACK[] = $this;
|
||||||
|
|
||||||
$level = count($TASKMAN_STACK)-1;
|
$level = count($TASKMAN_STACK)-1;
|
||||||
|
@ -143,7 +187,7 @@ class TaskmanTask
|
||||||
|
|
||||||
$bench = microtime(true);
|
$bench = microtime(true);
|
||||||
|
|
||||||
$task_result = call_user_func_array($this->func, array($this->args));
|
$task_result = call_user_func_array($this->func, array($this->args, $this));
|
||||||
array_pop($TASKMAN_STACK);
|
array_pop($TASKMAN_STACK);
|
||||||
|
|
||||||
if(!$TASKMAN_NO_DEPS)
|
if(!$TASKMAN_NO_DEPS)
|
||||||
|
@ -190,559 +234,153 @@ class TaskmanTask
|
||||||
if(is_array($deps))
|
if(is_array($deps))
|
||||||
return $deps;
|
return $deps;
|
||||||
else if($deps && is_string($deps))
|
else if($deps && is_string($deps))
|
||||||
return _parse_taskstr($deps);
|
return internal\_parse_taskstr($deps);
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
static function extractName($func)
|
function getPropOr(string $name, mixed $def) : mixed
|
||||||
{
|
|
||||||
if(strpos($func, "task_") === 0)
|
|
||||||
return substr($func, strlen('task_'), strlen($func));
|
|
||||||
else if(strpos($func, 'taskman\task_') === 0)
|
|
||||||
return substr($func, strlen('taskman\task_'), strlen($func));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPropOr($name, $def)
|
|
||||||
{
|
{
|
||||||
return isset($this->props[$name]) ? $this->props[$name] : $def;
|
return isset($this->props[$name]) ? $this->props[$name] : $def;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProp($name)
|
function getProp(string $name) : mixed
|
||||||
{
|
{
|
||||||
return $this->getPropOr($name, null);
|
return $this->getPropOr($name, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasProp($name)
|
function hasProp(string $name) : bool
|
||||||
{
|
{
|
||||||
return isset($this->props[$name]);
|
return isset($this->props[$name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProps()
|
function getProps() : array
|
||||||
{
|
{
|
||||||
return $this->props;
|
return $this->props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function _checkIfArtefactsStale() : bool
|
||||||
|
{
|
||||||
|
$file_changes = $this->getFileChanges();
|
||||||
|
|
||||||
|
$stale_found = false;
|
||||||
|
|
||||||
|
foreach($this->getArtefacts() as $artefact)
|
||||||
|
{
|
||||||
|
$artefact->initSources($file_changes);
|
||||||
|
|
||||||
|
if(!$stale_found && $artefact->checkNewerSources($file_changes))
|
||||||
|
$stale_found = true;
|
||||||
|
|
||||||
|
if($artefact->isStale())
|
||||||
|
{
|
||||||
|
msg_sys("Task '{$this->name}' artefact '{$artefact->getPath()}' (sources at ".implode(',', $artefact->getNewerSourcesIndices()).") is stale\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $stale_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findAnyStaleArtefact() : ?artefact\TaskmanArtefact
|
||||||
|
{
|
||||||
|
foreach($this->getArtefacts() as $artefact)
|
||||||
|
{
|
||||||
|
if($artefact->isStale())
|
||||||
|
return $artefact;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _collect_tasks()
|
class TaskmanFileChanges
|
||||||
{
|
{
|
||||||
global $TASKMAN_TASKS;
|
private $changed = array();
|
||||||
global $TASKMAN_TASK_ALIASES;
|
private $removed = array();
|
||||||
|
|
||||||
$TASKMAN_TASKS = array();
|
static function parse(string $json_or_file)
|
||||||
$TASKMAN_TASK_ALIASES = array();
|
|
||||||
|
|
||||||
$cands = _get_task_candidates();
|
|
||||||
foreach($cands as $name => $args)
|
|
||||||
{
|
{
|
||||||
if(isset($TASKMAN_TASKS[$name]))
|
if($json_or_file[0] == '[')
|
||||||
throw new TaskmanException("Task '$name' is already defined");
|
$json = $json_or_file;
|
||||||
|
|
||||||
if(is_array($args))
|
|
||||||
{
|
|
||||||
$props = array();
|
|
||||||
if(sizeof($args) > 2)
|
|
||||||
{
|
|
||||||
$props = $args[1];
|
|
||||||
$func = $args[2];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$func = $args[1];
|
|
||||||
|
|
||||||
$task = new TaskmanTask($func, $name, $props);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
throw new Exception("Task '$name' is invalid");
|
$json = file_get_contents($json_or_file);
|
||||||
|
|
||||||
$TASKMAN_TASKS[$name] = $task;
|
$decoded = json_decode($json, true);
|
||||||
|
if(!is_array($decoded))
|
||||||
|
throw new Exception('Bad json');
|
||||||
|
|
||||||
foreach($task->getAliases() as $alias)
|
$changed = array();
|
||||||
|
$removed = array();
|
||||||
|
|
||||||
|
$base_dir = dirname($_SERVER['PHP_SELF']);
|
||||||
|
|
||||||
|
foreach($decoded as $items)
|
||||||
{
|
{
|
||||||
if(isset($TASKMAN_TASKS[$alias]) || isset($TASKMAN_TASK_ALIASES[$alias]))
|
if(count($items) != 2)
|
||||||
throw new TaskmanException("Alias '$alias' is already defined for task '$name'");
|
throw new Exception('Bad entry');
|
||||||
$TASKMAN_TASK_ALIASES[$alias] = $task;
|
list($status, $file) = $items;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($TASKMAN_TASKS as $task)
|
if(strlen($file) > 0)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$before = $task->getPropOr("before", "");
|
|
||||||
if($before)
|
|
||||||
get_task($before)->addBeforeDep($task);
|
|
||||||
|
|
||||||
$after = $task->getPropOr("after", "");
|
|
||||||
if($after)
|
|
||||||
get_task($after)->addAfterDep($task);
|
|
||||||
|
|
||||||
foreach($task->getDeps() as $dep_task)
|
|
||||||
get_task($dep_task);
|
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
throw new Exception("Task '{$task->getName()}' validation error: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _get_task_candidates()
|
|
||||||
{
|
|
||||||
global $TASKMAN_CLOSURES;
|
|
||||||
|
|
||||||
$cands = array();
|
|
||||||
|
|
||||||
//get tasks defined as closures
|
|
||||||
foreach($TASKMAN_CLOSURES as $name => $args)
|
|
||||||
$cands[$name] = $args;
|
|
||||||
|
|
||||||
ksort($cands);
|
|
||||||
|
|
||||||
return $cands;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_task(string $task) : TaskmanTask
|
|
||||||
{
|
|
||||||
global $TASKMAN_TASKS;
|
|
||||||
global $TASKMAN_TASK_ALIASES;
|
|
||||||
|
|
||||||
if(!is_scalar($task))
|
|
||||||
throw new TaskmanException("Bad task name");
|
|
||||||
|
|
||||||
if(isset($TASKMAN_TASKS[$task]))
|
|
||||||
return $TASKMAN_TASKS[$task];
|
|
||||||
|
|
||||||
if(isset($TASKMAN_TASK_ALIASES[$task]))
|
|
||||||
return $TASKMAN_TASK_ALIASES[$task];
|
|
||||||
|
|
||||||
throw new TaskmanException("Task with name/alias '{$task}' does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
function _resolve_callable_prop($name)
|
|
||||||
{
|
|
||||||
$prop = $GLOBALS['TASKMAN_PROP_' . $name];
|
|
||||||
if(!($prop instanceof \Closure))
|
|
||||||
return;
|
|
||||||
$value = $prop();
|
|
||||||
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get($name)
|
|
||||||
{
|
|
||||||
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
|
||||||
throw new TaskmanException("Property '$name' is not set");
|
|
||||||
_resolve_callable_prop($name);
|
|
||||||
return $GLOBALS['TASKMAN_PROP_' . $name];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getor($name, $def)
|
|
||||||
{
|
|
||||||
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
|
||||||
return $def;
|
|
||||||
_resolve_callable_prop($name);
|
|
||||||
return $GLOBALS['TASKMAN_PROP_' . $name];
|
|
||||||
}
|
|
||||||
|
|
||||||
function set($name, $value)
|
|
||||||
{
|
|
||||||
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setor($name, $value)
|
|
||||||
{
|
|
||||||
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
|
||||||
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function is($name)
|
|
||||||
{
|
|
||||||
return isset($GLOBALS['TASKMAN_PROP_' . $name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function del($name)
|
|
||||||
{
|
|
||||||
unset($GLOBALS['TASKMAN_PROP_' . $name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function props()
|
|
||||||
{
|
|
||||||
$props = array();
|
|
||||||
foreach($GLOBALS as $key => $value)
|
|
||||||
{
|
|
||||||
if(($idx = strpos($key, 'TASKMAN_PROP_')) === 0)
|
|
||||||
{
|
|
||||||
$name = substr($key, strlen('TASKMAN_PROP_'));
|
|
||||||
$props[$name] = get($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $props;
|
|
||||||
}
|
|
||||||
|
|
||||||
function task($name)
|
|
||||||
{
|
|
||||||
global $TASKMAN_CLOSURES;
|
|
||||||
|
|
||||||
//TODO: can it be a feature?
|
|
||||||
if(isset($TASKMAN_CLOSURES[$name]))
|
|
||||||
throw new TaskmanException("Task '$name' is already defined");
|
|
||||||
|
|
||||||
$args = func_get_args();
|
|
||||||
$TASKMAN_CLOSURES[$name] = $args;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_tasks() : array
|
|
||||||
{
|
|
||||||
global $TASKMAN_TASKS;
|
|
||||||
return $TASKMAN_TASKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
function current_task()
|
|
||||||
{
|
|
||||||
global $TASKMAN_CURRENT_TASK;
|
|
||||||
return $TASKMAN_CURRENT_TASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
function run($task, array $args = array())
|
|
||||||
{
|
|
||||||
if($task instanceof TaskmanTask)
|
|
||||||
$task_obj = $task;
|
|
||||||
else
|
|
||||||
$task_obj = get_task($task);
|
|
||||||
|
|
||||||
return $task_obj->run($args);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_many($tasks, $args = array())
|
|
||||||
{
|
|
||||||
foreach($tasks as $task_spec)
|
|
||||||
{
|
|
||||||
if(is_array($task_spec))
|
|
||||||
run($task_spec[0], $task_spec[1]);
|
|
||||||
else
|
|
||||||
run($task_spec, $args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function msg_dbg($msg)
|
|
||||||
{
|
|
||||||
_log($msg, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function msg($msg)
|
|
||||||
{
|
|
||||||
_log($msg, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function msg_sys($msg)
|
|
||||||
{
|
|
||||||
_log($msg, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _log($msg, $level = 1)
|
|
||||||
{
|
|
||||||
global $TASKMAN_LOG_LEVEL;
|
|
||||||
|
|
||||||
if($TASKMAN_LOG_LEVEL < $level)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$logger = $GLOBALS['TASKMAN_LOGGER'];
|
|
||||||
call_user_func_array($logger, array($msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _($str)
|
|
||||||
{
|
|
||||||
if(strpos($str, '%') === false)
|
|
||||||
return $str;
|
|
||||||
|
|
||||||
$str = preg_replace_callback(
|
|
||||||
'~%\(([^\)]+)\)%~',
|
|
||||||
function($m) { return get($m[1]); },
|
|
||||||
$str
|
|
||||||
);
|
|
||||||
return $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isset_task($task)
|
|
||||||
{
|
|
||||||
global $TASKMAN_TASKS;
|
|
||||||
global $TASKMAN_TASK_ALIASES;
|
|
||||||
return isset($TASKMAN_TASKS[$task]) || isset($TASKMAN_TASK_ALIASES[$task]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _get_hints($task)
|
|
||||||
{
|
|
||||||
global $TASKMAN_TASKS;
|
|
||||||
global $TASKMAN_TASK_ALIASES;
|
|
||||||
$tasks = array_merge(array_keys($TASKMAN_TASKS), array_keys($TASKMAN_TASK_ALIASES));
|
|
||||||
$found = array_filter($tasks, function($v) use($task) { return strpos($v, $task) === 0; });
|
|
||||||
$found = array_merge($found, array_filter($tasks, function($v) use($task) { $pos = strpos($v, $task); return $pos !== false && $pos > 0; }));
|
|
||||||
return $found;
|
|
||||||
}
|
|
||||||
|
|
||||||
//e.g: run,build,zip
|
|
||||||
function _parse_taskstr($str)
|
|
||||||
{
|
|
||||||
$task_spec = array();
|
|
||||||
$items = explode(',', $str);
|
|
||||||
foreach($items as $item)
|
|
||||||
{
|
|
||||||
$args = null;
|
|
||||||
$task = $item;
|
|
||||||
if(strpos($item, ' ') !== false)
|
|
||||||
@list($task, $args) = explode(' ', $item, 2);
|
|
||||||
|
|
||||||
if($args)
|
|
||||||
$task_spec[] = array($task, explode(' ', $args));
|
|
||||||
else
|
|
||||||
$task_spec[] = $task;
|
|
||||||
}
|
|
||||||
return $task_spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _read_env_vars()
|
|
||||||
{
|
|
||||||
$envs = getenv();
|
|
||||||
|
|
||||||
foreach($envs as $k => $v)
|
|
||||||
{
|
|
||||||
if(strpos($k, 'TASKMAN_SET_') === 0)
|
|
||||||
{
|
|
||||||
$prop_name = substr($k, 12);
|
|
||||||
msg_sys("Setting prop '$prop_name' (with env '$k')\n");
|
|
||||||
set($prop_name, $v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _process_argv(&$argv)
|
|
||||||
{
|
|
||||||
global $TASKMAN_LOG_LEVEL;
|
|
||||||
global $TASKMAN_BATCH;
|
|
||||||
global $TASKMAN_NO_DEPS;
|
|
||||||
|
|
||||||
$filtered = array();
|
|
||||||
$process_defs = false;
|
|
||||||
for($i=0;$i<sizeof($argv);++$i)
|
|
||||||
{
|
|
||||||
$v = $argv[$i];
|
|
||||||
|
|
||||||
if($v == '--')
|
|
||||||
{
|
|
||||||
for($j=$i+1;$j<sizeof($argv);++$j)
|
|
||||||
$filtered[] = $argv[$j];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if($v == '-D')
|
|
||||||
{
|
|
||||||
$process_defs = true;
|
|
||||||
}
|
|
||||||
else if($v == '-V')
|
|
||||||
{
|
|
||||||
$TASKMAN_LOG_LEVEL = 2;
|
|
||||||
}
|
|
||||||
else if($v == '-q')
|
|
||||||
{
|
|
||||||
$TASKMAN_LOG_LEVEL = 0;
|
|
||||||
}
|
|
||||||
else if($v == '-b')
|
|
||||||
{
|
|
||||||
$TASKMAN_LOG_LEVEL = -1;
|
|
||||||
}
|
|
||||||
else if($v == '-O')
|
|
||||||
{
|
|
||||||
$TASKMAN_NO_DEPS = true;
|
|
||||||
}
|
|
||||||
else if($v == '-c')
|
|
||||||
{
|
|
||||||
if(!isset($argv[$i+1]))
|
|
||||||
throw new TaskmanException("Configuration file(-c option) is missing");
|
|
||||||
require_once($argv[$i+1]);
|
|
||||||
++$i;
|
|
||||||
}
|
|
||||||
else if($process_defs)
|
|
||||||
{
|
|
||||||
$eq_pos = strpos($v, '=');
|
|
||||||
if($eq_pos !== false)
|
|
||||||
{
|
{
|
||||||
$def_name = substr($v, 0, $eq_pos);
|
if(DIRECTORY_SEPARATOR == '/')
|
||||||
$def_value = substr($v, $eq_pos+1);
|
|
||||||
|
|
||||||
//TODO: this code must be more robust
|
|
||||||
if(strtolower($def_value) === 'true')
|
|
||||||
$def_value = true;
|
|
||||||
else if(strtolower($def_value) === 'false')
|
|
||||||
$def_value = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$def_name = $v;
|
|
||||||
$def_value = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_sys("Setting prop '$def_name'=" . var_export($def_value, true) . "\n");
|
|
||||||
set($def_name, $def_value);
|
|
||||||
|
|
||||||
$process_defs = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$filtered[] = $v;
|
|
||||||
}
|
|
||||||
$argv = $filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function main($argv = array(), $help_func = null, $proc_argv = true, $read_env_vars = true)
|
|
||||||
{
|
|
||||||
$GLOBALS['TASKMAN_START_TIME'] = microtime(true);
|
|
||||||
|
|
||||||
if($help_func)
|
|
||||||
$GLOBALS['TASKMAN_HELP_FUNC'] = $help_func;
|
|
||||||
|
|
||||||
if($read_env_vars)
|
|
||||||
_read_env_vars();
|
|
||||||
|
|
||||||
if($proc_argv)
|
|
||||||
_process_argv($argv);
|
|
||||||
|
|
||||||
$GLOBALS['TASKMAN_SCRIPT'] = array_shift($argv);
|
|
||||||
|
|
||||||
_collect_tasks();
|
|
||||||
|
|
||||||
$always_tasks = array();
|
|
||||||
$default_task = null;
|
|
||||||
foreach(get_tasks() as $task_obj)
|
|
||||||
{
|
|
||||||
if($task_obj->hasProp('always'))
|
|
||||||
array_unshift($always_tasks, $task_obj);
|
|
||||||
if($task_obj->hasProp('default'))
|
|
||||||
{
|
|
||||||
if($default_task)
|
|
||||||
throw new TaskmanException("Assigned default task '" . $default_task->getName() . "' conflicts with '" . $task_obj->getName() . "'");
|
|
||||||
else
|
|
||||||
$default_task = $task_obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($always_tasks as $always_task)
|
|
||||||
$always_task->run(array());
|
|
||||||
|
|
||||||
if(sizeof($argv) > 0)
|
|
||||||
{
|
|
||||||
$task_str = array_shift($argv);
|
|
||||||
$tasks = _parse_taskstr($task_str);
|
|
||||||
|
|
||||||
if(count($tasks) == 1 && !_isset_task($tasks[0]))
|
|
||||||
{
|
|
||||||
$pattern = $tasks[0];
|
|
||||||
if($pattern[0] == '~')
|
|
||||||
{
|
|
||||||
$pattern = substr($pattern, 1, strlen($pattern) - 1);
|
|
||||||
$is_similar = true;
|
|
||||||
}
|
|
||||||
elseif(substr($pattern, -1, 1) == '~')
|
|
||||||
{
|
|
||||||
$pattern = substr($pattern, 0, strlen($pattern) - 1);
|
|
||||||
$is_similar = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$is_similar = false;
|
|
||||||
$hints = _get_hints($pattern);
|
|
||||||
|
|
||||||
if($is_similar && count($hints) == 1)
|
|
||||||
$tasks = $hints;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("ERROR! Task %s not found\n", $tasks[0]);
|
|
||||||
if($hints)
|
|
||||||
{
|
{
|
||||||
printf("Similar tasks:\n");
|
if($file[0] != '/')
|
||||||
foreach($hints as $hint)
|
$file = $base_dir . DIRECTORY_SEPARATOR . $file;
|
||||||
printf(" %s\n", $hint);
|
|
||||||
}
|
}
|
||||||
exit(1);
|
else if(strlen($file) > 1 && $file[1] != ':')
|
||||||
|
$file = $base_dir . DIRECTORY_SEPARATOR . $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$file = artefact\normalize_path($file);
|
||||||
|
|
||||||
|
if($status == 'C')
|
||||||
|
$changed[$file] = true;
|
||||||
|
else if($status == 'R')
|
||||||
|
$removed[$file] = true;
|
||||||
|
else
|
||||||
|
throw new Exception('Unknown status: ' . $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
run_many($tasks, $argv);
|
return new TaskmanFileChanges($changed, $removed);
|
||||||
}
|
}
|
||||||
else if($default_task)
|
|
||||||
$default_task->run($argv);
|
|
||||||
|
|
||||||
msg_sys("***** All done (".round(microtime(true)-$GLOBALS['TASKMAN_START_TIME'],2)." sec.) *****\n");
|
//NOTE: these are actually maps: file => true
|
||||||
}
|
function __construct(array $changed, array $removed)
|
||||||
|
|
||||||
function usage($script_name = "<taskman-script>")
|
|
||||||
{
|
|
||||||
\_taskman_default_usage($script_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
task('help', function($args = array())
|
|
||||||
{
|
|
||||||
$filter = '';
|
|
||||||
if(isset($args[0]))
|
|
||||||
$filter = $args[0];
|
|
||||||
|
|
||||||
$maxlen = -1;
|
|
||||||
$tasks = array();
|
|
||||||
$all = get_tasks();
|
|
||||||
foreach($all as $task)
|
|
||||||
{
|
{
|
||||||
if($filter && (strpos($task->getName(), $filter) === false && strpos($task->getPropOr("alias", ""), $filter) === false))
|
$this->changed = $changed;
|
||||||
continue;
|
$this->removed = $removed;
|
||||||
|
|
||||||
if(strlen($task->getName()) > $maxlen)
|
|
||||||
$maxlen = strlen($task->getName());
|
|
||||||
|
|
||||||
$tasks[] = $task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$args)
|
function isEmpty() : bool
|
||||||
{
|
{
|
||||||
$help_func = $GLOBALS['TASKMAN_HELP_FUNC'];
|
return count($this->changed) == 0 && count($this->removed) == 0;
|
||||||
$help_func();
|
|
||||||
echo "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Available tasks:\n";
|
function matchDirectory(string $dir) : array
|
||||||
foreach($tasks as $task)
|
|
||||||
{
|
{
|
||||||
$props_string = '';
|
$dir = rtrim($dir, '/\\');
|
||||||
$pad = $maxlen - strlen($task->getName());
|
$dir .= DIRECTORY_SEPARATOR;
|
||||||
foreach($task->getProps() as $name => $value)
|
|
||||||
|
$filtered = [];
|
||||||
|
|
||||||
|
foreach($this->changed as $path => $_)
|
||||||
|
if(strpos($path, $dir) === 0)
|
||||||
|
$filtered[] = $path;
|
||||||
|
|
||||||
|
foreach($this->removed as $path => $_)
|
||||||
|
if(strpos($path, $dir) === 0)
|
||||||
|
$filtered[] = $path;
|
||||||
|
|
||||||
|
return $filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchFiles(iterable $files) : array
|
||||||
|
{
|
||||||
|
$filtered = [];
|
||||||
|
foreach($files as $file)
|
||||||
{
|
{
|
||||||
$props_string .= str_repeat(" ", $pad) .' @' . $name . ' ' . (is_string($value) ? $value : json_encode($value)) . "\n";
|
if(isset($this->changed[$file]) || isset($this->removed[$file]))
|
||||||
$pad = $maxlen + 1;
|
$filtered[] = $file;
|
||||||
}
|
}
|
||||||
$props_string = rtrim($props_string);
|
return $filtered;
|
||||||
|
|
||||||
echo "---------------------------------\n";
|
|
||||||
$file = $task->getFile();
|
|
||||||
$line = $task->getLine();
|
|
||||||
echo " " . $task->getName() . $props_string . " ($file@$line)\n";
|
|
||||||
}
|
}
|
||||||
echo "\n";
|
}
|
||||||
});
|
|
||||||
|
|
||||||
task('props', function($args = [])
|
|
||||||
{
|
|
||||||
$filter = '';
|
|
||||||
if(isset($args[0]))
|
|
||||||
$filter = $args[0];
|
|
||||||
|
|
||||||
$props = props();
|
|
||||||
|
|
||||||
echo "\n";
|
|
||||||
echo "Available props:\n";
|
|
||||||
foreach($props as $k => $v)
|
|
||||||
{
|
|
||||||
if($filter && stripos($k, $filter) === false)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
echo "---------------------------------\n";
|
|
||||||
echo "$k : " . var_export($v, true) . "\n";
|
|
||||||
}
|
|
||||||
echo "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
} //namespace taskman
|
|
||||||
//}}}
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
namespace taskman;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
task('help', function($args = array())
|
||||||
|
{
|
||||||
|
$filter = '';
|
||||||
|
if(isset($args[0]))
|
||||||
|
$filter = $args[0];
|
||||||
|
|
||||||
|
$maxlen = -1;
|
||||||
|
$tasks = array();
|
||||||
|
$all = get_tasks();
|
||||||
|
foreach($all as $task)
|
||||||
|
{
|
||||||
|
if($filter && (strpos($task->getName(), $filter) === false && strpos($task->getPropOr("alias", ""), $filter) === false))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(strlen($task->getName()) > $maxlen)
|
||||||
|
$maxlen = strlen($task->getName());
|
||||||
|
|
||||||
|
$tasks[] = $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$args)
|
||||||
|
{
|
||||||
|
$help_func = $GLOBALS['TASKMAN_HELP_FUNC'];
|
||||||
|
$help_func();
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Available tasks:\n";
|
||||||
|
foreach($tasks as $task)
|
||||||
|
{
|
||||||
|
$props_string = '';
|
||||||
|
$pad = $maxlen - strlen($task->getName());
|
||||||
|
foreach($task->getProps() as $name => $value)
|
||||||
|
{
|
||||||
|
$props_string .= str_repeat(" ", $pad) .' @' . $name . ' ' . (is_string($value) ? $value : json_encode($value)) . "\n";
|
||||||
|
$pad = $maxlen + 1;
|
||||||
|
}
|
||||||
|
$props_string = rtrim($props_string);
|
||||||
|
|
||||||
|
echo "---------------------------------\n";
|
||||||
|
$file = $task->getFile();
|
||||||
|
$line = $task->getLine();
|
||||||
|
echo " " . $task->getName() . $props_string . " ($file@$line)\n";
|
||||||
|
}
|
||||||
|
echo "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
task('props', function($args = [])
|
||||||
|
{
|
||||||
|
$filter = '';
|
||||||
|
if(isset($args[0]))
|
||||||
|
$filter = $args[0];
|
||||||
|
|
||||||
|
$props = props();
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
echo "Available props:\n";
|
||||||
|
foreach($props as $k => $v)
|
||||||
|
{
|
||||||
|
if($filter && stripos($k, $filter) === false)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
echo "---------------------------------\n";
|
||||||
|
echo "$k : " . var_export($v, true) . "\n";
|
||||||
|
}
|
||||||
|
echo "\n";
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
<?php
|
||||||
|
namespace taskman;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
function get_task(string $task) : TaskmanTask
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
global $TASKMAN_TASK_ALIASES;
|
||||||
|
|
||||||
|
if(!is_scalar($task))
|
||||||
|
throw new TaskmanException("Bad task name");
|
||||||
|
|
||||||
|
if(isset($TASKMAN_TASKS[$task]))
|
||||||
|
return $TASKMAN_TASKS[$task];
|
||||||
|
|
||||||
|
if(isset($TASKMAN_TASK_ALIASES[$task]))
|
||||||
|
return $TASKMAN_TASK_ALIASES[$task];
|
||||||
|
|
||||||
|
throw new TaskmanException("Task with name/alias '{$task}' does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($name)
|
||||||
|
{
|
||||||
|
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
||||||
|
throw new TaskmanException("Property '$name' is not set");
|
||||||
|
internal\_resolve_callable_prop($name);
|
||||||
|
return $GLOBALS['TASKMAN_PROP_' . $name];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getor($name, $def)
|
||||||
|
{
|
||||||
|
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
||||||
|
return $def;
|
||||||
|
internal\_resolve_callable_prop($name);
|
||||||
|
return $GLOBALS['TASKMAN_PROP_' . $name];
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($name, $value)
|
||||||
|
{
|
||||||
|
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setor($name, $value)
|
||||||
|
{
|
||||||
|
if(!isset($GLOBALS['TASKMAN_PROP_' . $name]))
|
||||||
|
$GLOBALS['TASKMAN_PROP_' . $name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is($name)
|
||||||
|
{
|
||||||
|
return isset($GLOBALS['TASKMAN_PROP_' . $name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function del($name)
|
||||||
|
{
|
||||||
|
unset($GLOBALS['TASKMAN_PROP_' . $name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function props()
|
||||||
|
{
|
||||||
|
$props = array();
|
||||||
|
foreach($GLOBALS as $key => $value)
|
||||||
|
{
|
||||||
|
if(($idx = strpos($key, 'TASKMAN_PROP_')) === 0)
|
||||||
|
{
|
||||||
|
$name = substr($key, strlen('TASKMAN_PROP_'));
|
||||||
|
$props[$name] = get($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $props;
|
||||||
|
}
|
||||||
|
|
||||||
|
function task($name)
|
||||||
|
{
|
||||||
|
global $TASKMAN_CLOSURES;
|
||||||
|
|
||||||
|
$args = func_get_args();
|
||||||
|
$TASKMAN_CLOSURES[$name] = $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_tasks() : array
|
||||||
|
{
|
||||||
|
global $TASKMAN_TASKS;
|
||||||
|
return $TASKMAN_TASKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function current_task()
|
||||||
|
{
|
||||||
|
global $TASKMAN_CURRENT_TASK;
|
||||||
|
return $TASKMAN_CURRENT_TASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run($task, array $args = array())
|
||||||
|
{
|
||||||
|
if($task instanceof TaskmanTask)
|
||||||
|
$task_obj = $task;
|
||||||
|
else
|
||||||
|
$task_obj = get_task($task);
|
||||||
|
|
||||||
|
return $task_obj->run($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_many($tasks, $args = array())
|
||||||
|
{
|
||||||
|
foreach($tasks as $task_spec)
|
||||||
|
{
|
||||||
|
if(is_array($task_spec))
|
||||||
|
run($task_spec[0], $task_spec[1]);
|
||||||
|
else
|
||||||
|
run($task_spec, $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//the lower the level the more important the message: 0 - is the highest priority
|
||||||
|
function log(int $level, $msg)
|
||||||
|
{
|
||||||
|
global $TASKMAN_LOG_LEVEL;
|
||||||
|
|
||||||
|
if($TASKMAN_LOG_LEVEL < $level)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$logger = $GLOBALS['TASKMAN_LOGGER'];
|
||||||
|
call_user_func_array($logger, array($msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
//obsolete
|
||||||
|
function _log(string $msg, int $level = 1)
|
||||||
|
{
|
||||||
|
log($level, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: obsolete
|
||||||
|
function msg_dbg(string $msg)
|
||||||
|
{
|
||||||
|
log(2, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: obsolete
|
||||||
|
function msg(string $msg)
|
||||||
|
{
|
||||||
|
log(1, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: obsolete
|
||||||
|
function msg_sys(string $msg)
|
||||||
|
{
|
||||||
|
log(0, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _(string $str) : string
|
||||||
|
{
|
||||||
|
if(strpos($str, '%') === false)
|
||||||
|
return $str;
|
||||||
|
|
||||||
|
$str = preg_replace_callback(
|
||||||
|
'~%\(([^\)]+)\)%~',
|
||||||
|
function($m) { return get($m[1]); },
|
||||||
|
$str
|
||||||
|
);
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main($argv = array(), $help_func = null, $proc_argv = true, $read_env_vars = true)
|
||||||
|
{
|
||||||
|
$GLOBALS['TASKMAN_START_TIME'] = microtime(true);
|
||||||
|
|
||||||
|
if($help_func)
|
||||||
|
$GLOBALS['TASKMAN_HELP_FUNC'] = $help_func;
|
||||||
|
|
||||||
|
if($read_env_vars)
|
||||||
|
internal\_read_env_vars();
|
||||||
|
|
||||||
|
if($proc_argv)
|
||||||
|
internal\_process_argv($argv);
|
||||||
|
|
||||||
|
$GLOBALS['TASKMAN_SCRIPT'] = array_shift($argv);
|
||||||
|
|
||||||
|
internal\_collect_tasks();
|
||||||
|
|
||||||
|
$always_tasks = array();
|
||||||
|
$default_task = null;
|
||||||
|
foreach(get_tasks() as $task_obj)
|
||||||
|
{
|
||||||
|
if($task_obj->hasProp('always'))
|
||||||
|
array_unshift($always_tasks, $task_obj);
|
||||||
|
if($task_obj->hasProp('default'))
|
||||||
|
{
|
||||||
|
if($default_task)
|
||||||
|
throw new TaskmanException("Assigned default task '" . $default_task->getName() . "' conflicts with '" . $task_obj->getName() . "'");
|
||||||
|
else
|
||||||
|
$default_task = $task_obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($always_tasks as $always_task)
|
||||||
|
run($always_task);
|
||||||
|
|
||||||
|
if(sizeof($argv) > 0)
|
||||||
|
{
|
||||||
|
$task_str = array_shift($argv);
|
||||||
|
$tasks = internal\_parse_taskstr($task_str);
|
||||||
|
|
||||||
|
if(count($tasks) == 1 && !internal\_isset_task($tasks[0]))
|
||||||
|
{
|
||||||
|
$pattern = $tasks[0];
|
||||||
|
if($pattern[0] == '~')
|
||||||
|
{
|
||||||
|
$pattern = substr($pattern, 1, strlen($pattern) - 1);
|
||||||
|
$is_similar = true;
|
||||||
|
}
|
||||||
|
elseif(substr($pattern, -1, 1) == '~')
|
||||||
|
{
|
||||||
|
$pattern = substr($pattern, 0, strlen($pattern) - 1);
|
||||||
|
$is_similar = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$is_similar = false;
|
||||||
|
$hints = internal\_get_hints($pattern);
|
||||||
|
|
||||||
|
if($is_similar && count($hints) == 1)
|
||||||
|
$tasks = $hints;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR! Task %s not found\n", $tasks[0]);
|
||||||
|
if($hints)
|
||||||
|
{
|
||||||
|
printf("Similar tasks:\n");
|
||||||
|
foreach($hints as $hint)
|
||||||
|
printf(" %s\n", $hint);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_many($tasks, $argv);
|
||||||
|
}
|
||||||
|
else if($default_task)
|
||||||
|
run($default_task, $argv);
|
||||||
|
|
||||||
|
msg_sys("***** All done (".round(microtime(true)-$GLOBALS['TASKMAN_START_TIME'],2)." sec.) *****\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage($script_name = "<taskman-script>")
|
||||||
|
{
|
||||||
|
internal\_default_usage($script_name);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue