// Spawn //------------------------------------------------------------------------------ bool Process::Spawn( const char * executable, const char * args, const char * workingDir, const char * environment, bool shareHandles ) { ASSERT( !m_Started ); ASSERT( executable ); #if defined( __WINDOWS__ ) // Set up the start up info struct. STARTUPINFO si; ZeroMemory( &si, sizeof(STARTUPINFO) ); si.cb = sizeof( STARTUPINFO ); si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; SECURITY_ATTRIBUTES sa; ZeroMemory( &sa, sizeof( SECURITY_ATTRIBUTES ) ); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = nullptr; m_SharingHandles = shareHandles; if ( m_RedirectHandles ) { // create the pipes if ( shareHandles ) { si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); si.hStdError = GetStdHandle(STD_ERROR_HANDLE); si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); } else { if ( ! CreatePipe( &m_StdOutRead, &m_StdOutWrite, &sa, MEGABYTE ) ) { return false; } SetHandleInformation( m_StdOutRead, HANDLE_FLAG_INHERIT, 0 ); if ( ! CreatePipe( &m_StdErrRead, &m_StdErrWrite, &sa, MEGABYTE ) ) { VERIFY( CloseHandle( m_StdOutRead ) ); VERIFY( CloseHandle( m_StdOutWrite ) ); return false; } SetHandleInformation( m_StdErrRead, HANDLE_FLAG_INHERIT, 0 ); si.hStdOutput = m_StdOutWrite; si.hStdError = m_StdErrWrite; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); // m_StdInRead; } si.dwFlags |= STARTF_USESTDHANDLES; } // Make sure the first arg is the executable // We also need to make a copy, as CreateProcess can write back to this string AStackString< 1024 > fullArgs; fullArgs += '\"'; fullArgs += executable; fullArgs += '\"'; if ( args ) { fullArgs += ' '; fullArgs += args; } //fullArgs.Format( "\"%s\" %s", executable, args ); // create the child if ( !CreateProcess( nullptr, //executable, fullArgs.Get(), nullptr, nullptr, (BOOL)m_RedirectHandles, // inherit handles 0, (void *)environment, workingDir, &si, (LPPROCESS_INFORMATION)&m_ProcessInfo ) ) { return false; } m_Started = true; return true; #elif defined( __LINUX__ ) || defined( __APPLE__ ) // create StdOut and StdErr pipes to capture output of spawned process int stdOutPipeFDs[ 2 ]; int stdErrPipeFDs[ 2 ]; VERIFY( pipe( stdOutPipeFDs ) == 0 ); VERIFY( pipe( stdErrPipeFDs ) == 0 ); // fork the process const pid_t childProcessPid = fork(); if ( childProcessPid == -1 ) { // cleanup pipes VERIFY( close( stdOutPipeFDs[ 0 ] ) == 0 ); VERIFY( close( stdOutPipeFDs[ 1 ] ) == 0 ); VERIFY( close( stdErrPipeFDs[ 0 ] ) == 0 ); VERIFY( close( stdErrPipeFDs[ 1 ] ) == 0 ); ASSERT( false ); // fork failed - should not happen in normal operation return false; } const bool isChild = ( childProcessPid == 0 ); if ( isChild ) { VERIFY( dup2( stdOutPipeFDs[ 1 ], STDOUT_FILENO ) != -1 ); VERIFY( dup2( stdErrPipeFDs[ 1 ], STDERR_FILENO ) != -1 ); VERIFY( close( stdOutPipeFDs[ 0 ] ) == 0 ); VERIFY( close( stdOutPipeFDs[ 1 ] ) == 0 ); VERIFY( close( stdErrPipeFDs[ 0 ] ) == 0 ); VERIFY( close( stdErrPipeFDs[ 1 ] ) == 0 ); if ( workingDir ) { FileIO::SetCurrentDir( AStackString<>( workingDir ) ); } // split args AString fullArgs( args ); Array< AString > splitArgs( 64, true ); fullArgs.Tokenize( splitArgs ); // prepare args const size_t numArgs = splitArgs.GetSize(); char ** argv = FNEW( char *[ numArgs + 2 ] ); argv[ 0 ] = const_cast< char * >( executable ); for ( size_t i=0; i<numArgs; ++i ) { AString & thisArg = splitArgs[ i ]; if ( thisArg.BeginsWith( '"' ) && thisArg.EndsWith( '"' ) ) { // strip quotes thisArg.SetLength( thisArg.GetLength() - 1 ); // trim end quote argv[ i + 1 ] = thisArg.Get() + 1; // skip start quote continue; } argv[ i + 1 ] = thisArg.Get(); // leave arg as-is } argv[ numArgs + 1 ] = nullptr; // transfer execution to new executable execv( executable, argv ); exit( -1 ); // only get here if execv fails } else {
// GetOtherLibraries //------------------------------------------------------------------------------ bool LinkerNode::GetOtherLibraries( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function, const AString & args, Dependencies & otherLibraries, bool msvc ) const { // split to individual tokens Array< AString > tokens; args.Tokenize( tokens ); bool ignoreAllDefaultLibs = false; Array< AString > defaultLibsToIgnore( 8, true ); Array< AString > defaultLibs( 16, true ); Array< AString > libs( 16, true ); Array< AString > dashlLibs( 16, true ); Array< AString > libPaths( 16, true ); Array< AString > envLibPaths( 32, true ); // extract lib path from system if present AStackString< 1024 > libVar; if ( Env::GetEnvVariable( "LIB", libVar ) ) { libVar.Tokenize( envLibPaths, ';' ); } const AString * const end = tokens.End(); for ( const AString * it = tokens.Begin(); it != end; ++it ) { const AString & token = *it; // MSVC style if ( msvc ) { // /NODEFAULTLIB if ( LinkerNode::IsLinkerArg_MSVC( token, "NODEFAULTLIB" ) ) { ignoreAllDefaultLibs = true; continue; } // /NODEFAULTLIB: if ( GetOtherLibsArg( "NODEFAULTLIB:", defaultLibsToIgnore, it, end, false, msvc ) ) { continue; } // /DEFAULTLIB: if ( GetOtherLibsArg( "DEFAULTLIB:", defaultLibs, it, end, false, msvc ) ) { continue; } // /LIBPATH: if ( GetOtherLibsArg( "LIBPATH:", libPaths, it, end, true, msvc ) ) // true = canonicalize path { continue; } // some other linker argument? if ( token.BeginsWith( '/' ) || token.BeginsWith( '-' ) ) { continue; } } // GCC/SNC style if ( !msvc ) { // We don't need to check for this, as there is no default lib passing on // the cmd line. // -nodefaultlibs //if ( token == "-nodefaultlibs" ) //{ // ignoreAllDefaultLibs = true; // continue; //} // -L (lib path) if ( GetOtherLibsArg( "L", libPaths, it, end, false, msvc ) ) { continue; } // -l (lib) if ( GetOtherLibsArg( "l", dashlLibs, it, end, false, msvc ) ) { continue; } // some other linker argument? if ( token.BeginsWith( '-' ) ) { continue; } } // build time substitution? if ( token.BeginsWith( '%' ) || // %1 token.BeginsWith( "'%" ) || // '%1' token.BeginsWith( "\"%" ) ) // "%1" { continue; } // anything left is an input to the linker AStackString<> libName; Args::StripQuotes( token.Get(), token.GetEnd(), libName ); if ( token.IsEmpty() == false ) { libs.Append( libName ); } } // filter default libs if ( ignoreAllDefaultLibs ) { // filter all default libs defaultLibs.Clear(); } else { // filter specifically listed default libs const AString * const endI = defaultLibsToIgnore.End(); for ( const AString * itI = defaultLibsToIgnore.Begin(); itI != endI; ++itI ) { const AString * const endD = defaultLibs.End(); for ( AString * itD = defaultLibs.Begin(); itD != endD; ++itD ) { if ( itI->CompareI( *itD ) == 0 ) { defaultLibs.Erase( itD ); break; } } } } if ( !msvc ) { // convert -l<name> style libs to lib<name>.a const AString * const endDL = dashlLibs.End(); for ( const AString * itDL = dashlLibs.Begin(); itDL != endDL; ++itDL ) { AStackString<> libName; libName += "lib"; libName += *itDL; libName += ".a"; libs.Append( libName ); } } // any remaining default libs are treated the same as libs libs.Append( defaultLibs ); // use Environment libpaths if found (but used after LIBPATH provided ones) libPaths.Append( envLibPaths ); // convert libs to nodes const AString * const endL = libs.End(); for ( const AString * itL = libs.Begin(); itL != endL; ++itL ) { bool found = false; // is the file a full path? if ( ( itL->GetLength() > 2 ) && ( (*itL)[ 1 ] == ':' ) ) { // check file exists in current location if ( !GetOtherLibrary( nodeGraph, iter, function, otherLibraries, AString::GetEmpty(), *itL, found ) ) { return false; // GetOtherLibrary will have emitted error } } else { // check each libpath const AString * const endP = libPaths.End(); for ( const AString * itP = libPaths.Begin(); itP != endP; ++itP ) { if ( !GetOtherLibrary( nodeGraph, iter, function, otherLibraries, *itP, *itL, found ) ) { return false; // GetOtherLibrary will have emitted error } if ( found ) { break; } // keep searching lib paths... } } // file does not exist on disk, and there is no rule to build it // Don't complain about this, because: // a) We may be parsing rules on another OS (i.e. parsing Linux rules on Windows) // b) User may have filtered some libs for platforms they don't care about (i.e. libs // for PS4 on a PC developer's machine on a cross-platform team) // If the file is actually needed, the linker will emit an error during link-time. } return true; }