Compare commits

..

No commits in common. "master" and "v2.1.1" have entirely different histories.

9 changed files with 513 additions and 1526 deletions

View File

@ -1,29 +0,0 @@
name: Publish PHP Package
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get tag name
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: zip and send
run: |
ls -la
apt-get update -y
apt-get install -y zip
cd ../
zip -r ${{ gitea.event.repository.name }}.zip ${{ gitea.event.repository.name }} -x '*.git*'
curl -v \
--user composer-pbl:${{ secrets.COMPOSER_PSWD }} \
--upload-file ${{ gitea.event.repository.name }}.zip \
https://git.bit5.ru/api/packages/bit/composer?version=${{ env.TAG }}

1
.gitignore vendored
View File

@ -1 +0,0 @@
tags

View File

@ -1,2 +0,0 @@
## v2.3.0
- Adding support for enviroment variables prefixed with **TASKMAN_SET_** being set as props. For example: **TASKMAN_SET_FOO=1** results in property **FOO=1**

235
README.md
View File

@ -1,235 +0,0 @@
## Taskman
Taskman is a simple library which allows to conveniently execute build 'tasks' written in PHP from the shell. Tasks can have dependencies on other tasks.
Taskman is inspired by Make, Capistrano, Ant, Maven and similar build tools.
The central unit of execution is 'task'. The main difference from the plain old function is the fact 'task' is executed **only once** no matter what.
### Examples
#### Hello world
Here's a simple example of a task:
```php
<?php
namespace taskman;
task('hello', function()
{
echo "Hello world!\n";
});
```
Executing it from the shell yields:
```
./gamectl hello
***** task 'hello' start *****
Hello world!
***** task 'hello' done(0/0.27 sec.) *****
***** All done (0.27 sec.) *****
```
#### Real world example
```php
<?php
namespace taskman;
task('ultimate_build_run',
[
'default' => true,
'alias' => 'urun',
'deps' => ['autogen', 'pack_configs', 'ensure_unity_player_settings', 'unity_defines']
], function() {});
```
### Get help and list all tasks
You can list all tasks and their definition locations using **help** task (yes, it's a task as well):
```
./gamectl help
***** task 'help' start *****
Usage:
gamectl [OPTIONS] <task-name1>[,<task-name2>,..] [-D PROP1=value [-D PROP2]]
Available options:
-c specify PHP script to be included (handy for setting props,config options,etc)
-V be super verbose
-q be quite, only system messages
-b batch mode: be super quite, don't even output any system messages
-- pass all options verbatim after this mark
Available tasks:
---------------------------------
apk_install_to_device (/Users/ps/dev/skeletor/gamectl.d/client.inc.php@58)
---------------------------------
apk_run_on_device (/Users/ps/dev/skeletor/gamectl.d/client.inc.php@67)
---------------------------------
autogen @deps ["unity_defines","cs_autogen","php_autogen"] (/Users/ps/dev/skeletor/gamectl.d/autogen.inc.php@6)
---------------------------------
bhl_autogen @deps ["bhl_make_upm_package"] (/Users/ps/dev/skeletor/gamectl.d/bhl.inc.php@19)
***** task 'help' done(0/0.01 sec.) *****
***** All done (0.01 sec.) *****
```
You can filter tasks you want to get help for by a partial match as follows:
```
./gamectl help name
```
### Tasks documentation
#### Task declaration
Task must be declared using library **task** function as follows:
```php
<?php
namespace taskman;
task('name', function() {});
```
The task above now can be invoked from the shell as follows:
```./gamectl name```
#### Task aliases
Task may have an alias for less typing in the shell. To specify an alias one should put it as an **alias** property of a task declaration:
```php
<?php
namespace taskman;
task('name', ['alias' => 'n'],
function() {});
```
You can invoke the task by the alias as follows:
```./gamectl n```
#### Task dependencies
Task may have an dependencies on other tasks. To specify all dependencies one should list them in **deps** section of a task declaration:
```php
<?php
namespace taskman;
task('c', ['deps' => ['a', 'b']],
function() {});
```
All dependencies are executed before running the specified task. Running the task above yields something as follows:
```
./gamectl c
***** task 'c' start *****
***** -task 'a' start *****
***** -task 'a' done(0/0.18 sec.) *****
***** -task 'b' start *****
***** -task 'b' done(0/0.18 sec.) *****
***** task 'c' done(0/0.18 sec.) *****
***** All done (0.18 sec.) *****
```
#### Always executed tasks
Sometimes it's convenient to define tasks which should be executed every time without explicit invocation. For example for setting the up the default environment, properties, etc. To achieve that one should specify the **always** property for a task:
```php
<?php
namespace taskman;
task('setup_env', ['always' => true],
function() {});
```
### Build properties
It's possible to specify miscellaneous build properties to setup a proper build environment conditions. There's a set of routines provided by taskman for properties manipulation.
#### Setting a property
Use **set** built-in function:
```php
<?php
namespace taskman;
task('setup_env', ['always' => true],
function() {
set("IOS_APP_ID", "4242jfhFD");
});
```
#### Setting a property only if it's not already set
Use **setor** built-in function. Property will be set only if it's not set some where before. It's a convenient pattern to have a default set of properties which can be overriden by the environment properties included from an external script.
```php
<?php
namespace taskman;
task('setup_env', ['always' => true],
function() {
setor("IOS_APP_ID", "4242jfhFD");
});
```
#### Getting a property
Use **get** built-in function:
```php
<?php
namespace taskman;
task('build_ios',
function() {
shell("xcode build app " . get("IOS_APP_ID"));
});
```
#### Getting a property or some default value
Use **getor** built-in function:
```php
<?php
namespace taskman;
task('build_ios',
function() {
shell("xcode build app " . getor("IOS_APP_ID", "3232232"));
});
```
#### Listing all build properties
To list all defined build properties one should use the **props** task:
```
./gamectl props
***** task 'props' start *****
Available props:
---------------------------------
UNITY_ASSETS_DIR : '/Users/ps/dev/skeletor//unity/Assets/'
---------------------------------
GAME_COMPANY_NAME : 'Black Hole Light'
***** task 'props' done(0.11/0.11 sec.) *****
***** All done (0.11 sec.) *****
```
You can filter properties you want to get information about by a partial match as follows:
```
./gamectl props FOO
```

View File

@ -1,415 +0,0 @@
<?php
namespace taskman\artefact;
use Exception;
class TaskmanArtefact
{
private \taskman\TaskmanTask $task;
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_affected = array();
function __construct(\taskman\TaskmanTask $task, string $path, array $sources_spec)
{
$this->task = $task;
$this->path = $path;
$this->sources_spec = $sources_spec;
}
function getTask() : \taskman\TaskmanTask
{
return $this->task;
}
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];
\taskman\log(2, "Task '{$this->task->getName()}' artefact '{$this->path}' resolving sources at $idx\n");
$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];
\taskman\log(2, "Task '{$this->task->getName()}' artefact '{$this->path}' resolving changed sources at $idx\n");
$changed = $fn();
$this->sources_changed[$idx] = $changed;
return $changed;
}
return array();
}
//obsolete
function isSourcesNewer(int $idx) : bool
{
return $this->isSourcesAffected($idx);
}
function isSourcesAffected(int $idx) : bool
{
return isset($this->sources_affected[$idx]) && $this->sources_affected[$idx];
}
function getAffectedSourcesIndices() : array
{
return array_keys($this->sources_affected);
}
function isStale() : bool
{
return count($this->sources_affected) > 0;
}
function initSources()
{
$file_changes = $this->task->getFileChanges();
$all_src_specs = $this->getSourcesSpec();
//let's process a convenience special case
if(count($all_src_specs) > 0 && !is_array($all_src_specs[0]))
$all_src_specs = [$all_src_specs];
foreach($all_src_specs 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, $src_spec[1]);
$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 checkAffectedSources() : bool
{
$file_changes = $this->task->getFileChanges();
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_affected[$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 __toString() : string
{
return var_export($this->dir2files, true);
}
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);
}

View File

@ -1,292 +0,0 @@
<?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)
{
$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(array &$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 == '-l')
{
if(!isset($argv[$i+1]))
throw new \taskman\TaskmanException("Log level is missing");
$TASKMAN_LOG_LEVEL = intval($argv[$i+1]);
++$i;
}
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;
}
function _extract_lines_from_file(string $file_path) : array
{
$lines = array();
$fh = fopen($file_path, 'r+');
if($fh === false)
return $lines;
try
{
if(flock($fh, LOCK_EX))
{
while(($line = fgets($fh)) !== false)
$lines[] = $line;
ftruncate($fh, 0);
flock($fh, LOCK_UN);
}
}
finally
{
fclose($fh);
}
return $lines;
}

