// Generate //------------------------------------------------------------------------------ bool ToolManifest::Generate( const Node * mainExecutable, const Dependencies & dependencies ) { m_Files.Clear(); m_TimeStamp = 0; m_Files.SetCapacity( 1 + dependencies.GetSize() ); // unify "main executable" and "extra files" // (loads contents of file into memory, and creates hashes) if ( !AddFile( mainExecutable ) ) { return false; // AddFile will have emitted error } for ( size_t i=0; i<dependencies.GetSize(); ++i ) { const FileNode & n = *( dependencies[ i ].GetNode()->CastTo< FileNode >() ); if ( !AddFile( &n ) ) { return false; // AddFile will have emitted error } } // create a hash for the whole tool chain const size_t numFiles( m_Files.GetSize() ); const size_t memSize( numFiles * sizeof( uint32_t ) * 2 ); uint32_t * mem = (uint32_t *)ALLOC( memSize ); uint32_t * pos = mem; for ( size_t i=0; i<numFiles; ++i ) { const File & f = m_Files[ i ]; // file contents *pos = f.m_Hash; ++pos; // file name & sub-path (relative to remote folder) AStackString<> relativePath; GetRemoteFilePath( (uint32_t)i, relativePath, false ); // false = don't use full path *pos = xxHash::Calc32( relativePath ); ++pos; } m_ToolId = xxHash::Calc64( mem, memSize ); FREE( mem ); // update time stamp (most recent file in manifest) for ( size_t i=0; i<numFiles; ++i ) { const File & f = m_Files[ i ]; ASSERT( f.m_TimeStamp ); // should have had an error before if the file was missing m_TimeStamp = Math::Max( m_TimeStamp, f.m_TimeStamp ); } return true; }
// 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; }
// 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; }
// CONSTRUCTOR //------------------------------------------------------------------------------ CSNode::CSNode( const AString & compilerOutput, const Dependencies & inputNodes, const AString & compiler, const AString & compilerArgs, const Dependencies & extraRefs ) : FileNode( compilerOutput, Node::FLAG_NONE ) , m_ExtraRefs( extraRefs ) { ASSERT( !inputNodes.IsEmpty() ); m_StaticDependencies.SetCapacity( inputNodes.GetSize() + extraRefs.GetSize() ); m_StaticDependencies.Append( inputNodes ); m_StaticDependencies.Append( extraRefs ); // store options we'll need to use when building m_CompilerPath = compiler; // TODO:C This should be a node we statically depend on m_CompilerArgs = compilerArgs; m_Type = CS_NODE; m_LastBuildTimeMs = 5000; // higher default than a file node }
// 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 ); }