// DoDynamicDependencies //------------------------------------------------------------------------------ /*virtual*/ bool CSNode::DoDynamicDependencies( bool UNUSED( forceClean ) ) { ASSERT( m_DynamicDependencies.GetSize() == 0 ); NodeGraph & ng = FBuild::Get().GetDependencyGraph(); // preallocate a reasonable amount of space m_DynamicDependencies.SetCapacity( m_StaticDependencies.GetSize() ); // convert static deps to dynamic deps // (ignore the extra refs here) size_t numDeps = m_StaticDependencies.GetSize() - m_ExtraRefs.GetSize(); for ( size_t i=0; i<numDeps; ++i ) { Node * n = m_StaticDependencies[ i ].GetNode(); if ( n->IsAFile() ) { m_DynamicDependencies.Append( Dependency( n ) ); continue; } if ( n->GetType() == Node::DIRECTORY_LIST_NODE ) { // get the list of files DirectoryListNode * dln = n->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 * sn = ng.FindNode( fIt->m_Name ); if ( sn == nullptr ) { sn = ng.CreateFileNode( fIt->m_Name ); } else if ( sn->IsAFile() == false ) { FLOG_ERROR( "CSAssembly() .CompilerInputFile '%s' is not a FileNode (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } m_DynamicDependencies.Append( Dependency( sn ) ); } continue; } FLOG_ERROR( "'%s' is not a supported node type (type: %s)", n->GetName().Get(), n->GetTypeName() ); return false; } return true; }
// GetOtherLibrary //------------------------------------------------------------------------------ bool LinkerNode::GetOtherLibrary( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function, Dependencies & libs, const AString & path, const AString & lib, bool & found ) const { found = false; AStackString<> potentialNodeName( path ); if ( !potentialNodeName.IsEmpty() ) { PathUtils::EnsureTrailingSlash( potentialNodeName ); } potentialNodeName += lib; AStackString<> potentialNodeNameClean; NodeGraph::CleanPath( potentialNodeName, potentialNodeNameClean ); // see if a node already exists Node * node = nodeGraph.FindNode( potentialNodeNameClean ); if ( node ) { // aliases not supported - must point to something that provides a file if ( node->IsAFile() == false ) { Error::Error_1103_NotAFile( iter, function, ".LinkerOptions", potentialNodeNameClean, node->GetType() ); return false; } // found existing node libs.Append( Dependency( node ) ); found = true; return true; // no error } // see if the file exists on disk at this location if ( FileIO::FileExists( potentialNodeNameClean.Get() ) ) { node = nodeGraph.CreateFileNode( potentialNodeNameClean ); libs.Append( Dependency( node ) ); found = true; FLOG_INFO( "Additional library '%s' assumed to be '%s'\n", lib.Get(), potentialNodeNameClean.Get() ); return true; // no error } return true; // no error }
REGISTER_TESTS_END // CreateNode //------------------------------------------------------------------------------ void TestExe::CreateNode() const { FBuild fb; NodeGraph & ng = fb.GetDependencyGraph(); Dependencies inputLibraries( 1, false ); inputLibraries.Append( Dependency( ng.CreateFileNode( AStackString<>( "dummy.lib" ) ) ) ); ExeNode * exeNode = ng.CreateExeNode( AStackString<>( "exe.exe" ), inputLibraries, Dependencies(), AString::GetEmpty(), AStackString<>( "linker.exe" ), AString::GetEmpty(), // args 0, // flags Dependencies(), AStackString<>(), nullptr, AString::GetEmpty() ); // assembly resources TEST_ASSERT( exeNode->GetType() == Node::EXE_NODE ); TEST_ASSERT( ExeNode::GetTypeS() == Node::EXE_NODE ); TEST_ASSERT( AStackString<>( "Exe" ) == exeNode->GetTypeName() ); }
// 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( const BFFIterator & iter, const Array< AString > & objectLists, const char * inputVarName, Dependencies & nodes ) const { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); 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 = ng.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; }
void PluginManifest::Load(pugi::xml_document* doc) { pugi::xml_node manifest = doc->child("PluginManifest"); version = manifest.attribute("Version").as_int(); name = manifest.attribute("Name").as_string(); icon_url = manifest.attribute("IconUrl").as_string(); screenshot_url = manifest.attribute("ScreenshotUrl").as_string(); homepage_url = manifest.attribute("HomepageUrl").as_string(); author = manifest.attribute("Author").as_string(); release_date = manifest.attribute("ReleaseDate").as_string(); version_major = manifest.attribute("VersionMajor").as_int(); version_minor = manifest.attribute("VersionMinor").as_int(); api_level_major = manifest.attribute("APILevelMajor").as_int(); api_level_minor = manifest.attribute("APILevelMinor").as_int(); plugin_name = manifest.child("PluginName").text().as_string(); plugin_description = manifest.child("Description").text().as_string(); for (pugi::xml_node node = manifest.child("CommonData"); node; node = node.next_sibling("CommonData")) { common_data.push_back(CommonData(&node)); } for (pugi::xml_node node = manifest.child("PlatformData"); node; node = node.next_sibling("PlatformData")) { platform_data.push_back(PlatformData(&node)); } for (pugi::xml_node node = manifest.child("Dependency"); node; node = node.next_sibling("Dependency")) { dependencies.push_back(Dependency(&node)); } }
Dependency& targetSet(Target& target,Target& target_from) { r_target=⌖ nameFree(); target.dependencyInverseAdd(Dependency(target_from,m_rel)); return *this; }
// GetDirectoryNodeList //------------------------------------------------------------------------------ bool Function::GetDirectoryListNodeList( 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 { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); // 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 = ng.FindNode( name ); if ( node == nullptr ) { node = ng.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; }
// CONSTRUCTOR //------------------------------------------------------------------------------ ExecNode::ExecNode( const AString & dstFileName, FileNode * sourceFile, FileNode * executable, const AString & arguments, const AString & workingDir, int32_t expectedReturnCode, const Dependencies & preBuildDependencies ) : FileNode( dstFileName, Node::FLAG_NONE ) , m_SourceFile( sourceFile ) , m_Executable( executable ) , m_Arguments( arguments ) , m_WorkingDir( workingDir ) , m_ExpectedReturnCode( expectedReturnCode ) { ASSERT( sourceFile ); ASSERT( executable ); m_StaticDependencies.SetCapacity( 2 ); m_StaticDependencies.Append( Dependency( sourceFile ) ); m_StaticDependencies.Append( Dependency( executable ) ); m_Type = EXEC_NODE; m_PreBuildDependencies = preBuildDependencies; }
// Save //------------------------------------------------------------------------------ /*virtual*/ void CSNode::Save( IOStream & stream ) const { NODE_SAVE( m_Name ); // Only save the original static deps here (remove the extra refs) size_t numBaseDeps = m_StaticDependencies.GetSize() - m_ExtraRefs.GetSize(); Dependencies staticDeps( numBaseDeps, false ); for ( size_t i=0; i<numBaseDeps; ++i ) { staticDeps.Append( Dependency( m_StaticDependencies[ i ].GetNode() ) ); } NODE_SAVE_DEPS( staticDeps ); NODE_SAVE( m_CompilerPath ); NODE_SAVE( m_CompilerArgs ); NODE_SAVE_DEPS( m_ExtraRefs ); }
// CONSTRUCTOR //------------------------------------------------------------------------------ LinkerNode::LinkerNode( const AString & linkerOutputName, const Dependencies & inputLibraries, const Dependencies & otherLibraries, const AString & linkerType, const AString & linker, const AString & linkerArgs, uint32_t flags, const Dependencies & assemblyResources, const AString & importLibName, Node * linkerStampExe, const AString & linkerStampExeArgs ) : FileNode( linkerOutputName, Node::FLAG_NONE ) , m_Flags( flags ) , m_AssemblyResources( assemblyResources ) , m_OtherLibraries( otherLibraries ) , m_ImportLibName( importLibName ) , m_LinkerStampExe( linkerStampExe ) , m_LinkerStampExeArgs( linkerStampExeArgs ) { m_LastBuildTimeMs = 20000; // presize vector size_t numStaticDeps = inputLibraries.GetSize() + assemblyResources.GetSize() + otherLibraries.GetSize(); if ( linkerStampExe ) { numStaticDeps++; } m_StaticDependencies.SetCapacity( numStaticDeps ); // depend on everything we'll link together m_StaticDependencies.Append( inputLibraries ); m_StaticDependencies.Append( assemblyResources ); m_StaticDependencies.Append( otherLibraries ); // manage optional LinkerStampExe if ( linkerStampExe ) { m_StaticDependencies.Append( Dependency( linkerStampExe ) ); } // store options we'll need to use dynamically m_LinkerType = linkerType; m_Linker = linker; // TODO:C This should be a node m_LinkerArgs = linkerArgs; }
// 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; }
// GetDirectoryNodeList //------------------------------------------------------------------------------ bool Function::GetDirectoryListNodeList( const BFFIterator & iter, const Array< AString > & paths, const Array< AString > & excludePaths, const Array< AString > & filesToExclude, bool recurse, const AString & pattern, const char * inputVarName, Dependencies & nodes ) const { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); 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, pattern, recurse, excludePaths, filesToExclude, name ); Node * node = ng.FindNode( name ); if ( node == nullptr ) { node = ng.CreateDirectoryListNode( name, path, pattern, recurse, excludePaths, filesToExclude ); } 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; }
// Load //------------------------------------------------------------------------------ bool Dependencies::Load( NodeGraph & nodeGraph, IOStream & stream ) { uint32_t numDeps; if ( stream.Read( numDeps ) == false ) { return false; } if ( GetCapacity() < GetSize() + numDeps ) { SetCapacity( GetSize() + numDeps ); } for ( uint32_t i=0; i<numDeps; ++i ) { // Read node index uint32_t index( INVALID_NODE_INDEX ); if ( stream.Read( index ) == false ) { return false; } // Convert to Node * Node * node = nodeGraph.GetNodeByIndex( index ); ASSERT( node ); // Read weak flag bool isWeak( false ); if ( stream.Read( isWeak ) == false ) { return false; } // Recombine dependency info Append( Dependency( node, isWeak ) ); } return true; }
// CONSTRUCTOR //------------------------------------------------------------------------------ SLNNode::SLNNode( const AString & solutionOuput, const AString & solutionBuildProject, const AString & solutionVisualStudioVersion, const AString & solutionMinimumVisualStudioVersion, const Array< VSProjectConfig > & configs, const Array< VCXProjectNode * > & projects, const Array< SLNSolutionFolder > & folders ) : FileNode( solutionOuput, Node::FLAG_NONE ) , m_SolutionBuildProject( solutionBuildProject ) , m_SolutionVisualStudioVersion( solutionVisualStudioVersion ) , m_SolutionMinimumVisualStudioVersion( solutionMinimumVisualStudioVersion ) , m_Configs( configs ) , m_Folders( folders ) { m_LastBuildTimeMs = 100; // higher default than a file node m_Type = Node::SLN_NODE; // depend on the input nodes VCXProjectNode * const * projectsEnd = projects.End(); for( VCXProjectNode ** it = projects.Begin() ; it != projectsEnd ; ++it ) { m_StaticDependencies.Append( Dependency( *it ) ); } }
// 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; }
// 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; }
// 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; }
// ProcessAlias //------------------------------------------------------------------------------ bool Function::ProcessAlias( const BFFIterator & iter, Node * nodeToAlias ) const { Dependencies nodesToAlias( 1, false ); nodesToAlias.Append( Dependency( nodeToAlias ) ); return ProcessAlias( iter, nodesToAlias ); }
// GetNodeListRecurse //------------------------------------------------------------------------------ bool Function::GetNodeListRecurse( const BFFIterator & iter, const char * name, Dependencies & nodes, const AString & nodeName, bool allowCopyDirNodes, bool allowUnityNodes, bool allowRemoveDirNodes ) const { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); // get node Node * n = ng.FindNode( nodeName ); if ( n == nullptr ) { // not found - create a new file node n = ng.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 ( !GetNodeListRecurse( iter, name, nodes, subName, allowCopyDirNodes, allowUnityNodes, allowRemoveDirNodes ) ) { return false; } } return true; } // don't know how to handle this type of node Error::Error_1005_UnsupportedNodeType( iter, this, name, n->GetName(), n->GetType() ); return false; }
// DependOnNode //------------------------------------------------------------------------------ /*static*/ bool LinkerNode::DependOnNode( const BFFIterator & iter, const Function * function, Node * node, Dependencies & nodes ) { ASSERT( node ); // a previously declared library? if ( node->GetType() == Node::LIBRARY_NODE ) { // can link directly to it nodes.Append( Dependency( node ) ); return true; } // a previously declared object list? if ( node->GetType() == Node::OBJECT_LIST_NODE ) { // can link directly to it nodes.Append( Dependency( node ) ); return true; } // a dll? if ( node->GetType() == Node::DLL_NODE ) { // TODO:B Depend on import lib nodes.Append( Dependency( node, true ) ); // NOTE: Weak dependency return true; } // a previously declared external file? if ( node->GetType() == Node::FILE_NODE ) { // can link directy against it nodes.Append( Dependency( node ) ); return true; } // a file copy? if ( node->GetType() == Node::COPY_FILE_NODE ) { // depend on copy - will use input at build time nodes.Append( Dependency( node ) ); return true; } // an external executable? if ( node->GetType() == Node::EXEC_NODE ) { // depend on ndoe - will use exe output at build time nodes.Append( Dependency( node ) ); return true; } // a group (alias)? if ( node->GetType() == Node::ALIAS_NODE ) { // handle all targets in alias AliasNode * an = node->CastTo< AliasNode >(); const Dependencies & aliasNodeList = an->GetAliasedNodes(); const Dependencies::Iter end = aliasNodeList.End(); for ( Dependencies::Iter it = aliasNodeList.Begin(); it != end; ++it ) { if ( DependOnNode( iter, function, it->GetNode(), nodes ) == false ) { return false; // something went wrong lower down } } return true; // all nodes in group handled ok } // don't know how to handle this type of node Error::Error_1005_UnsupportedNodeType( iter, function, "Libraries", node->GetName(), node->GetType() ); return false; }
// 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 ); }
// Initialize //------------------------------------------------------------------------------ bool LinkerNode::Initialize( NodeGraph & nodeGraph, const BFFIterator & iter, const Function * function ) { // .PreBuildDependencies if ( !InitializePreBuildDependencies( nodeGraph, iter, function, m_PreBuildDependencyNames ) ) { return false; // InitializePreBuildDependencies will have emitted an error } // Get linker exe Node * linkerExeNode = nullptr; if ( !function->GetFileNode( nodeGraph, iter, linkerExeNode, ".Linker" ) ) // TODO:B Use m_Linker property { return false; // GetFileNode will have emitted an error } m_Flags = DetermineFlags( m_LinkerType, m_Linker, m_LinkerOptions ); // Check for Import Library override if ( ( m_Flags & LinkerNode::LINK_FLAG_MSVC ) != 0 ) { GetImportLibName( m_LinkerOptions, m_ImportLibName ); } // Check input/output args for Linker { bool hasInputToken = ( m_LinkerOptions.Find( "%1" ) || m_LinkerOptions.Find( "\"%1\"" ) ); if ( hasInputToken == false ) { Error::Error_1106_MissingRequiredToken( iter, function, ".LinkerOptions", "%1" ); return false; } bool hasOutputToken = ( m_LinkerOptions.Find( "%2" ) || m_LinkerOptions.Find( "\"%2\"" ) ); if ( hasOutputToken == false ) { Error::Error_1106_MissingRequiredToken( iter, function, ".LinkerOptions", "%2" ); return false; } } // Standard library dependencies Dependencies libraries( 64, true ); for ( const AString & library : m_Libraries ) { if ( DependOnNode( nodeGraph, iter, function, library, libraries ) == false ) { return false; // DependOnNode will have emitted an error } } // Assembly Resources Dependencies assemblyResources( 32, true ); if ( !function->GetNodeList( nodeGraph, iter, ".LinkerAssemblyResources", assemblyResources, false ) ) // TODO:B Use m_LinkerAssemblyResources directly { return false; // GetNodeList will have emitted error } // get inputs not passed through 'LibraryNodes' (i.e. directly specified on the cmd line) Dependencies otherLibraryNodes( 64, true ); if ( ( m_Flags & ( LinkerNode::LINK_FLAG_MSVC | LinkerNode::LINK_FLAG_GCC | LinkerNode::LINK_FLAG_SNC | LinkerNode::LINK_FLAG_ORBIS_LD | LinkerNode::LINK_FLAG_GREENHILLS_ELXR | LinkerNode::LINK_FLAG_CODEWARRIOR_LD ) ) != 0 ) { const bool msvcStyle = ( ( m_Flags & LinkerNode::LINK_FLAG_MSVC ) == LinkerNode::LINK_FLAG_MSVC ); if ( !GetOtherLibraries( nodeGraph, iter, function, m_LinkerOptions, otherLibraryNodes, msvcStyle ) ) { return false; // will have emitted error } } // Handle LinkerStampExe Node * linkerStampExeNode = nullptr; if ( m_LinkerStampExe.IsEmpty() == false ) { if ( !function->GetFileNode( nodeGraph, iter, linkerStampExeNode, ".LinkerStampExe" ) ) // TODO: Use m_LinkerStampExe property { return false; // GetFileNode will have emitted an error } } // Store all dependencies m_StaticDependencies.SetCapacity( 1 + // for .Linker libraries.GetSize() + assemblyResources.GetSize() + otherLibraryNodes.GetSize() + ( linkerStampExeNode ? 1 : 0 ) ); m_StaticDependencies.Append( Dependency( linkerExeNode ) ); m_StaticDependencies.Append( libraries ); m_AssemblyResourcesStartIndex = (uint32_t)m_StaticDependencies.GetSize(); m_StaticDependencies.Append( assemblyResources ); m_AssemblyResourcesNum = (uint32_t)assemblyResources.GetSize(); m_StaticDependencies.Append( otherLibraryNodes ); if ( linkerStampExeNode ) { m_StaticDependencies.Append( Dependency( linkerStampExeNode ) ); } return true; }