예제 #1
0
// 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
        {
예제 #2
0
// 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;
}