Exemple #1
0
// WriteHeader
//------------------------------------------------------------------------------
void SLNGenerator::WriteHeader( const AString & solutionVisualStudioVersion,
                                const AString & solutionMinimumVisualStudioVersion )
{
    const char * defaultVersion         = "14.0.22823.1"; // Visual Studio 2015 RC
    const char * defaultMinimumVersion  = "10.0.40219.1"; // Visual Studio Express 2010

    const char * version = ( solutionVisualStudioVersion.GetLength() > 0 )
                           ? solutionVisualStudioVersion.Get()
                           : defaultVersion ;

    const char * minimumVersion = ( solutionMinimumVisualStudioVersion.GetLength() > 0 )
                                  ? solutionMinimumVisualStudioVersion.Get()
                                  : defaultMinimumVersion ;

    const char * shortVersionStart = version;
    const char * shortVersionEnd = version;
    for ( ; *shortVersionEnd && *shortVersionEnd != '.' ; ++shortVersionEnd );

    AStackString<> shortVersion( shortVersionStart, shortVersionEnd );

    // header
    Write( "\r\n" ); // Deliberate blank line
    Write( "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" );
    Write( "# Visual Studio %s\r\n", shortVersion.Get() );
    Write( "VisualStudioVersion = %s\r\n", version );
    Write( "MinimumVisualStudioVersion = %s\r\n", minimumVersion );
}
Exemple #2
0
// AStringAssignment
//------------------------------------------------------------------------------
void TestAString::AStringAssignment() const
{
    AString str;
    str = "test";
    TEST_ASSERT( str.GetLength() == 4 );
    TEST_ASSERT( str.GetReserved() >= 4 );
    TEST_ASSERT( str.IsEmpty() == false );
    TEST_ASSERT( str.MemoryMustBeFreed() == true );

    AString str2;
    str2 = str;
    TEST_ASSERT( str2.GetLength() == 4 );
    TEST_ASSERT( str2.GetReserved() >= 4 );
    TEST_ASSERT( str2.IsEmpty() == false );
    TEST_ASSERT( str2.MemoryMustBeFreed() == true );

    const char * testData = "hellozzzzzzzzz";
    AString str3;
    str3.Assign( testData, testData + 5 );
    TEST_ASSERT( str3.GetLength() == 5 );
    TEST_ASSERT( str3.GetReserved() >= 5 );
    TEST_ASSERT( str3.IsEmpty() == false );
    TEST_ASSERT( str3.MemoryMustBeFreed() == true );

    // assign empty
    {
        AString dst;
        dst.Assign( AString::GetEmpty() );
    }
    {
        AString dst;
        dst.Assign( AString::GetEmpty().Get(), AString::GetEmpty().Get() );
    }
}
Exemple #3
0
REGISTER_TESTS_END

// AStringConstructors
//------------------------------------------------------------------------------
void TestAString::AStringConstructors() const
{
    {
        // AString with no arguments
        AString empty;
        TEST_ASSERT( empty.GetLength() == 0 );
        TEST_ASSERT( empty.GetReserved() == 0 );
        TEST_ASSERT( empty.IsEmpty() == true );
        TEST_ASSERT( empty.MemoryMustBeFreed() == false );
    }
    {
        // AString with reserve capacity argument
        AString empty( 16 );
        TEST_ASSERT( empty.GetLength() == 0 );
        TEST_ASSERT( empty.GetReserved() == 16 );
        TEST_ASSERT( empty.IsEmpty() == true );
        TEST_ASSERT( empty.MemoryMustBeFreed() == true );
    }
    {
        // AString from char *
        AString fromCharStar( "hello" );
        TEST_ASSERT( fromCharStar.GetLength() == 5 );
        TEST_ASSERT( fromCharStar.GetReserved() >= 5 );
        TEST_ASSERT( fromCharStar.IsEmpty() == false );
        TEST_ASSERT( fromCharStar.MemoryMustBeFreed() == true );

        // AString from AString
        AString fromAString( fromCharStar );
        TEST_ASSERT( fromAString.GetLength() == 5 );
        TEST_ASSERT( fromAString.GetReserved() >= 5 );
        TEST_ASSERT( fromAString.IsEmpty() == false );
        TEST_ASSERT( fromAString.MemoryMustBeFreed() == true );
    }
    {
        const char * hello = "hellohellohello";
        AString fromCharStarPair( hello, hello + 5 );
        TEST_ASSERT( fromCharStarPair.GetLength() == 5 );
        TEST_ASSERT( fromCharStarPair.GetReserved() >= 5 );
        TEST_ASSERT( fromCharStarPair.IsEmpty() == false );
        TEST_ASSERT( fromCharStarPair.MemoryMustBeFreed() == true );
    }
}
Exemple #4
0
// CONSTRUCTOR (const AString &)
//------------------------------------------------------------------------------
AString::AString( const AString & string )
{
	uint32_t len = string.GetLength();
	m_Length = len;
	uint32_t reserved = Math::RoundUp( len, (uint32_t)2 );
	m_Contents = (char *)ALLOC( reserved + 1 );
	SetReserved( reserved, true );
	Copy( string.Get(), m_Contents, len ); // copy handles terminator
}
Exemple #5
0
// EnsWithI
//------------------------------------------------------------------------------
bool AString::EndsWithI( const AString & other ) const
{
	const size_t otherLen = other.GetLength();
	if ( otherLen > GetLength() )
	{
		return false;
	}
	return ( StrNCmpI( GetEnd() - otherLen, other.Get(), otherLen ) == 0 );
}
Exemple #6
0
// BeginsWithI
//------------------------------------------------------------------------------
bool AString::BeginsWithI( const AString & string ) const
{
	uint32_t otherLen = string.GetLength();
	if ( otherLen > GetLength() )
	{
		return false;
	}
	return ( StrNCmpI( m_Contents, string.Get(), otherLen ) == 0 );
}
Exemple #7
0
// operator == (const AString &)
//------------------------------------------------------------------------------
bool AString::operator == ( const AString & other ) const
{
	if ( other.GetLength() != GetLength() )
	{
		return false;
	}

	return ( *this == other.Get() );
}
Exemple #8
0
// Assign (const AString &)
//------------------------------------------------------------------------------
void AString::Assign( const AString & string )
{
	uint32_t len = string.GetLength();
	if ( len > GetReserved() )
	{
		GrowNoCopy( len );
	}
	else if ( m_Contents == s_EmptyString )
	{
		// if we are the special empty string, and we
		// didn't resize then the passed in string is empty too
		return;
	}
	Copy( string.Get(), m_Contents, len ); // handles terminator
	m_Length = len;
}
Exemple #9
0
// IsStartOfLinkerArg_MSVC
//------------------------------------------------------------------------------
/*static*/ bool LinkerNode::IsStartOfLinkerArg_MSVC( const AString & token, const char * arg )
{
    ASSERT( token.IsEmpty() == false );

    // MSVC Linker args can start with - or /
    if ( ( token[0] != '/' ) && ( token[0] != '-' ) )
    {
        return false;
    }

    // Length check to early out
    const size_t argLen = AString::StrLen( arg );
    if ( ( token.GetLength() - 1 ) < argLen )
    {
        return false; // token is too short
    }

    // MSVC Linker args are case-insensitive
    return ( AString::StrNCmpI( token.Get() + 1, arg, argLen ) == 0 );
}
Exemple #10
0
// CONSTRUCTOR
//------------------------------------------------------------------------------
DirectoryListNode::DirectoryListNode( const AString & name,
									  const AString & path,
									  const Array< AString > * patterns,
								      bool recursive,
									  const Array< AString > & excludePaths,
                                      const Array< AString > & filesToExclude )