View File

@ -1,6 +1,5 @@
<?php
namespace taskman;
use Exception;
namespace {
$GLOBALS['TASKMAN_TASKS'] = array();
$GLOBALS['TASKMAN_CLOSURES'] = array();
@ -10,48 +9,46 @@ $GLOBALS['TASKMAN_LOG_LEVEL'] = 1; //0 - important, 1 - normal, 2 - debug
$GLOBALS['TASKMAN_NO_DEPS'] = false;
$GLOBALS['TASKMAN_SCRIPT'] = '';
$GLOBALS['TASKMAN_CURRENT_TASK'] = null;
$GLOBALS['TASKMAN_HELP_FUNC'] = '\taskman\internal\_default_usage';
$GLOBALS['TASKMAN_LOGGER'] = '\taskman\internal\_default_logger';
$GLOBALS['TASKMAN_HELP_FUNC'] = '_taskman_default_usage';
$GLOBALS['TASKMAN_LOGGER'] = '_taskman_default_logger';
$GLOBALS['TASKMAN_ERROR_HANDLER'] = null;
$GLOBALS['TASKMAN_START_TIME'] = 0;
$GLOBALS['TASKMAN_FILES_CHANGES'] = null;
include_once(__DIR__ . '/internal.inc.php');
include_once(__DIR__ . '/util.inc.php');
include_once(__DIR__ . '/tasks.inc.php');
include_once(__DIR__ . '/artefact.inc.php');
function _taskman_default_logger($msg)
{
echo $msg;
}
class TaskmanException extends Exception
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 {
class TaskmanException extends \Exception
{}
class TaskmanTask
{
private \Closure $func;
private string $file;
private int $line;
private string $name;
//initialized lazily
private ?array $_aliases = null;
private $func;
private $name;
private $file;
private $line;
private $props = array();
private $is_running = false;
private $has_run = array();
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)
function __construct(\Closure $func, $name, $props = array())
{
$refl = new \ReflectionFunction($func);
$this->file = $refl->getFileName();
@ -60,43 +57,53 @@ class TaskmanTask
$this->func = $func;
$this->name = $name;
$this->props = $props;
$this->file_changes = $file_changes;
}
function getName() : string
function validate()
{
try
{
foreach($this->_getBeforeDeps() as $dep_task)
get_task($dep_task);
foreach($this->_getDeps() as $dep_task)
get_task($dep_task);
foreach($this->_getAfterDeps() as $dep_task)
get_task($dep_task);
}
catch(Exception $e)
{
throw new Exception("Task '{$this->name}' validation error: " . $e->getMessage());
}
}
function getName()
{
return $this->name;
}
function getFile() : string
function getFile()
{
return $this->file;
}
function getLine() : int
function getLine()
{
return $this->line;
}
function getFunc() : \Closure
function getFunc()
{
return $this->func;
}
function getArgs() : array
function getArgs()
{
return $this->args;
}
function getAliases() : array
{
if($this->_aliases === null)
$this->_aliases = $this->_parseAliases();
return $this->_aliases;
}
function _parseAliases() : array
function getAliases()
{
$alias = $this->getPropOr("alias", "");
if(is_array($alias))
@ -107,50 +114,6 @@ class TaskmanTask
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($this, $dst, $src_spec);
}
return $artefacts;
}
function getArtefact(int $idx) : artefact\TaskmanArtefact
{
return $this->getArtefacts()[$idx];
}
function getFileChanges() : ?TaskmanFileChanges
{
return $this->file_changes;
}
function isIncrementalBuild() : bool
{
return $this->file_changes != null;
}
function run($args = array())
{
global $TASKMAN_CURRENT_TASK;
@ -164,43 +127,31 @@ class TaskmanTask
$this->is_running)
return;
$task_result = null;
$this->is_running = true;
$this->args = $args;
try
{
if($this->getArtefacts() && !$this->_checkIfArtefactsStale())
{
$this->has_run[$args_str] = true;
return;
}
$this->is_running = true;
$this->args = $args;
$TASKMAN_STACK[] = $this;
$level = count($TASKMAN_STACK)-1;
log(0, "***** ".str_repeat('-', $level)."task '" . $this->getName() . "' start *****\n");
if(!$TASKMAN_NO_DEPS)
{
run_many($this->before_deps);
run_many($this->getDeps());
run_many($this->_getBeforeDeps());
run_many($this->_getDeps());
}
$TASKMAN_CURRENT_TASK = $this;
msg_sys("************************ Running task '" . $this->getName() . "' ************************\n");
$bench = microtime(true);
$task_result = call_user_func_array($this->func, array($this->args, $this));
$TASKMAN_CURRENT_TASK = $this;
$TASKMAN_STACK[] = $this;
call_user_func_array($this->func, array($this->args));
array_pop($TASKMAN_STACK);
if(!$TASKMAN_NO_DEPS)
run_many($this->after_deps);
run_many($this->_getAfterDeps());
log(0, "***** ".str_repeat('-', $level)."task '" . $this->getName() . "' done(" .
round(microtime(true)-$bench,2) . '/' .round(microtime(true)-$TASKMAN_START_TIME,2) . " sec.) *****\n");
msg_sys("************************* '" . $this->getName() . "' done (" .
round(microtime(true)-$bench,2) . '/' .round(microtime(true)-$TASKMAN_START_TIME,2) . " sec.)*************************\n");
$this->has_run[$args_str] = true;
$this->is_running = false;
@ -213,251 +164,425 @@ class TaskmanTask
else
throw $e;
}
return $task_result;
}
function addBeforeDep($task)
private function _getBeforeDeps()
{
$this->before_deps[] = $task;
return $this->_collectRelatedTasks("before");
}
function addAfterDep($task)
private function _getAfterDeps()
{
$this->after_deps[] = $task;
return $this->_collectRelatedTasks("after");
}
function getDeps() : array
private function _collectRelatedTasks($prop_name)
{
if($this->deps === null)
$this->deps = $this->_parseDeps();
return $this->deps;
$arr = array();
foreach(get_tasks() as $task_obj)
{
if($this->getName() == $task_obj->getName())
continue;
$value = $task_obj->getPropOr($prop_name, "");
if($value == $this->getName() || in_array($value, $this->getAliases()))
$arr[] = $task_obj;
}
return $arr;
}
private function _parseDeps() : array
private function _getDeps()
{
$deps = $this->getPropOr("deps", "");
$deps = $this->getPropOr('deps', "");
if(is_array($deps))
return $deps;
else if($deps && is_string($deps))
return internal\_parse_taskstr($deps);
return _parse_taskstr($deps);
return array();
}
function getPropOr(string $name, mixed $def) : mixed
private function _parseProps(ReflectionFunction $refl)
{
if(preg_match_all('~@(\S+)([^\n]+)?\n~', $refl->getDocComment(), $matches))
{
foreach($matches[1] as $idx => $match)
$this->props[$match] = trim($matches[2][$idx]);
}
}
static function extractName($func)
{
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;
}
function getProp(string $name) : mixed
function getProp($name)
{
return $this->getPropOr($name, null);
}
function hasProp(string $name) : bool
function hasProp($name)
{
return isset($this->props[$name]);
}
function getProps() : array
function getProps()
{
return $this->props;
}
private function _checkIfArtefactsStale() : bool
{
$stale_found = false;
foreach($this->getArtefacts() as $artefact)
{
$artefact->initSources();
if(!$stale_found && $artefact->checkAffectedSources())
$stale_found = true;
if($artefact->isStale())
{
log(0, "Task '{$this->name}' artefact '{$artefact->getPath()}' (sources at ".implode(',', $artefact->getAffectedSourcesIndices()).") is stale\n");
}
}
return $stale_found;
}
function findAnyStaleArtefact() : ?artefact\TaskmanArtefact
{
foreach($this->getArtefacts() as $artefact)
{
if($artefact->isStale())
return $artefact;
}
return null;
}
}
class TaskmanFileChanges
function _collect_tasks()
{
const Changed = 1;
const Created = 2;
const Renamed = 3;
const Deleted = 4;
global $TASKMAN_TASKS;
global $TASKMAN_TASK_ALIASES;
//file => status
private $changed = array();
$TASKMAN_TASKS = array();
$TASKMAN_TASK_ALIASES = array();
static function parse(string $json_or_file)
$cands = _get_task_candidates();
foreach($cands as $name => $args)
{
if($json_or_file[0] == '[')
$json = $json_or_file;
else
if(isset($TASKMAN_TASKS[$name]))
throw new TaskmanException("Task '$name' is already defined");
if(is_array($args))
{
$lines = internal\_extract_lines_from_file($json_or_file);
$json = '[' . implode(',', $lines) . ']';
}
$decoded = json_decode($json, true);
if(!is_array($decoded))
throw new Exception('Bad json: ' . $json);
$changed = array();
$base_dir = dirname($_SERVER['PHP_SELF']);
//for debug
//var_dump($decoded);
foreach($decoded as $items)
{
if(count($items) < 2)
throw new Exception('Bad entry');
list($status, $file) = $items;
if(strlen($file) > 0)
$props = array();
if(sizeof($args) > 2)
{
if(DIRECTORY_SEPARATOR == '/')
{
if($file[0] != '/')
$file = $base_dir . DIRECTORY_SEPARATOR . $file;
}
else if(strlen($file) > 1 && $file[1] != ':')
$file = $base_dir . DIRECTORY_SEPARATOR . $file;
$props = $args[1];
$func = $args[2];
}
$file = artefact\normalize_path($file);
if($status == 'Changed')
$changed[$file] = self::Changed;
else if($status == 'Created')
{
//let's status for files which were modified and then created
if(isset($changed[$file]) && $changed[$file] !== self::Changed)
$changed[$file] = self::Created;
}
else if($status == 'Renamed')
$changed[$file] = self::Renamed;
else if($status == 'Deleted')
$changed[$file] = self::Deleted;
else
throw new Exception('Unknown status: ' . $status);
$func = $args[1];
$task = new TaskmanTask($func, $name, $props);
}
else
throw new Exception("Task '$name' is invalid");
return new TaskmanFileChanges($changed);
}
$TASKMAN_TASKS[$name] = $task;
//NOTE: maps: file => status
function __construct(array $changed)
{
$this->changed = $changed;
}
function getStatus(string $file) : ?int
{
return isset($this->changed[$file]) ? $this->changed[$file] : null;
}
function isChanged(string $file) : bool
{
return $this->getStatus($file) == self::Changed;
}
function isCreated(string $file) : bool
{
return $this->getStatus($file) == self::Created;
}
function isDeleted(string $file) : bool
{
return $this->getStatus($file) == self::Deleted;
}
function isRenamed(string $file) : bool
{
return $this->getStatus($file) == self::Renamed;
}
function isEmpty() : bool
{
return count($this->changed) == 0;
}
function matchDirectory(string $dir, array $extensions = array()) : array
{
$dir = rtrim($dir, '/\\');
$dir .= DIRECTORY_SEPARATOR;
$filtered = [];
foreach($this->changed as $path => $_)
if(self::matchDirAndExtension($path, $dir, $extensions))
$filtered[] = $path;
return $filtered;
}
static function matchDirAndExtension(string $path, string $dir, array $extensions) : bool
{
if(strpos($path, $dir) !== 0)
return false;
foreach($extensions as $ext)
if(!str_ends_with($path, $ext))
return false;
return true;
}
function matchFiles(iterable $files) : array
{
$filtered = [];
foreach($files as $file)
foreach($task->getAliases() as $alias)
{
if(isset($this->changed[$file]))
$filtered[] = $file;
if(isset($TASKMAN_TASKS[$alias]) || isset($TASKMAN_TASK_ALIASES[$alias]))
throw new TaskmanException("Alias '$alias' is already defined for task '$name'");
$TASKMAN_TASK_ALIASES[$alias] = $task;
}
return $filtered;
}
foreach($TASKMAN_TASKS as $task)
$task->validate();
}
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 get_task($task)
{
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;
$args = func_get_args();
$TASKMAN_CLOSURES[$name] = $args;
}
function get_tasks()
{
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);
$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 main(
array $argv = array(),
callable $help_func = null,
bool $proc_argv = true,
bool $read_env_vars = true
)
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 _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);
$def_value = substr($v, $eq_pos+1);
if(in_array(strtolower($def_value), array('yes', 'true'), true/*strict*/))
$def_value = true;
else if(in_array(strtolower($def_value), array('no', 'false'), true/*strict*/))
$def_value = false;
}
else
{
$def_name = $v;
$def_value = 1;
}
msg_sys("Setting prop $def_name=" . (is_bool($def_value) ? ($def_value ? 'true' : 'false') : $def_value) . "\n");
set($def_name, $def_value);
$process_defs = false;
}
else
$filtered[] = $v;
}
$argv = $filtered;
}
function main($argv = array(), $help_func = null, $proc_argv = 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);
_process_argv($argv);
$GLOBALS['TASKMAN_SCRIPT'] = array_shift($argv);
internal\_collect_tasks();
_collect_tasks();
$always_tasks = array();
$default_task = null;
@ -475,14 +600,14 @@ function main(
}
foreach($always_tasks as $always_task)
run($always_task);
$always_task->run(array());
if(sizeof($argv) > 0)
{
$task_str = array_shift($argv);
$tasks = internal\_parse_taskstr($task_str);
$tasks = _parse_taskstr($task_str);
if(count($tasks) == 1 && !internal\_isset_task($tasks[0]))
if(count($tasks) == 1 && !_isset_task($tasks[0]))
{
$pattern = $tasks[0];
if($pattern[0] == '~')
@ -497,25 +622,104 @@ function main(
}
else
$is_similar = false;
$hints = internal\_get_hints($pattern);
$hints = _get_hints($pattern);
if($is_similar && count($hints) == 1)
$tasks = $hints;
else
{
$similars = '';
printf("ERROR! Task %s not found\n", $tasks[0]);
if($hints)
$similars .= "\nSimilar tasks: " . implode(', ', $hints) . ".";
throw new Exception("Task '{$tasks[0]}' not found. $similars");
{
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);
$default_task->run($argv);
log(0, "***** All done (".round(microtime(true)-$GLOBALS['TASKMAN_START_TIME'],2)." sec.) *****\n");
msg_sys("************************ All done (".round(microtime(true)-$GLOBALS['TASKMAN_START_TIME'],2)." sec.)************************\n");
}
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))
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";
});
} //namespace taskman
//}}}

View File

@ -1,73 +0,0 @@
<?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";
});

View File

@ -1,170 +0,0 @@
<?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;
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);
}
}
//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 usage($script_name = "<taskman-script>")
{
internal\_default_usage($script_name);
}