990 lines
23 KiB
PHP
990 lines
23 KiB
PHP
|
<?php
|
||
|
|
||
|
include_once(__DIR__ . "/../utils/json.inc.php");
|
||
|
include_once(__DIR__ . "/../utils/jzon.inc.php");
|
||
|
|
||
|
function is_release()
|
||
|
{
|
||
|
return taskman_prop("GAME_IS_DEV") == 0;
|
||
|
}
|
||
|
|
||
|
function is_dev()
|
||
|
{
|
||
|
return !is_release();
|
||
|
}
|
||
|
|
||
|
function is_dev_no_env()
|
||
|
{
|
||
|
return is_dev() && !getenv("GAME_ENV");
|
||
|
}
|
||
|
|
||
|
function is_win()
|
||
|
{
|
||
|
return !(DIRECTORY_SEPARATOR == '/');
|
||
|
}
|
||
|
|
||
|
function is_linux()
|
||
|
{
|
||
|
return PHP_OS == 'Linux';
|
||
|
}
|
||
|
|
||
|
function make_tmp_file_name($file_name)
|
||
|
{
|
||
|
$meta = stream_get_meta_data(tmpfile());
|
||
|
if(!isset($meta['uri']))
|
||
|
throw new Exception("Could not get temp directory name");
|
||
|
$tmp_dir = dirname($meta['uri']);
|
||
|
$tmp_file = "$tmp_dir/$file_name";
|
||
|
return $tmp_file;
|
||
|
}
|
||
|
|
||
|
function get_tmp_build_path($file)
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
|
||
|
$name = str_replace(":", "-", str_replace("\\", "-", str_replace("/", "-", normalize_path($file))));
|
||
|
$name = ltrim($name, "-");
|
||
|
return normalize_path("$GAME_ROOT/build/tmp/$name");
|
||
|
}
|
||
|
|
||
|
function find_recursive($dir, $ext)
|
||
|
{
|
||
|
if(is_win())
|
||
|
$cmd = "dir /s /b " . normalize_path("$dir/*$ext");
|
||
|
else
|
||
|
$cmd = "find " . normalize_path($dir, true) . " -name '*$ext' -print";
|
||
|
exec($cmd, $out);
|
||
|
return $out;
|
||
|
}
|
||
|
|
||
|
function scan_files_rec(array $dirs, array $only_extensions = array(), $mode = 1)
|
||
|
{
|
||
|
$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($path, $unix=null/*null means try to guess*/)
|
||
|
{
|
||
|
if(is_null($unix))
|
||
|
$unix = !is_win();
|
||
|
|
||
|
$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;
|
||
|
}
|
||
|
$res = implode($unix ? '/' : '\\', $absolutes);
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
function normalize_sed_path($path)
|
||
|
{
|
||
|
$path = normalize_path($path);
|
||
|
if(is_win())
|
||
|
$path = str_replace('\\', '\\\\', $path);
|
||
|
return $path;
|
||
|
}
|
||
|
|
||
|
//NOTE: Escaping multibyte unicode chars as \uXXXX impacts decoding performance drastically
|
||
|
//so we unescape them while encoding json
|
||
|
function json_encode_unescaped($arr)
|
||
|
{
|
||
|
array_walk_recursive($arr, function (&$item, $key) { if (is_string($item)) $item = mb_encode_numericentity($item, array (0x80, 0xffff, 0, 0xffff), 'UTF-8'); });
|
||
|
return mb_decode_numericentity(json_encode($arr), array (0x80, 0xffff, 0, 0xffff), 'UTF-8');
|
||
|
}
|
||
|
|
||
|
function json_make_pretty($json)
|
||
|
{
|
||
|
return prettyJSON($json);
|
||
|
}
|
||
|
|
||
|
function need_to_regen($file, array $deps, $debug = false)
|
||
|
{
|
||
|
if(!is_file($file))
|
||
|
{
|
||
|
if($debug)
|
||
|
echo "! $file\n";
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$fmtime = filemtime($file);
|
||
|
foreach($deps as $dep)
|
||
|
{
|
||
|
if(is_file($dep) && (filemtime($dep) > $fmtime))
|
||
|
{
|
||
|
if($debug)
|
||
|
echo "$dep > $file\n";
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//force mode: 0 - none, 1 - force write always, 2 - write only if content differs
|
||
|
function gen_file($tpl_file, $result_file, $deps = array(), $force = 0, $perms = 0640)
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
|
||
|
$deps[] = $tpl_file;
|
||
|
if($force > 0 || need_to_regen($result_file, $deps))
|
||
|
{
|
||
|
$txt = file_get_contents($tpl_file);
|
||
|
if($txt === false)
|
||
|
throw new Exception("Bad template settings file $tpl_file");
|
||
|
|
||
|
//replacing %(FOO)% alike entries with taskman config values
|
||
|
$out = taskman_str($txt);
|
||
|
|
||
|
if($force == 2 && is_file($result_file))
|
||
|
{
|
||
|
$prev = file_get_contents($result_file);
|
||
|
if($prev === false)
|
||
|
throw new Exception("Could not read file $result_file");
|
||
|
//if contents is similar no need to write it
|
||
|
if(strcmp($prev, $out) === 0)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ensure_write($result_file, $out);
|
||
|
chmod($result_file, $perms);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ensure_read($file)
|
||
|
{
|
||
|
$c = file_get_contents($file);
|
||
|
if($c === false)
|
||
|
throw new Exception("Could not read file '$file'");
|
||
|
return $c;
|
||
|
}
|
||
|
|
||
|
function ensure_write($dst, $txt, $dir_perms = 0777, $flags = 0)
|
||
|
{
|
||
|
$dir = dirname($dst);
|
||
|
if(!is_dir($dir))
|
||
|
mkdir($dir, $dir_perms, true);
|
||
|
|
||
|
taskman_dmsg("> $dst ...\n");
|
||
|
if(file_put_contents($dst, $txt, $flags) === false)
|
||
|
throw new Exception("Could not write to '$dst'");
|
||
|
}
|
||
|
|
||
|
function ensure_write_if_differs($dst, $txt, $dir_perms = 0777, $flags = 0)
|
||
|
{
|
||
|
if(is_file($dst) && file_get_contents($dst) == $txt)
|
||
|
return;
|
||
|
|
||
|
ensure_write($dst, $txt, $dir_perms, $flags);
|
||
|
}
|
||
|
|
||
|
function ensure_copy($src, $dst, $dir_perms = 0777, $excludes = array())
|
||
|
{
|
||
|
recurse_copy($src, $dst, $dir_perms, false, false, $excludes);
|
||
|
}
|
||
|
|
||
|
function ensure_sync($src, $dst, $dir_perms = 0777, $excludes = array())
|
||
|
{
|
||
|
recurse_copy($src, $dst, $dir_perms, false, true, $excludes);
|
||
|
}
|
||
|
|
||
|
function ensure_duplicate($src, $dst, $dir_perms = 0777)
|
||
|
{
|
||
|
recurse_copy($src, $dst, $dir_perms, true);
|
||
|
}
|
||
|
|
||
|
function recurse_copy($src, $dst, $dir_perms = 0777, $sys_copy = false, $mtime_check = false, $excludes = array())
|
||
|
{
|
||
|
taskman_dmsg("copying $src => $dst ...\n");
|
||
|
|
||
|
if(!is_file($src) && !is_dir($src))
|
||
|
throw new Exception("Bad file or dir '$src'");
|
||
|
|
||
|
foreach($excludes as $exclude_pattern)
|
||
|
{
|
||
|
if(preg_match("~$exclude_pattern~", $src))
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!is_dir($src))
|
||
|
{
|
||
|
_ensure_copy_file($src, $dst, $sys_copy, $mtime_check);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$dir = opendir($src);
|
||
|
ensure_mkdir($dst, $dir_perms);
|
||
|
while(false !== ($file = readdir($dir)))
|
||
|
{
|
||
|
if(($file != '.' ) && ($file != '..'))
|
||
|
{
|
||
|
if(is_dir($src . '/' . $file))
|
||
|
recurse_copy($src . '/' . $file, $dst . '/' . $file, $dir_perms, $sys_copy, $mtime_check, $excludes);
|
||
|
else
|
||
|
{
|
||
|
$excluded = false;
|
||
|
foreach($excludes as $exclude_pattern)
|
||
|
{
|
||
|
$excluded = $excluded || (bool)preg_match("~$exclude_pattern~", $src . '/' . $file);
|
||
|
}
|
||
|
|
||
|
if($excluded)
|
||
|
continue;
|
||
|
|
||
|
_ensure_copy_file($src . '/' . $file, $dst . '/' . $file, $sys_copy, $mtime_check);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
closedir($dir);
|
||
|
}
|
||
|
|
||
|
function _ensure_copy_file($src, $dst, $sys_copy = false, $mtime_check = false)
|
||
|
{
|
||
|
if($mtime_check && file_exists($dst) && filemtime($src) <= filemtime($dst))
|
||
|
return;
|
||
|
|
||
|
taskman_dmsg("$src => $dst\n");
|
||
|
ensure_mkdir(dirname($dst));
|
||
|
|
||
|
if($sys_copy)
|
||
|
taskman_shell_ensure("cp -a $src $dst");
|
||
|
else
|
||
|
{
|
||
|
if(!copy($src, $dst))
|
||
|
throw new Exception("Could not copy '$src' to '$dst'");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ensure_symlink($src, $dst, $dir_perms = 0777)
|
||
|
{
|
||
|
if(!is_file($src) && !is_dir($src))
|
||
|
throw new Exception("Bad file or dir '$src'");
|
||
|
|
||
|
$dir = dirname($dst);
|
||
|
if(!is_dir($dir))
|
||
|
mkdir($dir, $dir_perms, true);
|
||
|
|
||
|
ensure_rm($dst);
|
||
|
|
||
|
taskman_dmsg("symlinking $src -> $dst \n");
|
||
|
if(!symlink($src, $dst))
|
||
|
throw new Exception("Could not create symlink");
|
||
|
}
|
||
|
|
||
|
function ensure_rm($what)
|
||
|
{
|
||
|
if(is_dir($what) && !is_link($what))
|
||
|
rrmdir($what);
|
||
|
else if(is_file($what) || is_link($what))
|
||
|
unlink($what);
|
||
|
}
|
||
|
|
||
|
function ensure_mkdir($dir, $perms = 0775)
|
||
|
{
|
||
|
if(is_dir($dir))
|
||
|
return;
|
||
|
|
||
|
taskman_dmsg("mkdir $dir\n");
|
||
|
if(!mkdir($dir, $perms, true))
|
||
|
throw new Exception("Could not create dir '$dir'");
|
||
|
|
||
|
taskman_dmsg("chmod " . decoct($perms) . " $dir\n");
|
||
|
if(!chmod($dir, $perms))
|
||
|
throw new Exception("Could not chmod " . decoct($perms) . " dir '$dir'");
|
||
|
}
|
||
|
|
||
|
function ensure_var_dir($dir)
|
||
|
{
|
||
|
ensure_mkdir($dir, 0777);
|
||
|
$items = fmatch("$dir/*");
|
||
|
foreach($items as $item)
|
||
|
{
|
||
|
$perms = is_dir($item) ? 0770 : 0660;
|
||
|
|
||
|
taskman_dmsg("chmod " . decoct($perms) . " $item\n");
|
||
|
if(!chmod($item, $perms))
|
||
|
throw new Exception("Could not chmod " . decoct($perms) . " '$item'");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function compare_files_timestamp($file_a, $file_b)
|
||
|
{
|
||
|
$file_a_timestamp = file_exists($file_a) ? filemtime($file_a) : 0;
|
||
|
$file_b_timestamp = file_exists($file_b) ? filemtime($file_b) : 0;
|
||
|
if($file_a_timestamp >= $file_b_timestamp)
|
||
|
return 1;
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
function fmatch($pat)
|
||
|
{
|
||
|
$res = glob($pat);
|
||
|
if(!is_array($res))
|
||
|
return array();
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
function rrmdir($dir, $remove_top_dir = true)
|
||
|
{
|
||
|
if(is_dir($dir))
|
||
|
{
|
||
|
$objects = scandir($dir);
|
||
|
foreach($objects as $object)
|
||
|
{
|
||
|
if($object != "." && $object != "..")
|
||
|
{
|
||
|
if(filetype($dir."/".$object) == "dir")
|
||
|
rrmdir($dir."/".$object);
|
||
|
else
|
||
|
unlink($dir."/".$object);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if($remove_top_dir)
|
||
|
{
|
||
|
if(is_link($dir))
|
||
|
unlink($dir);
|
||
|
else if(is_dir($dir))
|
||
|
rmdir($dir);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function run_apple_script($script)
|
||
|
{
|
||
|
$vm = popen("osascript", "w");
|
||
|
fwrite($vm, $script);
|
||
|
// run script will always exit with status 1
|
||
|
pclose($vm);
|
||
|
}
|
||
|
|
||
|
function client_xcode_build($scheme, $config = 'Debug', $sdk = 'iphonesimulator4.3')
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
|
||
|
$xcode_filter = ". $GAME_ROOT/utils/xcodefilter.sh";
|
||
|
|
||
|
ensure_mkdir("$GAME_ROOT/build/client");
|
||
|
taskman_shell_ensure(taskman_str(
|
||
|
"cd $GAME_ROOT/client && " .
|
||
|
"$xcode_filter xcodebuild -workspace %XCODE_WKSPACE% -scheme $scheme -configuration $config -sdk $sdk SYMROOT=$GAME_ROOT/build/client"
|
||
|
));
|
||
|
}
|
||
|
|
||
|
function task_git_info()
|
||
|
{
|
||
|
list($rev_hash, $branch, $revision_number) = git_get_info();
|
||
|
$info = "==============GIT INFO==============\n"
|
||
|
. "$rev_hash - hash\n"
|
||
|
. "$branch - branch\n"
|
||
|
. "$revision_number - revision\n";
|
||
|
echo $info;
|
||
|
}
|
||
|
|
||
|
//returns cached array(rev_hash, branch),
|
||
|
//returns null in case of failure
|
||
|
function git_get_info($use_cache = true)
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
static $cached;
|
||
|
|
||
|
if($use_cache && $cached)
|
||
|
return $cached;
|
||
|
|
||
|
if(!is_dir("$GAME_ROOT/.git"))
|
||
|
throw new Exception("Not a Git repository");
|
||
|
|
||
|
$out = array();
|
||
|
exec("git rev-parse HEAD", $out);
|
||
|
$rev_hash = trim($out[0]);
|
||
|
if(!$rev_hash)
|
||
|
throw new Exception("Error getting git revision hash");
|
||
|
|
||
|
$out = array();
|
||
|
exec("git rev-parse --abbrev-ref HEAD", $out);
|
||
|
$branch = trim($out[0]);
|
||
|
if(!$branch)
|
||
|
throw new Exception("Error getting git branch");
|
||
|
|
||
|
$out = array();
|
||
|
exec("git branch -r", $out);
|
||
|
|
||
|
$revision_number = 0;
|
||
|
foreach ($out as $item)
|
||
|
{
|
||
|
if(strpos($item, "HEAD") !== false)
|
||
|
continue;
|
||
|
|
||
|
$count = array();
|
||
|
exec("git rev-list $item --count", $count);
|
||
|
$revision_number += (int)$count[0];
|
||
|
}
|
||
|
|
||
|
if(!$revision_number)
|
||
|
throw new Exception("Error getting git revision number");
|
||
|
|
||
|
$cached = array($rev_hash, $branch, $revision_number);
|
||
|
return $cached;
|
||
|
}
|
||
|
|
||
|
function git_get_rev_hash()
|
||
|
{
|
||
|
list($rev_hash, $_, $__) = git_get_info();
|
||
|
return $rev_hash;
|
||
|
}
|
||
|
|
||
|
function git_get_branch()
|
||
|
{
|
||
|
list($_, $branch, $__) = git_get_info();
|
||
|
return $branch;
|
||
|
}
|
||
|
|
||
|
function git_get_rev_number()
|
||
|
{
|
||
|
list($_, $__, $rev_number) = git_get_info();
|
||
|
return $rev_number;
|
||
|
}
|
||
|
|
||
|
function decode_json_ensure($json, $assoc = false)
|
||
|
{
|
||
|
$res = json_decode($json, $assoc);
|
||
|
if($res === null)
|
||
|
{
|
||
|
$parser = new JsonParser();
|
||
|
$err = $parser->lint($json);
|
||
|
echo $json;
|
||
|
throw $err;
|
||
|
}
|
||
|
return $res;
|
||
|
}
|
||
|
|
||
|
function convert_js_to_msgpack($file, $bin_file)
|
||
|
{
|
||
|
$bin_file = "$file.lib";
|
||
|
if(need_to_regen($bin_file, array($file)))
|
||
|
{
|
||
|
echo "Converting $file -> $bin_file...\n";
|
||
|
$data = decode_json_ensure(file_get_contents($file));
|
||
|
$msg = config_msgpack_pack($data);
|
||
|
ensure_write($bin_file, $msg);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function check_and_decode_json($json)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
$arr = decode_json_ensure($json, true);
|
||
|
return array(0, "", $arr);
|
||
|
}
|
||
|
catch(Exception $e)
|
||
|
{
|
||
|
return array(1, $e->getMessage(), array());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function check_and_decode_jzon($json)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
$arr = jzon_parse($json);
|
||
|
return array(0, "", $arr);
|
||
|
}
|
||
|
catch(Exception $e)
|
||
|
{
|
||
|
return array(1, $e->getMessage(), array());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function make_file_md5($file, $md5_file)
|
||
|
{
|
||
|
if(!need_to_regen($md5_file, array($file)))
|
||
|
return file_get_contents($md5_file);
|
||
|
|
||
|
$md5 = md5_file($file);
|
||
|
ensure_write($md5_file, $md5);
|
||
|
|
||
|
return $md5;
|
||
|
}
|
||
|
|
||
|
function make_dir_md5($dir, $md5_file)
|
||
|
{
|
||
|
$files = scan_files_rec(array($dir));
|
||
|
|
||
|
if(!need_to_regen($md5_file, $files))
|
||
|
return;
|
||
|
|
||
|
$md5s = array();
|
||
|
foreach($files as $file)
|
||
|
$md5s[] = md5_file($file);
|
||
|
|
||
|
$md5 = md5(implode('', $md5s));
|
||
|
ensure_write($md5_file, $md5);
|
||
|
}
|
||
|
|
||
|
function file_put_contents_atomic($filename, $content, $mode = 0644)
|
||
|
{
|
||
|
$temp = tempnam(dirname($filename), 'atomic');
|
||
|
if(!($f = @fopen($temp, 'wb')))
|
||
|
throw new Exception("Error writing temporary file '$temp'");
|
||
|
|
||
|
fwrite($f, $content);
|
||
|
fclose($f);
|
||
|
|
||
|
if(!@rename($temp, $filename))
|
||
|
{
|
||
|
@unlink($filename);
|
||
|
@rename($temp, $filename);
|
||
|
}
|
||
|
chmod($filename, $mode);
|
||
|
}
|
||
|
|
||
|
function gmgetdate($ts = null)
|
||
|
{
|
||
|
$k = array('seconds','minutes','hours','mday',
|
||
|
'wday','mon','year','yday','weekday','month',0);
|
||
|
return(array_combine($k, explode(":",
|
||
|
gmdate('s:i:G:j:w:n:Y:z:l:F:U',is_null($ts)?time():$ts))));
|
||
|
}
|
||
|
|
||
|
function prettyJSON($json)
|
||
|
{
|
||
|
$result = '';
|
||
|
$level = 0;
|
||
|
$prev_char = '';
|
||
|
$in_quotes = false;
|
||
|
$ends_line_level = NULL;
|
||
|
$json_length = strlen($json);
|
||
|
|
||
|
for($i = 0; $i < $json_length; $i++)
|
||
|
{
|
||
|
$char = $json[$i];
|
||
|
$new_line_level = NULL;
|
||
|
$post = "";
|
||
|
if($ends_line_level !== NULL)
|
||
|
{
|
||
|
$new_line_level = $ends_line_level;
|
||
|
$ends_line_level = NULL;
|
||
|
}
|
||
|
if($char === '"' && $prev_char != '\\')
|
||
|
{
|
||
|
$in_quotes = !$in_quotes;
|
||
|
}
|
||
|
else if(!$in_quotes)
|
||
|
{
|
||
|
switch($char)
|
||
|
{
|
||
|
case '}': case ']':
|
||
|
$level--;
|
||
|
$ends_line_level = NULL;
|
||
|
$new_line_level = $level;
|
||
|
break;
|
||
|
case '{': case '[':
|
||
|
$level++;
|
||
|
case ',':
|
||
|
$ends_line_level = $level;
|
||
|
break;
|
||
|
case ':':
|
||
|
$post = " ";
|
||
|
break;
|
||
|
case " ": case "\t": case "\n": case "\r":
|
||
|
$char = "";
|
||
|
$ends_line_level = $new_line_level;
|
||
|
$new_line_level = NULL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if($new_line_level !== NULL)
|
||
|
{
|
||
|
$result .= "\n".str_repeat(" ", $new_line_level);
|
||
|
}
|
||
|
$result .= $char.$post;
|
||
|
$prev_char = $char;
|
||
|
}
|
||
|
|
||
|
$result = str_replace('"<%', '<%', $result);
|
||
|
$result = str_replace('%>"', '%>', $result);
|
||
|
$result = str_replace('\"', '"', $result);
|
||
|
$result = str_replace('\\\\', '\\', $result);
|
||
|
$result = str_replace('\/', '/', $result);
|
||
|
$result = str_replace('"{', '{', $result);
|
||
|
$result = str_replace('}"', '}', $result);
|
||
|
|
||
|
return $result."\n";
|
||
|
}
|
||
|
|
||
|
function crc32_file($file)
|
||
|
{
|
||
|
$hash = hash_file('crc32b', $file);
|
||
|
$array = unpack('N', pack('H*', $hash));
|
||
|
return $array[1];
|
||
|
}
|
||
|
|
||
|
function run_shell_in_project_dir($cmd)
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
|
||
|
taskman_shell_ensure("cd $GAME_ROOT && $cmd", $out);
|
||
|
return $out[0];
|
||
|
}
|
||
|
|
||
|
function boolstr($b)
|
||
|
{
|
||
|
return $b ? 'true' : 'false';
|
||
|
}
|
||
|
|
||
|
function create_js_template_literal($str)
|
||
|
{
|
||
|
$result = str_replace('\\', '\\\\', $str); // replace \ -> \\
|
||
|
$result = str_replace('`', '\\`', $result); // replace ` -> \`
|
||
|
$result = str_replace('$', '\\$', $result); // replace $ -> \$
|
||
|
return "`$result`";
|
||
|
}
|
||
|
|
||
|
function create_go_string_literal($str)
|
||
|
{
|
||
|
$result = str_replace('\\', '\\\\', $str); // replace \ -> \\
|
||
|
$result = str_replace('"', '\\"', $result); // replace " -> \"
|
||
|
$result = str_replace("\n", '\\n', $result); // replace new_line -> \n
|
||
|
return '"' . $result . '"';
|
||
|
}
|
||
|
|
||
|
function convertFileFromDos2UnixFormat($file)
|
||
|
{
|
||
|
$file_tmp = $file.".tmp";
|
||
|
taskman_shell_ensure("sed \$'s/\\r\$//' $file > $file_tmp");
|
||
|
ensure_copy($file_tmp, $file);
|
||
|
ensure_rm($file_tmp);
|
||
|
}
|
||
|
|
||
|
function removeCarriageReturn($file)
|
||
|
{
|
||
|
$file_tmp = $file.".tmp";
|
||
|
taskman_shell_ensure("sed 's/\\\\r//' $file > $file_tmp");
|
||
|
ensure_copy($file_tmp, $file);
|
||
|
ensure_rm($file_tmp);
|
||
|
}
|
||
|
|
||
|
function gamectl_get_props_as_php_code()
|
||
|
{
|
||
|
$props = taskman_getprops();
|
||
|
$props_str = "<?php\n";
|
||
|
foreach($props as $k => $v)
|
||
|
$props_str .= "taskman_propset('$k', " . var_export($v, true). ");\n";
|
||
|
return $props_str;
|
||
|
}
|
||
|
|
||
|
class ProcWorker
|
||
|
{
|
||
|
public $start_time;
|
||
|
public $id;
|
||
|
public $in_file;
|
||
|
public $out_file;
|
||
|
public $log_file;
|
||
|
public $err_file;
|
||
|
public $progress_file;
|
||
|
private $script;
|
||
|
private $last_progress;
|
||
|
|
||
|
function __construct($id, $script, $data)
|
||
|
{
|
||
|
global $GAME_ROOT;
|
||
|
|
||
|
$this->start_time = microtime(true);
|
||
|
$this->id = $id;
|
||
|
$tmp_dir = "$GAME_ROOT/build/tmp";
|
||
|
$this->in_file = normalize_path("$tmp_dir/proc_$id.in");
|
||
|
$this->out_file = normalize_path("$tmp_dir/proc_$id.out");
|
||
|
$this->log_file = normalize_path("$tmp_dir/proc_$id.log");
|
||
|
$this->err_file = normalize_path("$tmp_dir/proc_$id.err");
|
||
|
$this->progress_file = normalize_path("$tmp_dir/proc_$id.prg");
|
||
|
$this->last_progress = null;
|
||
|
$this->script = normalize_path($script);
|
||
|
|
||
|
ensure_write($this->in_file, $data);
|
||
|
ensure_rm($this->out_file);
|
||
|
ensure_rm($this->log_file);
|
||
|
ensure_rm($this->err_file);
|
||
|
ensure_rm($this->progress_file);
|
||
|
|
||
|
run_background_proc("php {$this->script} {$this->in_file} {$this->out_file} {$this->progress_file} {$this->err_file} >{$this->log_file}");
|
||
|
}
|
||
|
|
||
|
function getProgress()
|
||
|
{
|
||
|
if(!is_file($this->progress_file))
|
||
|
return false;
|
||
|
$progress = file_get_contents($this->progress_file);
|
||
|
//NOTE: if progress hasn't changed don't report it
|
||
|
if($progress === $this->last_progress)
|
||
|
return false;
|
||
|
$this->last_progress = $progress;
|
||
|
return $progress;
|
||
|
}
|
||
|
|
||
|
function isDone()
|
||
|
{
|
||
|
return file_exists($this->out_file);
|
||
|
}
|
||
|
|
||
|
//1 - all done, 2 - error, 0 - not ready yet
|
||
|
function check(&$out, $unlink_result = true)
|
||
|
{
|
||
|
if(file_exists($this->out_file))
|
||
|
{
|
||
|
$out = file_get_contents($this->out_file);
|
||
|
//NOTE: since it may take a lot of space
|
||
|
if($unlink_result)
|
||
|
unlink($this->out_file);
|
||
|
return 1;
|
||
|
}
|
||
|
else if(file_exists($this->err_file))
|
||
|
{
|
||
|
$out = file_get_contents($this->err_file);
|
||
|
if($out)
|
||
|
{
|
||
|
if(file_exists($this->log_file))
|
||
|
$out = file_get_contents($this->log_file) . $out;
|
||
|
return 2;
|
||
|
}
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function run_background_proc($bin, array $args = array(), $redirect_out = '', $redirect_err = '')
|
||
|
{
|
||
|
if(is_win())
|
||
|
{
|
||
|
//TODO: migrate to background jobs
|
||
|
$cmd = "powershell.exe Start-Process $bin -NoNewWindow";
|
||
|
if($args)
|
||
|
{
|
||
|
$cmd .= ' -ArgumentList ';
|
||
|
foreach($args as $arg)
|
||
|
$cmd .= "'$arg',";
|
||
|
$cmd = rtrim($cmd, ',');
|
||
|
}
|
||
|
if($redirect_out)
|
||
|
$cmd .= " -RedirectStandardOutput '$redirect_out'";
|
||
|
if($redirect_err)
|
||
|
$cmd .= " -RedirectStandardError '$redirect_err'";
|
||
|
pclose(popen($cmd, 'r'));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$cmd = $bin;
|
||
|
foreach($args as $arg)
|
||
|
$cmd .= ' ' . escapeshellarg($arg);
|
||
|
if($redirect_out)
|
||
|
$cmd .= ' > ' . escapeshellarg($redirect_out);
|
||
|
if($redirect_err)
|
||
|
$cmd .= ' 2> ' . escapeshellarg($redirect_err);
|
||
|
$cmd .= ' &';
|
||
|
exec($cmd, $_, $ret);
|
||
|
if($ret !== 0)
|
||
|
throw new Exception("Error starting worker: $cmd ($ret)");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function run_background_gamectl_workers($task, array $worker_args)
|
||
|
{
|
||
|
$results = array();
|
||
|
$workers = array();
|
||
|
|
||
|
foreach($worker_args as $idx => $args)
|
||
|
{
|
||
|
$uid = uniqid();
|
||
|
$in_file = __DIR__ . "/../build/tmp/in_$uid.work";
|
||
|
$out_file = __DIR__ . "/../build/tmp/out_$uid.work";
|
||
|
$log_file = __DIR__ . "/../build/tmp/log_$uid.work";
|
||
|
$err_file = __DIR__ . "/../build/tmp/err_$uid.work";
|
||
|
|
||
|
$workers[] = array($in_file, $out_file, $log_file, $err_file);
|
||
|
|
||
|
ensure_write($in_file, serialize($args));
|
||
|
|
||
|
$cwd = getcwd();
|
||
|
chdir(__DIR__ . '/../');
|
||
|
run_background_proc(
|
||
|
is_win() ? 'gamectl.bat' : './gamectl',
|
||
|
array('-b', $task, $in_file, $out_file, $err_file),
|
||
|
$log_file, $err_file
|
||
|
);
|
||
|
chdir($cwd);
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
$log_handles = array();
|
||
|
while(sizeof($results) < sizeof($workers))
|
||
|
{
|
||
|
sleep(1);
|
||
|
clearstatcache();
|
||
|
|
||
|
foreach($workers as $idx => $worker)
|
||
|
{
|
||
|
if(isset($results[$idx]))
|
||
|
continue;
|
||
|
|
||
|
list($in_file, $out_file, $log_file, $err_file) = $worker;
|
||
|
|
||
|
if(!isset($log_handles[$idx]) && is_file($log_file))
|
||
|
$log_handles[$idx] = fopen($log_file, 'r');
|
||
|
|
||
|
if(isset($log_handles[$idx]))
|
||
|
{
|
||
|
while(($buffer = fgets($log_handles[$idx])) !== false)
|
||
|
echo $buffer;
|
||
|
|
||
|
$pos = ftell($log_handles[$idx]);
|
||
|
fclose($log_handles[$idx]);
|
||
|
$log_handles[$idx] = fopen($log_file, "r");
|
||
|
fseek($log_handles[$idx], $pos);
|
||
|
}
|
||
|
|
||
|
if(is_file($err_file) && filesize($err_file) > 0)
|
||
|
throw new Exception("Error in worker $idx:\n" . file_get_contents($err_file));
|
||
|
|
||
|
if(is_file($out_file))
|
||
|
$results[$idx] = @unserialize(ensure_read($out_file));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
foreach($workers as $item)
|
||
|
{
|
||
|
list($in_file, $log_file, $err_file) = $item;
|
||
|
@ensure_rm($in_file);
|
||
|
@ensure_rm($out_file);
|
||
|
@ensure_rm($log_file);
|
||
|
@ensure_rm($err_file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
function kb($str)
|
||
|
{
|
||
|
return kb_len(strlen($str));
|
||
|
}
|
||
|
|
||
|
function kb_len($len)
|
||
|
{
|
||
|
return round($len/1024,2) . "kb";
|
||
|
}
|
||
|
|
||
|
function gen_uuid_v4()
|
||
|
{
|
||
|
$UUID_LENGTH_BYTES = 16;
|
||
|
$data = random_bytes($UUID_LENGTH_BYTES);
|
||
|
|
||
|
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
|
||
|
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
|
||
|
|
||
|
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||
|
}
|
||
|
|
||
|
function watch_running_process($pid, $log_file, $exit_matches = array(), $error_matches = array(), $verbose = true, $break_after_sec = -1, $ignored_errors = array())
|
||
|
{
|
||
|
$matches_any_fn = function($buffer, array $matches) {
|
||
|
foreach($matches as $match)
|
||
|
{
|
||
|
if(strpos($buffer, $match) !== false)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
$h = fopen($log_file, "r");
|
||
|
$start = time();
|
||
|
while(true)
|
||
|
{
|
||
|
$buffer = fgets($h);
|
||
|
|
||
|
if($buffer !== false)
|
||
|
{
|
||
|
if($verbose)
|
||
|
echo $buffer;
|
||
|
|
||
|
//log success condition
|
||
|
if($matches_any_fn($buffer, $exit_matches))
|
||
|
break;
|
||
|
|
||
|
if($matches_any_fn($buffer, $error_matches))
|
||
|
{
|
||
|
if($matches_any_fn($buffer, $ignored_errors))
|
||
|
echo "Error in log file IGNORED\n";
|
||
|
else
|
||
|
throw new Exception("Error condition in log file detected");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//wait 2 seconds
|
||
|
usleep(200000);
|
||
|
$pos = ftell($h);
|
||
|
fclose($h);
|
||
|
$h = fopen($log_file, "r");
|
||
|
fseek($h, $pos);
|
||
|
|
||
|
//check if process still exists
|
||
|
/*exec("ps aux | grep '/Applications/Unity/Hub/Editor/2019.4.19f1/Unity.app/Contents/MacOS/Unity' | grep -v grep | awk '{ print $2 }'", $out, $ret);
|
||
|
if(count($out) > 0 && $ret === 0)
|
||
|
{
|
||
|
throw new Exception("Process with id: '$out[0]' not found");
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
if($break_after_sec > 0 && (time() - $start) > $break_after_sec)
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
fclose($h);
|
||
|
}
|