2015-02-20 04:41:01 -05:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Diagnostics ;
2015-05-15 09:03:27 -04:00
using System.IO ;
2015-02-20 04:41:01 -05:00
// Example Command Lines
// HTML5LaunchHelper -m -h -b /Applications/Safari.app localhost:8000
// HTML5LaunchHelper -m -h -b /Applications/Firefox.app http://localhost:8000 -silent -profile /Users/james.moran/webporfiles/nightly/UE4HTML5 --jsconsole
// HTML5LaunchHelper -m -h -b /Applications/Google\ Chrome.app http://localhost:8000 --user-data-dir="/Users/james.moran/webporfiles/chrome/UE4HTML5" --enable-logging --no-first-run
namespace HTML5LaunchHelper
{
class Program
{
static private bool bMultiprocessBrowser = false ;
static private bool bStartPythonServer = false ;
static private int PythonServerPort = 8000 ;
static private string PythonPath = "python" ;
static private string BrowserPath = null ;
static private string ServerDirectory = null ;
static private string Url = null ;
static private string AdditionalArgs = "" ;
static private List < Process > ProcessesToKill = new List < Process > ( ) ;
static private List < Process > ProcessesToWatch = new List < Process > ( ) ;
2015-05-15 09:03:27 -04:00
static bool IsRunningOnMac ( )
{
PlatformID Platform = Environment . OSVersion . Platform ;
switch ( Platform )
{
case PlatformID . Unix :
return System . IO . File . Exists ( "/System/Library/CoreServices/SystemVersion.plist" ) ;
case PlatformID . MacOSX :
return true ;
}
return false ;
}
2015-02-20 04:41:01 -05:00
static Process SpawnBrowserProcess ( string bpath , string args )
{
var Result = new Process ( ) ;
2015-05-15 09:03:27 -04:00
if ( IsRunningOnMac ( ) )
2015-02-20 04:41:01 -05:00
{
Result . StartInfo . FileName = "/usr/bin/open" ;
Result . StartInfo . UseShellExecute = false ;
Result . StartInfo . RedirectStandardOutput = true ;
Result . StartInfo . RedirectStandardInput = true ;
Result . StartInfo . Arguments = String . Format ( "-nW \"{0}\" --args {1}" , bpath , args ) ;
Result . EnableRaisingEvents = true ;
}
else
{
Result . StartInfo . FileName = bpath ;
Result . StartInfo . UseShellExecute = false ;
Result . StartInfo . RedirectStandardOutput = true ;
Result . StartInfo . RedirectStandardInput = true ;
Result . StartInfo . Arguments = args ;
Result . EnableRaisingEvents = true ;
}
System . Console . WriteLine ( "Spawning Browser Process {0} with args {1}\n" , bpath , args ) ;
return Result ;
}
static int Main ( string [ ] args )
{
if ( ! ParseArgs ( args ) )
{
// TODO: print error & options.
System . Console . Write ( "Invalid args\n" ) ;
return - 1 ;
}
if ( bStartPythonServer )
{
BeginPythonServer ( ) ;
2015-06-04 13:19:17 -04:00
// Give the server time to start, 1 Second should be enough.
2015-02-20 04:41:01 -05:00
System . Threading . Thread . Sleep ( 1000 ) ;
}
var bIsSafari = BrowserPath . Contains ( "Safari" ) ;
// Browsers can be multiprocess programs (Chrome, basically)
2015-06-04 13:19:17 -04:00
// so we need to catch spawning of other child processes. The trick is
// they aren't really child-processes at all. There appears to be no real binding between the two,
// So we kind of fudged it a bit here.
2015-02-20 04:41:01 -05:00
var PrevProcesses = Process . GetProcesses ( ) ;
var FirstProcess = SpawnBrowserProcess ( BrowserPath , ! bIsSafari ? Url + " " + AdditionalArgs : "" ) ;
ProcessesToWatch . Add ( FirstProcess ) ;
FirstProcess . Start ( ) ;
var ProcName = FirstProcess . ProcessName ;
if ( bIsSafari )
{
// Give Safari time to open...
System . Threading . Thread . Sleep ( 2000 ) ;
var Proc = new Process ( ) ;
Proc . StartInfo . FileName = "/usr/bin/osascript" ;
Proc . StartInfo . UseShellExecute = false ;
Proc . StartInfo . RedirectStandardOutput = true ;
Proc . StartInfo . RedirectStandardInput = true ;
Proc . StartInfo . Arguments = String . Format ( "-e 'tell application \"Safari\" to open location \"{1}\"'" , FirstProcess . Id , Url ) ;
Proc . EnableRaisingEvents = true ;
Proc . Start ( ) ;
Proc . WaitForExit ( ) ;
}
// We should now have a list of processes to watch to exit.
// Loop over the calling WaitForExit() until the list is empty.
while ( ProcessesToWatch . Count ( ) > 0 )
{
for ( var i = 0 ; i < ProcessesToWatch . Count ( ) ; + + i )
{
if ( ProcessesToWatch [ i ] . HasExited )
{
ProcessesToWatch . RemoveAt ( i ) ;
}
}
if ( bMultiprocessBrowser & & FirstProcess ! = null & & FirstProcess . HasExited )
{
var CurrentProcesses = Process . GetProcesses ( ) ;
foreach ( var item in CurrentProcesses )
{
var bWasAlive = false ;
foreach ( var pitem in PrevProcesses )
{
if ( pitem . Id = = item . Id )
{
bWasAlive = true ;
}
}
if ( ! bWasAlive )
{
try {
if ( ! item . HasExited & & item . ProcessName . StartsWith ( ProcName ) )
{
var PID = item . Id ;
System . Console . WriteLine ( "Found Process {0} with PID {1} which started at {2}. Waiting on that process to end." , item . ProcessName , item . Id , item . StartTime . ToString ( ) ) ;
ProcessesToWatch . Add ( item ) ;
}
} catch { }
}
}
FirstProcess = null ;
}
2015-06-04 13:19:17 -04:00
// It is considered an error if any service processes have died.
2015-02-20 04:41:01 -05:00
foreach ( var proc in ProcessesToKill )
{
if ( proc . HasExited )
{
System . Console . WriteLine ( "A spawned thread has died. Do you have a python server instance running?" ) ;
HardShutdown ( ) ;
return - 1 ;
}
}
System . Threading . Thread . Sleep ( 250 ) ;
}
System . Console . WriteLine ( "All processes being watched have exited.\n" ) ;
2015-06-04 13:19:17 -04:00
// All processes we cared about have finished, So it is time to clean up the services we spawned for them.
2015-02-20 04:41:01 -05:00
foreach ( var proc in ProcessesToKill )
{
if ( ! proc . HasExited )
{
System . Console . WriteLine ( "Killing spawned process {0}.\n" , proc . Id ) ;
proc . Kill ( ) ;
proc . WaitForExit ( ) ;
}
}
return 0 ;
}
static void HardShutdown ( )
{
foreach ( var proc in ProcessesToKill )
{
if ( ! proc . HasExited )
{
proc . Kill ( ) ;
proc . WaitForExit ( ) ;
}
}
foreach ( var proc in ProcessesToWatch )
{
if ( ! proc . HasExited )
{
proc . Kill ( ) ;
proc . WaitForExit ( ) ;
}
}
}
static void BeginPythonServer ( )
{
var Result = new Process ( ) ;
Result . StartInfo . FileName = PythonPath ;
Result . StartInfo . UseShellExecute = false ;
Result . StartInfo . RedirectStandardOutput = true ;
Result . StartInfo . RedirectStandardInput = true ;
Result . StartInfo . Arguments = String . Format ( "-m SimpleHTTPServer {0}" , PythonServerPort ) ;
Result . StartInfo . WorkingDirectory = ServerDirectory ;
Result . EnableRaisingEvents = true ;
Result . Start ( ) ;
System . Console . Write ( "Started New Process (PID={0} Name={1})\n" , Result . Id , Result . ProcessName ) ;
ProcessesToKill . Add ( Result ) ;
}
static bool ParseArgs ( string [ ] args )
{
bool bOptionalPythonName = false ;
bool bParseBrowserPath = false ;
bool bParseServerDir = false ;
bool bParseServerPort = false ;
foreach ( var arg in args )
{
var a = arg . Trim ( ) ;
if ( bOptionalPythonName | | bParseBrowserPath | | bParseServerDir | | bParseServerPort )
{
if ( ! a . StartsWith ( "-" ) )
{
if ( bOptionalPythonName )
{
PythonPath = a ;
}
else if ( bParseBrowserPath )
{
BrowserPath = a ;
}
else if ( bParseServerDir )
{
ServerDirectory = a ;
}
else if ( bParseServerPort )
{
PythonServerPort = Convert . ToInt32 ( a ) ;
}
}
bOptionalPythonName = bParseBrowserPath = bParseServerDir = bParseServerPort = false ;
}
else if ( a = = "-m" )
{
bMultiprocessBrowser = true ;
}
else if ( a = = "-p" )
{
bOptionalPythonName = true ;
}
else if ( a = = "-h" )
{
bStartPythonServer = true ;
}
else if ( a = = "-b" )
{
bParseBrowserPath = true ;
}
else if ( a = = "-w" )
{
bParseServerDir = true ;
}
else if ( a = = "-s" )
{
bParseServerPort = true ;
}
else if ( string . IsNullOrEmpty ( Url ) )
{
Url = a ;
}
else
{
if ( a . StartsWith ( "-" ) & & a . Contains ( '=' ) & & a . Contains ( ' ' ) )
{
var Parts = a . Split ( '=' ) ;
a = "" ;
for ( var p = 0 ; p < Parts . Count ( ) ; + + p )
{
if ( Parts [ p ] . Contains ( ' ' ) )
{
a + = "\"" + Parts [ p ] + "\"" ;
}
else
{
a + = Parts [ p ] ;
}
if ( p < Parts . Count ( ) - 1 )
{
a + = '=' ;
}
}
}
else if ( a . Contains ( ' ' ) )
{
a = "\"" + a + "\"" ;
}
AdditionalArgs + = a + " " ;
}
}
return ! string . IsNullOrEmpty ( BrowserPath ) & & ! string . IsNullOrEmpty ( Url ) & & ! string . IsNullOrEmpty ( ServerDirectory ) ;
}
}
}