// DoBuild //------------------------------------------------------------------------------ /*virtual*/ Node::BuildResult ExecNode::DoBuild( Job * job ) { // If the workingDir is empty, use the current dir for the process const char * workingDir = m_WorkingDir.IsEmpty() ? nullptr : m_WorkingDir.Get(); AStackString<> fullArgs( m_Arguments ); fullArgs.Replace( "%1", m_SourceFile->GetName().Get() ); fullArgs.Replace( "%2", GetName().Get() ); EmitCompilationMessage( fullArgs ); // spawn the process Process p; bool spawnOK = p.Spawn( m_Executable->GetName().Get(), fullArgs.Get(), workingDir, FBuild::Get().GetEnvironmentString() ); if ( !spawnOK ) { FLOG_ERROR( "Failed to spawn process 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(); // did the executable fail? if ( result != m_ExpectedReturnCode ) { // something went wrong, print details Node::DumpOutput( job, memOut.Get(), memOutSize ); Node::DumpOutput( job, memErr.Get(), memErrSize ); FLOG_ERROR( "Execution failed (error %i) '%s'", result, GetName().Get() ); return NODE_RESULT_FAILED; } // update the file's "last modified" time m_Stamp = FileIO::GetFileLastWriteTime( m_Name ); return NODE_RESULT_OK; }
/*static*/ bool ReflectionInfo::WriteDefinitions() { uint32_t numProblems = 0; const ReflectionInfo * ri = s_FirstReflectionInfo; for ( ; ri != nullptr; ri = ri->m_Next ) { // ignore abstract classes if ( ri->IsAbstract() ) { continue; } // Serialize a default instance to a MemoryStream MemoryStream ms; { // Create and serialize default instance if ( ri->IsObject() ) { RefObject * object = ri->CreateObject(); { TextWriter tw( ms ); tw.Write( object ); } FDELETE( object ); } else { ASSERT( ri->IsStruct() ) Struct * str = ri->CreateStruct(); { TextWriter tw( ms ); tw.Write( str, ri ); } FDELETE( str ); } } AStackString<> fileName; fileName.Format( "..\\Data\\Reflection\\.Definitions\\%s.definition", ri->GetTypeName() ); // avoid writing file if not changed // Try to open existing file FileStream f; if ( f.Open( fileName.Get(), FileStream::READ_ONLY ) ) { // read content const uint64_t fileSize = f.GetFileSize(); if ( fileSize == ms.GetSize() ) { AutoPtr< char > mem( (char *)Alloc( (size_t)fileSize ) ); if ( f.Read( mem.Get(), (size_t)fileSize ) == fileSize ) { if ( memcmp( mem.Get(), ms.GetData(), (size_t)fileSize ) == 0 ) { continue; // definition has not changed } } } f.Close(); } // Definition changed - try to save it int result = 0; AutoPtr< char > memOut; AutoPtr< char > memErr; uint32_t memOutSize; uint32_t memErrSize; // existing definition? if ( FileIO::FileExists( fileName.Get() ) ) { // existing - need to open for edit? if ( FileIO::GetReadOnly( fileName ) ) { AStackString<> args( "edit " ); args += fileName; Process p; if ( p.Spawn( "p4", args.Get(), nullptr, nullptr ) ) { p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize ); result = p.WaitForExit(); } } } else { // new - open for add AStackString<> args( "add " ); args += fileName; Process p; if ( p.Spawn( "p4", args.Get(), nullptr, nullptr ) ) { p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize ); result = p.WaitForExit(); } } if ( result == 0 ) { if ( f.Open( fileName.Get(), FileStream::WRITE_ONLY ) ) { if ( f.Write( ms.GetData(), ms.GetSize() ) == ms.GetSize() ) { continue; // all ok! } } } // PROBLEM! OUTPUT( "Error writing definition '%s'\n", fileName.Get() ); if ( result != 0 ) { OUTPUT( "Perforce error: %s\n", memErr.Get() ); } ++numProblems; } if ( numProblems > 0 ) { FATALERROR( "Problem writing %u definition(s).\n", numProblems ); } return ( numProblems == 0 ); }
// DoBuild //------------------------------------------------------------------------------ /*virtual*/ Node::BuildResult TestNode::DoBuild( Job * job ) { // If the workingDir is empty, use the current dir for the process const char * workingDir = m_TestWorkingDir.IsEmpty() ? nullptr : m_TestWorkingDir.Get(); EmitCompilationMessage( workingDir ); // spawn the process Process p; bool spawnOK = p.Spawn( GetTestExecutable()->GetName().Get(), m_TestArguments.Get(), workingDir, FBuild::Get().GetEnvironmentString() ); if ( !spawnOK ) { FLOG_ERROR( "Failed to spawn process 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; bool timedOut = !p.ReadAllData( memOut, &memOutSize, memErr, &memErrSize, m_TestTimeOut ); if ( timedOut ) { FLOG_ERROR( "Test timed out after %u ms (%s)", m_TestTimeOut, m_TestExecutable.Get() ); return NODE_RESULT_FAILED; } ASSERT( !p.IsRunning() ); // Get result int result = p.WaitForExit(); if ( result != 0 ) { // something went wrong, print details Node::DumpOutput( job, memOut.Get(), memOutSize ); Node::DumpOutput( job, memErr.Get(), memErrSize ); } // write the test output (saved for pass or fail) FileStream fs; if ( fs.Open( GetName().Get(), FileStream::WRITE_ONLY ) == false ) { FLOG_ERROR( "Failed to open test output file '%s'", GetName().Get() ); return NODE_RESULT_FAILED; } if ( ( memOut.Get() && ( fs.Write( memOut.Get(), memOutSize ) != memOutSize ) ) || ( memErr.Get() && ( fs.Write( memErr.Get(), memErrSize ) != memErrSize ) ) ) { FLOG_ERROR( "Failed to write test output file '%s'", GetName().Get() ); return NODE_RESULT_FAILED; } fs.Close(); // did the test fail? if ( result != 0 ) { FLOG_ERROR( "Test failed (error %i) '%s'", result, GetName().Get() ); return NODE_RESULT_FAILED; } // test passed // we only keep the "last modified" time of the test output for passed tests m_Stamp = FileIO::GetFileLastWriteTime( m_Name ); return NODE_RESULT_OK; }
// 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; }
// 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; }
// DoBuild //------------------------------------------------------------------------------ /*virtual*/ Node::BuildResult CSNode::DoBuild( Job * job ) { // 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; if ( p.Spawn( m_CompilerPath.Get(), fullArgs.GetFinalArgs().Get(), workingDir, environment ) == false ) { FLOG_ERROR( "Failed to spawn process to build '%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 ); // Get result ASSERT( !p.IsRunning() ); int result = p.WaitForExit(); bool ok = ( result == 0 ); if ( !ok ) { // something went wrong, print details Node::DumpOutput( job, memOut.Get(), memOutSize ); Node::DumpOutput( job, memErr.Get(), memErrSize ); goto failed; } if ( !FileIO::FileExists( m_Name.Get() ) ) { FLOG_ERROR( "Object missing despite success for '%s'", GetName().Get() ); return NODE_RESULT_FAILED; } // record new file time m_Stamp = FileIO::GetFileLastWriteTime( m_Name ); return NODE_RESULT_OK; failed: FLOG_ERROR( "Failed to build Object (error %i) '%s'", result, GetName().Get() ); return NODE_RESULT_FAILED; }