// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionLibrary::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { // make sure all required variables are defined const BFFVariable * outputLib; const BFFVariable * compiler; const BFFVariable * compilerOptions; AStackString<> compilerOptionsDeoptimized; AStackString<> compilerOutputPath; AStackString<> compilerOutputPrefix; const BFFVariable * compilerOutputExtension; const BFFVariable * librarian; const BFFVariable * librarianOptions; if ( !GetString( funcStartIter, outputLib, ".LibrarianOutput", true ) || !GetString( funcStartIter, compiler, ".Compiler", true ) || !GetString( funcStartIter, compilerOptions, ".CompilerOptions", true ) || !GetString( funcStartIter, compilerOptionsDeoptimized, ".CompilerOptionsDeoptimized", false ) || !GetString( funcStartIter, compilerOutputPath, ".CompilerOutputPath", true ) || !GetString( funcStartIter, compilerOutputPrefix, ".CompilerOutputPrefix", false ) || !GetString( funcStartIter, compilerOutputExtension, ".CompilerOutputExtension", false ) || !GetString( funcStartIter, librarian, ".Librarian", true ) || !GetString( funcStartIter, librarianOptions, ".LibrarianOptions", true ) ) { return false; } PathUtils::FixupFolderPath( compilerOutputPath ); // find or create the compiler node CompilerNode * compilerNode = nullptr; if ( !FunctionObjectList::GetCompilerNode( nodeGraph, funcStartIter, compiler->GetString(), compilerNode ) ) { return false; // GetCompilerNode will have emitted error } // Compiler Force Using Dependencies compilerForceUsing; if ( !GetNodeList( nodeGraph, funcStartIter, ".CompilerForceUsing", compilerForceUsing, false ) ) { return false; // GetNodeList will have emitted an error } // de-optimization setting bool deoptimizeWritableFiles = false; bool deoptimizeWritableFilesWithToken = false; if ( !GetBool( funcStartIter, deoptimizeWritableFiles, ".DeoptimizeWritableFiles", false, false ) ) { return false; // GetBool will have emitted error } if ( !GetBool( funcStartIter, deoptimizeWritableFilesWithToken, ".DeoptimizeWritableFilesWithToken", false, false ) ) { return false; // GetBool will have emitted error } if ( ( deoptimizeWritableFiles || deoptimizeWritableFilesWithToken ) && compilerOptionsDeoptimized.IsEmpty() ) { Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".CompilerOptionsDeoptimized" ) ); return false; } // cache & distribution control bool allowDistribution( true ); bool allowCaching( true ); if ( !GetBool( funcStartIter, allowDistribution, ".AllowDistribution", true ) || !GetBool( funcStartIter, allowCaching, ".AllowCaching", true ) ) { return false; // GetBool will have emitted error } // Precompiled Header support ObjectNode * precompiledHeaderNode = nullptr; AStackString<> compilerOutputExtensionStr( compilerOutputExtension ? compilerOutputExtension->GetString().Get() : ".obj" ); if ( !GetPrecompiledHeaderNode( nodeGraph, funcStartIter, compilerNode, compilerOptions, compilerForceUsing, precompiledHeaderNode, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken, allowDistribution, allowCaching, compilerOutputExtensionStr ) ) { return false; // GetPrecompiledHeaderNode will have emitted error } // Sanity check compile flags const bool usingPCH = ( precompiledHeaderNode != nullptr ); uint32_t objFlags = ObjectNode::DetermineFlags( compilerNode, compilerOptions->GetString(), false, usingPCH ); if ( ( objFlags & ObjectNode::FLAG_MSVC ) && ( objFlags & ObjectNode::FLAG_CREATING_PCH ) ) { // must not specify use of precompiled header (must use the PCH specific options) Error::Error_1303_PCHCreateOptionOnlyAllowedOnPCH( funcStartIter, this, "Yc", "CompilerOptions" ); return false; } // Check input/output for Compiler { bool hasInputToken = false; bool hasOutputToken = false; bool hasCompileToken = false; const AString & args = compilerOptions->GetString(); Array< AString > tokens; args.Tokenize( tokens ); for ( const AString & token : tokens ) { if ( token.Find( "%1" ) ) { hasInputToken = true; } else if ( token.Find( "%2" ) ) { hasOutputToken = true; } else { if ( objFlags & ObjectNode::FLAG_MSVC ) { if ( ( token == "/c" ) || ( token == "-c" ) ) { hasCompileToken = true; } } else { if ( token == "-c" ) { hasCompileToken = true; } } } } if ( hasInputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%1" ); return false; } if ( hasOutputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%2" ); return false; } // check /c or -c if ( objFlags & ObjectNode::FLAG_MSVC ) { if ( hasCompileToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c or -c" ); return false; } } else if ( objFlags & ( ObjectNode::FLAG_SNC | ObjectNode::FLAG_GCC | ObjectNode::FLAG_CLANG ) ) { if ( hasCompileToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "-c" ); return false; } } } // Check input/output for Librarian { const AString & args = librarianOptions->GetString(); bool hasInputToken = ( args.Find( "%1" ) || args.Find( "\"%1\"" ) ); if ( hasInputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".LibrarianOptions", "%1" ); return false; } bool hasOutputToken = ( args.Find( "%2" ) || args.Find( "\"%2\"" ) ); if ( hasOutputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".LibrarianOptions", "%2" ); return false; } } // Get the (optional) Preprocessor & PreprocessorOptions const BFFVariable * preprocessor = nullptr; const BFFVariable * preprocessorOptions = nullptr; CompilerNode * preprocessorNode = nullptr; if ( !GetString( funcStartIter, preprocessor, ".Preprocessor", false ) ) { return false; // GetString will have emitted an error } if ( preprocessor ) { // get the preprocessor executable if ( !FunctionObjectList::GetCompilerNode( nodeGraph, funcStartIter, preprocessor->GetString(), preprocessorNode ) ) { return false; // GetCompilerNode will have emitted an error } // get the command line args for the preprocessor if ( !GetString( funcStartIter, preprocessorOptions, ".PreprocessorOptions", true ) ) // required { return false; // GetString will have emitted an error } } // Pre-build dependencies Dependencies preBuildDependencies; if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) ) { return false; // GetNodeList will have emitted an error } Dependencies staticDeps( 32, true ); if ( !GetInputs( nodeGraph, funcStartIter, staticDeps ) ) { return false; // GetStaticDeps will gave emitted error } // are the additional inputs to merge into the libaray? Dependencies additionalInputs; if ( !GetNodeList( nodeGraph, funcStartIter, ".LibrarianAdditionalInputs", additionalInputs, false ) ) { return false;// helper will emit error } if ( staticDeps.IsEmpty() && additionalInputs.IsEmpty() ) { Error::Error_1006_NothingToBuild( funcStartIter, this ); return false; } uint32_t flags = LibraryNode::DetermineFlags( librarian->GetString() ); // Create library node which depends on the single file or list if ( nodeGraph.FindNode( outputLib->GetString() ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, outputLib->GetString() ); return false; } AStackString<> baseDirectory; if ( !GetBaseDirectory( funcStartIter, baseDirectory ) ) { return false; // GetBaseDirectory will have emitted error } AStackString<> extraPDBPath, extraASMPath; GetExtraOutputPaths( compilerOptions->GetString(), extraPDBPath, extraASMPath ); LibraryNode * libNode = nodeGraph.CreateLibraryNode( outputLib->GetString(), staticDeps, compilerNode, compilerOptions->GetString(), compilerOptionsDeoptimized, compilerOutputPath, librarian->GetString(), librarianOptions->GetString(), flags, precompiledHeaderNode, compilerForceUsing, preBuildDependencies, additionalInputs, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken, allowDistribution, allowCaching, preprocessorNode, preprocessorOptions ? preprocessorOptions->GetString() : AString::GetEmpty(), baseDirectory ); if ( compilerOutputExtension ) { libNode->m_ObjExtensionOverride = compilerOutputExtension->GetString(); } libNode->m_CompilerOutputPrefix = compilerOutputPrefix; libNode->m_ExtraPDBPath = extraPDBPath; libNode->m_ExtraASMPath = extraASMPath; return ProcessAlias( nodeGraph, funcStartIter, libNode ); }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionObjectList::Commit( const BFFIterator & funcStartIter ) const { // make sure all required variables are defined const BFFVariable * compiler; const BFFVariable * compilerOptions; AStackString<> compilerOptionsDeoptimized; AStackString<> compilerOutputPath; AStackString<> compilerOutputPrefix; const BFFVariable * compilerOutputExtension; if ( !GetString( funcStartIter, compiler, ".Compiler", true ) || !GetString( funcStartIter, compilerOptions, ".CompilerOptions", true ) || !GetString( funcStartIter, compilerOptionsDeoptimized, ".CompilerOptionsDeoptimized", false ) || !GetString( funcStartIter, compilerOutputPath, ".CompilerOutputPath", true ) || !GetString( funcStartIter, compilerOutputPrefix, ".CompilerOutputPrefix", false ) || !GetString( funcStartIter, compilerOutputExtension, ".CompilerOutputExtension", false ) ) { return false; } PathUtils::FixupFolderPath( compilerOutputPath ); NodeGraph & ng = FBuild::Get().GetDependencyGraph(); // find or create the compiler node CompilerNode * compilerNode = nullptr; if ( !FunctionObjectList::GetCompilerNode( funcStartIter, compiler->GetString(), compilerNode ) ) { return false; // GetCompilerNode will have emitted error } // Sanity check compile flags uint32_t objFlags = ObjectNode::DetermineFlags( compilerNode, compilerOptions->GetString() ); if ( ( objFlags & ObjectNode::FLAG_MSVC ) && ( objFlags & ObjectNode::FLAG_CREATING_PCH ) ) { // must not specify use of precompiled header (must use the PCH specific options) Error::Error_1303_PCHCreateOptionOnlyAllowedOnPCH( funcStartIter, this, "/Yc", "CompilerOptions" ); return false; } // Check input/output for Compiler { const AString & args = compilerOptions->GetString(); bool hasInputToken = ( args.Find( "%1" ) || args.Find( "\"%1\"" ) ); if ( hasInputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%1" ); return false; } bool hasOutputToken = ( args.Find( "%2" ) || args.Find( "\"%2\"" ) ); if ( hasOutputToken == false ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%2" ); return false; } // check /c or -c if ( objFlags & ObjectNode::FLAG_MSVC ) { if ( args.Find( "/c" ) == nullptr && args.Find( "-c" ) == nullptr) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c or -c" ); return false; } } else if ( objFlags & ( ObjectNode::FLAG_SNC | ObjectNode::FLAG_GCC | ObjectNode::FLAG_CLANG ) ) { if ( args.Find( "-c" ) == nullptr ) { Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "-c" ); return false; } } } // Compiler Force Using Dependencies compilerForceUsing; if ( !GetNodeList( funcStartIter, ".CompilerForceUsing", compilerForceUsing, false ) ) { return false; // GetNodeList will have emitted an error } // Get the (optional) Preprocessor & PreprocessorOptions const BFFVariable * preprocessor = nullptr; const BFFVariable * preprocessorOptions = nullptr; CompilerNode * preprocessorNode = nullptr; if ( !GetString( funcStartIter, preprocessor, ".Preprocessor", false ) ) { return false; // GetString will have emitted an error } if ( preprocessor ) { // get the preprocessor executable if ( !FunctionObjectList::GetCompilerNode( funcStartIter, preprocessor->GetString(), preprocessorNode ) ) { return false; // GetCompilerNode will have emitted an error } // get the command line args for the preprocessor if ( !GetString( funcStartIter, preprocessorOptions, ".PreprocessorOptions", true ) ) // required { return false; // GetString will have emitted an error } } // Pre-build dependencies Dependencies preBuildDependencies; if ( !GetNodeList( funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) ) { return false; // GetNodeList will have emitted an error } // de-optimization setting bool deoptimizeWritableFiles = false; bool deoptimizeWritableFilesWithToken = false; if ( !GetBool( funcStartIter, deoptimizeWritableFiles, ".DeoptimizeWritableFiles", false, false ) ) { return false; // GetBool will have emitted error } if ( !GetBool( funcStartIter, deoptimizeWritableFilesWithToken, ".DeoptimizeWritableFilesWithToken", false, false ) ) { return false; // GetBool will have emitted error } if ( ( deoptimizeWritableFiles || deoptimizeWritableFilesWithToken ) && compilerOptionsDeoptimized.IsEmpty() ) { Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".CompilerOptionsDeoptimized" ) ); return false; } // Precompiled Header support ObjectNode * precompiledHeaderNode = nullptr; if ( !GetPrecompiledHeaderNode( funcStartIter, compilerNode, objFlags, compilerOptions, compilerForceUsing, precompiledHeaderNode, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken ) ) { return false; // GetPrecompiledHeaderNode will have emitted error } Dependencies staticDeps( 32, true ); if ( !GetInputs( funcStartIter, staticDeps ) ) { return false; // GetStaticDeps will gave emitted error } if ( staticDeps.IsEmpty() ) { Error::Error_1006_NothingToBuild( funcStartIter, this ); return false; } // parsing logic should guarantee we have a string for our name ASSERT( m_AliasForFunction.IsEmpty() == false ); // Check for existing node if ( ng.FindNode( m_AliasForFunction ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, m_AliasForFunction ); return false; } // Create library node which depends on the single file or list ObjectListNode * o = ng.CreateObjectListNode( m_AliasForFunction, staticDeps, compilerNode, compilerOptions->GetString(), compilerOptionsDeoptimized, compilerOutputPath, precompiledHeaderNode, compilerForceUsing, preBuildDependencies, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken, preprocessorNode, preprocessorOptions ? preprocessorOptions->GetString() : AString::GetEmpty() ); if ( compilerOutputExtension ) { o->m_ObjExtensionOverride = compilerOutputExtension->GetString(); } o->m_CompilerOutputPrefix = compilerOutputPrefix; return true; }