Esempio n. 1
0
// 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;
}
Esempio n. 2
0
// 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;
}
Esempio n. 3
0
// 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;
}
Esempio n. 4
0
// 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;
}
Esempio n. 5
0
// 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;
}
Esempio n. 6
0
// 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 );
}
Esempio n. 7
0
// 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;
}
Esempio n. 8
0
// 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;
}
Esempio n. 9
0
// 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
}
Esempio n. 10
0
// 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 );
}
Esempio n. 11
0
// 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 );
}
Esempio n. 12
0
// 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;
}
Esempio n. 13
0
// 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;
}
Esempio n. 14
0
// 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;
}
Esempio n. 15
0
// 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;
}
Esempio n. 16
0
// 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;
}
Esempio n. 17
0
// 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;
}
Esempio n. 18
0
// 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 );
}
Esempio n. 19
0
// 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 );
}
Esempio n. 21
0
// 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;
}
Esempio n. 22
0
// 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;
}
Esempio n. 23
0
// 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 );
}
Esempio n. 24
0
// 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;
}