file = $refl->getFileName(); $this->line = $refl->getStartLine(); if(is_string($func) && $name === null && $props === null) { $this->name = self::extractName($func); $this->_parseProps($refl); } else { $this->name = $name; $this->props = $props; } $this->func = $func; } function validate() { try { foreach($this->_getBeforeDeps() as $dep_task) taskman_gettask($dep_task); foreach($this->_getDeps() as $dep_task) taskman_gettask($dep_task); foreach($this->_getAfterDeps() as $dep_task) taskman_gettask($dep_task); } catch(Exception $e) { throw new Exception("Task '{$this->name}' validation error: " . $e->getMessage()); } } function getName() { return $this->name; } function getFile() { return $this->file; } function getLine() { return $this->line; } function getFunc() { return $this->func; } function getArgs() { return $this->args; } function getAliases() { $alias = $this->getPropOr("alias", ""); if(is_array($alias)) return $alias; else if($alias && is_string($alias)) return explode(",", $alias); else return array(); } function run($args = array()) { global $TASKMAN_CURRENT_TASK; global $TASKMAN_STACK; global $TASKMAN_NO_DEPS; global $TASKMAN_START_TIME; $args_str = serialize($args); if((isset($this->has_run[$args_str]) && $this->has_run[$args_str]) || $this->is_running) return; $this->is_running = true; $this->args = $args; try { if(!$TASKMAN_NO_DEPS) { taskman_runtasks($this->_getBeforeDeps()); taskman_runtasks($this->_getDeps()); } taskman_sysmsg("************************ Running task '" . $this->getName() . "' ************************\n"); $bench = microtime(true); $TASKMAN_CURRENT_TASK = $this; $TASKMAN_STACK[] = $this; call_user_func_array($this->func, array($this->args)); array_pop($TASKMAN_STACK); if(!$TASKMAN_NO_DEPS) taskman_runtasks($this->_getAfterDeps()); taskman_sysmsg("************************* '" . $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; } catch(Exception $e) { $error_handler = $GLOBALS['TASKMAN_ERROR_HANDLER']; if($error_handler) $error_handler($e); else throw $e; } } private function _getBeforeDeps() { return $this->_collectRelatedTasks("before"); } private function _getAfterDeps() { return $this->_collectRelatedTasks("after"); } private function _collectRelatedTasks($prop_name) { $arr = array(); foreach(taskman_gettasks() 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 _getDeps() { $deps = $this->getPropOr('deps', ""); if(is_array($deps)) return $deps; else if($deps && is_string($deps)) return _taskman_parse_taskstr($deps); return array(); } 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($name) { return $this->getPropOr($name, null); } function hasProp($name) { return isset($this->props[$name]); } function getProps() { return $this->props; } } function taskman_reset() { $GLOBALS['TASKMAN_TASKS'] = array(); $GLOBALS['TASKMAN_TASK_ALIASES'] = array(); $GLOBALS['TASKMAN_CURRENT_TASK'] = null; $GLOBALS['TASKMAN_CONFIG'] = array(); _taskman_collecttasks(); } function taskman_str($str) { if(strpos($str, '%') === false) return $str; $str = preg_replace_callback( '~%\(([^\)]+)\)%~', function($m) { return taskman_prop($m[1]); }, $str ); return $str; } function taskman_run($argv = array(), $help_func = null, $proc_argv = true) { $GLOBALS['TASKMAN_START_TIME'] = microtime(true); if($help_func) $GLOBALS['TASKMAN_HELP_FUNC'] = $help_func; if($proc_argv) taskman_process_argv($argv); $GLOBALS['TASKMAN_SCRIPT'] = array_shift($argv); _taskman_collecttasks(); $always_tasks = array(); $default_task = null; foreach(taskman_gettasks() 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 = _taskman_parse_taskstr($task_str); if(count($tasks) == 1 && !taskman_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 = taskman_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); } } taskman_runtasks($tasks, $argv); } else if($default_task) $default_task->run($argv); taskman_sysmsg("************************ All done (".round(microtime(true)-$GLOBALS['TASKMAN_START_TIME'],2)." sec.)************************\n"); } function taskman_isset_task($task) { global $TASKMAN_TASKS; global $TASKMAN_TASK_ALIASES; return isset($TASKMAN_TASKS[$task]) || isset($TASKMAN_TASK_ALIASES[$task]); } function taskman_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; } function taskman_process_argv(&$argv) { global $TASKMAN_LOG_LEVEL; global $TASKMAN_BATCH; global $TASKMAN_NO_DEPS; $filtered = array(); $process_defs = false; for($i=0;$i $args) { if(isset($TASKMAN_TASKS[$name])) throw new TaskmanException("Task '$name' is already defined"); $task = null; if(is_string($args)) { $task = new TaskmanTask($args); } else 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 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 TaskmanException("Alias '$alias' is already defined for task '$name'"); $TASKMAN_TASK_ALIASES[$alias] = $task; } } foreach($TASKMAN_TASKS as $task) $task->validate(); } function _taskman_get_task_candidates() { global $TASKMAN_CLOSURES; $cands = array(); //1. get tasks defined as regular functions $funcs = get_defined_functions(); foreach($funcs['user'] as $func) { $name = TaskmanTask::extractName($func); if(!$name) continue; $cands[$name] = $func; } //2. 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 taskman_gettasks() { global $TASKMAN_TASKS; return $TASKMAN_TASKS; } function taskman_gettask($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 _taskman_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 taskman_runtask($task, $args = array()) { if($task instanceof TaskmanTask) $task_obj = $task; else $task_obj = taskman_gettask($task); $task_obj->run($args); } function taskman_runtasks($tasks, $args = array()) { foreach($tasks as $task_spec) { if(is_array($task_spec)) taskman_runtask($task_spec[0], $task_spec[1]); else taskman_runtask($task_spec, $args); } } function taskman_current_task() { global $TASKMAN_CURRENT_TASK; return $TASKMAN_CURRENT_TASK; } function taskman_shell_get($cmd, $as_string = true) { exec($cmd, $out, $code); if($code !== 0) throw new Exception("Error($code) executing shell cmd '$cmd'"); return $as_string ? implode("", $out) : $out; } function taskman_shell_ensure($cmd, &$out=null) { taskman_shell($cmd, $ret, $out); if($ret != 0) throw new TaskmanException("Shell execution error(exit code $ret)"); } function taskman_shell($cmd, &$ret=null, &$out=null) { taskman_msg(" shell: $cmd\n"); taskman_msg(" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); if(func_num_args() < 3) system($cmd, $ret); else _taskman_execute_proc_cmd($cmd, $ret, $out); } function _taskman_execute_proc_cmd($cmd, &$ret, &$out) { //TODO: do we really need to redirect error stream? $proc = popen("$cmd 2>&1", 'r'); $log = ''; //TODO: how can this be? if(is_string($proc)) { $log = $proc; taskman_log($log, 1); } else { while($logline = fgets($proc)) { $log .= $logline; taskman_log($logline, 1); } } $out = explode("\n", $log); $ret = pclose($proc); } function taskman_dmsg($msg) { taskman_log($msg, 2); } function taskman_msg($msg) { taskman_log($msg, 1); } function taskman_sysmsg($msg) { taskman_log($msg, 0); } function taskman_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 _taskman_default_logger($msg) { echo $msg; } function _taskman_resolve_callable_prop($name) { $prop = $GLOBALS['TASKMAN_PROP_' . $name]; if(!($prop instanceof \Closure)) return; $value = $prop(); $GLOBALS['TASKMAN_PROP_' . $name] = $value; } function taskman_prop($name) { if(!isset($GLOBALS['TASKMAN_PROP_' . $name])) throw new TaskmanException("Property '$name' is not set"); _taskman_resolve_callable_prop($name); return $GLOBALS['TASKMAN_PROP_' . $name]; } function taskman_propor($name, $def) { if(!isset($GLOBALS['TASKMAN_PROP_' . $name])) return $def; _taskman_resolve_callable_prop($name); return $GLOBALS['TASKMAN_PROP_' . $name]; } function taskman_propset($name, $value) { $GLOBALS['TASKMAN_PROP_' . $name] = $value; } function taskman_propadd($name, $value) { if(!isset($GLOBALS['TASKMAN_PROP_' . $name])) $GLOBALS['TASKMAN_PROP_' . $name] = ''; _taskman_resolve_callable_prop($name); $GLOBALS['TASKMAN_PROP_' . $name] .= $value; } function taskman_propsetor($name, $value) { if(!isset($GLOBALS['TASKMAN_PROP_' . $name])) $GLOBALS['TASKMAN_PROP_' . $name] = $value; } function taskman_propunset($name) { unset($GLOBALS['TASKMAN_PROP_' . $name]); } function taskman_getprops() { $props = array(); foreach($GLOBALS as $key => $value) { if(($idx = strpos($key, 'TASKMAN_PROP_')) === 0) { $name = substr($key, strlen('TASKMAN_PROP_')); $props[$name] = taskman_prop($name); } } return $props; } function taskman_isprop($name) { return isset($GLOBALS['TASKMAN_PROP_' . $name]); } function taskman_default_usage($script_name = "") { echo "\nUsage:\n $script_name [OPTIONS] [,,..] [-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"; } /** * @desc Shows this help */ function task_help($args = array()) { $filter = ''; if(isset($args[0])) $filter = $args[0]; $maxlen = -1; $tasks = array(); $all = taskman_gettasks(); 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"; } /** * @desc Encodes tasks as json array */ function task_json() { $json = array(); foreach(taskman_gettasks() as $task) { $json[$task->getName()] = $task->getProps(); } echo json_encode($json); } } //namespace global namespace taskman { function get($name) { return \taskman_prop($name); } function getor($name, $def) { return \taskman_propor($name, $def); } function set($name, $value) { \taskman_propset($name, $value); } function setor($name, $value) { \taskman_propsetor($name, $value); } function add($name, $value) { \taskman_propadd($name, $value); } function is($name) { return \taskman_isprop($name); } function del($name) { \taskman_propunset($name); } function props() { return \taskman_getprops(); } function task($name) { global $TASKMAN_CLOSURES; $args = func_get_args(); $TASKMAN_CLOSURES[$name] = $args; } function run($task, array $args = array()) { \taskman_runtask($task, $args); } function msg_dbg($msg) { \taskman_dmsg($msg); } function msg($msg) { \taskman_msg($msg); } function msg_sys($msg) { \taskman_sysmsg($msg); } function shell($cmd, &$out=null) { \taskman_shell_ensure($cmd, $out); } function shell_try($cmd, &$ret=null, &$out=null) { \taskman_shell($cmd, $ret, $out); } function _($str) { return \taskman_str($str); } function main($argv = array(), $help_func = null, $proc_argv = true) { \taskman_run($argv, $help_func, $proc_argv); } function usage($script_name = "") { \taskman_default_usage($script_name); } } //namespace taskman //}}}