Exemple #1
0
// 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;
}
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionCopyDir::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const
{
    // Get input paths
    Array< AString > inputPaths;
    if ( !GetFolderPaths( funcStartIter, inputPaths, ".SourcePaths", true ) )
    {
        return false; // GetFolderPaths will have emitted an error
    }

    // get the optional params
    Array< AString > patterns;
    bool recurse = true;
    Array< AString > excludePaths;
    if ( !GetStrings( funcStartIter, patterns, ".SourcePathsPattern" ) ||
         !GetBool( funcStartIter, recurse, ".SourcePathsRecurse", true ) || // recursive by default
         !GetStrings( funcStartIter, excludePaths, ".SourceExcludePaths" ) )
    {
        return false; // Get* will have emitted error
    }

    // convert input paths to DirectoryListNodes
    Dependencies staticDeps( inputPaths.GetSize() );
    if ( !GetDirectoryListNodeList( nodeGraph, funcStartIter, inputPaths, excludePaths, Array< AString >(), Array< AString >(), recurse, patterns.IsEmpty() ? nullptr : &patterns, ".SourcePaths", staticDeps ) )
    {
        return false; // GetDirectoryListNodeList will have emitted an error
    }

    // Get output path
    AStackString<> destPath;
    if ( !GetString( funcStartIter, destPath, ".Dest", true ) )
    {
        return false;
    }
    NodeGraph::CleanPath( destPath );

    // Pre-build dependencies
    Dependencies preBuildDeps;
    if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDeps, false ) )
    {
        return false; // GetNodeList will have emitted an error
    }

    // sanity check we defined something useful
    if ( staticDeps.IsEmpty() )
    {
        Error::Error_1006_NothingToBuild( funcStartIter, this );
        return false;
    }

    // check node doesn't already exist
    if ( nodeGraph.FindNode( m_AliasForFunction ) )
    {
        Error::Error_1100_AlreadyDefined( funcStartIter, this, m_AliasForFunction );
        return false;
    }

    // create our node
    nodeGraph.CreateCopyDirNode( m_AliasForFunction, staticDeps, destPath, preBuildDeps );
    return true;
}
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionCompiler::Commit( const BFFIterator & funcStartIter ) const
{
	const BFFVariable * executableV;
	if ( !GetString( funcStartIter, executableV, ".Executable", true ) )
	{
		return false; // GetString will have emitted error
	}

	// path cleanup
	AStackString<> exe;
	NodeGraph::CleanPath( executableV->GetString(), exe );

	// exe must be a file, not a path
	if ( PathUtils::IsFolderPath( exe ) )
	{
		Error::Error_1105_PathNotAllowed( funcStartIter, this, ".Executable", exe );
		return false;
	}

	//
	Dependencies extraFiles( 32, true );
	if ( !GetNodeList( funcStartIter, ".ExtraFiles", extraFiles, false ) ) // optional
	{
		return false; // GetNodeList will have emitted an error
	}

	// get executable node
	NodeGraph & ng = FBuild::Get().GetDependencyGraph();
	if ( ng.FindNode( exe ) )
	{
		Error::Error_1100_AlreadyDefined( funcStartIter, this, exe );
		return false;
	}

	// create our node
	const bool allowDistribution = true;
	Node * compilerNode = ng.CreateCompilerNode( exe, extraFiles, allowDistribution );

	// handle alias creation
	return ProcessAlias( funcStartIter, compilerNode );
}
// 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 );
}
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionObjectList::Commit( const BFFIterator & funcStartIter ) const
{
	// make sure all required variables are defined
	const BFFVariable * compiler;
	const BFFVariable * compilerOptions;
	AStackString<> compilerOptionsDeoptimized;
	AStackString<> compilerOutputPath;
    AStackString<> compilerOutputPrefix;
	const BFFVariable * compilerOutputExtension;
	if ( !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 ) )
	{
		return false;
	}

    PathUtils::FixupFolderPath( compilerOutputPath );

	NodeGraph & ng = FBuild::Get().GetDependencyGraph();

	// find or create the compiler node
	CompilerNode * compilerNode = nullptr;
	if ( !FunctionObjectList::GetCompilerNode( funcStartIter, compiler->GetString(), compilerNode ) )
	{
		return false; // GetCompilerNode will have emitted error
	}

	// Sanity check compile flags
	uint32_t objFlags = ObjectNode::DetermineFlags( compilerNode, compilerOptions->GetString() );
	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
	{
		const AString & args = compilerOptions->GetString();
		bool hasInputToken = ( args.Find( "%1" ) || args.Find( "\"%1\"" ) );
		if ( hasInputToken == false )
		{
			Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%1" );
			return false;
		}
		bool hasOutputToken = ( args.Find( "%2" ) || args.Find( "\"%2\"" ) );
		if ( hasOutputToken == false )
		{
			Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "%2" );
			return false;
		}

        // check /c or -c
        if ( objFlags & ObjectNode::FLAG_MSVC )
        {
            if ( args.Find( "/c" ) == nullptr &&
				args.Find( "-c" ) == nullptr)
            {
		        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 ( args.Find( "-c" ) == nullptr )
            {
		        Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "-c" );
			    return false;
		    }
        }
	}

	// Compiler Force Using
	Dependencies compilerForceUsing;
	if ( !GetNodeList( funcStartIter, ".CompilerForceUsing", compilerForceUsing, false ) )
	{
		return false; // GetNodeList will have emitted an error
	}

	// 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( 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( funcStartIter, ".PreBuildDependencies", preBuildDependencies, 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;
	}

	// Precompiled Header support
	ObjectNode * precompiledHeaderNode = nullptr;
	if ( !GetPrecompiledHeaderNode( funcStartIter, compilerNode, objFlags, compilerOptions, compilerForceUsing, precompiledHeaderNode, deoptimizeWritableFiles, deoptimizeWritableFilesWithToken ) )
	{
		return false; // GetPrecompiledHeaderNode will have emitted error
	}

	Dependencies staticDeps( 32, true );
	if ( !GetInputs( funcStartIter, staticDeps ) )
	{
		return false; // GetStaticDeps will gave emitted error
	}

	if ( staticDeps.IsEmpty() )
	{
		Error::Error_1006_NothingToBuild( funcStartIter, this );
		return false;
	}

	// parsing logic should guarantee we have a string for our name
	ASSERT( m_AliasForFunction.IsEmpty() == false );

	// Check for existing node
	if ( ng.FindNode( m_AliasForFunction ) )
	{
		Error::Error_1100_AlreadyDefined( funcStartIter, this, m_AliasForFunction );
		return false;
	}

	// Create library node which depends on the single file or list
	ObjectListNode * o = ng.CreateObjectListNode( m_AliasForFunction,
												  staticDeps,
												  compilerNode,
												  compilerOptions->GetString(),
												  compilerOptionsDeoptimized,
												  compilerOutputPath,
												  precompiledHeaderNode,
												  compilerForceUsing,
												  preBuildDependencies,
												  deoptimizeWritableFiles,
												  deoptimizeWritableFilesWithToken,
                                                  preprocessorNode,
                                                  preprocessorOptions ? preprocessorOptions->GetString() : AString::GetEmpty() );
	if ( compilerOutputExtension )
	{
		o->m_ObjExtensionOverride = compilerOutputExtension->GetString();
	}
    o->m_CompilerOutputPrefix = compilerOutputPrefix;

	return true;
}
// GetInputs
//------------------------------------------------------------------------------
bool FunctionObjectList::GetInputs( const BFFIterator & iter, Dependencies & inputs ) const
{
	NodeGraph & ng = FBuild::Get().GetDependencyGraph();

	// do we want to build files via a unity blob?
    Array< AString > inputUnities;
    if ( !GetStrings( iter, inputUnities, ".CompilerInputUnity", false ) ) // not required
	{
		return false;
	}
    for ( const auto& unity : inputUnities )
    {
        Node * n = ng.FindNode( unity );
		if ( n == nullptr )
		{
			Error::Error_1104_TargetNotDefined( iter, this, "CompilerInputUnity", unity );
			return false;
		}
		if ( n->GetType() != Node::UNITY_NODE )
		{
			Error::Error_1102_UnexpectedType( iter, this, "CompilerInputUnity", unity, n->GetType(), Node::UNITY_NODE );
			return false;
		}
		inputs.Append( Dependency( n ) );
    }

	// do we want to build a files in a directory?
	const BFFVariable * inputPath = BFFStackFrame::GetVar( ".CompilerInputPath" );
	if ( inputPath )
	{
		// get the optional pattern and recurse options related to InputPath
		Array< AString > patterns;
		if ( !GetStrings( iter, patterns, ".CompilerInputPattern", false ) )
		{
			return false; // GetString will have emitted an error
		}
		if ( patterns.IsEmpty() )
		{
			patterns.Append( AStackString<>( "*.cpp" ) );
		}

		// recursive?  default to true
		bool recurse = true;
		if ( !GetBool( iter, recurse, ".CompilerInputPathRecurse", true, false ) )
		{
			return false; // GetBool will have emitted an error
		}

		// Support an exclusion path
		Array< AString > excludePaths;
		if ( !GetFolderPaths( iter, excludePaths, ".CompilerInputExcludePath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

        Array< AString > filesToExclude;
        if ( !GetStrings( iter, filesToExclude, ".CompilerInputExcludedFiles", false ) ) // not required
        {
            return false; // GetStrings will have emitted an error
        }
	    CleanFileNames( filesToExclude );

		// Input paths
		Array< AString > inputPaths;
		if ( !GetFolderPaths( iter, inputPaths, ".CompilerInputPath", false ) )
		{
			return false; // GetFolderPaths will have emitted an error
		}

		Dependencies dirNodes( inputPaths.GetSize() );
		if ( !GetDirectoryListNodeList( iter, inputPaths, excludePaths, filesToExclude, recurse, &patterns, "CompilerInputPath", dirNodes ) )
		{
			return false; // GetDirectoryListNodeList will have emitted an error
		}
		inputs.Append( dirNodes );
	}

	// do we want to build a specific list of files?
	if ( !GetNodeList( iter, ".CompilerInputFiles", inputs, false ) )
	{
		// helper will emit error
		return false;
	}

	return true;
}
Exemple #7
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 );
}
Exemple #8
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 );
}
Exemple #10
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;
}