// ReadOnly //------------------------------------------------------------------------------ void TestFileIO::ReadOnly() const { // generate a process unique file path AStackString<> path; GenerateTempFileName( path ); // create it FileStream f; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Close(); // should not be read only TEST_ASSERT( FileIO::GetReadOnly( path ) == false ); // set readonly TEST_ASSERT( FileIO::SetReadOnly( path.Get(), true ) == true ); // should be read only TEST_ASSERT( FileIO::GetReadOnly( path ) == true ); // delete should fail TEST_ASSERT( FileIO::FileDelete( path.Get() ) == false ); // clean up TEST_ASSERT( FileIO::SetReadOnly( path.Get(), false ) == true ); TEST_ASSERT( FileIO::GetReadOnly( path ) == false ); TEST_ASSERT( FileIO::FileDelete( path.Get() ) == true ); }
int LaunchSubProcess( const AString & args ) { // try to make a copy of our exe AStackString<> exeName; Env::GetExePath( exeName ); AStackString<> exeNameCopy( exeName ); exeNameCopy += ".copy"; Timer t; while ( FileIO::FileCopy( exeName.Get(), exeNameCopy.Get() ) == false ) { if ( t.GetElapsed() > 5.0f ) { AStackString<> msg; msg.Format( "Failed to make sub-process copy - error: %u (0x%x)\n\nSrc: %s\nDst: %s\n", Env::GetLastErr(), Env::GetLastErr(), exeName.Get(), exeNameCopy.Get() ); ShowMsgBox( msg.Get() ); return -2; } Thread::Sleep( 100 ); } AStackString<> argsCopy( args ); argsCopy += " -subprocess"; // allow subprocess to access the mutex g_OneProcessMutex.Unlock(); Process p; #if defined( __WINDOWS__ ) p.DisableHandleRedirection(); // TODO:MAC TODO:LINUX is this needed? #endif p.Spawn( exeNameCopy.Get(), argsCopy.Get(), nullptr, nullptr ); p.Detach(); return 0; }
REGISTER_TESTS_END // FileExists //------------------------------------------------------------------------------ void TestFileIO::FileExists() const { // generate a process unique file path AStackString<> path; GenerateTempFileName( path ); // ensure doesn't exist FileIO::FileDelete( path.Get() ); // delete in case left over from previous test run TEST_ASSERT( FileIO::FileExists( path.Get() ) == false ); // create it FileStream f; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Close(); // ensure exists TEST_ASSERT( FileIO::FileExists( path.Get() ) == true ); // clean up TEST_ASSERT( FileIO::FileDelete( path.Get() ) == true ); TEST_ASSERT( FileIO::FileExists( path.Get() ) == false ); }
// FileMove //------------------------------------------------------------------------------ void TestFileIO::FileMove() const { // generate a process unique file path AStackString<> path; GenerateTempFileName( path ); // generate copy file name AStackString<> pathCopy( path ); pathCopy += ".copy"; // make sure nothing is left from previous runs FileIO::FileDelete( path.Get() ); FileIO::FileDelete( pathCopy.Get() ); // create it FileStream f; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Close(); // move it TEST_ASSERT( FileIO::FileMove( path, pathCopy ) ); TEST_ASSERT( FileIO::FileExists( path.Get() ) == false ); TEST_ASSERT( FileIO::FileExists( pathCopy.Get() ) == true ); // cleanup VERIFY( FileIO::FileDelete( pathCopy.Get() ) ); }
// StoreVariableString //------------------------------------------------------------------------------ bool BFFParser::StoreVariableString( const AString & name, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter, BFFStackFrame * frame ) { // unescape and subsitute embedded variables AStackString< 2048 > value; if ( PerformVariableSubstitutions( valueStart, valueEnd, value ) == false ) { return false; } // are we concatenating? const BFFVariable * varToConcat = nullptr; if ( *operatorIter == BFF_VARIABLE_CONCATENATION ) { // find existing varToConcat = BFFStackFrame::GetVar( name, frame ); if ( varToConcat == nullptr ) { Error::Error_1026_VariableNotFoundForConcatenation( operatorIter, name ); return false; } // make sure types are compatible if ( varToConcat->IsString() ) { // OK - can concat String to String AStackString< 1024 > finalValue( varToConcat->GetString() ); finalValue += value; BFFStackFrame::SetVarString( name, finalValue, frame ); FLOG_INFO( "Appended '%s' to <String> variable '%s' with result '%s'", value.Get(), name.Get(), finalValue.Get() ); return true; } else if ( varToConcat->IsArrayOfStrings() ) { // OK - can concat String to ArrayOfStrings Array< AString > finalValues( varToConcat->GetArrayOfStrings().GetSize() + 1, false ); finalValues = varToConcat->GetArrayOfStrings(); finalValues.Append( value ); BFFStackFrame::SetVarArrayOfStrings( name, finalValues, frame ); FLOG_INFO( "Appended '%s' to <ArrayOfStrings> variable '%s' with result of %i items", value.Get(), name.Get(), finalValues.GetSize() ); return true; } else { Error::Error_1027_CannotConcatenate( operatorIter, name, varToConcat->GetType(), BFFVariable::VAR_STRING ); return false; } } // handle regular assignment of string BFFStackFrame::SetVarString( name, value, frame ); FLOG_INFO( "Registered <string> variable '%s' with value '%s'", name.Get(), value.Get() ); return true; }
// GetOtherLibrary //------------------------------------------------------------------------------ bool LinkerNode::GetOtherLibrary( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function, Dependencies & libs, const AString & path, const AString & lib, bool & found ) const { found = false; AStackString<> potentialNodeName( path ); if ( !potentialNodeName.IsEmpty() ) { PathUtils::EnsureTrailingSlash( potentialNodeName ); } potentialNodeName += lib; AStackString<> potentialNodeNameClean; NodeGraph::CleanPath( potentialNodeName, potentialNodeNameClean ); // see if a node already exists Node * node = nodeGraph.FindNode( potentialNodeNameClean ); if ( node ) { // aliases not supported - must point to something that provides a file if ( node->IsAFile() == false ) { Error::Error_1103_NotAFile( iter, function, ".LinkerOptions", potentialNodeNameClean, node->GetType() ); return false; } // found existing node libs.Append( Dependency( node ) ); found = true; return true; // no error } // see if the file exists on disk at this location if ( FileIO::FileExists( potentialNodeNameClean.Get() ) ) { node = nodeGraph.CreateFileNode( potentialNodeNameClean ); libs.Append( Dependency( node ) ); found = true; FLOG_INFO( "Additional library '%s' assumed to be '%s'\n", lib.Get(), potentialNodeNameClean.Get() ); return true; // no error } return true; // no error }
// DoToggleSection //------------------------------------------------------------------------------ void Report::DoToggleSection( size_t numMore ) { static int tableId = 0; ++tableId; AStackString<> tableIdStr; tableIdStr.Format( "table%u", tableId ); DoTableStop(); AStackString<> more; if ( numMore ) { more.Format( "%u ", (uint32_t)numMore ); } Write( "<a href='javascript:toggleTable(\"%s\");'>%sMore...</a>\n", tableIdStr.Get(), more.Get() ); DoTableStart( DEFAULT_TABLE_WIDTH, tableIdStr.Get(), true ); // hide table }
// Generate //------------------------------------------------------------------------------ void Report::Generate( const FBuildStats & stats ) { Timer t; // pre-allocate a large string for output m_Output.SetReserved( MEGABYTE ); m_Output.SetLength( 0 ); // generate some common data used in reporting GetLibraryStats( stats ); // build the report CreateHeader(); CreateTitle(); CreateOverview( stats ); DoCPUTimeByType( stats ); DoCacheStats( stats ); DoCPUTimeByLibrary(); DoCPUTimeByItem( stats ); DoIncludes(); CreateFooter(); // patch in time take const float time = t.GetElapsed(); AStackString<> timeTakenBuffer; stats.FormatTime( time, timeTakenBuffer ); char * placeholder = m_Output.Find( "^^^^ " ); memcpy( placeholder, timeTakenBuffer.Get(), timeTakenBuffer.GetLength() ); }
// Format //------------------------------------------------------------------------------ void TestAString::Format() const { // Create a really long input string AStackString<> longInput; const size_t longStringLen( 1024 * 1024 ); for ( size_t i=0; i<longStringLen; ++i ) { longInput += 'A'; } // Make sure we correctly handle formatting large strings AStackString<> buffer; buffer.Format( "%s", longInput.Get() ); TEST_ASSERT( buffer.GetLength() == longStringLen ); TEST_ASSERT( AString::StrLen( buffer.Get() ) == longStringLen ); }
// Load //------------------------------------------------------------------------------ /*static*/ Object * ReflectionInfo::Load( const char * scopedName ) { AStackString<> fullPath; fullPath.Format( "Reflection\\%s.obj", scopedName ); FileStream fs; if ( fs.Open( fullPath.Get(), FileStream::READ_ONLY ) == false ) { return nullptr; } const size_t fileSize = (size_t)fs.GetFileSize(); AutoPtr< char > mem( (char *)Alloc( fileSize + 1 ) ); if ( fs.Read( mem.Get(), fileSize ) != fileSize ) { return nullptr; } mem.Get()[ fileSize ] = 0; ConstMemoryStream ms( mem.Get(), fileSize + 1 ); TextReader tr( ms ); RefObject * refObject = tr.Read(); ASSERT( !refObject || DynamicCast< Object >( refObject ) ); return (Object *)refObject; }
// StartBuild //------------------------------------------------------------------------------ /*static*/ void FLog::StartBuild() { if ( FBuild::Get().GetOptions().m_EnableMonitor ) { // TODO:B Change the monitoring log path // - it's not uniquified per instance // - we already have a .fbuild.tmp folder we should use AStackString<> fullPath; FileIO::GetTempDir( fullPath ); fullPath += "FastBuild/FastBuildLog.log"; ASSERT( g_MonitorFileStream == nullptr ); MutexHolder lock( g_MonitorMutex ); g_MonitorFileStream = new FileStream(); if ( g_MonitorFileStream->Open( fullPath.Get(), FileStream::WRITE_ONLY ) == false ) { delete g_MonitorFileStream; g_MonitorFileStream = nullptr; } Monitor( "START_BUILD %u %u\n", FBUILD_MONITOR_VERSION, Process::GetCurrentId() ); } Tracing::AddCallbackOutput( &TracingOutputCallback ); }
// Load //------------------------------------------------------------------------------ void WorkerSettings::Load() { AStackString<> settingsPath; Env::GetExePath( settingsPath ); settingsPath += ".settings"; FileStream f; if ( f.Open( settingsPath.Get(), FileStream::READ_ONLY ) ) { char header[ 4 ] = { 0 }; f.Read( &header, 4 ); if ( ( header[ 3 ] < FBUILDWORKER_SETTINGS_MIN_VERSION ) || ( header[ 3 ] > FBUILDWORKER_SETTINGS_CURRENT_VERSION ) ) { return; // version is too old, or newer, and cannot be read } // settings uint32_t mode; f.Read( mode ); m_Mode = (Mode)mode; f.Read( m_NumCPUsToUse ); f.Read( m_StartMinimized ); } }
// GetNumProcessors //------------------------------------------------------------------------------ /*static*/ uint32_t Env::GetNumProcessors() { #if defined( __WINDOWS__ ) // Default to NUMBER_OF_PROCESSORS uint32_t numProcessors = 1; AStackString< 32 > var; if ( GetEnvVariable( "NUMBER_OF_PROCESSORS", var ) ) { if ( sscanf_s( var.Get(), "%u", &numProcessors ) != 1 ) { numProcessors = 1; } } return numProcessors; #elif defined( __LINUX__ ) || defined( __APPLE__ ) long numCPUs = sysconf( _SC_NPROCESSORS_ONLN ); if ( numCPUs <= 0 ) { ASSERT( false ); // this should never fail numCPUs = 1; } return ( uint32_t )numCPUs; #else #error Unknown platform #endif }
// Lock //------------------------------------------------------------------------------ bool SystemMutex::TryLock() { #if defined( __WINDOWS__ ) void * handle = (void *)CreateMutex( nullptr, TRUE, m_Name.Get() ); if ( GetLastError() == ERROR_ALREADY_EXISTS ) { if ( ( handle != INVALID_HANDLE_VALUE ) && ( handle != 0 ) ) { CloseHandle( handle ); } return false; } m_Handle = handle; return true; #elif defined( __LINUX__ ) || defined( __APPLE__ ) AStackString<> tempFileName; tempFileName.Format( "/var/run/%s.lock", m_Name.Get() ); m_Handle = open( tempFileName.Get(), O_CREAT | O_RDWR, 0666 ); int rc = flock( m_Handle, LOCK_EX | LOCK_NB ); if ( rc ) { if ( errno == EWOULDBLOCK ) { return false; // locked by another process } } return true; // we own it now #else #error Unknown platform #endif }
// WriteNestedProjects //------------------------------------------------------------------------------ void SLNGenerator::WriteNestedProjects( const Array< AString > & solutionProjectsToFolder, const Array< AString > & solutionFolderPaths ) { if ( solutionProjectsToFolder.GetSize() == 0 && solutionFolderPaths.GetSize() == 0 ) { return; // skip global section } Write( "\tGlobalSection(NestedProjects) = preSolution\r\n" ); // Write every project to solution folder relationships const AString * const solutionProjectsToFolderEnd = solutionProjectsToFolder.End(); for( const AString * it = solutionProjectsToFolder.Begin() ; it != solutionProjectsToFolderEnd ; ++it ) { Write( it->Get() ); } // Write every intermediate path const AString * const solutionFolderPathsEnd = solutionFolderPaths.End(); for( const AString * it = solutionFolderPaths.Begin() ; it != solutionFolderPathsEnd ; ++it ) { // parse solution folder parent path AStackString<> solutionFolderParentGuid; const char * lastSlash = it->FindLast( NATIVE_SLASH ); if ( lastSlash ) { AStackString<> solutionFolderParentPath( it->Get(), lastSlash ); VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderParentGuid, solutionFolderParentPath ); } if ( solutionFolderParentGuid.GetLength() > 0 ) { // generate a guid for the solution folder AStackString<> solutionFolderGuid; VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderGuid, *it ); solutionFolderGuid.ToUpper(); solutionFolderParentGuid.ToUpper(); // write parent solution folder relationship Write( "\t\t%s = %s\r\n", solutionFolderGuid.Get(), solutionFolderParentGuid.Get() ); } } Write( "\tEndGlobalSection\r\n" ); }
// ReadRef //------------------------------------------------------------------------------ bool TextReader::ReadRef() { // Name AStackString<> name; if ( !GetToken( name ) ) { Error( "Missing ref name" ); return false; } AStackString<> value; if ( !GetString( value ) ) { Error( "Missing ref value" ); return false; } Ref< RefObject > ref; if ( value != "null" ) { ref = (RefObject *)ReflectionInfo::CreateObject( value ); if ( ref.Get() == nullptr ) { Error( "Unable to create sub-object" ); ASSERT( false ); // TODO: Handle failure/skipping gracefully return false; } } const StackFrame & sf = m_DeserializationStack.Top(); if ( sf.m_ArrayProperty ) { void * arrayBase = (void *)( (size_t)sf.m_Base + (size_t)sf.m_ArrayProperty->GetOffset() ); Array< Ref< RefObject > > *array = ( Array< Ref< RefObject > > *)( arrayBase ); array->Append( ref ); } else { sf.m_Reflection->SetProperty( sf.m_Base, name.Get(), ref ); } // do sub-object properties if needed if ( ref.Get() ) { StackFrame newFrame; newFrame.m_Base = (void *)ref.Get(); newFrame.m_Reflection = ref->GetReflectionInfoV(); newFrame.m_ArrayProperty = nullptr; #ifdef DEBUG newFrame.m_RefObject = ref.Get(); newFrame.m_Struct = nullptr; #endif m_DeserializationStack.Append( newFrame ); } return true; }
// CreateThreadLocalTmpDir //------------------------------------------------------------------------------ /*static*/ void WorkerThread::CreateThreadLocalTmpDir() { // create isolated subdir AStackString<> tmpFileName; CreateTempFilePath( ".tmp", tmpFileName ); char * lastSlash = tmpFileName.FindLast( NATIVE_SLASH ); tmpFileName.SetLength( (uint32_t)( lastSlash - tmpFileName.Get() ) ); FileIO::EnsurePathExists( tmpFileName ); }
// TestValidExeWithDLL //------------------------------------------------------------------------------ void TestDLL::TestValidExeWithDLL() const { const AStackString<> exe( "../../../../tmp/Test/DLL/exe.exe" ); Process p; p.Spawn( exe.Get(), nullptr, nullptr, nullptr ); int ret = p.WaitForExit(); TEST_ASSERT( ret == 99 ); }
// Work //------------------------------------------------------------------------------ int Worker::Work() { // start listening if ( m_ConnectionPool->Listen( Protocol::PROTOCOL_PORT ) == false ) { ShowMessageBox( "Failed to listen on port %u. Check port is not in use.", Protocol::PROTOCOL_PORT ); return -1; } // Special folder for Orbis Clang // We just create this folder whether it's needed or not { AStackString<> tmpPath; VERIFY( FileIO::GetTempDir( tmpPath ) ); #if defined( __WINDOWS__ ) tmpPath += ".fbuild.tmp\\target\\include"; #else tmpPath += "_fbuild.tmp/target/include"; #endif if ( !FileIO::EnsurePathExists( tmpPath ) ) { ShowMessageBox( "Failed to initialize tmp folder. Error: 0x%x", Env::GetLastErr() ); return -2; } #if defined( __WINDOWS__ ) tmpPath += "\\.lock"; #else tmpPath += "/_lock"; #endif if ( !m_TargetIncludeFolderLock.Open( tmpPath.Get(), FileStream::WRITE_ONLY ) ) { ShowMessageBox( "Failed to lock tmp folder. Error: 0x%x", Env::GetLastErr() ); return -2; } } while ( WorkerWindow::Get().WantToQuit() == false ) { UpdateAvailability(); UpdateUI(); CheckForExeUpdate(); PROFILE_SYNCHRONIZE Thread::Sleep( 100 ); } // allow to UI to shutdown // the application MUST NOT try to update the UI from this point on m_MainWindow->SetAllowQuit(); m_WorkerBrokerage.SetAvailability( false ); return 0; }
// FileDelete //------------------------------------------------------------------------------ void TestFileIO::FileDelete() const { // generate a process unique file path AStackString<> path; GenerateTempFileName( path ); // create it FileStream f; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Close(); // ensure exists TEST_ASSERT( FileIO::FileExists( path.Get() ) == true ); // delete TEST_ASSERT( FileIO::FileDelete( path.Get() ) == true ); TEST_ASSERT( FileIO::FileExists( path.Get() ) == false ); }
PRAGMA_DISABLE_POP_MSVC // warning C6262: Function uses '262212' bytes of stack // DoPieChart //------------------------------------------------------------------------------ void Report::DoPieChart( const Array< PieItem > & items, const char * units ) { AStackString<> buffer; uint32_t height = Math::Max< uint32_t >( 140, 40 + 25 * (uint32_t)items.GetSize() ); m_NumPieCharts++; Write( "<section>\n" ); Write( "<div>\n" ); Write( "<canvas id=\"canvas%u\" width=\"500\" height=\"%u\">\n", m_NumPieCharts, height ); Write( "HTML5 Canvas support required.\n" ); Write( "</canvas>\n" ); Write( "</div>\n" ); Write( "<script type=\"text/javascript\">\n" ); Write( " var myData = [" ); for ( size_t i=0; i<items.GetSize(); ++i ) { if ( i > 0 ) { Write( "," ); } buffer.Format( "%2.3f", items[ i ].value ); Write( buffer.Get() ); } Write( "];\n" ); Write( " var myLabels = [" ); for ( size_t i=0; i<items.GetSize(); ++i ) { if ( i > 0 ) { Write( "," ); } Write( "\"%s\"", items[ i ].label ); } Write( "];\n" ); Write( " var myColors = [" ); for ( size_t i=0; i<items.GetSize(); ++i ) { if ( i > 0 ) { Write( "," ); } Write( "\"#%x\"", items[ i ].color ); } Write( "];\n" ); Write( " plotData(\"canvas%u\",myData,myLabels,myColors,\"%s\");\n", m_NumPieCharts, units ); Write( "</script>\n" ); Write( "</section>\n" ); }
// StopBuild //------------------------------------------------------------------------------ /*static*/ void FLog::StopBuild() { Tracing::RemoveCallbackOutput( &TracingOutputCallback ); if ( s_ShowProgress ) { fputs( g_ClearLineString.Get(), stdout ); m_ProgressText.Clear(); } }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionSettings::Commit( NodeGraph & /*nodeGraph*/, const BFFIterator & funcStartIter ) const { // using a cache plugin? AStackString<> cachePluginDLL; if ( !GetString( funcStartIter, cachePluginDLL, ".CachePluginDLL" ) ) { return false; } FBuild::Get().SetCachePluginDLL( cachePluginDLL ); if ( !cachePluginDLL.IsEmpty() ) { FLOG_INFO( "CachePluginDLL: '%s'", cachePluginDLL.Get() ); } // try to get the cache path from the config const BFFVariable * cachePathVar; if ( !GetString( funcStartIter, cachePathVar, ".CachePath" ) ) { return false; } if ( cachePathVar ) { s_CachePath = cachePathVar->GetString(); // override environment default only if not empty if ( s_CachePath.IsEmpty() == false ) { FBuild::Get().SetCachePath( s_CachePath ); } } // "Workers" Array< AString > workerList; if ( !GetStrings( funcStartIter, workerList, ".Workers" ) ) { return false; } if ( !workerList.IsEmpty() ) { FBuild::Get().SetWorkerList( workerList ); } // "Environment" Array< AString > environment; if ( !GetStrings( funcStartIter, environment, ".Environment" ) ) { return false; } if ( !environment.IsEmpty() ) { ProcessEnvironment( environment ); } return true; }
// Build //------------------------------------------------------------------------------ /*static*/ void FLog::Build( const char * formatString, ... ) { AStackString< 8192 > buffer; va_list args; va_start(args, formatString); buffer.VFormat( formatString, args ); va_end( args ); Tracing::Output( buffer.Get() ); }
// Info //------------------------------------------------------------------------------ /*static*/ void FLog::Info( const char * formatString, ... ) { AStackString< 8192 > buffer; va_list args; va_start(args, formatString); buffer.VFormat( formatString, args ); va_end( args ); Output( "Info:", buffer.Get() ); }
// UpdateUI //------------------------------------------------------------------------------ void Worker::UpdateUI() { // throttle UI updates if ( m_UIUpdateTimer.GetElapsed() < 0.25f ) { return; } // title bar size_t numConnections = m_ConnectionPool->GetNumConnections(); AStackString<> status; status.Format( "%u Connections", (uint32_t)numConnections ); if ( m_RestartNeeded ) { status += " (Restart Pending)"; } #if defined( __WINDOWS__ ) if ( m_LastDiskSpaceResult == 0 ) { status += " (Low Disk Space)"; } #endif m_MainWindow->SetStatus( status.Get() ); // thread output JobQueueRemote & jqr = JobQueueRemote::Get(); const size_t numWorkers = jqr.GetNumWorkers(); for ( size_t i=0; i<numWorkers; ++i ) { // get status of worker AStackString<> workerStatus; AStackString<> hostName; bool isIdle; jqr.GetWorkerStatus( i, hostName, workerStatus, isIdle ); // are we syncing tools? if ( isIdle ) { AStackString<> statusStr; if ( m_ConnectionPool->IsSynchingTool( statusStr ) ) { // show status of synchronization workerStatus = statusStr; } } // reflect in UI m_MainWindow->SetWorkerState( i, hostName, workerStatus ); } m_UIUpdateTimer.Start(); }
// Monitor //------------------------------------------------------------------------------ /*static*/ void FLog::Monitor( const char * formatString, ... ) { // Is monitoring enabled? if ( g_MonitorFileStream == nullptr ) { return; // No - nothing to do } PROFILE_SECTION( "FLog::Monitor" ) AStackString< 1024 > buffer; va_list args; va_start( args, formatString ); buffer.VFormat( formatString, args ); va_end( args ); AStackString< 1024 > finalBuffer; finalBuffer.Format( "%llu %s", Time::GetCurrentFileTime(), buffer.Get() ); MutexHolder lock( g_MonitorMutex ); g_MonitorFileStream->WriteBuffer( finalBuffer.Get(), finalBuffer.GetLength() ); }
// Publish //------------------------------------------------------------------------------ /*virtual*/ bool Cache::Publish( const AString & cacheId, const void * data, size_t dataSize ) { AStackString<> cacheFileName; GetCacheFileName( cacheId, cacheFileName ); // make sure the cache output path exists char * lastSlash = cacheFileName.FindLast( NATIVE_SLASH ); *lastSlash = 0; if ( !FileIO::EnsurePathExists( cacheFileName ) ) { return false; } *lastSlash = NATIVE_SLASH; // open output cache (tmp) file AStackString<> cacheFileTmpName( cacheFileName ); cacheFileTmpName += ".tmp"; FileStream cacheTmpFile; if( !cacheTmpFile.Open( cacheFileTmpName.Get(), FileStream::WRITE_ONLY ) ) { return false; } // write data bool cacheTmpWriteOk = ( cacheTmpFile.Write( data, dataSize ) == dataSize ); cacheTmpFile.Close(); if ( !cacheTmpWriteOk ) { // failed to write to cache tmp file FileIO::FileDelete( cacheFileTmpName.Get() ); // try to cleanup failure return false; } // rename tmp file to real file if ( FileIO::FileMove( cacheFileTmpName, cacheFileName ) == false ) { // try to delete (possibly) existing file FileIO::FileDelete( cacheFileName.Get() ); // try rename again if ( FileIO::FileMove( cacheFileTmpName, cacheFileName ) == false ) { // problem renaming file FileIO::FileDelete( cacheFileTmpName.Get() ); // try to cleanup tmp file return false; } } return true; }
// FileTime //------------------------------------------------------------------------------ void TestFileIO::FileTime() const { // generate a process unique file path AStackString<> path; GenerateTempFileName( path ); // create it FileStream f; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Close(); // get last write time const uint64_t oldTime = FileIO::GetFileLastWriteTime( path ); TEST_ASSERT( oldTime != 0 ); // wait for some time that is bigger than filesystem time granularity #if defined( __OSX__ ) // HFS+ has surprisingly poor time resolution (1 second) Thread::Sleep( 1100 ); #else Thread::Sleep( 500 ); #endif // modify file FileStream f2; TEST_ASSERT( f.Open( path.Get(), FileStream::WRITE_ONLY ) == true ); f.Write( (uint32_t)0 ); f.Close(); // get new last write time const uint64_t newTime = FileIO::GetFileLastWriteTime( path ); TEST_ASSERT( newTime > oldTime ); // manually set time back TEST_ASSERT( FileIO::SetFileLastWriteTime( path, oldTime ) == true ); uint64_t timeNow = FileIO::GetFileLastWriteTime( path ); TEST_ASSERT( timeNow == oldTime ); }
// AStackStringOverflow //------------------------------------------------------------------------------ void TestAString::AStackStringOverflow() const { { // constructor with string longer than buffer AStackString< 8 > string( "01234567890123456789" ); TEST_ASSERT( string.GetLength() == 20 ); TEST_ASSERT( string.GetLength() == AString::StrLen( string.Get() ) ); } { // assigned of string longer than buffer AStackString< 8 > string; string = "01234567890123456789"; TEST_ASSERT( string.GetLength() == 20 ); TEST_ASSERT( string.GetLength() == AString::StrLen( string.Get() ) ); } { // concetentation of string longer than buffer AStackString< 8 > string; string += "01234567890123456789"; TEST_ASSERT( string.GetLength() == 20 ); TEST_ASSERT( string.GetLength() == AString::StrLen( string.Get() ) ); } }