: Node( name, Node::DIRECTORY_LIST_NODE, Node::FLAG_NONE )
	, m_Path( path )
	, m_Patterns()
	, m_ExcludePaths( excludePaths )
    , m_FilesToExclude( filesToExclude )
	, m_Recursive( recursive )
	, m_Files( 4096, true )
{
	if ( patterns )
	{
		m_Patterns = *patterns;
	}

	// ensure name is correctly formatted
	//   path|[patterns]|recursive|[excludePath]
	ASSERT( name.BeginsWith( path ) );
	ASSERT( name[ path.GetLength() ] == '|' );
	ASSERT( m_Patterns.IsEmpty() || ( name.Find( m_Patterns[ 0 ].Get() ) == name.Get() + path.GetLength() + 1 ) );
	ASSERT( ( recursive && name.Find( "|true|" ) ) || 
			( !recursive && name.Find( "|false|" ) ) );

	// paths must have trailing slash
	ASSERT( path.EndsWith( NATIVE_SLASH ) );

	// make sure exclusion path has trailing slash if provided
	#ifdef DEBUG
		const AString * const end = excludePaths.End();
		for ( const AString * it=excludePaths.Begin(); it != end; ++it )
		{
			ASSERT( ( *it ).EndsWith( NATIVE_SLASH ) );
		}
	#endif
}
Exemple #11
0
// Finalize
//------------------------------------------------------------------------------
bool Args::Finalize( const AString & exe, const AString & nodeNameForError, bool canUseResponseFile )
{
	ASSERT( !m_Finalized );

	#if defined( __WINDOWS__ ) || defined( __OSX__ )
		#if defined( __WINDOWS__ )
			// Windows has a 32KiB (inc null terminator) command line length limit with CreateProcess
			// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
			const uint32_t argLimit( 32767 );
		#elif defined( __OSX__ )
			const uint32_t argLimit( ARG_MAX - 1 );		
		#endif
	
		// Calculate final length of args (including exe name)
		const uint32_t exeLen = exe.GetLength();
		const uint32_t extraLen = 3; // quotes around exe name and space
		const uint32_t argLen = m_Args.GetLength();

		// We need to consider the executable, quotes around the exe and a space 
		// as well as the args: "%exe%" %args%
		const uint32_t totalLen = ( argLen + exeLen + extraLen );

		// Small enough?
		if ( totalLen <= argLimit )
		{
			#if defined( ASSERTS_ENABLED )
				m_Finalized = true;
			#endif
			return true; // Ok to proceed
		}

		// Args are too long. Can we cope using a Response File?
		if ( canUseResponseFile )
		{
			// Handle per-line limit within response files (e.g. link.exe)
			#if defined( __WINDOWS__ )
				if ( argLen >= 131071 ) // From LNK1170
				{
					// Change spaces to carriage returns
					for ( uint32_t i : m_DelimiterIndices )
					{
						ASSERT( m_Args[ i ] == ' ' );
						m_Args[ i ] = '\n';
					}
				}
			#endif

			#if defined( ASSERTS_ENABLED )
				m_Finalized = true;
			#endif

			// Write args to response file
			{
				PROFILE_SECTION( "CreateResponseFile" )
				m_ResponseFile.Create( *this );
			}

			// Create new args referencing response file
			m_ResponseFileArgs = "@\"";
			m_ResponseFileArgs += m_ResponseFile.GetResponseFilePath();
			m_ResponseFileArgs += "\"";

			return true; // Ok to proceed
		}

		// Need response file but not supported
		FLOG_ERROR( "FBuild: Error: Command Line Limit Exceeded (len: %u, limit: %u) '%s'\n", argLen, argLimit, nodeNameForError.Get() );
		return false;
	#elif defined( __LINUX__ )
		// TODO:LINUX Difficult to reliable determine this due to complex interaction with environment
		#if defined( ASSERTS_ENABLED )
			m_Finalized = true;
		#endif
		return true; // Ok to proceed
	#endif
}
Exemple #12
0
// GetFilesRecurse
//------------------------------------------------------------------------------
/*static*/ void FileIO::GetFilesRecurseEx( AString & pathCopy, 
										 const Array< AString > * patterns,
										 Array< FileInfo > * results )
{
    const uint32_t baseLength = pathCopy.GetLength();
    
    #if defined( __WINDOWS__ )
        pathCopy += '*'; // don't want to use wildcard to filter folders

        // recurse into directories
        WIN32_FIND_DATA findData;
        HANDLE hFind = FindFirstFileEx( pathCopy.Get(), FindExInfoBasic, &findData, FindExSearchLimitToDirectories, nullptr, 0 );
        if ( hFind == INVALID_HANDLE_VALUE)
        {
            return;
        }

        do
        {
            if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                // ignore magic '.' and '..' folders
                // (don't need to check length of name, as all names are at least 1 char
                // which means index 0 and 1 are valid to access)
                if ( findData.cFileName[ 0 ] == '.' &&
                    ( ( findData.cFileName[ 1 ] == '.' ) || ( findData.cFileName[ 1 ] == '\000' ) ) )
                {
                    continue;
                }

                pathCopy.SetLength( baseLength );
                pathCopy += findData.cFileName;
                pathCopy += NATIVE_SLASH;
                GetFilesRecurseEx( pathCopy, patterns, results );
            }
        }
        while ( FindNextFile( hFind, &findData ) != 0 );
        FindClose( hFind );

        // do files in this directory
        pathCopy.SetLength( baseLength );
        pathCopy += '*';
        hFind = FindFirstFileEx( pathCopy.Get(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0 );
        if ( hFind == INVALID_HANDLE_VALUE)
        {
            return;
        }

        do
        {
            if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                continue;
            }

			if ( IsMatch( patterns, findData.cFileName ) )
			{
				pathCopy.SetLength( baseLength );
				pathCopy += findData.cFileName;
				if ( results->GetSize() == results->GetCapacity() )
				{
					results->SetCapacity( results->GetSize() * 2 );
				}
				results->SetSize( results->GetSize() + 1 );
				FileInfo & newInfo = results->Top();
				newInfo.m_Name = pathCopy;
				newInfo.m_Attributes = findData.dwFileAttributes;
				newInfo.m_LastWriteTime = (uint64_t)findData.ftLastWriteTime.dwLowDateTime | ( (uint64_t)findData.ftLastWriteTime.dwHighDateTime << 32 );
				newInfo.m_Size = (uint64_t)findData.nFileSizeLow | ( (uint64_t)findData.nFileSizeHigh << 32 );
			}
        }
        while ( FindNextFile( hFind, &findData ) != 0 );

        FindClose( hFind );
        
	#elif defined( __LINUX__ ) || defined( __APPLE__ )
        DIR * dir = opendir( pathCopy.Get() );
        if ( dir == nullptr )
        {
            return;
        }
        for ( ;; )
        {
            dirent * entry = readdir( dir );
            if ( entry == nullptr )
            {
                break; // no more entries
            }
            
            // dir?
            if ( ( entry->d_type & DT_DIR ) == DT_DIR )
            {
                // ignore . and ..
                if ( entry->d_name[ 0 ] == '.' )
                {
                    if ( ( entry->d_name[ 1 ] == 0 ) ||
                         ( ( entry->d_name[ 1 ] == '.' ) && ( entry->d_name[ 2 ] == 0 ) ) )
                    {
                        continue;
                    }
                }

                // regular dir
                pathCopy.SetLength( baseLength );
                pathCopy += entry->d_name;
                pathCopy += NATIVE_SLASH;
                GetFilesRecurseEx( pathCopy, patterns, results ); 
                continue;
            }
            
            // file - does it match wildcard?
			if ( IsMatch( patterns, entry->d_name ) )
            {
                pathCopy.SetLength( baseLength );
                pathCopy += entry->d_name;
                
				if ( results->GetSize() == results->GetCapacity() )
				{
					results->SetCapacity( results->GetSize() * 2 );
				}
                results->SetSize( results->GetSize() + 1 );
                FileInfo & newInfo = results->Top();
                newInfo.m_Name = pathCopy;
                
                // get additional info
                struct stat info;
                VERIFY( stat( pathCopy.Get(), &info ) == 0 );
                newInfo.m_Attributes = info.st_mode;
				#if defined( __APPLE__ )
					newInfo.m_LastWriteTime = ( ( (uint64_t)info.st_mtimespec.tv_sec * 1000000000ULL ) + (uint64_t)info.st_mtimespec.tv_nsec );
				#else
	                newInfo.m_LastWriteTime = ( ( (uint64_t)info.st_mtim.tv_sec * 1000000000ULL ) + (uint64_t)info.st_mtim.tv_nsec );
				#endif
                newInfo.m_Size = info.st_size;
            }            
        }
        closedir( dir );        
    #else
        #error Unknown platform
    #endif
}
Exemple #13
0
// GetFilesRecurse
//------------------------------------------------------------------------------
/*static*/ void FileIO::GetFilesRecurse( AString & pathCopy, 
										 const AString & wildCard,
										 Array< AString > * results )
{
    const uint32_t baseLength = pathCopy.GetLength();
    
    #if defined( __WINDOWS__ )
        pathCopy += '*'; // don't want to use wildcard to filter folders

        // recurse into directories
        WIN32_FIND_DATA findData;
        HANDLE hFind = FindFirstFileEx( pathCopy.Get(), FindExInfoBasic, &findData, FindExSearchLimitToDirectories, nullptr, 0 );
        if ( hFind == INVALID_HANDLE_VALUE)
        {
            return;
        }

        do
        {
            if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                // ignore magic '.' and '..' folders
                // (don't need to check length of name, as all names are at least 1 char
                // which means index 0 and 1 are valid to access)
                if ( findData.cFileName[ 0 ] == '.' &&
                    ( ( findData.cFileName[ 1 ] == '.' ) || ( findData.cFileName[ 1 ] == '\000' ) ) )
                {
                    continue;
                }

                pathCopy.SetLength( baseLength );
                pathCopy += findData.cFileName;
                pathCopy += NATIVE_SLASH;
                GetFilesRecurse( pathCopy, wildCard, results );
            }
        }
        while ( FindNextFile( hFind, &findData ) != 0 );
        FindClose( hFind );

        // do files in this directory
        pathCopy.SetLength( baseLength );
        pathCopy += '*';
        hFind = FindFirstFileEx( pathCopy.Get(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0 );
        if ( hFind == INVALID_HANDLE_VALUE)
        {
            return;
        }

        do
        {
            if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                continue;
            }

			if ( PathUtils::IsWildcardMatch( wildCard.Get(), findData.cFileName ) )
			{
				pathCopy.SetLength( baseLength );
				pathCopy += findData.cFileName;
				results->Append( pathCopy );
			}
        }
        while ( FindNextFile( hFind, &findData ) != 0 );

        FindClose( hFind );
        
	#elif defined( __LINUX__ ) || defined( __APPLE__ )
        DIR * dir = opendir( pathCopy.Get() );
        if ( dir == nullptr )
        {
            return;
        }
        for ( ;; )
        {
            dirent * entry = readdir( dir );
            if ( entry == nullptr )
            {
                break; // no more entries
            }
            
            // dir?
            if ( ( entry->d_type & DT_DIR ) == DT_DIR )
            {
                // ignore . and ..
                if ( entry->d_name[ 0 ] == '.' )
                {
                    if ( ( entry->d_name[ 1 ] == 0 ) ||
                         ( ( entry->d_name[ 1 ] == '.' ) && ( entry->d_name[ 2 ] == 0 ) ) )
                    {
                        continue;
                    }
                }

                // regular dir
                pathCopy.SetLength( baseLength );
                pathCopy += entry->d_name;
                pathCopy += NATIVE_SLASH;
                GetFilesRecurse( pathCopy, wildCard, results ); 
                continue;
            }
            
            // file - does it match wildcard?
			if ( PathUtils::IsWildcardMatch( wildCard.Get(), entry->d_name ) )
            {
                pathCopy.SetLength( baseLength );
                pathCopy += entry->d_name;
                results->Append( pathCopy );
            }            
        }
        closedir( dir );
    #else
        #error Unknown platform
    #endif
}
Exemple #14
0
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionSLN::Commit( const BFFIterator & funcStartIter ) const
{
    AStackString<> solutionOutput;
    Array< AString > solutionProjects( 8, true );
    if ( !GetString( funcStartIter, solutionOutput,     ".SolutionOutput", true ) ||
         !GetStrings( funcStartIter, solutionProjects,  ".SolutionProjects", false ) )
    {
        return false;
    }

    // optional inputs
    AString solutionBuildProject;
    AString solutionVisualStudioVersion;
    AString solutionMinimumVisualStudioVersion;
    if ( !GetString( funcStartIter, solutionBuildProject,                 ".SolutionBuildProject", false ) ||
         !GetString( funcStartIter, solutionVisualStudioVersion,          ".SolutionVisualStudioVersion", false ) ||
         !GetString( funcStartIter, solutionMinimumVisualStudioVersion,   ".SolutionMinimumVisualStudioVersion", false ) )
    {
        return false;
    }

    // base config
    VSProjectConfig baseConfig;

    // create configs
    Array< VSProjectConfig > configs( 16, true );

    const BFFVariable * solutionConfigs = BFFStackFrame::GetVar( ".SolutionConfigs" );
    if ( solutionConfigs )
    {
        if ( solutionConfigs->IsArrayOfStructs() == false )
        {
            Error::Error_1050_PropertyMustBeOfType( funcStartIter, this, ".SolutionConfigs", solutionConfigs->GetType(), BFFVariable::VAR_ARRAY_OF_STRUCTS );
            return false;
        }

        const Array< const BFFVariable * > & structs = solutionConfigs->GetArrayOfStructs();
        const BFFVariable * const * end = structs.End();
        for ( const BFFVariable ** it = structs.Begin(); it != end; ++it )
        {
            const BFFVariable * s = *it;

            // start with the base configuration
            VSProjectConfig newConfig( baseConfig );

            // .Platform must be provided
            if ( !GetStringFromStruct( s, ".Platform",  newConfig.m_Platform ) )
            {
                // TODO:B custom error
                Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Platform" ) );
                return false;
            }

            // .Config must be provided
            if ( !GetStringFromStruct( s, ".Config",    newConfig.m_Config ) )
            {
                // TODO:B custom error
                Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Config" ) );
                return false;
            }

            configs.Append( newConfig );
        }
    }
    else
    {
        // no user specified configs, make some defaults

        // start from the default
        VSProjectConfig config( baseConfig );

        // make the configs
        config.m_Platform   = "Win32";
        config.m_Config     = "Debug";
        configs.Append( config );
        config.m_Config     = "Release";
        configs.Append( config );
        config.m_Platform   = "x64";
        configs.Append( config );
        config.m_Config     = "Debug";
        configs.Append( config );
    }

    // sort project configs by config and by platform (like visual)
    configs.Sort( VSProjectConfigComp() );

    // create solution folders
    Array< SLNSolutionFolder > folders( 16, true );

    const BFFVariable * solutionFolders = BFFStackFrame::GetVar( ".SolutionFolders" );
    if ( solutionFolders )
    {
        if ( solutionFolders->IsArrayOfStructs() == false )
        {
            Error::Error_1050_PropertyMustBeOfType( funcStartIter, this, ".SolutionFolders", solutionFolders->GetType(), BFFVariable::VAR_ARRAY_OF_STRUCTS );
            return false;
        }

        const Array< const BFFVariable * > & structs = solutionFolders->GetArrayOfStructs();
        const BFFVariable * const * end = structs.End();
        for ( const BFFVariable ** it = structs.Begin(); it != end; ++it )
        {
            const BFFVariable * s = *it;

            // start with the base configuration
            SLNSolutionFolder newFolder;

            // .Path must be provided
            if ( !GetStringFromStruct( s, ".Path", newFolder.m_Path ) )
            {
                // TODO:B custom error
                Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Path" ) );
                return false;
            }

            newFolder.m_Path.Replace( OTHER_SLASH, NATIVE_SLASH );

            // check if this path was already defined
            {
                const SLNSolutionFolder * const end2 = folders.End();
                for ( const SLNSolutionFolder * it2 = folders.Begin() ; it2 != end2 ; ++it2 )
                {
                    if ( it2->m_Path == newFolder.m_Path  )
                    {
                        // TODO:B custom error
                        Error::Error_1100_AlreadyDefined( funcStartIter, this, it2->m_Path );
                        return false;
                    }
                }
            }

            // .Projects must be provided
            if ( !GetStringOrArrayOfStringsFromStruct( funcStartIter, s, ".Projects", newFolder.m_ProjectNames ) )
            {
                return false; // GetStringOrArrayOfStringsFromStruct has emitted an error
            }

            // check if this project is included in the solution
            for ( const AString & projectName : newFolder.m_ProjectNames )
            {
                if ( solutionProjects.Find( projectName ) == nullptr )
                {
					solutionProjects.Append( projectName );
                }
            }

            folders.Append( newFolder );
        }
    }

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

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

    // resolves VCXProject nodes associated to solutionProjects
    Array< VCXProjectNode * > projects( solutionProjects.GetSize(), false );
    {
        const AString * const end = solutionProjects.End();
        for ( const AString * it = solutionProjects.Begin(); it != end; ++it )
        {
            VCXProjectNode * project = ResolveVCXProject( funcStartIter, *it );
            if ( project == nullptr )
            {
                return false; // ResolveVCXProject will have emitted error
            }

            // check that this project contains all .SolutionConfigs
            const Array< VSProjectConfig > & projectConfigs = project->GetConfigs();

            const size_t configsSize = configs.GetSize();
            for ( size_t i = 0 ; i < configsSize ; ++i )
            {
                bool containsConfig = false;

                const VSProjectConfig * const config = &configs[i];
                const VSProjectConfig * const end2 = projectConfigs.End();
                for ( const VSProjectConfig * it2 = projectConfigs.Begin(); it2 != end2; ++it2 )
                {
                    if ( it2->m_Platform == config->m_Platform &&
                         it2->m_Config   == config->m_Config )
                    {
                        containsConfig = true;
                        break;
                    }
                }

                if ( containsConfig == false )
                {
                    // TODO: specific error message "ProjectConfigNotFound"
                    AStackString<> configName;
                    configName.Format( "%s|%s", config->m_Platform.Get(), config->m_Config.Get() );
                    Error::Error_1104_TargetNotDefined( funcStartIter, this, configName.Get(), project->GetName() );
                    return false;
                }
            }

            // append vcxproject node to solution
            projects.Append( project );
        }
    }

    // sort projects by name (like visual)
    projects.Sort( VCXProjectNodeComp() );

    // resolves VCXProject nodes associated to solutionFolders
    {
        SLNSolutionFolder * const end = folders.End();
        for ( SLNSolutionFolder * it = folders.Begin(); it != end; ++it )
        {
            // retrieves full path of contained vcxprojects

            AString * const end2 = it->m_ProjectNames.End();
            for ( AString * it2 = it->m_ProjectNames.Begin(); it2 != end2; ++it2 )
            {
				// Get associate project file
				VCXProjectNode * project = ResolveVCXProject( funcStartIter, *it2 );
				if ( project == nullptr )
				{
					return false; // ResolveVCXProjectRecurse will have emitted error
				}

                ASSERT( projects.Find( project ) ); // Sanity check in global list

				// fixup name to be to final project
                *it2 = project->GetName();
            }
        }
    }

    // resolves VCXProject node referenced by solutionBuildProject
    if ( solutionBuildProject.GetLength() > 0 )
    {
		// Get associate project file
		const VCXProjectNode * project = ResolveVCXProject( funcStartIter, solutionBuildProject );
		if ( project == nullptr )
		{
			return false; // ResolveVCXProject will have emitted error
		}

        if ( projects.Find( project ) == nullptr )
        {
            // project referenced in .SolutionBuildProject is not referenced in .SolutionProjects
            Error::Error_1104_TargetNotDefined( funcStartIter, this, ".SolutionBuildProject", project->GetName() );
            return false;
        }

        solutionBuildProject = project->GetName();
    }

    // Project Dependencies
    Array< SLNDependency > slnDeps( 0, true );
    const BFFVariable * projectDepsVar = BFFStackFrame::GetVar( ".SolutionDependencies" );
    if ( projectDepsVar )
    {
        if ( projectDepsVar->IsArrayOfStructs() == false )
        {
            Error::Error_1050_PropertyMustBeOfType( funcStartIter, this, ".SolutionDependencies", projectDepsVar->GetType(), BFFVariable::VAR_ARRAY_OF_STRUCTS );
            return false;
        }
	
		slnDeps.SetCapacity( projectDepsVar->GetArrayOfStructs().GetSize() );
		for ( const BFFVariable * s : projectDepsVar->GetArrayOfStructs() )
		{
            // .Projects must be provided
            // .Dependencies must be provided
			SLNDependency deps;
            if ( !GetStringOrArrayOfStringsFromStruct( funcStartIter, s, ".Projects", deps.m_Projects ) ||
				 !GetStringOrArrayOfStringsFromStruct( funcStartIter, s, ".Dependencies", deps.m_Dependencies ) )
            {
                return false; // GetStringOrArrayOfStringsFromStruct has emitted an error
            }

			// fixup
			for ( AString & projectName : deps.m_Projects )
			{
				// Get associated project file
				const VCXProjectNode * project = ResolveVCXProject( funcStartIter, projectName );
				if ( project == nullptr )
				{
					return false; // ResolveVCXProject will have emitted error
				}
				projectName = project->GetName();
			}
			for ( AString & projectName : deps.m_Dependencies )
			{
				// Get associated project file
				const VCXProjectNode * project = ResolveVCXProject( funcStartIter, projectName );
				if ( project == nullptr )
				{
					return false; // ResolveVCXProject will have emitted error
				}
				projectName = project->GetName();
			}

			slnDeps.Append( deps );
		}
	}

    SLNNode * sln = ng.CreateSLNNode(   solutionOutput,
                                        solutionBuildProject,
                                        solutionVisualStudioVersion,
                                        solutionMinimumVisualStudioVersion,
                                        configs,
                                        projects,
										slnDeps,
                                        folders );

    ASSERT( sln );

    return ProcessAlias( funcStartIter, sln );
}
// WriteIfDifferent
//------------------------------------------------------------------------------
/*static*/ bool ProjectGeneratorBase::WriteIfDifferent( const char * generatorId, const AString & content, const AString & fileName )
{
    bool needToWrite = false;

    FileStream old;
    if ( FBuild::Get().GetOptions().m_ForceCleanBuild )
    {
        needToWrite = true;
    }
    else if ( old.Open( fileName.Get(), FileStream::READ_ONLY ) == false )
    {
        needToWrite = true;
    }
    else
    {
        // files differ in size?
        size_t oldFileSize = (size_t)old.GetFileSize();
        if ( oldFileSize != content.GetLength() )
        {
            needToWrite = true;
        }
        else
        {
            // check content
            AutoPtr< char > mem( ( char *)ALLOC( oldFileSize ) );
            if ( old.Read( mem.Get(), oldFileSize ) != oldFileSize )
            {
                FLOG_ERROR( "%s - Failed to read '%s'", generatorId, fileName.Get() );
                return false;
            }

            // compare content
            if ( memcmp( mem.Get(), content.Get(), oldFileSize ) != 0 )
            {
                needToWrite = true;
            }
        }

        // ensure we are closed, so we can open again for write if needed
        old.Close();
    }

    // only save if missing or different
    if ( needToWrite == false )
    {
        return true; // nothing to do.
    }

    FLOG_BUILD( "%s: %s\n", generatorId, fileName.Get() );

    // ensure path exists (normally handled by framework, but Projects
    // are not necessarily a single file)
    if ( Node::EnsurePathExistsForFile( fileName ) == false )
    {
        FLOG_ERROR( "%s - Invalid path for '%s' (error: %u)", generatorId, fileName.Get(), Env::GetLastErr() );
        return false;
    }

    // actually write
    FileStream f;
    if ( !f.Open( fileName.Get(), FileStream::WRITE_ONLY ) )
    {
        FLOG_ERROR( "%s - Failed to open '%s' for write (error: %u)", generatorId, fileName.Get(), Env::GetLastErr() );
        return false;
    }
    if ( f.Write( content.Get(), content.GetLength() ) != content.GetLength() )
    {
        FLOG_ERROR( "%s - Error writing to '%s' (error: %u)", generatorId, fileName.Get(), Env::GetLastErr() );
        return false;
    }
    f.Close();

    return true;
}
Exemple #16
0
// EmbeddedNuls
//------------------------------------------------------------------------------
void TestAString::EmbeddedNuls() const
{
    // Create a string with an embedded nul and check various behaviours
    AStackString<> string( "0123456789" );
    const uint32_t originalStringLen = string.GetLength();
    string[ 5 ] = 0; // insert null terminator

    // Copy construction
    {
        AString copy( string );
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );
    }

    // Assignment (operator =)
    {
        AString copy;
        copy = string;
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );
    }

    // Assignment (Assign)
    {
        AString copy;
        copy.Assign( string );
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );
    }

    // Assignment (Assign with iterators)
    {
        AString copy;
        copy.Assign( string.Get(), string.GetEnd() );
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );
    }

    // Append (operator +=)
    {
        // Append to empty
        AString copy;
        copy += string;
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( AString::StrNCmp( string.Get(), copy.Get(), originalStringLen ) == 0 );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );

        // Append to existing
        AString copy2( string );
        copy2 += string;
        TEST_ASSERT( copy2.GetLength() == ( originalStringLen * 2 ) );
        TEST_ASSERT( memcmp( "01234" "\0" "678901234" "\0" "6789", copy2.Get(), ( originalStringLen * 2 ) ) == 0 );
    }

    // Append (Append)
    {
        // Append to empty
        AString copy;
        copy.Append( string );
        TEST_ASSERT( copy.GetLength() == originalStringLen );
        TEST_ASSERT( memcmp( "01234" "\0" "6789", copy.Get(), originalStringLen ) == 0 );

        // Append to existing
        AString copy2( string );
        copy2.Append( string );
        TEST_ASSERT( copy2.GetLength() == ( originalStringLen * 2 ) );
        TEST_ASSERT( memcmp( "01234" "\0" "678901234" "\0" "6789", copy2.Get(), ( originalStringLen * 2 ) ) == 0 );
    }
}
Exemple #17
0
// ParseNamedVariableName
//------------------------------------------------------------------------------
/*static*/ bool BFFParser::ParseVariableName( BFFIterator & iter, AString & name, bool & parentScope )
{
	// skip over the declaration symbol
	ASSERT( *iter == BFF_DECLARE_VAR_INTERNAL ||
			*iter == BFF_DECLARE_VAR_PARENT );

	parentScope = ( *iter == BFF_DECLARE_VAR_PARENT );

	const BFFIterator varNameStart = iter; // include type token in var name
	iter++;

	// make sure we haven't hit the end of the file
	if ( iter.IsAtEnd() )
	{
		Error::Error_1012_UnexpectedEndOfFile( iter );
		return false;
	}

	if ( *iter == '\'' || *iter == '"' )
	{
		// parse the string
		const BFFIterator openToken = iter;
		iter.SkipString( *openToken );
		if ( *iter != *openToken )
		{
			Error::Error_1002_MatchingClosingTokenNotFound( openToken, nullptr, *openToken );
			return false;
		}
		BFFIterator stringStart = openToken;
		stringStart++;

		// unescape and subsitute embedded variables
		AStackString< 256 > value;
		if ( PerformVariableSubstitutions( stringStart, iter, value ) == false )
		{
			return false;
		}
		iter++; // skip close token

		BFFIterator varNameIter( value.Get(), value.GetLength(), iter.GetFileName().Get(), iter.GetFileTimeStamp() );

		// sanity check it is a sensible length
		if ( value.GetLength() + 1/* '.' will be added */  > MAX_VARIABLE_NAME_LENGTH )
		{
			Error::Error_1014_VariableNameIsTooLong( varNameIter, (uint32_t)value.GetLength(), (uint32_t)MAX_VARIABLE_NAME_LENGTH );
			return false;
		}

		// sanity check it is a valid variable name
		while ( varNameIter.IsAtEnd() == false )
		{
			if ( varNameIter.IsAtValidVariableNameCharacter() == false )
			{
				Error::Error_1013_UnexpectedCharInVariableName( varNameIter, nullptr );
				return false;
			}
			varNameIter++;
		}

		// append '.' to variable name
		name = ".";
		name.Append( value );
	}
	else
	{
		// make sure immediately after the symbol starts a variable name
		if ( iter.IsAtValidVariableNameCharacter() == false )
		{
			Error::Error_1013_UnexpectedCharInVariableName( iter, nullptr );
			return false;
		}

		// find the end of the variable name
		iter.SkipVariableName();
		const BFFIterator varNameEnd = iter;

		// sanity check it is a sensible length
		size_t varNameLen = varNameStart.GetDistTo( varNameEnd );
		if ( varNameLen > MAX_VARIABLE_NAME_LENGTH )
		{
			Error::Error_1014_VariableNameIsTooLong( iter, (uint32_t)varNameLen, (uint32_t)MAX_VARIABLE_NAME_LENGTH );
			return false;
		}

		// store variable name
		name.Assign( varNameStart.GetCurrent(), varNameEnd.GetCurrent() );
	}

	ASSERT( name.GetLength() > 0 );
	if ( parentScope )
	{
		// exchange '^' with '.'
		ASSERT( BFF_DECLARE_VAR_PARENT == name[0] );
		name[0] = BFF_DECLARE_VAR_INTERNAL;
	}

	return true;
}
Exemple #18
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;
}
Exemple #19
0
// Save
//------------------------------------------------------------------------------
bool SLNNode::Save( const AString & content, const AString & fileName ) const
{
    bool needToWrite = false;

    FileStream old;
    if ( FBuild::Get().GetOptions().m_ForceCleanBuild )
    {
        needToWrite = true;
    }
    else if ( old.Open( fileName.Get(), FileStream::READ_ONLY ) == false )
    {
        needToWrite = true;
    }
    else
    {
        // files differ in size?
        size_t oldFileSize = (size_t)old.GetFileSize();
        if ( oldFileSize != content.GetLength() )
        {
            needToWrite = true;
        }
        else
        {
            // check content
            AutoPtr< char > mem( ( char *)ALLOC( oldFileSize ) );
            if ( old.Read( mem.Get(), oldFileSize ) != oldFileSize )
            {
                FLOG_ERROR( "SLN - Failed to read '%s'", fileName.Get() );
                return false;
            }

            // compare content
            if ( memcmp( mem.Get(), content.Get(), oldFileSize ) != 0 )
            {
                needToWrite = true;
            }
        }

        // ensure we are closed, so we can open again for write if needed
        old.Close();
    }

    // only save if missing or ner
    if ( needToWrite == false )
    {
        return true; // nothing to do.
    }

    FLOG_BUILD( "SLN: %s\n", fileName.Get() );

    // actually write
    FileStream f;
    if ( !f.Open( fileName.Get(), FileStream::WRITE_ONLY ) )
    {
        FLOG_ERROR( "SLN - Failed to open '%s' for write (error: %u)", fileName.Get(), Env::GetLastErr() );
        return false;
    }
    if ( f.Write( content.Get(), content.GetLength() ) != content.GetLength() )
    {
        FLOG_ERROR( "SLN - Error writing to '%s' (error: %u)", fileName.Get(), Env::GetLastErr() );
        return false;
    }
    f.Close();

    return true;
}
Exemple #20
0
// WriteProjectListings
//------------------------------------------------------------------------------
void SLNGenerator::WriteProjectListings(    const AString& solutionBasePath,
        const AString& solutionBuildProject,
        const Array< VCXProjectNode * > & projects,
        const Array< SLNSolutionFolder > & folders,
        const Array< SLNDependency > & slnDeps,
        AString & solutionBuildProjectGuid,
        Array< AString > & projectGuids,
        Array< AString > & solutionProjectsToFolder )
{
    // Project Listings

    VCXProjectNode ** const projectsEnd = projects.End();
    for( VCXProjectNode ** it = projects.Begin() ; it != projectsEnd ; ++it )
    {
        // check if this project is the master project
        const bool projectIsActive = ( solutionBuildProject.CompareI( (*it)->GetName() ) == 0 );

        AStackString<> projectPath( (*it)->GetName() );

        // get project base name only
        const char * lastSlash  = projectPath.FindLast( NATIVE_SLASH );
        const char * lastPeriod = projectPath.FindLast( '.' );
        AStackString<> projectName( lastSlash  ? lastSlash + 1  : projectPath.Get(),
                                    lastPeriod ? lastPeriod		: projectPath.GetEnd() );

        // retrieve projectGuid
        AStackString<> projectGuid;
        if ( (*it)->GetProjectGuid().GetLength() == 0 )
        {
            // For backward compatibility, keep the preceding slash and .vcxproj extension for GUID generation
            AStackString<> projectNameForGuid( lastSlash ? lastSlash : projectPath.Get() );
            VSProjectGenerator::FormatDeterministicProjectGUID( projectGuid, projectNameForGuid );
        }
        else
        {
            projectGuid = (*it)->GetProjectGuid();
        }

        // make project path relative
        projectPath.Replace( solutionBasePath.Get(), "" );

        // projectGuid must be uppercase (visual does that, it changes the .sln otherwise)
        projectGuid.ToUpper();

        if ( projectIsActive )
        {
            ASSERT( solutionBuildProjectGuid.GetLength() == 0 );
            solutionBuildProjectGuid = projectGuid;
        }

        Write( "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"%s\", \"%s\", \"%s\"\r\n",
               projectName.Get(), projectPath.Get(), projectGuid.Get() );

        // Manage dependencies
        Array< AString > dependencyGUIDs( 64, true );
        const AString & fullProjectPath = (*it)->GetName();
        for ( const SLNDependency & deps : slnDeps )
        {
            // is the set of deps relevant to this project?
            if ( deps.m_Projects.Find( fullProjectPath ) )
            {
                // get all the projects this project depends on
                for ( const AString & dependency : deps.m_Dependencies )
                {
                    // For backward compatibility, keep the preceding slash and .vcxproj extension for GUID generation
                    const char * projNameFromSlash = dependency.FindLast( NATIVE_SLASH );
                    AStackString<> projectNameForGuid( projNameFromSlash ? projNameFromSlash : dependency.Get() );

                    AStackString<> newGUID;
                    VSProjectGenerator::FormatDeterministicProjectGUID( newGUID, projectNameForGuid );
                    dependencyGUIDs.Append( newGUID );
                }
            }
        }
        if ( !dependencyGUIDs.IsEmpty() )
        {
            Write( "\tProjectSection(ProjectDependencies) = postProject\r\n" );
            for ( const AString & guid : dependencyGUIDs )
            {
                Write( "\t\t%s = %s\r\n", guid.Get(), guid.Get() );
            }
            Write( "\tEndProjectSection\r\n" );
        }

        Write( "EndProject\r\n" );

        projectGuids.Append( projectGuid );

        // check if this project is in a solution folder
        const SLNSolutionFolder * const foldersEnd = folders.End();
        for ( const SLNSolutionFolder * it2 = folders.Begin() ; it2 != foldersEnd ; ++it2 )
        {
            // this has to be done here to have the same order of declaration (like visual)
            if ( it2->m_ProjectNames.Find( (*it)->GetName() ) )
            {
                // generate a guid for the solution folder
                AStackString<> solutionFolderGuid;
                VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderGuid, it2->m_Path );

                solutionFolderGuid.ToUpper();

                AStackString<> projectToFolder;
                projectToFolder.Format( "\t\t%s = %s\r\n", projectGuid.Get(), solutionFolderGuid.Get() );

                solutionProjectsToFolder.Append( projectToFolder );
            }
        }
    }
}