// CreateDynamicObjectNode //------------------------------------------------------------------------------ bool ObjectListNode::CreateDynamicObjectNode( NodeGraph & nodeGraph, Node * inputFile, const AString & baseDir, bool isUnityNode, bool isIsolatedFromUnityNode ) { const AString & fileName = inputFile->GetName(); // Transform src file to dst object path // get file name only (no path, no ext) const char * lastSlash = fileName.FindLast( NATIVE_SLASH ); lastSlash = lastSlash ? ( lastSlash + 1 ) : fileName.Get(); const char * lastDot = fileName.FindLast( '.' ); lastDot = lastDot && ( lastDot > lastSlash ) ? lastDot : fileName.GetEnd(); // if source comes from a directory listing, use path relative to dirlist base // to replicate the folder hierearchy in the output AStackString<> subPath; if ( baseDir.IsEmpty() == false ) { ASSERT( NodeGraph::IsCleanPath( baseDir ) ); if ( PathUtils::PathBeginsWith( fileName, baseDir ) ) { // ... use everything after that subPath.Assign( fileName.Get() + baseDir.GetLength(), lastSlash ); // includes last slash } } else { if ( !m_BaseDirectory.IsEmpty() && PathUtils::PathBeginsWith( fileName, m_BaseDirectory ) ) { // ... use everything after that subPath.Assign( fileName.Get() + m_BaseDirectory.GetLength(), lastSlash ); // includes last slash } } AStackString<> fileNameOnly( lastSlash, lastDot ); AStackString<> objFile( m_CompilerOutputPath ); objFile += subPath; objFile += m_CompilerOutputPrefix; objFile += fileNameOnly; objFile += GetObjExtension(); // Create an ObjectNode to compile the above file // and depend on that Node * on = nodeGraph.FindNode( objFile ); if ( on == nullptr ) { // determine flags - TODO:B Move DetermineFlags call out of build-time const bool usingPCH = ( m_PrecompiledHeader != nullptr ); uint32_t flags = ObjectNode::DetermineFlags( m_Compiler, m_CompilerArgs, false, usingPCH ); if ( isUnityNode ) { flags |= ObjectNode::FLAG_UNITY; } if ( isIsolatedFromUnityNode ) { flags |= ObjectNode::FLAG_ISOLATED_FROM_UNITY; } uint32_t preprocessorFlags = 0; if ( m_Preprocessor ) { // determine flags - TODO:B Move DetermineFlags call out of build-time preprocessorFlags = ObjectNode::DetermineFlags( m_Preprocessor, m_PreprocessorArgs, false, usingPCH ); } on = nodeGraph.CreateObjectNode( objFile, inputFile, m_Compiler, m_CompilerArgs, m_CompilerArgsDeoptimized, m_PrecompiledHeader, flags, m_CompilerForceUsing, m_DeoptimizeWritableFiles, m_DeoptimizeWritableFilesWithToken, m_AllowDistribution, m_AllowCaching, m_Preprocessor, m_PreprocessorArgs, preprocessorFlags ); } else if ( on->GetType() != Node::OBJECT_NODE ) { FLOG_ERROR( "Node '%s' is not an ObjectNode (type: %s)", on->GetName().Get(), on->GetTypeName() ); return false; } else { ObjectNode * other = on->CastTo< ObjectNode >(); if ( inputFile != other->GetSourceFile() ) { FLOG_ERROR( "Conflicting objects found:\n" " File A: %s\n" " File B: %s\n" " Both compile to: %s\n", inputFile->GetName().Get(), other->GetSourceFile()->GetName().Get(), objFile.Get() ); return false; } } m_DynamicDependencies.Append( Dependency( on ) ); return true; }
// GetPrecompiledHeaderNode //------------------------------------------------------------------------------ bool FunctionObjectList::GetPrecompiledHeaderNode( NodeGraph & nodeGraph, const BFFIterator & iter, CompilerNode * compilerNode, const BFFVariable * compilerOptions, const Dependencies & compilerForceUsing, ObjectNode * & precompiledHeaderNode, bool deoptimizeWritableFiles, bool deoptimizeWritableFilesWithToken, bool allowDistribution, bool allowCaching, const AString& compilerOutputExtension ) const { const BFFVariable * pchInputFile = nullptr; const BFFVariable * pchOutputFile = nullptr; const BFFVariable * pchOptions = nullptr; if ( !GetString( iter, pchInputFile, ".PCHInputFile" ) || !GetString( iter, pchOutputFile, ".PCHOutputFile" ) || !GetString( iter, pchOptions, ".PCHOptions" ) ) { return false; } precompiledHeaderNode = nullptr; AStackString<> pchObjectName; if ( pchInputFile ) { if ( !pchOutputFile || !pchOptions ) { Error::Error_1300_MissingPCHArgs( iter, this ); return false; } Node * pchInputNode = nodeGraph.FindNode( pchInputFile->GetString() ); if ( pchInputNode ) { // is it a file? if ( pchInputNode->IsAFile() == false ) { Error::Error_1103_NotAFile( iter, this, "PCHInputFile", pchInputNode->GetName(), pchInputNode->GetType() ); return false; } } else { // Create input node pchInputNode = nodeGraph.CreateFileNode( pchInputFile->GetString() ); } if ( nodeGraph.FindNode( pchOutputFile->GetString() ) ) { Error::Error_1301_AlreadyDefinedPCH( iter, this, pchOutputFile->GetString().Get() ); return false; } uint32_t pchFlags = ObjectNode::DetermineFlags( compilerNode, pchOptions->GetString(), true, false ); if ( pchFlags & ObjectNode::FLAG_MSVC ) { // sanity check arguments bool foundYcInPCHOptions = false; bool foundFpInPCHOptions = false; // Find /Fo option to obtain pch object file name Array< AString > pchTokens; pchOptions->GetString().Tokenize( pchTokens ); for ( const AString & token : pchTokens ) { if ( ObjectNode::IsStartOfCompilerArg_MSVC( token, "Fo" ) ) { // Extract filename (and remove quotes if found) pchObjectName = token.Get() + 3; pchObjectName.Trim( pchObjectName.BeginsWith( '"' ) ? 1 : 0, pchObjectName.EndsWith( '"' ) ? 1 : 0 ); // Auto-generate name? if ( pchObjectName == "%3" ) { // example 'PrecompiledHeader.pch' to 'PrecompiledHeader.pch.obj' pchObjectName = pchOutputFile->GetString(); pchObjectName += compilerOutputExtension; } } else if ( ObjectNode::IsStartOfCompilerArg_MSVC( token, "Yc" ) ) { foundYcInPCHOptions = true; } else if ( ObjectNode::IsStartOfCompilerArg_MSVC( token, "Fp" ) ) { foundFpInPCHOptions = true; } } // PCH must have "Create PCH" (e.g. /Yc"PrecompiledHeader.h") if ( foundYcInPCHOptions == false ) { Error::Error_1302_MissingPCHCompilerOption( iter, this, "Yc", "PCHOptions" ); return false; } // PCH must have "Precompiled Header to Use" (e.g. /Fp"PrecompiledHeader.pch") if ( foundFpInPCHOptions == false ) { Error::Error_1302_MissingPCHCompilerOption( iter, this, "Fp", "PCHOptions" ); return false; } // PCH must have object output option (e.g. /Fo"PrecompiledHeader.obj") if ( pchObjectName.IsEmpty() ) { Error::Error_1302_MissingPCHCompilerOption( iter, this, "Fo", "PCHOptions" ); return false; } // Check Compiler Options bool foundYuInCompilerOptions = false; bool foundFpInCompilerOptions = false; Array< AString > compilerTokens; compilerOptions->GetString().Tokenize( compilerTokens ); for ( const AString & token : compilerTokens ) { if ( ObjectNode::IsStartOfCompilerArg_MSVC( token, "Yu" ) ) { foundYuInCompilerOptions = true; } else if ( ObjectNode::IsStartOfCompilerArg_MSVC( token, "Fp" ) ) { foundFpInCompilerOptions = true; } } // Object using the PCH must have "Use PCH" option (e.g. /Yu"PrecompiledHeader.h") if ( foundYuInCompilerOptions == false ) { Error::Error_1302_MissingPCHCompilerOption( iter, this, "Yu", "CompilerOptions" ); return false; } // Object using the PCH must have "Precompiled header to use" (e.g. /Fp"PrecompiledHeader.pch") if ( foundFpInCompilerOptions == false ) { Error::Error_1302_MissingPCHCompilerOption( iter, this, "Fp", "CompilerOptions" ); return false; } } precompiledHeaderNode = nodeGraph.CreateObjectNode( pchOutputFile->GetString(), pchInputNode, compilerNode, pchOptions->GetString(), AString::GetEmpty(), nullptr, pchFlags, compilerForceUsing, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken, allowDistribution, allowCaching, nullptr, AString::GetEmpty(), 0 ); // preprocessor args not supported precompiledHeaderNode->m_PCHObjectFileName = pchObjectName; } return true; }