Example #1
0
// DoBuild
//------------------------------------------------------------------------------
/*virtual*/ Node::BuildResult LibraryNode::DoBuild( Job * UNUSED( job ) )
{
	// delete library before creation (so ar.exe will not merge old symbols)
	if ( FileIO::FileExists( GetName().Get() ) )
	{
		FileIO::FileDelete( GetName().Get() );
	}

	// Format compiler args string
	Args fullArgs;
	if ( !BuildArgs( fullArgs ) )
	{
		return NODE_RESULT_FAILED; // BuildArgs will have emitted an error
	}

	// use the exe launch dir as the working dir
	const char * workingDir = nullptr;

	const char * environment = FBuild::Get().GetEnvironmentString();

	EmitCompilationMessage( fullArgs );

	// spawn the process
	Process p;
	bool spawnOK = p.Spawn( m_LibrarianPath.Get(),
							fullArgs.GetFinalArgs().Get(),
							workingDir,
							environment );

	if ( !spawnOK )
	{
		FLOG_ERROR( "Failed to spawn process for Library creation for '%s'", GetName().Get() );
		return NODE_RESULT_FAILED;
	}

	// capture all of the stdout and stderr
	AutoPtr< char > memOut;
	AutoPtr< char > memErr;
	uint32_t memOutSize = 0;
	uint32_t memErrSize = 0;
	p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize );

	ASSERT( !p.IsRunning() );
	// Get result
	int result = p.WaitForExit();

	if ( result != 0 )
	{
		if ( memOut.Get() ) { FLOG_ERROR_DIRECT( memOut.Get() ); }
		if ( memErr.Get() ) { FLOG_ERROR_DIRECT( memErr.Get() ); }
	}

	// did the executable fail?
	if ( result != 0 )
	{
		FLOG_ERROR( "Failed to build Library (error %i) '%s'", result, GetName().Get() );
		return NODE_RESULT_FAILED;
	}

	// record time stamp for next time
	m_Stamp = FileIO::GetFileLastWriteTime( m_Name );
	ASSERT( m_Stamp );

	return NODE_RESULT_OK;
}
Example #2
0
// DumpOutput
//------------------------------------------------------------------------------
/*static*/ void Node::DumpOutput( Job * job,
								  const char * data, 
								  uint32_t dataSize,
								  const Array< AString > * exclusions )
{
	if ( ( data == nullptr ) || ( dataSize == 0 ) )
	{
		return;
	}

	// preallocate a large buffer
	AString buffer( MEGABYTE );

	const char * end = data + dataSize;
	while( data < end )
	{
		// find the limits of the current line
		const char * lineStart = data;
		const char * lineEnd = lineStart;
		while ( lineEnd < end )
		{
			if ( ( *lineEnd == '\r' ) || ( *lineEnd == '\n' ) )
			{
				break;
			}
			lineEnd++;
		}
		if ( lineStart != lineEnd ) // ignore empty
		{
			// make a copy of the line to output
			AStackString< 1024 > copy( lineStart, lineEnd );

			// skip this line?
			bool skip = false;
			if ( exclusions )
			{
				const AString * iter = exclusions->Begin();
				const AString * const endIter = exclusions->End();
				while ( iter != endIter )
				{
					if ( copy.BeginsWith( *iter ) )
					{
						skip = true;
						break;
					}
					iter++;
				}
			}
			if ( !skip )
			{
				copy += '\n';

				// Clang format fixup for Visual Studio
				// (FBuild is null in remote context - fixup occurs on master)
				if ( FBuild::IsValid() && FBuild::Get().GetOptions().m_FixupErrorPaths )
				{
					FixupPathForVSIntegration( copy );
				}

				// if the buffer needs to grow, grow in 1MiB chunks
				if ( buffer.GetLength() + copy.GetLength() > buffer.GetReserved() )
				{
					buffer.SetReserved( buffer.GetReserved() + MEGABYTE );
				}
				buffer += copy;
			}
		}
		data = ( lineEnd + 1 );
	}

	// print everything at once
	FLOG_ERROR_DIRECT( buffer.Get() );

	// send output back to client if operating remotely
	if ( job && ( !job->IsLocal() ) )
	{
		job->Error( "%s", buffer.Get() );
	}
}
Example #3
0
// DoBuild
//------------------------------------------------------------------------------
/*virtual*/ Node::BuildResult LinkerNode::DoBuild( Job * job )
{
    DoPreLinkCleanup();

    // Make sure the implib output directory exists
    if (m_ImportLibName.IsEmpty() == false)
    {
        AStackString<> cleanPath;
        NodeGraph::CleanPath(m_ImportLibName, cleanPath);

        if (EnsurePathExistsForFile(cleanPath) == false)
        {
            // EnsurePathExistsForFile will have emitted error
            return NODE_RESULT_FAILED;
        }
    }

    // Format compiler args string
    Args fullArgs;
    if ( !BuildArgs( fullArgs ) )
    {
        return NODE_RESULT_FAILED; // BuildArgs will have emitted an error
    }

    // use the exe launch dir as the working dir
    const char * workingDir = nullptr;

    const char * environment = FBuild::Get().GetEnvironmentString();

    EmitCompilationMessage( fullArgs );

    // we retry if linker crashes
    uint32_t attempt( 0 );

    for (;;)
    {
        ++attempt;

        // spawn the process
        Process p( FBuild::Get().GetAbortBuildPointer() );
        bool spawnOK = p.Spawn( m_Linker.Get(),
                                fullArgs.GetFinalArgs().Get(),
                                workingDir,
                                environment );

        if ( !spawnOK )
        {
            if ( p.HasAborted() )
            {
                return NODE_RESULT_FAILED;
            }

            FLOG_ERROR( "Failed to spawn process '%s' for %s creation for '%s'", m_Linker.Get(), GetDLLOrExe(), GetName().Get() );
            return NODE_RESULT_FAILED;
        }

        // capture all of the stdout and stderr
        AutoPtr< char > memOut;
        AutoPtr< char > memErr;
        uint32_t memOutSize = 0;
        uint32_t memErrSize = 0;
        p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize );

        ASSERT( !p.IsRunning() );
        // Get result
        int result = p.WaitForExit();
        if ( p.HasAborted() )
        {
            return NODE_RESULT_FAILED;
        }

        // did the executable fail?
        if ( result != 0 )
        {
            // Handle bugs in the MSVC linker
            if ( GetFlag( LINK_FLAG_MSVC ) && ( attempt == 1 ) )
            {
                // Did the linker have an ICE (crash) (LNK1000)?
                if ( result == 1000 )
                {
                    FLOG_WARN( "FBuild: Warning: Linker crashed (LNK1000), retrying '%s'", GetName().Get() );
                    continue; // try again
                }

                // Did the linker have an "unexpected PDB error" (LNK1318)?
                // Example: "fatal error LNK1318: Unexpected PDB error; CORRUPT (13)"
                // (The linker or mspdbsrv.exe (as of VS2017) seems to have bugs which cause the PDB
                // to sometimes be corrupted when doing very large links, possibly because the linker
                // is running out of memory)
                if ( result == 1318 )
                {
                    FLOG_WARN( "FBuild: Warning: Linker corrupted the PDB (LNK1318), retrying '%s'", GetName().Get() );
                    continue; // try again
                }
            }

            if ( memOut.Get() )
            {
                job->ErrorPreformatted( memOut.Get() );
            }

            if ( memErr.Get() )
            {
                job->ErrorPreformatted( memErr.Get() );
            }

            // some other (genuine) linker failure
            FLOG_ERROR( "Failed to build %s (error %i) '%s'", GetDLLOrExe(), result, GetName().Get() );
            return NODE_RESULT_FAILED;
        }
        else
        {
            // If "warnings as errors" is enabled (/WX) we don't need to check
            // (since compilation will fail anyway, and the output will be shown)
            if ( GetFlag( LINK_FLAG_MSVC ) && !GetFlag( LINK_FLAG_WARNINGS_AS_ERRORS_MSVC ) )
            {
                HandleWarningsMSVC( job, GetName(), memOut.Get(), memOutSize );
            }
            break; // success!
        }
    }

    // post-link stamp step
    if ( m_LinkerStampExe.IsEmpty() == false )
    {
        const Node * linkerStampExe = m_StaticDependencies.End()[ -1 ].GetNode();
        EmitStampMessage();

        Process stampProcess( FBuild::Get().GetAbortBuildPointer() );
        bool spawnOk = stampProcess.Spawn( linkerStampExe->GetName().Get(),
                                           m_LinkerStampExeArgs.Get(),
                                           nullptr,     // working dir
                                           nullptr );   // env
        if ( spawnOk == false )
        {
            if ( stampProcess.HasAborted() )
            {
                return NODE_RESULT_FAILED;
            }

            FLOG_ERROR( "Failed to spawn process '%s' for '%s' stamping of '%s'", linkerStampExe->GetName().Get(), GetDLLOrExe(), GetName().Get() );
            return NODE_RESULT_FAILED;
        }

        // capture all of the stdout and stderr
        AutoPtr< char > memOut;
        AutoPtr< char > memErr;
        uint32_t memOutSize = 0;
        uint32_t memErrSize = 0;
        stampProcess.ReadAllData( memOut, &memOutSize, memErr, &memErrSize );
        ASSERT( !stampProcess.IsRunning() );

        // Get result
        int result = stampProcess.WaitForExit();
        if ( stampProcess.HasAborted() )
        {
            return NODE_RESULT_FAILED;
        }

        // did the executable fail?
        if ( result != 0 )
        {
            if ( memOut.Get() ) { FLOG_ERROR_DIRECT( memOut.Get() ); }
            if ( memErr.Get() ) { FLOG_ERROR_DIRECT( memErr.Get() ); }
            FLOG_ERROR( "Failed to stamp %s '%s' (error %i - '%s')", GetDLLOrExe(), GetName().Get(), result, m_LinkerStampExe.Get() );
            return NODE_RESULT_FAILED;
        }

        // success!
    }

    // record time stamp for next time
    m_Stamp = FileIO::GetFileLastWriteTime( m_Name );
    ASSERT( m_Stamp );

    return NODE_RESULT_OK;
}
Example #4
0
// DoBuild
//------------------------------------------------------------------------------
/*virtual*/ Node::BuildResult LinkerNode::DoBuild( Job * UNUSED( job ) )
{
    DoPreLinkCleanup();

    // Make sure the implib output directory exists
    if (m_ImportLibName.IsEmpty() == false)
    {
        AStackString<> cleanPath;
        NodeGraph::CleanPath(m_ImportLibName, cleanPath);

        if (EnsurePathExistsForFile(cleanPath) == false)
        {
            // EnsurePathExistsForFile will have emitted error
            return NODE_RESULT_FAILED;
        }
    }

    // Format compiler args string
    Args fullArgs;
    if ( !BuildArgs( fullArgs ) )
    {
        return NODE_RESULT_FAILED; // BuildArgs will have emitted an error
    }

    // use the exe launch dir as the working dir
    const char * workingDir = nullptr;

    const char * environment = FBuild::Get().GetEnvironmentString();

    EmitCompilationMessage( fullArgs );

    // we retry if linker crashes
    uint32_t attempt( 0 );

    for (;;)
    {
        ++attempt;

        // spawn the process
        Process p;
        bool spawnOK = p.Spawn( m_Linker.Get(),
                                fullArgs.GetFinalArgs().Get(),
                                workingDir,
                                environment );

        if ( !spawnOK )
        {
            FLOG_ERROR( "Failed to spawn process '%s' for %s creation for '%s'", m_Linker.Get(), GetDLLOrExe(), GetName().Get() );
            return NODE_RESULT_FAILED;
        }

        // capture all of the stdout and stderr
        AutoPtr< char > memOut;
        AutoPtr< char > memErr;
        uint32_t memOutSize = 0;
        uint32_t memErrSize = 0;
        p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize );

        ASSERT( !p.IsRunning() );
        // Get result
        int result = p.WaitForExit();

        // did the executable fail?
        if ( result != 0 )
        {
            // did the linker have an ICE (LNK1000)?
            if ( GetFlag( LINK_FLAG_MSVC ) && ( result == 1000 ) && ( attempt == 1 ) )
            {
                FLOG_WARN( "FBuild: Warning: Linker crashed (LNK1000), retrying '%s'", GetName().Get() );
                continue; // try again
            }

            if ( memOut.Get() )
            {
                m_BuildOutputMessages.Append( memOut.Get(), memOutSize );
                FLOG_ERROR_DIRECT( memOut.Get() );
            }

            if ( memErr.Get() )
            {
                m_BuildOutputMessages.Append( memErr.Get(), memErrSize );
                FLOG_ERROR_DIRECT( memErr.Get() );
            }

            // some other (genuine) linker failure
            FLOG_ERROR( "Failed to build %s (error %i) '%s'", GetDLLOrExe(), result, GetName().Get() );
            return NODE_RESULT_FAILED;
        }
        else
        {
            break; // success!
        }
    }

    // post-link stamp step
    if ( m_LinkerStampExe )
    {
        EmitStampMessage();

        Process stampProcess;
        bool spawnOk = stampProcess.Spawn( m_LinkerStampExe->GetName().Get(),
                                           m_LinkerStampExeArgs.Get(),
                                           nullptr,     // working dir
                                           nullptr );   // env
        if ( spawnOk == false )
        {
            FLOG_ERROR( "Failed to spawn process '%s' for '%s' stamping of '%s'", m_LinkerStampExe->GetName().Get(), GetDLLOrExe(), GetName().Get() );
            return NODE_RESULT_FAILED;
        }

        // capture all of the stdout and stderr
        AutoPtr< char > memOut;
        AutoPtr< char > memErr;
        uint32_t memOutSize = 0;
        uint32_t memErrSize = 0;
        stampProcess.ReadAllData( memOut, &memOutSize, memErr, &memErrSize );
        ASSERT( !stampProcess.IsRunning() );

        // Get result
        int result = stampProcess.WaitForExit();

        // did the executable fail?
        if ( result != 0 )
        {
            if ( memOut.Get() ) { FLOG_ERROR_DIRECT( memOut.Get() ); }
            if ( memErr.Get() ) { FLOG_ERROR_DIRECT( memErr.Get() ); }
            FLOG_ERROR( "Failed to stamp %s '%s' (error %i - '%s')", GetDLLOrExe(), GetName().Get(), result, m_LinkerStampExe->GetName().Get() );
            return NODE_RESULT_FAILED;
        }

        // success!
    }

    // record time stamp for next time
    m_Stamp = FileIO::GetFileLastWriteTime( m_Name );
    ASSERT( m_Stamp );

    return NODE_RESULT_OK;
}