/dev/null 2>&1&"; system($cmd, $res); } }); task('unity_kill', function() { unity_kill(); }); function unity_exec($func, $build_target = "", $quit = true, $batchmode = true) { global $GAME_ROOT; $proj_path = normalize_path(get("UNITY_ASSETS_DIR") . '/../'); $log_file = "$GAME_ROOT/build/unity.log"; ensure_rm($log_file); $pid_file = "$GAME_ROOT/build/unity.pid"; $username = getor('UNITY_ACCOUNT_USERNAME', null); $password = getor('UNITY_ACCOUNT_PASSWORD', null); $shared_cmd = "-projectPath $proj_path -logFile $log_file " . ($batchmode ? "-batchmode" : "") . " -accept-apiupdate" . ($quit ? " -quit" : "") . ($build_target ? " -buildTarget $build_target" : "") . ($func != "" ? " -executeMethod $func" : ""); if($username !== null && $password !== null) $shared_cmd .= " -username \"$username\" -password \"$password\""; $pid = unity_run_proc($shared_cmd); if(!$pid) throw new Exception("Error starting cmd: $shared_cmd"); while(!is_file($log_file)) usleep(200000); return array($log_file, $pid); } function unity_batch_exec($func, $build_target = "") { global $GAME_ROOT; unity_kill(); list($log_file, $pid) = unity_exec($func, $build_target, /*$quit = */ true, /*$batchmode = */ true); watch_running_process( $pid, $log_file, array( 'Exiting batchmode successfully', 'Batchmode quit successfully invoked - shutting down!' ), array( 'Build Finished, Result: Failure', 'UnityException:', 'Aborting batchmode due to failure', 'Launching bug reporter', 'Unrecognized assets cannot be included in AssetBundles:' ), true, -1, //ignored errors: array( //NOTE: weird editor crash, sometimes happens on exit after batch task is actually done // seems like it's never been fixed though issue tracker says otherwise (https://vk.cc/bYpyUS) "Fatal Error! GetManagerFromContext: pointer to object of manager 'MonoManager' is NULL (table index 5)" ), //noted warnings: they don't abort execution but show up at the end of the log if command fails array( ': error CS', ) ); } function unity_run_proc($shared_cmd) { $app_dir = get('UNITY_APP_DIR'); echo "Unity App dir: $app_dir\n"; if(is_win()) { $unity_app_path = $app_dir . "Editor/Unity.exe"; $cmd = "powershell.exe (Start-Process '$unity_app_path'-ArgumentList '$shared_cmd' -passthru).Id"; } else if(is_linux()) { $cmd = "'$app_dir/Editor/Unity' $shared_cmd > /dev/null & echo $!"; } else { $cmd = "'$app_dir/Unity.app/Contents/MacOS/Unity' $shared_cmd > /dev/null & echo $!"; } exec($cmd, $out, $ret); if($ret !== 0) throw new Exception("Error starting Unity: $cmd ($ret)"); return trim($out[0]); } function unity_kill() { $proc_id = unity_find_proc_id(); if($proc_id) { echo "Found Unity process '$proc_id', killing it...\n"; system("kill -9 $proc_id"); sleep(5); } else echo "No Unity process found to kill\n"; } function unity_find_proc_id() { global $GAME_ROOT; exec("ps aux | grep 'Unity' | grep -i 'projectpath' | grep -v grep", $out); foreach($out as $line) if(preg_match('~.*?\s+(\d+).*-projectpath\s+(.*?)\s+-~i', $line, $ms)) if(strpos(realpath($ms[2]), realpath($GAME_ROOT)) === 0) return $ms[1]; return null; } function guess_unity_app_dir() { global $GAME_ROOT; if(getenv("UNITY_APP_DIR")) return getenv("UNITY_APP_DIR"); $path = "/Applications/Unity/"; if(is_win()) $path = getenv("ProgramFiles")."/Unity/"; $proj_version_file = $GAME_ROOT.'/unity/ProjectSettings/ProjectVersion.txt'; if(is_file($proj_version_file)) { list($_, $unity_version) = array_map('trim', explode(":", file($proj_version_file)[0])); $path .= "Hub/Editor/$unity_version/"; } return $path; } function get_unity_dir() { $app_dir = get('UNITY_APP_DIR'); if(is_win() || is_linux()) return "$app_dir/Editor/Data"; else return "$app_dir/Unity.app/Contents/"; } function mono_mcs_bin() { if(!get("USE_UNITY_MONO")) return 'mcs'; $unity_dir = get_unity_dir(); if(!is_dir($unity_dir)) throw new Exception("Unity directory doesn't exist: $unity_dir"); $canidates = array( "$unity_dir/Mono/bin/gmcs", "$unity_dir/MonoBleedingEdge/bin/mcs", "$unity_dir/Frameworks/Mono/bin/gmcs", "$unity_dir/Mono/bin/gmcs", ); foreach($canidates as $mcs_bin) { if(is_file($mcs_bin)) { if(is_win()) return '"' . normalize_path($mcs_bin, false) . '"'; else return $mcs_bin; } } throw new Exception("Mono compiler binary not found"); } function mono_try_override_path() { if(!get("USE_UNITY_MONO")) return ''; $mono_dir = dirname(trim(mono_mcs_bin(), '"')); $prev_path = getenv('PATH'); echo "UNITY MONO PATH=$mono_dir\n"; putenv("PATH=$mono_dir".(is_win() ? ";" : ":")."$prev_path"); return $prev_path; } function mono_try_restore_path($prev_path) { if(!get("USE_UNITY_MONO")) return; putenv("PATH=$prev_path"); } function build_mono_dll($src_spec, $out_file, $ref_dlls = array(), $mcs_opts = "") { build_mono($src_spec, $out_file, $ref_dlls, "$mcs_opts -target:library"); } function build_mono($src_spec, $out_file, $ref_dlls = array(), $mcs_opts = "") { global $GAME_ROOT; $unity_dir = get_unity_dir(); $mcs_bin = mono_mcs_bin(); if(is_array($src_spec)) $sources = $src_spec; else if(is_dir($src_spec)) $sources = scan_files_rec(array($src_spec), array('.cs')); else $sources = glob($src_spec); $sources_str = ''; foreach($sources as $src) { if(is_win()) $sources_str .= '"'.normalize_path($src).'" '; else $sources_str .= '\''.normalize_path($src).'\' '; } $deps = $sources; $refs_str = ""; foreach($ref_dlls as $ref_dll) { if($ref_dll === "UnityEngine.dll") { $ref_dll = "$unity_dir/Managed/UnityEngine.dll"; if(!is_file($ref_dll)) $ref_dll = "$unity_dir/Frameworks/Managed/UnityEngine.dll"; } $ref_dll = normalize_path($ref_dll, !is_win()); $refs_str .= "-r:\"$ref_dll\" "; $deps[] = $ref_dll; } $out_file = normalize_path($out_file, !is_win()); $cmd = "$mcs_bin $mcs_opts $refs_str -out:$out_file $sources_str"; if(is_string($src_spec) && is_dir($src_spec)) $cmd = "cd $src_spec && $cmd"; $cmd_hash = crc32($cmd); $cmd_hash_file = "$GAME_ROOT/build/" . crc32($out_file) . ".mhash"; if(!is_file($cmd_hash_file) || file_get_contents($cmd_hash_file) != "$cmd_hash") file_put_contents($cmd_hash_file, "$cmd_hash"); $deps[] = $cmd_hash_file; if(need_to_regen($out_file, $deps)) { ensure_mkdir(dirname($out_file)); shell($cmd); echo "> $out_file " . round(filesize($out_file)/1024, 2) . "kb\n"; } } function run_mono($exe_file) { $app_dir = get('UNITY_APP_DIR'); if(is_win()) { $unity_dir = "$app_dir/Editor/Data"; $mono_bin = '"' . normalize_path("$unity_dir/Mono/bin/mono") . '"'; } else { $unity_dir = "$app_dir/Unity.app/Contents/"; if(is_dir("$unity_dir/Frameworks/Mono")) $mono_bin = "$unity_dir/Frameworks/Mono/bin/mono"; else $mono_bin = "$unity_dir/Mono/bin/mono"; } $exe_file = normalize_path($exe_file, !is_win()); $mono_path = realpath(dirname($mono_bin) . '/../lib/mono/unity'); putenv("MONO_PATH=$mono_path"); $cmd = "$mono_bin $exe_file"; shell($cmd); } function build_option_exists($option) { return strpos(getor("BUILD_CLIENT_OPTS", ""), $option) !== FALSE; } function build_option_add($option) { if(build_option_exists($option)) return; $propname = "BUILD_CLIENT_OPTS"; $opts = getor($propname, ""); if(strlen($opts) > 0) $opts .= ",$option"; else $opts = $option; set($propname, $opts); } function is_gradle_project_export() { return build_option_exists("AcceptExternalModificationsToPlayer"); }