// PluginOptionsSavedToDB
//------------------------------------------------------------------------------
void TestCachePlugin::PluginOptionsSavedToDB() const
{
	FBuildOptions options;
	options.m_ConfigFile = "Data/TestCachePlugin/useplugin.bff";

	AStackString<> cachePath;
	AStackString<> cachePluginDLL;

	{
		// Init the DB from the BFF
		FBuild f( options );
		TEST_ASSERT( f.Initialize() );

		// sotre a copy of the cache params
		cachePath = f.GetCachePath();
		cachePluginDLL = f.GetCachePluginDLL();
		TEST_ASSERT( !cachePath.IsEmpty() );
		TEST_ASSERT( !cachePluginDLL.IsEmpty() );

		// save the db to disk
		TEST_ASSERT( f.SaveDependencyGraph( "../../../../ftmp/Test/CachePlugin/CachePlugin.fdb" ) );
	}

	{
		// reload from the db
		FBuild f( options );
		TEST_ASSERT( f.Initialize( "../../../../ftmp/Test/CachePlugin/CachePlugin.fdb" ) );

		// check that the cache params were persisted
		TEST_ASSERT( cachePath == f.GetCachePath() );
		TEST_ASSERT( cachePluginDLL == f.GetCachePluginDLL() );
	}	
}
Beispiel #2
0
// GetFileNode
//------------------------------------------------------------------------------
bool Function::GetFileNode( 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
	NodeGraph & ng = FBuild::Get().GetDependencyGraph();
	Node * n = ng.FindNode( fileNodeName );
	if ( n == nullptr )
	{
		n = ng.CreateFileNode( fileNodeName );
	}
	else if ( n->IsAFile() == false )
	{
		Error::Error_1103_NotAFile( iter, this, name, n->GetName(), n->GetType() );
		return false;
	}
	fileNode = n;
	return true;
}
Beispiel #3
0
// LoadNode
//------------------------------------------------------------------------------
/*static*/ bool Node::LoadNode( IOStream & stream, Node * & node )
{
	// read the name of the node
	AStackString< 512 > nodeName;
	if ( stream.Read( nodeName ) == false )
	{
        node = nullptr;
		return false;
	}

	// empty name means the pointer was null, which is supported
	if ( nodeName.IsEmpty() )
	{
		node = nullptr;
		return true;
	}

	// find the node by name - this should never fail
	NodeGraph & ng = FBuild::Get().GetDependencyGraph();
	Node * n = ng.FindNode( nodeName );
	if ( n == nullptr )
	{
        node = nullptr;
		return false;
	}
	node = n;

	return true;
}
Beispiel #4
0
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionSettings::Commit( NodeGraph & /*nodeGraph*/, const BFFIterator & funcStartIter ) const
{
    // using a cache plugin?
    AStackString<> cachePluginDLL;
    if ( !GetString( funcStartIter, cachePluginDLL, ".CachePluginDLL" ) )
    {
        return false;
    }
    FBuild::Get().SetCachePluginDLL( cachePluginDLL );
    if ( !cachePluginDLL.IsEmpty() )
    {
        FLOG_INFO( "CachePluginDLL: '%s'", cachePluginDLL.Get() );
    }

    // try to get the cache path from the config
    const BFFVariable * cachePathVar;
    if ( !GetString( funcStartIter, cachePathVar, ".CachePath" ) )
    {
        return false;
    }
    if ( cachePathVar )
    {
        s_CachePath = cachePathVar->GetString();

        // override environment default only if not empty
        if ( s_CachePath.IsEmpty() == false )
        {
            FBuild::Get().SetCachePath( s_CachePath );
        }
    }

    // "Workers"
    Array< AString > workerList;
    if ( !GetStrings( funcStartIter, workerList, ".Workers" ) )
    {
        return false;
    }
    if ( !workerList.IsEmpty() )
    {
        FBuild::Get().SetWorkerList( workerList );
    }

    // "Environment"
    Array< AString > environment;
    if ( !GetStrings( funcStartIter, environment, ".Environment" ) )
    {
        return false;
    }
    if ( !environment.IsEmpty() )
    {
        ProcessEnvironment( environment );
    }

    return true;
}
Beispiel #5
0
// CONSTRUCTOR - FBuild
//------------------------------------------------------------------------------
FBuild::FBuild( const FBuildOptions & options )
	: m_DependencyGraph( nullptr )
	, m_JobQueue( nullptr )
	, m_Client( nullptr )
	, m_Cache( nullptr )
	, m_LastProgressOutputTime( 0.0f )
	, m_LastProgressCalcTime( 0.0f )
	, m_SmoothedProgressCurrent( 0.0f )
	, m_SmoothedProgressTarget( 0.0f )
	, m_WorkerList( 0, true )
	, m_EnvironmentString( nullptr )
	, m_EnvironmentStringSize( 0 )
	, m_ImportedEnvironmentVars( 0, true )
{
	#ifdef DEBUG_CRT_MEMORY_USAGE
		_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | 
						_CRTDBG_CHECK_ALWAYS_DF | //_CRTDBG_CHECK_EVERY_16_DF |
						_CRTDBG_CHECK_CRT_DF | 
						_CRTDBG_DELAY_FREE_MEM_DF | 
						_CRTDBG_LEAK_CHECK_DF );
	#endif

	m_Macros = FNEW( BFFMacros() );

	m_DependencyGraph = FNEW( NodeGraph() );

	// store all user provided options
	m_Options = options;

	// track the old working dir to restore if modified (mainly for unit tests)
	VERIFY( FileIO::GetCurrentDir( m_OldWorkingDir ) );

	// check for cache environment variable to use as default
	AStackString<> cachePath;
	if ( Env::GetEnvVariable( "FASTBUILD_CACHE_PATH", cachePath ) )
	{
		if ( cachePath.IsEmpty() == false )
		{
			SetCachePath( cachePath );
		}
	}

	// poke options where required
	FLog::SetShowInfo( m_Options.m_ShowInfo );
	FLog::SetShowErrors( m_Options.m_ShowErrors );
	FLog::SetShowProgress( m_Options.m_ShowProgress );

	Function::Create();
}
// GetBaseDirectory
//------------------------------------------------------------------------------
bool FunctionObjectList::GetBaseDirectory( const BFFIterator & iter, AStackString<> & baseDirectory) const
{
    AStackString<> baseDir;
    if ( !GetString( iter, baseDir, ".CompilerInputFilesRoot", false ) ) // false = optional
    {
        return false; // GetString will have emitted error
    }
    if ( !baseDir.IsEmpty() )
    {
        NodeGraph::CleanPath( baseDir, baseDirectory );
    }
    else
    {
        baseDirectory.Clear();
    }

    return true;
}
Beispiel #7
0
// AStackStringConstructors
//------------------------------------------------------------------------------
void TestAString::AStackStringConstructors() const
{
    {
        // AStackString<> with no arguments
        AStackString<> empty;
        TEST_ASSERT( empty.GetLength() == 0 );
        TEST_ASSERT( empty.GetReserved() > 0 );
        TEST_ASSERT( empty.IsEmpty() == true );
        TEST_ASSERT( empty.MemoryMustBeFreed() == false );
    }
    {
        // AStackString<> from char *
        AStackString<> fromCharStar( "hello" );
        TEST_ASSERT( fromCharStar.GetLength() == 5 );
        TEST_ASSERT( fromCharStar.GetReserved() >= 5 );
        TEST_ASSERT( fromCharStar.IsEmpty() == false );
        TEST_ASSERT( fromCharStar.MemoryMustBeFreed() == false );

        // AStackString<> from AStackString
        AStackString<> fromAString( fromCharStar );
        TEST_ASSERT( fromAString.GetLength() == 5 );
        TEST_ASSERT( fromAString.GetReserved() >= 5 );
        TEST_ASSERT( fromAString.IsEmpty() == false );
        TEST_ASSERT( fromAString.MemoryMustBeFreed() == false );
    }
    {
        // AStackString<> from AString
        AString astring( "hello" );
        AStackString<> fromAString( astring );
        TEST_ASSERT( fromAString.GetLength() == 5 );
        TEST_ASSERT( fromAString.GetReserved() >= 5 );
        TEST_ASSERT( fromAString.IsEmpty() == false );
        TEST_ASSERT( fromAString.MemoryMustBeFreed() == false );
    }
    {
        const char * hello = "hellohellohello";
        AStackString<> fromCharStarPair( hello, hello + 5 );
        TEST_ASSERT( fromCharStarPair.GetLength() == 5 );
        TEST_ASSERT( fromCharStarPair.GetReserved() >= 5 );
        TEST_ASSERT( fromCharStarPair.IsEmpty() == false );
        TEST_ASSERT( fromCharStarPair.MemoryMustBeFreed() == false );
    }
}
Beispiel #8
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 );
}
// GenerateVCXProjFilters
//------------------------------------------------------------------------------
const AString & VSProjectGenerator::GenerateVCXProjFilters( const AString & projectFile )
{
    // preallocate to avoid re-allocations
    m_Tmp.SetReserved( MEGABYTE );
    m_Tmp.SetLength( 0 );

    // determine folder for project
    const char * lastProjSlash = projectFile.FindLast( NATIVE_SLASH );
    AStackString<> projectBasePath( projectFile.Get(), lastProjSlash ? lastProjSlash + 1 : projectFile.Get() );

    // header
    Write( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" );
    Write( "<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" );

    // list of all folders
    Array< AString > folders( 1024, true );

    // files
    {
        Write( "  <ItemGroup>\n" );
        const AString * const fEnd = m_Files.End();
        for ( const AString * fIt = m_Files.Begin(); fIt!=fEnd; ++fIt )
        {
            // get folder part, relative to base dir
            AStackString<> folder;
            GetFolderPath( *fIt, folder );
            const char * fileName = fIt->BeginsWithI( projectBasePath ) ? fIt->Get() + projectBasePath.GetLength() : fIt->Get();
            Write( "    <CustomBuild Include=\"%s\">\n", fileName );
            if ( !folder.IsEmpty() )
            {
                Write( "      <Filter>%s</Filter>\n", folder.Get() );
            }
            Write( "    </CustomBuild>\n" );

            // add new folders
            if ( !folder.IsEmpty() )
            {
                for (;;)
                {
                    // add this folder if unique
                    bool found = false;
                    for ( const AString * it=folders.Begin(); it!=folders.End(); ++it )
                    {
                        if ( it->CompareI( folder ) == 0 )
                        {
                            found = true;
                            break;
                        }
                    }
                    if ( !found )
                    {
                        folders.Append( folder );
                    }

                    // handle intermediate folders
                    const char * lastSlash = folder.FindLast( BACK_SLASH );
                    if ( lastSlash == nullptr )
                    {
                        break;
                    }
                    folder.SetLength( (uint32_t)( lastSlash - folder.Get() ) );
                }
            }
        }
        Write( "  </ItemGroup>\n" );
    }

    // folders
    {
        const AString * const fEnd = folders.End();
        for ( const AString * fIt = folders.Begin(); fIt!=fEnd; ++fIt )
        {
            Write( "  <ItemGroup>\n" );
            Write( "    <Filter Include=\"%s\">\n", fIt->Get() );
            Write( "      <UniqueIdentifier>{%08x-6c94-4f93-bc2a-7f5284b7d434}</UniqueIdentifier>\n", CRC32::Calc( *fIt ) );
            Write( "    </Filter>\n" );
            Write( "  </ItemGroup>\n" );
        }
    }

    // footer
    Write( "</Project>" ); // no carriage return

    m_OutputVCXProjFilters = m_Tmp;
    return m_OutputVCXProjFilters;
}
// 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;
}
// ExtractIntellisenseOptions
//------------------------------------------------------------------------------
/*static*/ void ProjectGeneratorBase::ExtractIntellisenseOptions( const AString & compilerArgs,
                                                                  const char * option,
                                                                  const char * alternateOption,
                                                                  Array< AString > & outOptions,
                                                                  bool escapeQuotes )
{
    ASSERT( option );
    Array< AString > tokens;
    compilerArgs.Tokenize( tokens );

    const size_t optionLen = AString::StrLen( option );
    const size_t alternateOptionLen = alternateOption ? AString::StrLen( alternateOption ) : 0;

    for ( size_t i=0; i<tokens.GetSize(); ++i )
    {
        AString & token = tokens[ i ];

        // strip quotes around token, e.g:    "-IFolder/Folder"
        if ( token.BeginsWith( '"' ) && token.EndsWith( '"' ) )
        {
            token.Assign( token.Get() + 1, token.GetEnd() - 1 );
        }

        AStackString<> optionBody;

        // Handle space between option and payload
        if ( ( token == option ) || ( token == alternateOption ) )
        {
            // Handle an incomplete token at the end of list
            if ( i == ( tokens.GetSize() - 1 ) )
            {
                break;
            }

            // Use next token
            optionBody = tokens[ i + 1 ];
        }
        else if ( token.BeginsWith( option ) )
        {
            // use everything after token
            optionBody.Assign( token.Get() + optionLen );
        }
        else if ( alternateOption && token.BeginsWith( alternateOption ) )
        {
            // use everything after token
            optionBody.Assign( token.Get() + alternateOptionLen );
        }

        // Strip quotes around body (e.g. -I"Folder/Folder")
        if ( optionBody.BeginsWith( '"' ) && optionBody.EndsWith( '"' ) )
        {
            optionBody.Trim( 1, 1 );
        }

        // Did we find something?
        if ( optionBody.IsEmpty() == false )
        {
            if ( escapeQuotes )
            {
                optionBody.Replace( "\"", "\\\"" );
            }
            outOptions.Append( optionBody );
        }
    }
}
Beispiel #12
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 );
}
Beispiel #13
0
// GetOtherLibsArg
//------------------------------------------------------------------------------
/*static*/ bool LinkerNode::GetOtherLibsArg( const char * arg,
                                             Array< AString > & list,
                                             const AString * & it,
                                             const AString * const & end,
                                             bool canonicalizePath,
                                             bool isMSVC )
{
    // check for expected arg
    if ( isMSVC )
    {
        if ( LinkerNode::IsStartOfLinkerArg_MSVC( *it, arg ) == false )
        {
            return false; // not our arg, not consumed
        }
    }
    else
    {
        if ( it->BeginsWith( arg ) == false )
        {
            return false; // not our arg, not consumed
        }
    }

    // get remainder of token after arg
    const char * valueStart = it->Get() + AString::StrLen( arg ) + 1;
    const char * valueEnd = it->GetEnd();

    // if no remainder, arg value is next token
    if ( valueStart == valueEnd )
    {
        ++it;

        // no more tokens? (malformed input)
        if ( it == end )
        {
            // ignore this item and let the linker complain about that
            return true; // arg consumed
        }

        // use next token a value
        valueStart = it->Get();
        valueEnd = it->GetEnd();
    }

    // eliminate quotes
    AStackString<> value;
    Args::StripQuotes( valueStart, valueEnd, value );

    // store if useful
    if ( value.IsEmpty() == false )
    {
        if ( canonicalizePath )
        {
            AStackString<> cleanValue;
            NodeGraph::CleanPath( value, cleanValue );
            PathUtils::EnsureTrailingSlash( cleanValue );
            list.Append( cleanValue );
        }
        else
        {
            list.Append( value );
        }
    }

    return true; // arg consumed
}
// 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;
}