// GetFileNode //------------------------------------------------------------------------------ bool Function::GetFileNode( NodeGraph & nodeGraph, const BFFIterator & iter, Node * & fileNode, const char * name, bool required ) const { // get the string containing the node name AStackString<> fileNodeName; if ( GetString( iter, fileNodeName, name, required ) == false ) { return false; } // handle not-present if ( fileNodeName.IsEmpty() ) { ASSERT( required == false ); // GetString should have managed required string fileNode = nullptr; return true; } // get/create the FileNode Node * n = nodeGraph.FindNode( fileNodeName ); if ( n == nullptr ) { n = nodeGraph.CreateFileNode( fileNodeName ); } else if ( n->IsAFile() == false ) { Error::Error_1103_NotAFile( iter, this, name, n->GetName(), n->GetType() ); return false; } fileNode = n; return true; }
// GetCompilerNode //------------------------------------------------------------------------------ bool FunctionObjectList::GetCompilerNode( NodeGraph & nodeGraph, const BFFIterator & iter, const AString & compiler, CompilerNode * & compilerNode ) const { Node * cn = nodeGraph.FindNode( compiler ); compilerNode = nullptr; if ( cn != nullptr ) { if ( cn->GetType() == Node::ALIAS_NODE ) { AliasNode * an = cn->CastTo< AliasNode >(); cn = an->GetAliasedNodes()[ 0 ].GetNode(); } if ( cn->GetType() != Node::COMPILER_NODE ) { Error::Error_1102_UnexpectedType( iter, this, "Compiler", cn->GetName(), cn->GetType(), Node::COMPILER_NODE ); return false; } compilerNode = cn->CastTo< CompilerNode >(); } else { // create a compiler node - don't allow distribution // (only explicitly defined compiler nodes can be distributed) AStackString<> compilerClean; NodeGraph::CleanPath( compiler, compilerClean ); compilerNode = nodeGraph.CreateCompilerNode( compilerClean ); VERIFY( compilerNode->GetReflectionInfoV()->SetProperty( compilerNode, "AllowDistribution", false ) ); } return true; }
// DependOnNode //------------------------------------------------------------------------------ /*static*/ bool LinkerNode::DependOnNode( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function, const AString & nodeName, Dependencies & nodes ) { // silently ignore empty nodes if ( nodeName.IsEmpty() ) { return true; } Node * node = nodeGraph.FindNode( nodeName ); // does it exist? if ( node != nullptr ) { // process it return DependOnNode( iter, function, node, nodes ); } // node not found - create a new FileNode, assuming we are // linking against an externally built library node = nodeGraph.CreateFileNode( nodeName ); nodes.Append( Dependency( node ) ); return true; }
// GetObjectListNodes //------------------------------------------------------------------------------ bool Function::GetObjectListNodes( NodeGraph & nodeGraph, const BFFIterator & iter, const Array< AString > & objectLists, const char * inputVarName, Dependencies & nodes ) const { const AString * const end = objectLists.End(); for ( const AString * it = objectLists.Begin(); it != end; ++it ) { const AString & objectList = *it; // get node for the dir we depend on Node * node = nodeGraph.FindNode( objectList ); if ( node == nullptr ) { Error::Error_1104_TargetNotDefined( iter, this, inputVarName, objectList ); return false; } else if ( node->GetType() != Node::OBJECT_LIST_NODE ) { Error::Error_1102_UnexpectedType( iter, this, inputVarName, node->GetName(), node->GetType(), Node::OBJECT_LIST_NODE ); return false; } nodes.Append( Dependency( node ) ); } 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 FunctionXCodeProject::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { AStackString<> name; if ( GetNameForNode( nodeGraph, funcStartIter, XCodeProjectNode::GetReflectionInfoS(), name ) == false ) { return false; } if ( nodeGraph.FindNode( name ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, name ); return false; } auto * xcodeProjNode = nodeGraph.CreateXCodeProjectNode( name ); if ( !PopulateProperties( nodeGraph, funcStartIter, xcodeProjNode ) ) { return false; } if ( !xcodeProjNode->Initialize( nodeGraph, funcStartIter, this ) ) { return false; } // handle alias creation return ProcessAlias( nodeGraph, funcStartIter, xcodeProjNode ); }
// GetDirectoryNodeList //------------------------------------------------------------------------------ bool Function::GetDirectoryListNodeList( NodeGraph & nodeGraph, const BFFIterator & iter, const Array< AString > & paths, const Array< AString > & excludePaths, const Array< AString > & filesToExclude, bool recurse, const Array< AString > * patterns, const char * inputVarName, Dependencies & nodes ) const { // Handle special case of excluded files beginning with ../ // Since they can be used seinsibly by matching just the end // of a path, assume they are relative to the working dir. // TODO:C Move this during bff parsing when everything is using reflection Array< AString > filesToExcludeCleaned( filesToExclude.GetSize(), true ); for ( const AString& file : filesToExclude ) { if ( file.BeginsWith( ".." ) ) { AStackString<> fullPath; NodeGraph::CleanPath( file, fullPath ); filesToExcludeCleaned.Append( fullPath ); } else { filesToExcludeCleaned.Append( file ); } } const AString * const end = paths.End(); for ( const AString * it = paths.Begin(); it != end; ++it ) { const AString & path = *it; // get node for the dir we depend on AStackString<> name; DirectoryListNode::FormatName( path, patterns, recurse, excludePaths, filesToExcludeCleaned, name ); Node * node = nodeGraph.FindNode( name ); if ( node == nullptr ) { node = nodeGraph.CreateDirectoryListNode( name, path, patterns, recurse, excludePaths, filesToExcludeCleaned ); } else if ( node->GetType() != Node::DIRECTORY_LIST_NODE ) { Error::Error_1102_UnexpectedType( iter, this, inputVarName, node->GetName(), node->GetType(), Node::DIRECTORY_LIST_NODE ); return false; } nodes.Append( Dependency( node ) ); } return true; }
// GetNameForNode //------------------------------------------------------------------------------ bool Function::GetNameForNode( NodeGraph & nodeGraph, const BFFIterator & iter, const ReflectionInfo * ri, AString & name ) const { // get object MetaData const Meta_Name * nameMD = ri->HasMetaData< Meta_Name >(); ASSERT( nameMD ); // should not call this on types without this MetaData // Format "Name" as ".Name" - TODO:C Would be good to eliminate this string copy AStackString<> propertyName( "." ); propertyName += nameMD->GetName(); // Find the value for this property from the BFF const BFFVariable * variable = BFFStackFrame::GetVar( propertyName ); if ( variable == nullptr ) { Error::Error_1101_MissingProperty( iter, this, propertyName ); return false; } if ( variable->IsString() ) { Array< AString > strings; if ( !PopulateStringHelper( nodeGraph, iter, nullptr, ri->HasMetaData< Meta_File >(), variable, strings ) ) { return false; // PopulateStringHelper will have emitted an error } // Handle empty strings if ( strings.IsEmpty() || strings[0].IsEmpty() ) { Error::Error_1004_EmptyStringPropertyNotAllowed( iter, this, variable->GetName().Get() ); return false; } if ( strings.GetSize() != 1 ) { Error::Error_1050_PropertyMustBeOfType( iter, this, variable->GetName().Get(), BFFVariable::VAR_ARRAY_OF_STRINGS, BFFVariable::VAR_STRING ); return false; } // Check that name isn't already used if ( nodeGraph.FindNode( strings[0] ) ) { Error::Error_1100_AlreadyDefined( iter, this, strings[0] ); return false; } name = strings[0]; return true; } Error::Error_1050_PropertyMustBeOfType( iter, this, variable->GetName().Get(), variable->GetType(), BFFVariable::VAR_STRING ); return false; }
// 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 }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionAlias::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { if ( nodeGraph.FindNode( m_AliasForFunction ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, m_AliasForFunction ); return false; } AliasNode * aliasNode = nodeGraph.CreateAliasNode( m_AliasForFunction ); if ( !PopulateProperties( nodeGraph, funcStartIter, aliasNode ) ) { return false; } return aliasNode->Initialize( nodeGraph, funcStartIter, this ); }
// Commit //------------------------------------------------------------------------------ /*virtual*/ bool FunctionSettings::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const { AStackString<> name( "$$Settings$$" ); if ( nodeGraph.FindNode( name ) ) { Error::Error_1100_AlreadyDefined( funcStartIter, this, name ); return false; } SettingsNode * settingsNode = nodeGraph.CreateSettingsNode( name ); if ( !PopulateProperties( nodeGraph, funcStartIter, settingsNode ) ) { return false; } return settingsNode->Initialize( nodeGraph, funcStartIter, this ); }
// GetFileNode //------------------------------------------------------------------------------ bool Function::GetFileNode( NodeGraph & nodeGraph, const BFFIterator & iter, const AString & file, const char * inputVarName, Dependencies & nodes ) const { // get node for the dir we depend on Node * node = nodeGraph.FindNode( file ); if ( node == nullptr ) { node = nodeGraph.CreateFileNode( file ); } else if ( node->IsAFile() == false ) { Error::Error_1005_UnsupportedNodeType( iter, this, inputVarName, node->GetName(), node->GetType() ); return false; } nodes.Append( Dependency( node ) ); return true; }
// ProcessAlias //------------------------------------------------------------------------------ bool Function::ProcessAlias( NodeGraph & nodeGraph, const BFFIterator & iter, Dependencies & nodesToAlias ) const { if ( m_AliasForFunction.IsEmpty() ) { return true; // no alias required } // check for duplicates if ( nodeGraph.FindNode( m_AliasForFunction ) ) { Error::Error_1100_AlreadyDefined( iter, this, m_AliasForFunction ); return false; } // create an alias against the node AliasNode * an = nodeGraph.CreateAliasNode( m_AliasForFunction ); an->m_StaticDependencies = nodesToAlias; // TODO: make this use m_Targets & Initialize() // clear the string so it can't be used again m_AliasForFunction.Clear(); return true; }
// PopulateStringHelper //------------------------------------------------------------------------------ bool Function::PopulateStringHelper( NodeGraph & nodeGraph, const BFFIterator & iter, const Meta_Path * pathMD, const Meta_File * fileMD, const BFFVariable * variable, const AString & string, Array< AString > & outStrings ) const { // Full paths to files can support aliases if ( fileMD && ( !fileMD->IsRelative() ) ) { // Is it an Alias? Node * node = nodeGraph.FindNode( string ); if ( node && ( node->GetType() == Node::ALIAS_NODE ) ) { AliasNode * aliasNode = node->CastTo< AliasNode >(); for ( const auto& aliasedNode : aliasNode->GetAliasedNodes() ) { if ( !PopulateStringHelper( nodeGraph, iter, pathMD, fileMD, variable, aliasedNode.GetNode()->GetName(), outStrings ) ) { return false; // PopulateStringHelper will have emitted an error } } return true; } // Not an alias - fall through to normal handling } AStackString<> stringToFix( string ); if ( !PopulatePathAndFileHelper( iter, pathMD, fileMD, variable->GetName(), stringToFix ) ) { return false; // PopulatePathAndFileHelper will have emitted an error } outStrings.Append( stringToFix ); 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; }
// GatherDynamicDependencies //------------------------------------------------------------------------------ /*virtual*/ bool ObjectListNode::GatherDynamicDependencies( NodeGraph & nodeGraph, bool forceClean ) { (void)forceClean; // dynamic deps are always re-added here, so this is meaningless // clear dynamic deps from previous passes m_DynamicDependencies.Clear(); #if defined( __WINDOWS__ ) // On Windows, with MSVC we compile a cpp file to generate the PCH // Filter here to ensure that doesn't get compiled twice Node * pchCPP = nullptr; if ( m_PrecompiledHeader && m_PrecompiledHeader->IsMSVC() ) { pchCPP = m_PrecompiledHeader->GetPrecompiledHeaderCPPFile(); } #endif // if we depend on any directory lists, we need to use them to get our files for ( Dependencies::Iter i = m_StaticDependencies.Begin(); i != m_StaticDependencies.End(); i++ ) { // is this a dir list? if ( i->GetNode()->GetType() == Node::DIRECTORY_LIST_NODE ) { // get the list of files DirectoryListNode * dln = i->GetNode()->CastTo< DirectoryListNode >(); const Array< FileIO::FileInfo > & files = dln->GetFiles(); m_DynamicDependencies.SetCapacity( m_DynamicDependencies.GetSize() + files.GetSize() ); for ( Array< FileIO::FileInfo >::Iter fIt = files.Begin(); fIt != files.End(); fIt++ ) { // Create the file node (or find an existing one) Node * n = nodeGraph.FindNode( fIt->m_Name ); if ( n == nullptr ) { n = nodeGraph.CreateFileNode( fIt->m_Name ); } else if ( n->IsAFile() == false ) { FLOG_ERROR( "Library() .CompilerInputFile '%s' is not a FileNode (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } // ignore the precompiled header as a convenience for the user // so they don't have to exclude it explicitly #if defined( __WINDOWS__ ) if ( n == pchCPP ) { continue; } #endif // create the object that will compile the above file if ( CreateDynamicObjectNode( nodeGraph, n, dln->GetPath() ) == false ) { return false; // CreateDynamicObjectNode will have emitted error } } } else if ( i->GetNode()->GetType() == Node::UNITY_NODE ) { // get the dir list from the unity node UnityNode * un = i->GetNode()->CastTo< UnityNode >(); // unity files const Array< AString > & unityFiles = un->GetUnityFileNames(); for ( Array< AString >::Iter it = unityFiles.Begin(); it != unityFiles.End(); it++ ) { Node * n = nodeGraph.FindNode( *it ); if ( n == nullptr ) { n = nodeGraph.CreateFileNode( *it ); } else if ( n->IsAFile() == false ) { FLOG_ERROR( "Library() .CompilerInputUnity '%s' is not a FileNode (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } // create the object that will compile the above file if ( CreateDynamicObjectNode( nodeGraph, n, AString::GetEmpty(), true ) == false ) { return false; // CreateDynamicObjectNode will have emitted error } } // files from unity to build individually const Array< UnityNode::FileAndOrigin > & isolatedFiles = un->GetIsolatedFileNames(); for ( Array< UnityNode::FileAndOrigin >::Iter it = isolatedFiles.Begin(); it != isolatedFiles.End(); it++ ) { Node * n = nodeGraph.FindNode( it->GetName() ); if ( n == nullptr ) { n = nodeGraph.CreateFileNode( it->GetName() ); } else if ( n->IsAFile() == false ) { FLOG_ERROR( "Library() Isolated '%s' is not a FileNode (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } // create the object that will compile the above file const AString & baseDir = it->GetDirListOrigin() ? it->GetDirListOrigin()->GetPath() : AString::GetEmpty(); if ( CreateDynamicObjectNode( nodeGraph, n, baseDir, false, true ) == false ) { return false; // CreateDynamicObjectNode will have emitted error } } } else if ( i->GetNode()->IsAFile() ) { // a single file, create the object that will compile it if ( CreateDynamicObjectNode( nodeGraph, i->GetNode(), AString::GetEmpty() ) == false ) { return false; // CreateDynamicObjectNode will have emitted error } } else { ASSERT( false ); // unexpected node type } } // If we have a precompiled header, add that to our dynamic deps so that // any symbols in the PCH's .obj are also linked, when either: // a) we are a static library // b) a DLL or executable links our .obj files if ( m_PrecompiledHeader ) { m_DynamicDependencies.Append( Dependency( m_PrecompiledHeader ) ); } return true; }
// 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; }
// 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; }
// DoDynamicDependencies //------------------------------------------------------------------------------ /*virtual*/ bool CopyDirNode::DoDynamicDependencies( NodeGraph & nodeGraph, bool forceClean ) { (void)forceClean; // dynamic deps are always re-added here, so this is meaningless ASSERT( !m_StaticDependencies.IsEmpty() ); Array< AString > preBuildDependencyNames( m_PreBuildDependencies.GetSize(), false ); for ( const auto & dep : m_PreBuildDependencies ) { preBuildDependencyNames.Append( dep.GetNode()->GetName() ); } // Iterate all the DirectoryListNodes const Dependency * const depEnd = m_StaticDependencies.End(); for ( const Dependency * dep = m_StaticDependencies.Begin(); dep != depEnd; ++dep ) { // Grab the files DirectoryListNode * dln = dep->GetNode()->CastTo< DirectoryListNode >(); const Array< FileIO::FileInfo > & files = dln->GetFiles(); const FileIO::FileInfo * const fEnd = files.End(); for ( const FileIO::FileInfo * fIt = files.Begin(); fIt != fEnd; ++fIt ) { // Create a CopyFileNode for each dynamically discovered file // source file (full path) const AString & srcFile = fIt->m_Name; // source file (relative to base path) const AStackString<> srcFileRel( srcFile.Get() + dln->GetPath().GetLength() ); // source file (as a node) Node * srcFileNode = nodeGraph.FindNode( srcFile ); if ( srcFileNode == nullptr ) { srcFileNode = nodeGraph.CreateFileNode( srcFile ); } else if ( srcFileNode->IsAFile() == false ) { FLOG_ERROR( "CopyDir() Node '%s' is not a FileNode (type: %s)", srcFile.Get(), srcFileNode->GetTypeName() ); return false; } // generate dest file name const AStackString<> dstFile( m_DestPath ); (AString &)dstFile += (AString &)srcFileRel; // make sure dest doesn't already exist Node * n = nodeGraph.FindNode( dstFile ); if ( n == nullptr ) { CopyFileNode * copyFileNode = nodeGraph.CreateCopyFileNode( dstFile ); copyFileNode->m_Source = srcFileNode->GetName(); copyFileNode->m_PreBuildDependencyNames = preBuildDependencyNames; // inherit PreBuildDependencies BFFIterator iter; if ( !copyFileNode->Initialize( nodeGraph, iter, nullptr ) ) { return false; // Initialize will have emitted an error } n = copyFileNode; } else if ( n->GetType() != Node::COPY_FILE_NODE ) { FLOG_ERROR( "Node '%s' is not a CopyFileNode (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } else { CopyFileNode * cn = n->CastTo< CopyFileNode >(); if ( srcFileNode != cn->GetSourceNode() ) { FLOG_ERROR( "Conflicting objects found during CopyDir:\n" " File A: %s\n" " File B: %s\n" " Both copy to: %s\n", srcFile.Get(), cn->GetSourceNode()->GetName().Get(), dstFile.Get() ); return false; } } m_DynamicDependencies.Append( Dependency( n ) ); } } return true; }
// 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 ); }
// GetInputs //------------------------------------------------------------------------------ bool FunctionObjectList::GetInputs( NodeGraph & nodeGraph, const BFFIterator & iter, Dependencies & inputs ) const { // 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 = nodeGraph.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 ); Array< AString > excludePatterns; if ( !GetStrings( iter, excludePatterns, ".CompilerInputExcludePattern", false ) ) // not required { return false; // GetStrings will have emitted an error } // 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( nodeGraph, iter, inputPaths, excludePaths, filesToExclude, excludePatterns, 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( nodeGraph, iter, ".CompilerInputFiles", inputs, false ) ) { // helper will emit error return false; } return true; }