// 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; }
// 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; }
// 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 }
// 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; }
// 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; }
// 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 ); }
// 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; }
// 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; }