// GetNodeList //------------------------------------------------------------------------------ bool Function::GetNodeList( NodeGraph & nodeGraph, const BFFIterator & iter, const char * propertyName, Dependencies & nodes, bool required, bool allowCopyDirNodes, bool allowUnityNodes, bool allowRemoveDirNodes ) const { ASSERT( propertyName ); const BFFVariable * var = BFFStackFrame::GetVar( propertyName ); if ( !var ) { // missing if ( required ) { Error::Error_1101_MissingProperty( iter, this, AStackString<>( propertyName ) ); return false; // required! } return true; // missing but not required } if ( var->IsArrayOfStrings() ) { // an array of references const Array< AString > & nodeNames = var->GetArrayOfStrings(); nodes.SetCapacity( nodes.GetSize() + nodeNames.GetSize() ); for ( const AString & nodeName : nodeNames ) { if ( nodeName.IsEmpty() ) { Error::Error_1004_EmptyStringPropertyNotAllowed( iter, this, propertyName ); return false; } if ( !GetNodeList( nodeGraph, iter, this, propertyName, nodeName, nodes, allowCopyDirNodes, allowUnityNodes, allowRemoveDirNodes ) ) { // child func will have emitted error return false; } } } else if ( var->IsString() ) { if ( var->GetString().IsEmpty() ) { Error::Error_1004_EmptyStringPropertyNotAllowed( iter, this, propertyName ); return false; } if ( !GetNodeList( nodeGraph, iter, this, propertyName, var->GetString(), nodes, allowCopyDirNodes, allowUnityNodes, allowRemoveDirNodes ) ) { // child func will have emitted error return false; } } else { // unsupported type Error::Error_1050_PropertyMustBeOfType( iter, this, propertyName, var->GetType(), BFFVariable::VAR_STRING, BFFVariable::VAR_ARRAY_OF_STRINGS ); return false; } return true; }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionCopyDir::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { // Get input paths Array< AString > inputPaths; if ( !GetFolderPaths( funcStartIter, inputPaths, ".SourcePaths", true ) ) { return false; // GetFolderPaths will have emitted an error } // get the optional params Array< AString > patterns; bool recurse = true; Array< AString > excludePaths; if ( !GetStrings( funcStartIter, patterns, ".SourcePathsPattern" ) || !GetBool( funcStartIter, recurse, ".SourcePathsRecurse", true ) || // recursive by default !GetStrings( funcStartIter, excludePaths, ".SourceExcludePaths" ) ) { return false; // Get* will have emitted error } // convert input paths to DirectoryListNodes Dependencies staticDeps( inputPaths.GetSize() ); if ( !GetDirectoryListNodeList( nodeGraph, funcStartIter, inputPaths, excludePaths, Array< AString >(), Array< AString >(), recurse, patterns.IsEmpty() ? nullptr : &patterns, ".SourcePaths", staticDeps ) ) { return false; // GetDirectoryListNodeList will have emitted an error } // Get output path AStackString<> destPath; if ( !GetString( funcStartIter, destPath, ".Dest", true ) ) { return false; } NodeGraph::CleanPath( destPath ); // Pre-build dependencies Dependencies preBuildDeps; if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDeps, false ) ) { return false; // GetNodeList will have emitted an error } // sanity check we defined something useful if ( staticDeps.IsEmpty() ) { Error::Error_1006_NothingToBuild( funcStartIter, this ); return false; } // check node doesn't already exist if ( nodeGraph.FindNode( m_AliasForFunction ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, m_AliasForFunction ); return false; } // create our node nodeGraph.CreateCopyDirNode( m_AliasForFunction, staticDeps, destPath, preBuildDeps ); return true; }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionCompiler::Commit( const BFFIterator & funcStartIter ) const { const BFFVariable * executableV; if ( !GetString( funcStartIter, executableV, ".Executable", true ) ) { return false; // GetString will have emitted error } // path cleanup AStackString<> exe; NodeGraph::CleanPath( executableV->GetString(), exe ); // exe must be a file, not a path if ( PathUtils::IsFolderPath( exe ) ) { Error::Error_1105_PathNotAllowed( funcStartIter, this, ".Executable", exe ); return false; } // Dependencies extraFiles( 32, true ); if ( !GetNodeList( funcStartIter, ".ExtraFiles", extraFiles, false ) ) // optional { return false; // GetNodeList will have emitted an error } // get executable node NodeGraph & ng = FBuild::Get().GetDependencyGraph(); if ( ng.FindNode( exe ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, exe ); return false; } // create our node const bool allowDistribution = true; Node * compilerNode = ng.CreateCompilerNode( exe, extraFiles, allowDistribution ); // handle alias creation return ProcessAlias( funcStartIter, compilerNode ); }
// 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; }
// GetInputs //------------------------------------------------------------------------------ bool FunctionObjectList::GetInputs( const BFFIterator & iter, Dependencies & inputs ) const { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); // do we want to build files via a unity blob? Array< AString > inputUnities; if ( !GetStrings( iter, inputUnities, ".CompilerInputUnity", false ) ) // not required { return false; } for ( const auto& unity : inputUnities ) { Node * n = ng.FindNode( unity ); if ( n == nullptr ) { Error::Error_1104_TargetNotDefined( iter, this, "CompilerInputUnity", unity ); return false; } if ( n->GetType() != Node::UNITY_NODE ) { Error::Error_1102_UnexpectedType( iter, this, "CompilerInputUnity", unity, n->GetType(), Node::UNITY_NODE ); return false; } inputs.Append( Dependency( n ) ); } // do we want to build a files in a directory? const BFFVariable * inputPath = BFFStackFrame::GetVar( ".CompilerInputPath" ); if ( inputPath ) { // get the optional pattern and recurse options related to InputPath Array< AString > patterns; if ( !GetStrings( iter, patterns, ".CompilerInputPattern", false ) ) { return false; // GetString will have emitted an error } if ( patterns.IsEmpty() ) { patterns.Append( AStackString<>( "*.cpp" ) ); } // recursive? default to true bool recurse = true; if ( !GetBool( iter, recurse, ".CompilerInputPathRecurse", true, false ) ) { return false; // GetBool will have emitted an error } // Support an exclusion path Array< AString > excludePaths; if ( !GetFolderPaths( iter, excludePaths, ".CompilerInputExcludePath", false ) ) { return false; // GetFolderPaths will have emitted an error } Array< AString > filesToExclude; if ( !GetStrings( iter, filesToExclude, ".CompilerInputExcludedFiles", false ) ) // not required { return false; // GetStrings will have emitted an error } CleanFileNames( filesToExclude ); // Input paths Array< AString > inputPaths; if ( !GetFolderPaths( iter, inputPaths, ".CompilerInputPath", false ) ) { return false; // GetFolderPaths will have emitted an error } Dependencies dirNodes( inputPaths.GetSize() ); if ( !GetDirectoryListNodeList( iter, inputPaths, excludePaths, filesToExclude, recurse, &patterns, "CompilerInputPath", dirNodes ) ) { return false; // GetDirectoryListNodeList will have emitted an error } inputs.Append( dirNodes ); } // do we want to build a specific list of files? if ( !GetNodeList( iter, ".CompilerInputFiles", inputs, false ) ) { // helper will emit error return false; } return true; }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionCopy::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { // make sure all required variables are defined Array< AString > sources( 16, true ); const BFFVariable * dstFileV; if ( !GetStrings( funcStartIter, sources, ".Source", true ) || !GetString( funcStartIter, dstFileV, ".Dest", true ) ) { return false; // GetString will have emitted errors } // Optional AStackString<> sourceBasePath; if ( !GetString( funcStartIter, sourceBasePath, ".SourceBasePath", false ) ) { return false; // GetString will have emitted errors } // Canonicalize the SourceBasePath if ( !sourceBasePath.IsEmpty() ) { AStackString<> cleanValue; NodeGraph::CleanPath( sourceBasePath, cleanValue ); PathUtils::EnsureTrailingSlash( cleanValue ); sourceBasePath = cleanValue; } // check sources are not paths { const AString * const end = sources.End(); for ( const AString * it = sources.Begin(); it != end; ++it ) { const AString & srcFile( *it ); // source must be a file, not a path if ( PathUtils::IsFolderPath( srcFile ) ) { Error::Error_1105_PathNotAllowed( funcStartIter, this, ".Source", srcFile ); return false; } } } // Pre-build dependencies Dependencies preBuildDependencies; if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) ) { return false; // GetNodeList will have emitted an error } Array< AString > preBuildDependencyNames( preBuildDependencies.GetSize(), false ); for ( const auto & dep : preBuildDependencies ) { preBuildDependencyNames.Append( dep.GetNode()->GetName() ); } // get source node Array< Node * > srcNodes; { const AString * const end = sources.End(); for ( const AString * it = sources.Begin(); it != end; ++it ) { Node * srcNode = nodeGraph.FindNode( *it ); if ( srcNode ) { if ( GetSourceNodes( funcStartIter, srcNode, srcNodes ) == false ) { return false; } } else { // source file not defined by use - assume an external file srcNodes.Append( nodeGraph.CreateFileNode( *it ) ); } } } AStackString<> dstFile; NodeGraph::CleanPath( dstFileV->GetString(), dstFile ); const bool dstIsFolderPath = PathUtils::IsFolderPath( dstFile ); // make all the nodes for copies Dependencies copyNodes( srcNodes.GetSize(), false ); for ( const Node * srcNode : srcNodes ) { AStackString<> dst( dstFile ); // dest can be a file OR a path. If it's a path, use the source filename part if ( dstIsFolderPath ) { // find filename part of source const AString & srcName = srcNode->GetName(); // If the sourceBasePath is specified (and valid) use the name relative to that if ( !sourceBasePath.IsEmpty() && PathUtils::PathBeginsWith( srcName, sourceBasePath ) ) { // Use everything relative to the SourceBasePath dst += srcName.Get() + sourceBasePath.GetLength(); } else { // Use just the file name const char * lastSlash = srcName.FindLast( NATIVE_SLASH ); dst += lastSlash ? ( lastSlash + 1 ) // append filename part if found : srcName.Get(); // otherwise append whole thing } } // check node doesn't already exist if ( nodeGraph.FindNode( dst ) ) { // TODO:C could have a specific error for multiple sources with only 1 output // to differentiate from two rules creating the same dst target Error::Error_1100_AlreadyDefined( funcStartIter, this, dst ); return false; } // create our node CopyFileNode * copyFileNode = nodeGraph.CreateCopyFileNode( dst ); copyFileNode->m_Source = srcNode->GetName(); copyFileNode->m_PreBuildDependencyNames = preBuildDependencyNames; if ( !copyFileNode->Initialize( nodeGraph, funcStartIter, this ) ) { return false; // Initialize will have emitted an error } copyNodes.Append( Dependency( copyFileNode ) ); } // handle alias creation return ProcessAlias( nodeGraph, funcStartIter, copyNodes ); }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionExec::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { // make sure all required variables are defined const BFFVariable * outputV; const BFFVariable * executableV; const BFFVariable * argsV; const BFFVariable * workingDirV; int32_t expectedReturnCode; bool useStdOutAsOutput; if ( !GetString( funcStartIter, outputV, ".ExecOutput", true ) || !GetString( funcStartIter, executableV, ".ExecExecutable", true ) || !GetString( funcStartIter, argsV, ".ExecArguments" ) || !GetString( funcStartIter, workingDirV, ".ExecWorkingDir" ) || !GetInt( funcStartIter, expectedReturnCode, ".ExecReturnCode", 0, false ) || !GetBool( funcStartIter, useStdOutAsOutput, ".ExecUseStdOutAsOutput", false, false)) { return false; } // check for duplicates if ( nodeGraph.FindNode( outputV->GetString() ) != nullptr ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, outputV->GetString() ); return false; } // Pre-build dependencies Dependencies preBuildDependencies; if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) ) { return false; // GetNodeList will have emitted an error } // get executable node Node * exeNode = nodeGraph.FindNode( executableV->GetString() ); if ( exeNode == nullptr ) { exeNode = nodeGraph.CreateFileNode( executableV->GetString() ); } else if ( exeNode->IsAFile() == false ) { Error::Error_1103_NotAFile( funcStartIter, this, "ExecExecutable", exeNode->GetName(), exeNode->GetType() ); return false; } // source node Dependencies inputNodes; if ( !GetNodeList( nodeGraph, funcStartIter, ".ExecInput", inputNodes, false ) ) { return false; // GetNodeList will have emitted an error } else { // Make sure all nodes are files const Dependency * const end = inputNodes.End(); for (const Dependency * it = inputNodes.Begin(); it != end; ++it) { Node * node = it->GetNode(); if (node->IsAFile() == false) { Error::Error_1103_NotAFile(funcStartIter, this, "ExecInput", node->GetName(), node->GetType()); return false; } } } // optional args const AString & arguments( argsV ? argsV->GetString() : AString::GetEmpty() ); const AString & workingDir( workingDirV ? workingDirV->GetString(): AString::GetEmpty() ); // create the TestNode Node * outputNode = nodeGraph.CreateExecNode( outputV->GetString(), inputNodes, (FileNode *)exeNode, arguments, workingDir, expectedReturnCode, preBuildDependencies, useStdOutAsOutput); return ProcessAlias( nodeGraph, funcStartIter, outputNode ); }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionCSAssembly::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { // make sure all required variables are defined const BFFVariable * compiler; const BFFVariable * compilerOptions; const BFFVariable * compilerOutput; if ( !GetString( funcStartIter, compiler, ".Compiler", true ) || !GetString( funcStartIter, compilerOptions, ".CompilerOptions", true ) || !GetString( funcStartIter, compilerOutput, ".CompilerOutput", true ) ) { return false; } Dependencies staticDeps( 32, true ); // do we want to build a files in a directory? const BFFVariable * inputPath = BFFStackFrame::GetVar( ".CompilerInputPath" ); if ( inputPath ) { // get the optional pattern and recurse options related to InputPath Array< AString > patterns; if ( !GetStrings( funcStartIter, patterns, ".CompilerInputPattern", false ) ) { return false; // GetString will have emitted an error } if ( patterns.IsEmpty() ) { patterns.Append( AStackString<>( "*.cs" ) ); } // recursive? default to true bool recurse = true; if ( !GetBool( funcStartIter, recurse, ".CompilerInputPathRecurse", true, false ) ) { return false; // GetBool will have emitted an error } // Support an exclusion path Array< AString > excludePaths; if ( !GetFolderPaths( funcStartIter, excludePaths, ".CompilerInputExcludePath", false ) ) { return false; // GetFolderPaths will have emitted an error } Array< AString > filesToExclude(0, true); if ( !GetStrings( funcStartIter, filesToExclude, ".CompilerInputExcludedFiles", false ) ) // not required { return false; // GetStrings will have emitted an error } CleanFileNames( filesToExclude ); // Input paths Array< AString > inputPaths; if ( !GetFolderPaths( funcStartIter, inputPaths, ".CompilerInputPath", false ) ) { return false; // GetFolderPaths will have emitted an error } Dependencies dirNodes( inputPaths.GetSize() ); if ( !GetDirectoryListNodeList( nodeGraph, funcStartIter, inputPaths, excludePaths, filesToExclude, recurse, &patterns, "CompilerInputPath", dirNodes ) ) { return false; // GetDirectoryListNodeList will have emitted an error } staticDeps.Append( dirNodes ); } // do we want to build a specific list of files? if ( !GetNodeList( nodeGraph, funcStartIter, ".CompilerInputFiles", staticDeps, false ) ) { // helper will emit error return false; } if ( staticDeps.IsEmpty() ) { Error::Error_1006_NothingToBuild( funcStartIter, this ); return false; } // additional references? Dependencies extraRefs( 0, true ); if ( !GetNodeList( nodeGraph, funcStartIter, ".CompilerReferences", extraRefs, false ) ) { // helper function will have emitted an error return false; } // Pre-build dependencies Dependencies preBuildDependencies; if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) ) { return false; // GetNodeList will have emitted an error } // Create library node which depends on the single file or list if ( nodeGraph.FindNode( compilerOutput->GetString() ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, compilerOutput->GetString() ); return false; } Node * csNode = nodeGraph.CreateCSNode( compilerOutput->GetString(), staticDeps, compiler->GetString(), compilerOptions->GetString(), extraRefs, preBuildDependencies ); // should we create an alias? return ProcessAlias( nodeGraph, funcStartIter, csNode ); }
// GetNodeList //------------------------------------------------------------------------------ /*static*/ bool Function::GetNodeList( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function, const char * propertyName, const AString & nodeName, Dependencies & nodes, bool allowCopyDirNodes, bool allowUnityNodes, bool allowRemoveDirNodes ) { // get node Node * n = nodeGraph.FindNode( nodeName ); if ( n == nullptr ) { // not found - create a new file node n = nodeGraph.CreateFileNode( nodeName ); nodes.Append( Dependency( n ) ); return true; } // found - is it a file? if ( n->IsAFile() ) { // found file - just use as is nodes.Append( Dependency( n ) ); return true; } // found - is it an ObjectList? if ( n->GetType() == Node::OBJECT_LIST_NODE ) { // use as-is nodes.Append( Dependency( n ) ); return true; } // extra types if ( allowCopyDirNodes ) { // found - is it an ObjectList? if ( n->GetType() == Node::COPY_DIR_NODE ) { // use as-is nodes.Append( Dependency( n ) ); return true; } } if ( allowRemoveDirNodes ) { // found - is it a RemoveDirNode? if ( n->GetType() == Node::REMOVE_DIR_NODE ) { // use as-is nodes.Append( Dependency( n ) ); return true; } } if ( allowUnityNodes ) { // found - is it an ObjectList? if ( n->GetType() == Node::UNITY_NODE ) { // use as-is nodes.Append( Dependency( n ) ); return true; } } // found - is it a group? if ( n->GetType() == Node::ALIAS_NODE ) { AliasNode * an = n->CastTo< AliasNode >(); const Dependencies & aNodes = an->GetAliasedNodes(); for ( const Dependency * it = aNodes.Begin(); it != aNodes.End(); ++it ) { // TODO:C by passing as string we'll be looking up again for no reason const AString & subName = it->GetNode()->GetName(); if ( !GetNodeList( nodeGraph, iter, function, propertyName, subName, nodes, allowCopyDirNodes, allowUnityNodes, allowRemoveDirNodes ) ) { return false; } } return true; } // don't know how to handle this type of node Error::Error_1005_UnsupportedNodeType( iter, function, propertyName, n->GetName(), n->GetType() ); return false; }