diff --git a/Code/Core/Core.bff b/Code/Core/Core.bff index 02ea43cd4..8cdd6d43f 100644 --- a/Code/Core/Core.bff +++ b/Code/Core/Core.bff @@ -9,7 +9,7 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' diff --git a/Code/Core/CoreTest/CoreTest.bff b/Code/Core/CoreTest/CoreTest.bff index d81c0c824..c4dcdd86c 100644 --- a/Code/Core/CoreTest/CoreTest.bff +++ b/Code/Core/CoreTest/CoreTest.bff @@ -8,11 +8,11 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' - .LocalDebuggerCommand = '^$(SolutionDir)..\..\..\tmp\^$(Configuration)\Core\CoreTest\CoreTest.exe' + .LocalDebuggerCommand = '^$(SolutionDir)..\^$(Configuration)\Core\CoreTest\CoreTest.exe' } // Unity diff --git a/Code/Core/CoreTest/TestMain.cpp b/Code/Core/CoreTest/TestMain.cpp index 817e7db3b..9ea429a75 100644 --- a/Code/Core/CoreTest/TestMain.cpp +++ b/Code/Core/CoreTest/TestMain.cpp @@ -15,6 +15,7 @@ int main(int , char * []) // Tests to run REGISTER_TESTGROUP( TestAtomic ) REGISTER_TESTGROUP( TestAString ) + REGISTER_TESTGROUP( TestEnv ) REGISTER_TESTGROUP( TestFileIO ) REGISTER_TESTGROUP( TestHash ) REGISTER_TESTGROUP( TestMemPoolBlock ) diff --git a/Code/Core/CoreTest/Tests/TestEnv.cpp b/Code/Core/CoreTest/Tests/TestEnv.cpp new file mode 100644 index 000000000..b402e397d --- /dev/null +++ b/Code/Core/CoreTest/Tests/TestEnv.cpp @@ -0,0 +1,52 @@ +// TestEnv.cpp +//------------------------------------------------------------------------------ + +// Includes +//------------------------------------------------------------------------------ +#include "TestFramework/UnitTest.h" + +// Core +#include +#include + +// TestEnv +//------------------------------------------------------------------------------ +class TestEnv : public UnitTest +{ +private: + DECLARE_TESTS + + void GetCommandLine() const; + void GetExePath() const; +}; + +// Register Tests +//------------------------------------------------------------------------------ +REGISTER_TESTS_BEGIN( TestEnv ) + REGISTER_TEST( GetCommandLine ) + REGISTER_TEST( GetExePath ) +REGISTER_TESTS_END + +// GetCommandLine +//------------------------------------------------------------------------------ +void TestEnv::GetCommandLine() const +{ + AStackString<> cmdLine; + Env::GetCmdLine( cmdLine ); + TEST_ASSERT( cmdLine.FindI( "CoreTest" ) ); +} + +// GetExePath +//------------------------------------------------------------------------------ +void TestEnv::GetExePath() const +{ + AStackString<> cmdLine; + Env::GetExePath( cmdLine ); + #if defined( __WINDOWS__ ) + TEST_ASSERT( cmdLine.EndsWithI( "CoreTest.exe" ) ); + #else + TEST_ASSERT( cmdLine.EndsWithI( "CoreTest" ) ); + #endif +} + +//------------------------------------------------------------------------------ diff --git a/Code/Core/CoreTest/Tests/TestTCPConnectionPool.cpp b/Code/Core/CoreTest/Tests/TestTCPConnectionPool.cpp index 3fcbf24ff..0c3f3faae 100644 --- a/Code/Core/CoreTest/Tests/TestTCPConnectionPool.cpp +++ b/Code/Core/CoreTest/Tests/TestTCPConnectionPool.cpp @@ -160,16 +160,6 @@ void TestTestTCPConnectionPool::TestConnectionCount() const //------------------------------------------------------------------------------ void TestTestTCPConnectionPool::TestDataTransfer() const { - const uint16_t testPort( TEST_PORT ); - - // a big piece of data, initialized to some known pattern - #define maxDataSize size_t( 1024 * 1024 * 10 ) - AutoPtr< char > data( (char *)ALLOC( maxDataSize ) ); - for ( size_t i=0; i< maxDataSize; ++i ) - { - data.Get()[ i ] = (char)i; - } - // a special server which will assert that it receives some expected data class TestServer : public TCPConnectionPool { @@ -186,6 +176,17 @@ void TestTestTCPConnectionPool::TestDataTransfer() const bool m_ReceivedOK; size_t m_DataSize; }; + + const uint16_t testPort( TEST_PORT ); + + // a big piece of data, initialized to some known pattern + #define maxDataSize size_t( 1024 * 1024 * 10 ) + AutoPtr< char > data( (char *)ALLOC( maxDataSize ) ); + for ( size_t i=0; i< maxDataSize; ++i ) + { + data.Get()[ i ] = (char)i; + } + TestServer server; TEST_ASSERT( server.Listen( testPort ) ); diff --git a/Code/Core/Env/Env.cpp b/Code/Core/Env/Env.cpp index c23bf55ed..4a9f6a1d9 100644 --- a/Code/Core/Env/Env.cpp +++ b/Code/Core/Env/Env.cpp @@ -17,10 +17,23 @@ #if defined( __LINUX__ ) || defined( __APPLE__ ) #include + #include #include #include #endif +#if defined( __LINUX__ ) + #include +#endif + +#if defined( __APPLE__ ) + extern "C" + { + int *_NSGetArgc(void); + char ***_NSGetArgv(void); + }; +#endif + // GetNumProcessors //------------------------------------------------------------------------------ /*static*/ uint32_t Env::GetNumProcessors() @@ -96,18 +109,42 @@ // GetCmdLine //------------------------------------------------------------------------------ -/*static*/ const char * Env::GetCmdLine() +/*static*/ void Env::GetCmdLine( AString & cmdLine ) { #if defined( __WINDOWS__ ) - return ::GetCommandLine(); + cmdLine = ::GetCommandLine(); #elif defined( __APPLE__ ) - ASSERT( false ); // TODO:MAC Implement GetCmdLine - return nullptr; - #elif defined( __LINUX__ ) - ASSERT( false ); // TODO:LINUX Implement GetCmdLine - return nullptr; + int argc = *_NSGetArgc(); + const char ** argv = const_cast< const char ** >( *_NSGetArgv() ); + for ( int i=0; i 0 ) + { + cmdLine += ' '; + } + cmdLine += argv[i]; + } #else - #error Unknown platform + FILE* f = fopen( "/proc/self/cmdline", "rb" ); + VERIFY( f != 0 ); + char buffer[ 4096 ]; + for (;;) + { + int size = fread( buffer, 1, 4096, f ); + if ( size == 0 ) + { + break; + } + + // Append + for ( int i=0; i( *_NSGetArgv() ); + output = argv[0]; + #else + char path[ PATH_MAX ]; + VERIFY( readlink( "/proc/self/exe", path, PATH_MAX ) != -1 ); + output = path; #endif } diff --git a/Code/Core/Env/Env.h b/Code/Core/Env/Env.h index 66d06e557..ab33f17af 100644 --- a/Code/Core/Env/Env.h +++ b/Code/Core/Env/Env.h @@ -33,7 +33,7 @@ class Env static uint32_t GetNumProcessors(); static bool GetEnvVariable( const char * envVarName, AString & envVarValue ); - static const char * GetCmdLine(); + static void GetCmdLine( AString & cmdLine ); static void GetExePath( AString & path ); static uint32_t GetLastErr(); diff --git a/Code/Core/Math/Random.h b/Code/Core/Math/Random.h index b11a9d50a..db58006e8 100644 --- a/Code/Core/Math/Random.h +++ b/Code/Core/Math/Random.h @@ -19,7 +19,7 @@ class Random Random(); // seed with a specific value - inline Random( uint32_t seed ) : m_Seed( seed ) {} + explicit inline Random( uint32_t seed ) : m_Seed( seed ) {} // random number from 0 to RAND_MAX uint32_t GetRand(); diff --git a/Code/Core/Mem/MemPoolBlock.cpp b/Code/Core/Mem/MemPoolBlock.cpp index 698604070..41a204a9d 100644 --- a/Code/Core/Mem/MemPoolBlock.cpp +++ b/Code/Core/Mem/MemPoolBlock.cpp @@ -30,7 +30,9 @@ MemPoolBlock::MemPoolBlock( size_t blockSize, size_t blockAlignment ) MemPoolBlock::~MemPoolBlock() { // Ensure no memory leaks - ASSERT( m_NumAllocations == 0 ); + #ifdef DEBUG + ASSERT( m_NumAllocations == 0 ); + #endif // free pages void ** end = m_Pages.End(); @@ -54,7 +56,9 @@ void * MemPoolBlock::Alloc( size_t size ) ASSERT( m_FreeBlockChain ); } - m_NumAllocations++; + #ifdef DEBUG + m_NumAllocations++; + #endif // Take first block from free chain FreeBlock * block = m_FreeBlockChain; @@ -66,14 +70,18 @@ void * MemPoolBlock::Alloc( size_t size ) //------------------------------------------------------------------------------ void MemPoolBlock::Free( void * ptr ) { - ASSERT( m_NumAllocations > 0 ); + #ifdef DEBUG + ASSERT( m_NumAllocations > 0 ); + #endif // Insert free block into head of chain FreeBlock * freeBlock = reinterpret_cast< FreeBlock * >( ptr ); freeBlock->m_Next = m_FreeBlockChain; m_FreeBlockChain = freeBlock; - --m_NumAllocations; + #ifdef DEBUG + --m_NumAllocations; + #endif } // AlocPage diff --git a/Code/Core/Mem/MemPoolBlock.h b/Code/Core/Mem/MemPoolBlock.h index 42a601908..59dda21c4 100644 --- a/Code/Core/Mem/MemPoolBlock.h +++ b/Code/Core/Mem/MemPoolBlock.h @@ -31,7 +31,9 @@ class MemPoolBlock FreeBlock * m_FreeBlockChain; // total number of active allocations - uint32_t m_NumAllocations; + #ifdef DEBUG + uint32_t m_NumAllocations; + #endif // internal control params size_t m_BlockSize; diff --git a/Code/Core/Mem/MemTracker.cpp b/Code/Core/Mem/MemTracker.cpp index 777a97039..4161e9531 100644 --- a/Code/Core/Mem/MemTracker.cpp +++ b/Code/Core/Mem/MemTracker.cpp @@ -23,7 +23,7 @@ //------------------------------------------------------------------------------ /*static*/ uint32_t MemTracker::s_Id( 0 ); /*static*/ bool MemTracker::s_Enabled( true ); - /*static*/ bool MemTracker::s_Initialized( false ); + /*static*/ volatile bool MemTracker::s_Initialized( false ); /*static*/ uint32_t MemTracker::s_AllocationCount( 0 ); /*static*/ uint64_t MemTracker::s_Mutex[]; /*static*/ MemTracker::Allocation ** MemTracker::s_AllocationHashTable = nullptr; diff --git a/Code/Core/Mem/MemTracker.h b/Code/Core/Mem/MemTracker.h index 534bfd679..d755dcfd3 100644 --- a/Code/Core/Mem/MemTracker.h +++ b/Code/Core/Mem/MemTracker.h @@ -7,7 +7,7 @@ // Enabled in DEBUG only //------------------------------------------------------------------------------ #ifdef DEBUG - #if !defined( __APPLE__ ) // TODO:MAC Fix MemTracker + #if !defined( __APPLE__ ) && !defined( __LINUX__ )// TODO:MAC TODO:LINUX Fix MemTracker #define MEMTRACKER_ENABLED #endif #endif @@ -63,7 +63,7 @@ static uint32_t s_Id; static bool s_Enabled; - static bool s_Initialized; + static volatile bool s_Initialized; static uint32_t s_AllocationCount; static Allocation * s_LastAllocation; static uint64_t s_Mutex[ sizeof( Mutex ) / sizeof( uint64_t ) ]; diff --git a/Code/Core/Reflection/MetaData/MetaData.cpp b/Code/Core/Reflection/MetaData/MetaData.cpp index 742937bf5..c03972fce 100644 --- a/Code/Core/Reflection/MetaData/MetaData.cpp +++ b/Code/Core/Reflection/MetaData/MetaData.cpp @@ -52,10 +52,11 @@ IMetaData & MetaNone() { // We have to return by reference to be able to implement the chainign + operator // but everything is managed as a ptr internally so this is ok - PRAGMA_DISABLE_PUSH_MSVC( 6011 ); // null deref is deliberate IMetaData * md = nullptr; - return *md; + PRAGMA_DISABLE_PUSH_MSVC( 6011 ); // null deref is deliberate + IMetaData& mdRef = *md; PRAGMA_DISABLE_POP_MSVC + return mdRef; } // Basic MetaData Types diff --git a/Code/Core/Reflection/RefObject.cpp b/Code/Core/Reflection/RefObject.cpp index 28a3e7090..f3425f989 100644 --- a/Code/Core/Reflection/RefObject.cpp +++ b/Code/Core/Reflection/RefObject.cpp @@ -35,7 +35,7 @@ void RefObject_ReflectionInfo_Bind() } src = src->GetSuperClass(); } - return nullptr; + return false; } //------------------------------------------------------------------------------ diff --git a/Code/Core/Reflection/ReflectedProperty.h b/Code/Core/Reflection/ReflectedProperty.h index 2d9bbb178..7e3be1ccf 100644 --- a/Code/Core/Reflection/ReflectedProperty.h +++ b/Code/Core/Reflection/ReflectedProperty.h @@ -117,7 +117,7 @@ class ReflectedProperty uint32_t m_Offset:16; // validated by MAX_OFFSET uint32_t m_Type:8; uint32_t m_IsArray:1; - uint32_t m_Unused:7; + //uint32_t m_Unused:7; #if defined( REFLECTION_KEEP_STRING_NAMES ) const char * m_Name; diff --git a/Code/Core/Reflection/ReflectionInfo.cpp b/Code/Core/Reflection/ReflectionInfo.cpp index 37bb0f208..ef1557e45 100644 --- a/Code/Core/Reflection/ReflectionInfo.cpp +++ b/Code/Core/Reflection/ReflectionInfo.cpp @@ -32,7 +32,8 @@ // CONSTRUCTOR //------------------------------------------------------------------------------ ReflectionInfo::ReflectionInfo() - : m_Properties( 0, true ) + : m_TypeNameCRC( 0 ) + , m_Properties( 0, true ) , m_SuperClass( nullptr ) , m_Next( nullptr ) , m_TypeName( nullptr ) diff --git a/Code/Core/Reflection/Serialization/TextReader.h b/Code/Core/Reflection/Serialization/TextReader.h index 304824a3f..ea44169ed 100644 --- a/Code/Core/Reflection/Serialization/TextReader.h +++ b/Code/Core/Reflection/Serialization/TextReader.h @@ -38,7 +38,6 @@ class TextReader bool ReadChildren(); bool ReadProperty( PropertyType propertyType ); - inline bool IsAtEnd() const { return ( m_Pos == m_End ); } void SkipWhitespace( bool stopAtLineEnd = false ); bool GetToken( AString & token ); bool GetString( AString & string ); diff --git a/Code/Core/Strings/AString.cpp b/Code/Core/Strings/AString.cpp index de905e79e..693049dbe 100644 --- a/Code/Core/Strings/AString.cpp +++ b/Code/Core/Strings/AString.cpp @@ -513,6 +513,24 @@ void AString::ToLower() } } +// ToUpper +//------------------------------------------------------------------------------ +void AString::ToUpper() +{ + char * pos = m_Contents; + char * end = m_Contents + m_Length; + while ( pos < end ) + { + char c = *pos; + if ( ( c >= 'a' ) && ( c <= 'z' ) ) + { + c = 'A' + ( c - 'a' ); + *pos = c; + } + pos++; + } +} + // Replace ( char *, char * ) //------------------------------------------------------------------------------ uint32_t AString::Replace( const char * from, const char * to, uint32_t maxReplaces ) diff --git a/Code/Core/Strings/AString.h b/Code/Core/Strings/AString.h index 986c61893..8d3719593 100644 --- a/Code/Core/Strings/AString.h +++ b/Code/Core/Strings/AString.h @@ -86,6 +86,7 @@ class AString uint32_t Replace( char from, char to, uint32_t maxReplaces = 0 ); uint32_t Replace( const char * from, const char * to, uint32_t maxReplaces = 0 ); void ToLower(); + void ToUpper(); // searching const char * Find( char c, const char * startPos = nullptr ) const; @@ -119,8 +120,8 @@ class AString static int32_t StrNCmpI( const char * a, const char * b, size_t num ); protected: - enum { MEM_MUST_BE_FREED_FLAG = 0x00000001 }; - enum { RESERVED_MASK = 0xFFFFFFFE }; + enum : uint32_t { MEM_MUST_BE_FREED_FLAG = 0x00000001 }; + enum : uint32_t { RESERVED_MASK = 0xFFFFFFFE }; inline void SetReserved( uint32_t reserved, bool mustFreeMemory ) { diff --git a/Code/TestFramework/TestFramework.bff b/Code/TestFramework/TestFramework.bff index 11b2b0c63..11bc22f03 100644 --- a/Code/TestFramework/TestFramework.bff +++ b/Code/TestFramework/TestFramework.bff @@ -8,7 +8,7 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' } diff --git a/Code/TestFramework/UnitTestManager.cpp b/Code/TestFramework/UnitTestManager.cpp index 1c9e1f75e..049eb95e1 100644 --- a/Code/TestFramework/UnitTestManager.cpp +++ b/Code/TestFramework/UnitTestManager.cpp @@ -182,6 +182,10 @@ void UnitTestManager::TestBegin( UnitTest * testGroup, const char * testName ) MemTracker::Reset(); #endif m_Timer.Start(); + + #ifdef PROFILING_ENABLED + ProfileManager::Start( testName ); + #endif } // TestEnd @@ -189,6 +193,8 @@ void UnitTestManager::TestBegin( UnitTest * testGroup, const char * testName ) void UnitTestManager::TestEnd() { #ifdef PROFILING_ENABLED + ProfileManager::Stop(); + // flush the profiling buffers, but don't profile the sync itself // (because it leaves an outstanding memory alloc, it looks like a leak) ProfileManager::SynchronizeNoTag(); diff --git a/Code/Tools/FBuild/Documentation/docs/changelog.html b/Code/Tools/FBuild/Documentation/docs/changelog.html index 80ff6e653..232b0e6de 100644 --- a/Code/Tools/FBuild/Documentation/docs/changelog.html +++ b/Code/Tools/FBuild/Documentation/docs/changelog.html @@ -14,6 +14,52 @@

Changelog

+ +
+ v0.84 (28-Jul-2015) +
+
+ Note +
    +
  • DB version has changed : clean build will occur
  • +
  • Behaviour of operator + on structs has changed
  • +
+ New +
    +
  • #import directive allows importation of environment variables (Thanks to Poppolopoppo)
  • +
  • Workers will self-deactivate when low on disk space (Thanks to Jean-Sebastien Pelletier)
  • +
  • Visual Studio Solution generation (thanks to Poppolopoppo)
  • +
  • Add "-nostoponerror" option: Try to build as much as possible instead of stopping on the first error
  • +
+ Fixes +
    +
  • Includes are correctly tracked when using /showIncludes with non-English MSVC compiler (Thanks to Poppolopoppo)
  • +
  • .StructA + .StructB results in members being recursively concatenated instead of replaced (Thanks to Poppolopoppo)
  • +
  • Recognize -c as well as /c when checking for required compiler options with MSVC
  • +
  • [OSX/Linux] Fix crash using -report
  • +
  • Automatically strip /Gm compiler option with MSVC compiler (incompatible with FASTBuild)
  • +
  • Compiled object extension defaults to .o on Linux/OS X
  • +
  • Fix formatting of ToolManifest errors so they appear in VS Error Window (Thanks to Josh Green)
  • +
  • Warn about invalid values passed to -j[x] option (local thread count) (values <0 or >64 are invalid)
  • +
  • Prevent UnityNode excluded file list containing duplicates if using Precompiled Headers
  • +
  • Ensure path exists /IMPLIB option if different from normal output to prevent linker option (Thanks to Josh Green)
  • +
  • Fix crash when using /Fi or /P command line option with MSVC compiler (Thanks to Poppolopoppo)
  • +
+ Improvements +
    +
  • Print() can now output any variable, array or structure with new Print( .Var ) syntax
  • +
  • Optimizations
  • +
      +
    • Reduce latency starting a new jobs on a remote worker
    • +
    • Reduce latency returning completed remote jobs
    • +
    • Workers request 1 more job than available CPUs to overlap network transfer and compilation
    • +
    • Reduce remote worker thread context switches
    • +
    +
  • #include now supports variable substitutions (e.g. #include "Compilers/$VSVersion$.h") (Thanks to Poppolopoppo)
  • +
  • Excluded files support relative paths (starting with ../ or ..\) (Unity, ObjectList, Library and CSAssembly)
  • +
+
+
v0.83 (18-Jun-2015)
diff --git a/Code/Tools/FBuild/Documentation/docs/common.js b/Code/Tools/FBuild/Documentation/docs/common.js index 8c5198fec..986423d9f 100644 --- a/Code/Tools/FBuild/Documentation/docs/common.js +++ b/Code/Tools/FBuild/Documentation/docs/common.js @@ -15,6 +15,7 @@ function generateHeaderParent() function generateHeaderinternal( path ) { write("
"); + write("
"); write("
"); write("
"); write(" "); @@ -45,6 +46,7 @@ function generateFooter() write("
"); write(""); write("
"); + write("
"); } function caddr() diff --git a/Code/Tools/FBuild/Documentation/docs/download.html b/Code/Tools/FBuild/Documentation/docs/download.html index d9a236d24..3c42a0547 100644 --- a/Code/Tools/FBuild/Documentation/docs/download.html +++ b/Code/Tools/FBuild/Documentation/docs/download.html @@ -17,26 +17,26 @@

Download

- Current Version - v0.83 + Current Version - v0.84

See the changelog for a detailed list of changes.

Binaries Source Syntax Highlighting
@@ -48,6 +48,16 @@

Download

+ + + + + + + + diff --git a/Code/Tools/FBuild/Documentation/docs/features.html b/Code/Tools/FBuild/Documentation/docs/features.html index 18fbbb19e..7d1ed2d76 100644 --- a/Code/Tools/FBuild/Documentation/docs/features.html +++ b/Code/Tools/FBuild/Documentation/docs/features.html @@ -73,7 +73,7 @@

Features

  • Unit Test support
  • Build statistics
  • Progress bar
  • -
  • Visual Studio project generation
  • +
  • Visual Studio Project and Solution generation
  • @@ -130,7 +130,7 @@

    Lightweight

    No external dependencies
    -FASTBuild requires no external DLLs, libraries, 3rd party software or executables (other than those belonging to your compiler(s)). It runs on a clean install of Windows (Vista, 7, 8.1 or server class equivalents), OS X (10.7+) or Linux. +FASTBuild requires no external DLLs, libraries, 3rd party software or executables (other than those belonging to your compiler(s)). It runs on a clean install of Windows (Vista, 7, 8.1, 10 or server class equivalents), OS X (10.7+) or Linux.
    No installation required
    @@ -143,7 +143,7 @@

    Platforms

    Build multiple platforms at once
    -FASTBuild can target mutiple (and any combination of) defined targets (or even all of them) simultaneously. This is ideal for building mutiple platforms when verifying compilation before check-in, or as part of an automated build system. This is especially useful if some of your targets have large linking bottlenecks, such as those introduced by Link-Time Code Generation. +FASTBuild can target multiple (and any combination of) defined targets (or even all of them) simultaneously. This is ideal for building mutiple platforms when verifying compilation before check-in, or as part of an automated build system. This is especially useful if some of your targets have large linking bottlenecks, such as those introduced by Link-Time Code Generation.
    Build multiple targets at once
    @@ -194,7 +194,7 @@

    More...

    FASTBuild tracks build times across build and can use this information to estimate progress through the current build. -
    Visual Studio project generation
    +
    Visual Studio Project and Solution generation
    FASTBuild can generate vcxproj and vcxproj.filters to integrate FASTBuild compilation into the Visual Studio IDE.
    diff --git a/Code/Tools/FBuild/Documentation/docs/functions.html b/Code/Tools/FBuild/Documentation/docs/functions.html index f4eb97a9a..c002125b1 100644 --- a/Code/Tools/FBuild/Documentation/docs/functions.html +++ b/Code/Tools/FBuild/Documentation/docs/functions.html @@ -33,7 +33,8 @@

    Summary

    - + +
    VersionWindowsOS XLinuxSourceMarkup
    v0.83x64 | + x86x64 (Alpha)x64 (Alpha)Zip | + GitHubNotePad++Visual Studio
    v0.82 x64 | x86
    TestBuilds and runs a Unit Test.
    UnityGenerates a Unity/Blob file to speed up compilation.
    UsingPromotes structure members to local scope.
    VCXProjectGenerates a Visual Studio project to launch FASTBuild.
    VCXProjectGenerates a Visual Studio Project for integration with FASTBuild.
    VSSolutionGenerates a Visual Studio Solution for integration with FASTBuild.
    diff --git a/Code/Tools/FBuild/Documentation/docs/functions/csassembly.html b/Code/Tools/FBuild/Documentation/docs/functions/csassembly.html index 0310cf7ee..c4fb17b53 100644 --- a/Code/Tools/FBuild/Documentation/docs/functions/csassembly.html +++ b/Code/Tools/FBuild/Documentation/docs/functions/csassembly.html @@ -26,7 +26,7 @@

    CSAssembly

    .CompilerInputPathRecurse ; (optional) Whether to recurse into sub-dirs (default true) .CompilerInputPattern ; (optional) Pattern of input files (default *.cs) .CompilerInputExcludePath ; (optional) Path(s) to exclude from compilation - .CompilerInputExcludedFiles ; (optional) File(s) to exclude from compilation + .CompilerInputExcludedFiles ; (optional) File(s) to exclude from compilation (partial, root-relative of full path) .CompilerReferences ; (optional) References for the assembly }

    diff --git a/Code/Tools/FBuild/Documentation/docs/functions/library.html b/Code/Tools/FBuild/Documentation/docs/functions/library.html index e83ad03ca..b1dcd9cca 100644 --- a/Code/Tools/FBuild/Documentation/docs/functions/library.html +++ b/Code/Tools/FBuild/Documentation/docs/functions/library.html @@ -24,7 +24,7 @@

    Library

    .Compiler ; Compiler to use .CompilerOptions ; Options for compiler .CompilerOutputPath ; Path to store intermediate objects - .CompilerOutputExtension ; (optional) Specify the file extension for generated objects (default .obj) + .CompilerOutputExtension ; (optional) Specify the file extension for generated objects (default .obj or .o) .CompilerOutputPrefix ; (optional) Specify a prefix for generated objects (default none) ; Options for librarian @@ -38,7 +38,7 @@

    Library

    .CompilerInputPattern ; (optional) Pattern to use when finding files (default *.cpp) .CompilerInputPathRecurse ; (optional) Recurse into dirs when finding files (default true) .CompilerInputExcludePath ; (optional) Path(s) to exclude from compilation - .CompilerInputExcludedFiles;(optional) File(s) to exclude from compilation + .CompilerInputExcludedFiles;(optional) File(s) to exclude from compilation (partial, root-relative of full path) .CompilerInputFiles ; (optional) Explicit array of files to build .CompilerInputUnity ; (optional) Unity to build (or Unities) diff --git a/Code/Tools/FBuild/Documentation/docs/functions/objectlist.html b/Code/Tools/FBuild/Documentation/docs/functions/objectlist.html index eadfccc88..d30075fea 100644 --- a/Code/Tools/FBuild/Documentation/docs/functions/objectlist.html +++ b/Code/Tools/FBuild/Documentation/docs/functions/objectlist.html @@ -24,7 +24,7 @@

    ObjectList

    .Compiler ; Compiler to use .CompilerOptions ; Options for compiler .CompilerOutputPath ; Path to store intermediate objects - .CompilerOutputExtension ; (optional) Specify the file extension for generated objects (default .obj) + .CompilerOutputExtension ; (optional) Specify the file extension for generated objects (default .obj or .o) .CompilerOutputPrefix ; (optional) Specify a prefix for generated objects (default none) ; Specify inputs for compilation @@ -32,7 +32,7 @@

    ObjectList

    .CompilerInputPattern ; (optional) Pattern to use when finding files (default *.cpp) .CompilerInputPathRecurse ; (optional) Recurse into dirs when finding files (default true) .CompilerInputExcludePath ; (optional) Path(s) to exclude from compilation - .CompilerInputExcludedFiles;(optional) File(s) to exclude from compilation + .CompilerInputExcludedFiles;(optional) File(s) to exclude from compilation (partial, root-relative of full path) .CompilerInputFiles ; (optional) Explicit array of files to build .CompilerInputUnity ; (optional) Unity to build (or Unities) diff --git a/Code/Tools/FBuild/Documentation/docs/functions/unity.html b/Code/Tools/FBuild/Documentation/docs/functions/unity.html index bdf97343b..c548edb22 100644 --- a/Code/Tools/FBuild/Documentation/docs/functions/unity.html +++ b/Code/Tools/FBuild/Documentation/docs/functions/unity.html @@ -27,7 +27,7 @@

    Unity

    .UnityInputPattern ; (optional) Pattern of files to find (default *.cpp) .UnityInputPathRecurse ; (optional) Recurse when searching for files (default true) .UnityInputFiles ; (optional) Explicit list of files to include - .UnityInputExcludedFiles ; (optional) Explicit list of excluded files + .UnityInputExcludedFiles ; (optional) Explicit list of excluded files (partial, root-relative of full path) .UnityInputObjectLists ; (optional) ObjectList(s) to use as input .UnityInputIsolateWritableFiles ; (optional) Build writable files individually (default false) .UnityInputIsolateWritableFilesLimit ; (optional) Disable isolation when many files are writable (default 0) diff --git a/Code/Tools/FBuild/Documentation/docs/functions/vssolution.html b/Code/Tools/FBuild/Documentation/docs/functions/vssolution.html new file mode 100644 index 000000000..2d21a017e --- /dev/null +++ b/Code/Tools/FBuild/Documentation/docs/functions/vssolution.html @@ -0,0 +1,165 @@ + + + + + + + +FASTBuild - Function Reference - VSSolution + + + + +

    VSSolution

    + +
    +

    NOTE: Solution generation syntax may be refined slightly in future versions as the capabilities are extended.

    +
    + +
    + Summary +
    +
    +

    +Generates a Solution file for use with Visual Studio, allowing integration of FASTBuild into Visual Studio. +

    +
    VSSolution( 'alias' ) // (optional) Alias +{ + // Basic options + .SolutionOutput // Path to Solution file to be generated + .SolutionProjects // Project(s) to include in Solution + .SolutionConfigs // (optional) Solution configurations (see below) + .SolutionBuildProject // (optional) Project set to build when "Build Solution" is selected + + // Folders + .SolutionFolders // (optional) Folders to organize projects (see below) + + // Version Info + .SolutionVisualStudioVersion // (optional) Version of Solution (default "14.0.22823.1" VS2015 RC) + .SolutionMinimumVisualStudioVersion // (optional) Min version of Solution (default "10.0.40219.1" VS2010 Express) +} + +// SolutionConfigs - structs in the following format +//--------------------------------------------------- +[ + .Platform // Platform(s) (default "Win32", "x64") + .Config // Config(s) (default "Debug", "Release") +] + +// SolutionFolders - structs in the following format +//--------------------------------------------------- +[ + .Path // Folder path in Solution + .Projects // Project(s) to store in this folder +] +
    +
    + +
    + Details +
    +
    +

    + VSSolution generates a Visual Studio solution, referencing projects generated with VCXProject. It supports + organizing the Solution with in folders. Solutions are compatible with VS 2010 and later. +

    +
    + +
    + Basic Options +
    +
    + +

    .SolutionOutput - String - (Required)

    +

    The output location of the .sln file.

    + Example: +
    .SolutionOutput = 'tmp/VisualStudio/MySolution.sln'
    +
    + +

    .SolutionProjects - String or ArrayOfStrings - (Required)

    +

    The previously defined VCXProject item(s) to include in the solution.

    + Example: +
    .SolutionProjects = +{ + 'LibraryA-proj' // Previously defined with VCXProject + 'LibraryB-proj' // Previously defined with VCXProject + 'Exe-proj' // Previously defined with VCXProject +}
    +
    + +

    .SolutionConfigs - Array of SolutionConfig Structure(s) - (Optional)

    +

    The platform/configuration pairs you wish to appear in Visual Studio can be controlled here. They need + to match those specified in your generated projects.

    + Example: +
    .Solution_Config_Debug = +[ + .Platform = 'Win32' + .Config = 'Debug' +] +.Solution_Config_Release = +[ + .Platform = 'Win32' + .Config = 'Release' +] +.SolutionConfigs = { .Solution_Config_Debug, .Solution_Config_Release }
    +

    If not specified, a default matrix of Win32|Debug, Win32|Release, x64|Debug and x64|Release configurations is used.

    +
    + +

    .SolutionBuildProject - String - (Optional)

    +

    The root project which will be compiled when a Solution Build is performed.

    +
    .SolutionBuildProject = 'Exe-proj'
    +

    If not specified, no project will be active in a Solution Build.

    + +

    +
    + + +
    + Folders +
    +
    + +

    .SolutionFolders - Array of SolutionFolder Structure(s) - (Optional)

    +

    Projects within a Solution can be organized into folders.

    + Example: +
    .FolderA = +[ + .Path = 'Libraries' + .Projects = { 'LibraryA-proj', 'LibraryB-proj' } +] +.FolderB = +[ + .Path = 'Executables' + .Projects = { 'Exe-proj' } +] +.SolutionFolders = { .FolderA, .FolderB }
    +

    If not specified, all Project files will appear as a flat list in the Solution.

    + +

    +
    + + +
    + Version Info +
    +
    + +

    .SolutionVisualStudioVersion - String - (Optional)

    +

    Specify the VisualStudio version that you would like to appear as the generator of this Solution file.

    + Example: +
    .SolutionVisualStudioVersion = "14.0.22823.1"
    +

    If not specified, "14.0.22823.1" will be used (VS2015 RC).

    +
    + +

    .SolutionMinimumVisualStudioVersion - String - (Optional)

    +

    Specify the minimum VisualStudio version necessary toopen this Solution file.

    + Example: +

    If not specified, "10.0.40219.1" will be used (VS2010 Express).

    +
    .SolutionMinimumVisualStudioVersion = "10.0.40219.1"
    + +

    +
    + + + + \ No newline at end of file diff --git a/Code/Tools/FBuild/Documentation/docs/home.html b/Code/Tools/FBuild/Documentation/docs/home.html index a60023307..1f1c49df2 100644 --- a/Code/Tools/FBuild/Documentation/docs/home.html +++ b/Code/Tools/FBuild/Documentation/docs/home.html @@ -25,7 +25,7 @@ Share compilation results between users with a network object cache. Use all your CPUs by scaling compilation across the network. No-op builds are near instantaneous.  Great for continuous integration. - Accelarate compilation using automatically generated Unity/Blob files. + Accelerate compilation using automatically generated Unity/Blob files. Analyze your build to find further speedups. @@ -33,13 +33,15 @@
    - FASTBuild v0.83 released!  (18-June-2015) + FASTBuild v0.84 released!  (28-July-2015)
    An updated version of FASTBuild can now be downloaded, featuring:
      -
    • Bug fixes and enhancements
    • -
    • Several optimizations
    • +
    • Several language syntax enhancements
    • +
    • Visual Studio Solution generation
    • +
    • Distributed compilation optimizations
    • +
    • Bug fixes
    • More...
    diff --git a/Code/Tools/FBuild/Documentation/docs/options.html b/Code/Tools/FBuild/Documentation/docs/options.html index 5cfca6148..4cecccbec 100644 --- a/Code/Tools/FBuild/Documentation/docs/options.html +++ b/Code/Tools/FBuild/Documentation/docs/options.html @@ -51,13 +51,17 @@

    FBuild.exe

    Enable multiple options for IDE integration. - -jX - Explicitly set worker thread count. + -j[x] + Explicitly set local worker thread count. -noprogress Don't show the progress bar while building. + + -nostoponerror + Don't stop building on first error. + -report Output a report at build termination. @@ -166,18 +170,35 @@

    FBuild.exe Detailed

    -
    -jX
    +
    -j[x]
    -

    Overrides the number of tasks which can be built in parallel, which by default is controlled by the standard NUMBER_OF_PROCESSORS windows environment variable.

    -

    A value of 0 for X indicates that no additional threads should be spawned, and build graph processing and compilation should occur on the same thread. This can be useful for buid process debugging, especially when combined with the '-verbose' option.

    -

    Positive values can be used to exactly set the number of tasks which can be performed in parallel. This can be used to limit CPU usage on a machine that needs to perform other work while compilation is in progress. Values greater than the number of physical processors are accepted, but will almost certainly result in degraded performance.

    -

    Generally, this option should be omitted for optimal compilation performance, but it's possible that on some systems, tweaking this might yield improved performance.

    +

    Generally, the default behaviour will give best performance, and this option should +only be used in very specific situations.

    +

    The -j[x] option allows you to artificially control local parallelism by modifying the local thread pool size.

    +

    FASTBuild will normally determine the optimal number of local threads to use by detecting the number of hardware +cores present on the host. The -j option allows you to override this.

    +

    Positive values for x can be used to set the number of tasks which can be performed locally in parallel. This can be used +to limit CPU usage on a machine that needs to perform other work while compilation is in progress. Values greater than the +number of physical processors are also accepted, but will almost always result in degraded performance.

    +

    A value of 0 for x indicates that no additional threads should be spawned, and build graph processing and compilation +should occur on the same thread. This can be useful for build process debugging, especially when combined with the +'-verbose' option.

    +

    This option has no direct bearing on distributed compilation, but modifying local parallelism will reduce the ability +of FASTBuild to distribute work efficiently.

    -noprogress

    Suppresses the progress bar that is normally shown while compiling.

    This should be used when targetting compilation from within Visual Studio or another IDE. (or use -vs)

    +
    + +
    -nostoponerror
    +
    +

    When encountering build errors, FASTBuild will normally stop as quickly as possible.

    +

    -nostoponerror instructs FASTBuild to instead build as much as possible before stopping when failures occur. This is +useful if you want to see as many errors as possible in your compilation output.

    +

    NOTE: When specifying multiple targets to compile on the command line, -nostoponerror is implied.

    -report
    diff --git a/Code/Tools/FBuild/Documentation/docs/status.html b/Code/Tools/FBuild/Documentation/docs/status.html index 854f6a60c..d51f52dba 100644 --- a/Code/Tools/FBuild/Documentation/docs/status.html +++ b/Code/Tools/FBuild/Documentation/docs/status.html @@ -11,7 +11,7 @@ -

    Feature Status @ v0.83

    +

    Feature Status @ v0.84

    Overview
    diff --git a/Code/Tools/FBuild/Documentation/docs/style.css b/Code/Tools/FBuild/Documentation/docs/style.css index 8fed587a0..09d3b673c 100644 --- a/Code/Tools/FBuild/Documentation/docs/style.css +++ b/Code/Tools/FBuild/Documentation/docs/style.css @@ -177,6 +177,13 @@ h3 color: #7d8206; } +hr +{ + border: 0; + height: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(255, 255, 255, 0.3); +} .boxdiv { diff --git a/Code/Tools/FBuild/Documentation/docs/syntaxguide.html b/Code/Tools/FBuild/Documentation/docs/syntaxguide.html index 945754bda..a9ebea3db 100644 --- a/Code/Tools/FBuild/Documentation/docs/syntaxguide.html +++ b/Code/Tools/FBuild/Documentation/docs/syntaxguide.html @@ -30,6 +30,7 @@

    Syntax Guide

    Directives @@ -120,6 +121,21 @@

    Directives

    +
    + #import +
    +
    +

    +The #import directive allows a bff configuration file to import an Environment Variable. A variable with the same name as the Environment +Variable will be created at the scope of the #import, as if it was locally declared by a normal variable assignment. +

    +Examples: +
    #import VS120COMNTOOLS +.VisualStudio2012Compiler = '$VS120COMNTOOLS$\..\..\VC\bin\cl.exe' +
    +
    + +
    #include
    diff --git a/Code/Tools/FBuild/FASTBuild.sln b/Code/Tools/FBuild/FASTBuild.sln deleted file mode 100644 index 8af4f5ce5..000000000 --- a/Code/Tools/FBuild/FASTBuild.sln +++ /dev/null @@ -1,127 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30324.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "..\..\Core\Core.vcxproj", "{AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreTest", "..\..\Core\CoreTest\CoreTest.vcxproj", "{E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestFramework", "..\..\TestFramework\TestFramework.vcxproj", "{0768DEFB-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FBuildApp", "FBuildApp\FBuildApp.vcxproj", "{52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FBuildCore", "FBuildCore\FBuildCore.vcxproj", "{97469300-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FBuildTest", "FBuildTest\FBuildTest.vcxproj", "{5910F2FB-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FBuildWorker", "FBuildWorker\FBuildWorker.vcxproj", "{4619FCB7-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "All", "..\..\All.vcxproj", "{73202180-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1. Test", "1. Test", "{1BDE8FA2-F826-4E8F-8130-DA6733325F97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2. Libs", "2. Libs", "{36229640-23E2-42B7-A7CD-AEC8AC2AE307}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3. Apps", "3. Apps", "{767B2E35-85FD-42CE-9B8C-1D219A0EC606}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0. External", "0. External", "{C8586BEA-5A87-4AD4-9D9F-B46719D6EF97}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LZ4", "..\..\..\External\LZ4\lz4-r127\LZ4.vcxproj", "{14D37D96-6C94-4F93-BC2A-7F5284B7D434}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - x64-Debug|Mixed Platforms = x64-Debug|Mixed Platforms - x64-Profile|Mixed Platforms = x64-Profile|Mixed Platforms - x64-Release|Mixed Platforms = x64-Release|Mixed Platforms - x86Clang-Debug|Mixed Platforms = x86Clang-Debug|Mixed Platforms - x86-Debug|Mixed Platforms = x86-Debug|Mixed Platforms - x86-Profile|Mixed Platforms = x86-Profile|Mixed Platforms - x86-Release|Mixed Platforms = x86-Release|Mixed Platforms - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {97469300-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.Build.0 = x64-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.Build.0 = x64-Profile|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.Build.0 = x64-Release|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.Build.0 = x86Clang-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.Build.0 = x86-Debug|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.Build.0 = x86-Profile|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - {73202180-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.Build.0 = x86-Release|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x64-Debug|Mixed Platforms.ActiveCfg = x64-Debug|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x64-Profile|Mixed Platforms.ActiveCfg = x64-Profile|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x64-Release|Mixed Platforms.ActiveCfg = x64-Release|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x86Clang-Debug|Mixed Platforms.ActiveCfg = x86Clang-Debug|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x86-Debug|Mixed Platforms.ActiveCfg = x86-Debug|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x86-Profile|Mixed Platforms.ActiveCfg = x86-Profile|Win32 - {14D37D96-6C94-4F93-BC2A-7F5284B7D434}.x86-Release|Mixed Platforms.ActiveCfg = x86-Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {AB13FEBB-6C94-4F93-BC2A-7F5284B7D434} = {36229640-23E2-42B7-A7CD-AEC8AC2AE307} - {E63CE8FE-6C94-4F93-BC2A-7F5284B7D434} = {1BDE8FA2-F826-4E8F-8130-DA6733325F97} - {0768DEFB-6C94-4F93-BC2A-7F5284B7D434} = {1BDE8FA2-F826-4E8F-8130-DA6733325F97} - {52CF9BA7-6C94-4F93-BC2A-7F5284B7D434} = {767B2E35-85FD-42CE-9B8C-1D219A0EC606} - {97469300-6C94-4F93-BC2A-7F5284B7D434} = {36229640-23E2-42B7-A7CD-AEC8AC2AE307} - {5910F2FB-6C94-4F93-BC2A-7F5284B7D434} = {1BDE8FA2-F826-4E8F-8130-DA6733325F97} - {4619FCB7-6C94-4F93-BC2A-7F5284B7D434} = {767B2E35-85FD-42CE-9B8C-1D219A0EC606} - {14D37D96-6C94-4F93-BC2A-7F5284B7D434} = {C8586BEA-5A87-4AD4-9D9F-B46719D6EF97} - EndGlobalSection -EndGlobal diff --git a/Code/Tools/FBuild/FBuildApp/FBuildApp.bff b/Code/Tools/FBuild/FBuildApp/FBuildApp.bff index 48a09f782..631f599c7 100644 --- a/Code/Tools/FBuild/FBuildApp/FBuildApp.bff +++ b/Code/Tools/FBuild/FBuildApp/FBuildApp.bff @@ -8,12 +8,12 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' - .LocalDebuggerCommand = '^$(SolutionDir)..\..\..\tmp\^$(Configuration)\Tools\FBuild\FBuildApp\FBuild.exe' - .LocalDebuggerWorkingDirectory = '^$(SolutionDir)..\..\..\Code' + .LocalDebuggerCommand = '^$(SolutionDir)..\^$(Configuration)\Tools\FBuild\FBuildApp\FBuild.exe' + .LocalDebuggerWorkingDirectory = '^$(SolutionDir)..\..\Code' } // Unity diff --git a/Code/Tools/FBuild/FBuildApp/Main.cpp b/Code/Tools/FBuild/FBuildApp/Main.cpp index 01dbefcc3..ba577c4c3 100644 --- a/Code/Tools/FBuild/FBuildApp/Main.cpp +++ b/Code/Tools/FBuild/FBuildApp/Main.cpp @@ -106,6 +106,7 @@ int Main(int argc, char * argv[]) bool report = false; bool fixupErrorPaths = false; bool waitMode = false; + bool noStopOnError = false; int32_t numWorkers = -1; WrapperMode wrapperMode( WRAPPER_MODE_NONE ); AStackString<> args; @@ -182,7 +183,11 @@ int Main(int argc, char * argv[]) else if ( thisArg.BeginsWith( "-j" ) && sscanf( thisArg.Get(), "-j%i", &numWorkers ) == 1 ) { - continue; // 'numWorkers' will contain value now + // only accept within sensible range + if ( ( numWorkers >= 0 ) && ( numWorkers <= 64 ) ) + { + continue; // 'numWorkers' will contain value now + } } else if ( thisArg == "-nooutputbuffering" ) { @@ -195,6 +200,11 @@ int Main(int argc, char * argv[]) progressBar = false; continue; } + else if ( thisArg == "-nostoponerror") + { + noStopOnError = true; + continue; + } else if ( thisArg == "-report" ) { report = true; @@ -398,7 +408,7 @@ int Main(int argc, char * argv[]) options.m_GenerateReport = report; options.m_WrapperChild = ( wrapperMode == WRAPPER_MODE_FINAL_PROCESS ); options.m_FixupErrorPaths = fixupErrorPaths; - if ( targets.GetSize() > 1 ) + if ( ( targets.GetSize() > 1 ) || ( noStopOnError ) ) { options.m_StopOnFirstError = false; // when building multiple targets, try to build as much as possible } @@ -465,10 +475,11 @@ void DisplayHelp() " -ide Enable multiple options when building from an IDE.\n" " Enables: -noprogress, -fixuperrorpaths &\n" " -wrapper (Windows)\n" - " -jX Explicitly set worker thread count X, instead of\n" - " default of NUMBER_OF_PROCESSORS. Set to 0 to build\n" - " everything in the main thread.\n" + " -j[x] Explicitly set LOCAL worker thread count X, instead of\n" + " default of hardware thread count.\n" " -noprogress Don't show the progress bar while building.\n" + " -nostoponerror Don't stop building on first error. Try to build as much" + " as possible.\n" " -report Ouput a detailed report at the end of the build,\n" " to report.html. This will lengthen the total build\n" " time.\n" diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.cpp b/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.cpp index 0865de4cb..eb66c9e07 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.cpp @@ -598,6 +598,10 @@ bool BFFParser::ParsePreprocessorDirective( BFFIterator & iter ) { return ParseEndIfDirective( directiveStartIter ); } + else if ( directive == "import" ) + { + return ParseImportDirective( directiveStart, iter ); + } // unknown Error::Error_1030_UnknownDirective( directiveStartIter, directive ); @@ -632,7 +636,13 @@ bool BFFParser::ParseIncludeDirective( BFFIterator & iter ) return false; } - AStackString<> include( stringStart.GetCurrent(), iter.GetCurrent() ); + // unescape and substitute variables + AStackString<> include; + if ( PerformVariableSubstitutions( stringStart, iter, include ) == false ) + { + return false; + } + iter++; // skip closing quote before returning FLOG_INFO( "Including: %s\n", include.Get() ); @@ -807,6 +817,65 @@ bool BFFParser::CheckIfCondition( const BFFIterator & conditionStart, const BFFI return false; } +// ParseImportDirective +//------------------------------------------------------------------------------ +bool BFFParser::ParseImportDirective( const BFFIterator & directiveStart, BFFIterator & iter ) +{ + iter.SkipWhiteSpace(); + + // make sure we haven't hit the end of the file + if ( iter.IsAtEnd() ) + { + Error::Error_1012_UnexpectedEndOfFile( directiveStart ); + return false; + } + + // make sure this is a variable name + if ( iter.IsAtValidVariableNameCharacter() == false ) + { + Error::Error_1013_UnexpectedCharInVariableName( iter, nullptr ); + return false; + } + + // find the end of the variable name + const BFFIterator varNameStart( iter ); + iter.SkipVariableName(); + if ( iter.IsAtEnd() ) + { + Error::Error_1012_UnexpectedEndOfFile( iter ); + return false; + } + 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; + } + AStackString<> varName( varNameStart.GetCurrent(), varNameEnd.GetCurrent() ); + + // look for varName in system environment + AStackString<> varValue; + uint32_t varHash = 0; + if ( FBuild::Get().ImportEnvironmentVar( varName.Get(), varValue, varHash ) == false ) + { + Error::Error_1009_UnknownVariable( varNameStart, nullptr ); + return false; + } + + // add the dot to variable name + varName = "."; + varName.Append( varNameStart.GetCurrent(), varNameLen ); + + // import variable in current scope + BFFStackFrame::SetVarString( varName, varValue ); + FLOG_INFO( "Imported variable '%s' with value '%s' from system environment", varName.Get(), varValue.Get() ); + + return true; +} + // StoreVariableString //------------------------------------------------------------------------------ bool BFFParser::StoreVariableString( const char * varNameStart, const char * varNameEnd, @@ -1295,37 +1364,8 @@ bool BFFParser::StoreVariableToVariable( const char * varNameDstStart, const cha const Array< const BFFVariable * > & srcMembers = varSrc->GetStructMembers(); if ( concat ) { - // set all the variable in - ASSERT( varDst ); // have checked this earlier - const Array< const BFFVariable * > & dstMembers = varDst->GetStructMembers(); - - // we'll take all the src members - Array< const BFFVariable * > allMembers( srcMembers.GetSize() + dstMembers.GetSize(), false ); - allMembers = srcMembers; - - // and keep original (dst) members where the name doesn't clash - for ( const BFFVariable ** it = dstMembers.Begin(); it != dstMembers.End(); ++it ) - { - // if the existing members with conflicting names - bool exists = false; - for ( const BFFVariable ** it2 = srcMembers.Begin(); it2 != srcMembers.End(); ++it2 ) - { - if ( ( *it2 )->GetName() == ( *it )->GetName() ) - { - // already exists in src data, which will override existing in dst - exists = true; - break; - } - } - if ( exists == false ) - { - // keep existing dst since there is no src override - allMembers.Append( *it ); - } - } - - BFFStackFrame::SetVarStruct( dstName, allMembers ); - FLOG_INFO( "Registered variable '%s' with %u members", dstName.Get(), allMembers.GetSize() ); + BFFVariable *const newVar = BFFStackFrame::ConcatVars( dstName, varSrc, varDst ); + FLOG_INFO( "Registered variable '%s' with %u members", dstName.Get(), newVar->GetStructMembers().GetSize() ); } else { diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.h b/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.h index 5fc469492..e104d0810 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.h +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFParser.h @@ -67,6 +67,7 @@ class BFFParser bool ParseIfDirective( const BFFIterator & directiveStart, BFFIterator & iter ); bool ParseEndIfDirective( const BFFIterator & directiveStart ); bool CheckIfCondition( const BFFIterator & conditionStart, const BFFIterator & conditionEnd, bool & result ); + bool ParseImportDirective( const BFFIterator & directiveStart, BFFIterator & iter ); bool StoreVariableString( const char * varNameStart, const char * varNameEnd, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter ); bool StoreVariableArray( const char * varNameStart, const char * varNameEnd, const BFFIterator & valueStart, const BFFIterator & valueEnd, const BFFIterator & operatorIter ); diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.cpp b/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.cpp index 52ab20b73..2da757082 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.cpp @@ -178,6 +178,25 @@ BFFStackFrame::~BFFStackFrame() } } +// ConcatVars +//------------------------------------------------------------------------------ +BFFVariable * BFFStackFrame::ConcatVars( const AString & name, + const BFFVariable * lhs, + const BFFVariable * rhs, + BFFStackFrame * frame ) +{ + frame = frame ? frame : s_StackHead; + + ASSERT( frame ); + ASSERT( lhs ); + ASSERT( rhs ); + + BFFVariable *const newVar = lhs->ConcatVarsRecurse( name, *rhs ); + frame->CreateOrReplaceVarMutableNoRecurse( newVar ); + + return newVar; +} + // GetVar //------------------------------------------------------------------------------ /*static*/ const BFFVariable * BFFStackFrame::GetVar( const char * name ) @@ -289,4 +308,27 @@ BFFVariable * BFFStackFrame::GetVarMutableNoRecurse( const AString & name ) return nullptr; } +// CreateOrReplaceVarMutableNoRecurse +//------------------------------------------------------------------------------ +void BFFStackFrame::CreateOrReplaceVarMutableNoRecurse( BFFVariable *var ) +{ + ASSERT( s_StackHead ); // we shouldn't be calling this if there aren't any stack frames + ASSERT( var ); + + // look at this scope level + Array< BFFVariable * >::Iter i = m_Variables.Begin(); + Array< BFFVariable * >::Iter end = m_Variables.End(); + for( ; i < end ; ++i ) + { + if ( ( *i )->GetName() == var->GetName() ) + { + FDELETE *i; + *i = var; + return; + } + } + + m_Variables.Append( var ); +} + //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h b/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h index 9938f40fe..5ab710a16 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h @@ -44,6 +44,12 @@ class BFFStackFrame // set from an existing variable static void SetVar( const BFFVariable * var, BFFStackFrame * frame = nullptr ); + // set from two existing variable + static BFFVariable * ConcatVars( const AString & name, + const BFFVariable * lhs, + const BFFVariable * rhs, + BFFStackFrame * frame = nullptr ); + // get a variable (caller passes complete name indicating type (user vs system)) static const BFFVariable * GetVar( const char * name ); static const BFFVariable * GetVar( const AString & name ); @@ -66,6 +72,8 @@ class BFFStackFrame BFFVariable::VarType type ) const; BFFVariable * GetVarMutableNoRecurse( const AString & name ); + void CreateOrReplaceVarMutableNoRecurse( BFFVariable * var ); + // variables at current scope Array< BFFVariable * > m_Variables; diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.cpp b/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.cpp index 7384fb653..56ea2fe4a 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.cpp @@ -6,6 +6,7 @@ #include "Tools/FBuild/FBuildCore/PrecompiledHeader.h" #include "BFFVariable.h" +#include "Tools/FBuild/FBuildCore/FLog.h" #include "Core/Mem/Mem.h" @@ -22,6 +23,20 @@ "ArrayOfStructs" }; +// CONSTRUCTOR +//------------------------------------------------------------------------------ +BFFVariable::BFFVariable( const AString & name, VarType type ) +: m_Name( name ) +, m_Type( type ) +//, m_StringValue() // default construct this +, m_BoolValue( false ) +, m_ArrayValues( 0, true ) +, m_IntValue( 0 ) +, m_StructMembers( 0, true ) +, m_ArrayOfStructs( 0, true ) +{ +} + // CONSTRUCTOR (copy) //------------------------------------------------------------------------------ BFFVariable::BFFVariable( const BFFVariable & other ) @@ -251,4 +266,166 @@ void BFFVariable::SetValueArrayOfStructs( const Array< const BFFVariable * > & v m_ArrayOfStructs.Swap( newVars ); } +// GetMemberByName +//------------------------------------------------------------------------------ +/*static*/ const BFFVariable ** BFFVariable::GetMemberByName( const AString & name, const Array< const BFFVariable * > & members ) +{ + ASSERT( !name.IsEmpty() ); + + for ( const BFFVariable ** it = members.Begin(); it != members.End(); ++it ) + { + if ( (*it)->GetName() == name ) + return it; + } + + return nullptr; +} + +// ConcatVarsRecurse +//------------------------------------------------------------------------------ +BFFVariable * BFFVariable::ConcatVarsRecurse( const AString & dstName, const BFFVariable & other ) const +{ + const BFFVariable *varDst = this; + const BFFVariable *varSrc = &other; + + const VarType dstType = m_Type; + const VarType srcType = other.m_Type; + + // handle supported types + + if ( srcType != dstType ) + { + // Mismatched - is there a supported conversion? + + // String to ArrayOfStrings + if ( ( dstType == BFFVariable::VAR_ARRAY_OF_STRINGS ) && + ( srcType == BFFVariable::VAR_STRING) ) + { + uint32_t num = (uint32_t)(1 + other.GetArrayOfStrings().GetSize()); + Array< AString > values(num, false); + values.Append( varDst->GetArrayOfStrings() ); + values.Append( varSrc->GetString() ); + + BFFVariable *result = FNEW(BFFVariable(dstName, values)); + FLOG_INFO("Concatenated variable '%s' with %u elements", dstName.Get(), num); + return result; + } + + // Struct to ArrayOfStructs + if ( ( dstType == BFFVariable::VAR_ARRAY_OF_STRUCTS ) && + ( srcType == BFFVariable::VAR_STRUCT ) ) + { + uint32_t num = (uint32_t)( 1 + varDst->GetArrayOfStructs().GetSize() ); + Array< const BFFVariable * > values( num, false ); + values.Append( varDst->GetArrayOfStructs() ); + values.Append( varSrc ); + + BFFVariable *result = FNEW( BFFVariable( dstName, values ) ); + FLOG_INFO( "Concatenated variable '%s' with %u elements", dstName.Get(), num ); + return result; + } + + } + else + { + // Matching Src and Dst + + if ( srcType == BFFVariable::VAR_STRING ) + { + AStackString< 2048 > finalValue; + finalValue = varDst->GetString(); + finalValue += varSrc->GetString(); + + BFFVariable *result = FNEW( BFFVariable( dstName, finalValue ) ); + FLOG_INFO( "Concatenated variable '%s' with value '%s'", dstName.Get(), finalValue.Get() ); + return result; + } + + if ( srcType == BFFVariable::VAR_ARRAY_OF_STRINGS ) + { + const unsigned int num = (unsigned int)( varSrc->GetArrayOfStrings().GetSize() + varDst->GetArrayOfStrings().GetSize() ); + Array< AString > values(num, false); + values.Append( varDst->GetArrayOfStrings() ); + values.Append( varSrc->GetArrayOfStrings() ); + + BFFVariable *result = FNEW( BFFVariable( dstName, values ) ); + FLOG_INFO( "Concatenated variable '%s' with %u elements", dstName.Get(), num ); + return result; + } + + if ( srcType == BFFVariable::VAR_ARRAY_OF_STRUCTS ) + { + const unsigned int num = (unsigned int)( varSrc->GetArrayOfStructs().GetSize() + varDst->GetArrayOfStructs().GetSize() ); + Array< const BFFVariable * > values( num, false ); + values.Append( varDst->GetArrayOfStructs() ); + values.Append( varSrc->GetArrayOfStructs() ); + + BFFVariable *result = FNEW(BFFVariable(dstName, values)); + FLOG_INFO("Concatenated variable '%s' with %u elements", dstName.Get(), num); + return result; + } + + if ( srcType == BFFVariable::VAR_INT ) + { + int newVal( varSrc->GetInt() ); + newVal += varDst->GetInt(); + + BFFVariable * result = FNEW( BFFVariable( dstName, newVal ) ); + FLOG_INFO( "Concatenated variable '%s' with value %d", dstName.Get(), newVal ); + return result; + } + + if ( srcType == BFFVariable::VAR_BOOL ) + { + // Assume + <=> OR + bool newVal( varSrc->GetBool() ); + newVal |= varDst->GetBool(); + + BFFVariable * result = FNEW( BFFVariable( dstName, newVal ) ); + FLOG_INFO("Concatenated variable '%s' with value %d", dstName.Get(), newVal); + return result; + } + + if ( srcType == BFFVariable::VAR_STRUCT ) + { + const Array< const BFFVariable * > & srcMembers = varSrc->GetStructMembers(); + // set all the variable in + const Array< const BFFVariable * > & dstMembers = varDst->GetStructMembers(); + + BFFVariable * const result = FNEW( BFFVariable( dstName, BFFVariable::VAR_STRUCT ) ); + result->m_StructMembers.SetCapacity( srcMembers.GetSize() + dstMembers.GetSize() ); + Array< BFFVariable * > & allMembers = result->m_StructMembers; + + // keep original (dst) members where the name doesn't clash + // or concatenate recursively values where the name clash + for ( const BFFVariable ** it = dstMembers.Begin(); it != dstMembers.End(); ++it ) + { + const BFFVariable ** it2 = GetMemberByName( (*it)->GetName(), srcMembers ); + + BFFVariable * const newVar = (it2) + ? (*it2)->ConcatVarsRecurse( (*it2)->GetName(), **it ) + : FNEW( BFFVariable( **it ) ); + + allMembers.Append( newVar ); + } + + // and keep original (src) members where the name doesn't clash + for ( const BFFVariable ** it = srcMembers.Begin(); it != srcMembers.End(); ++it ) + { + const BFFVariable ** it2 = GetMemberByName( (*it)->GetName(), result->GetStructMembers() ); + if ( nullptr == it2 ) + { + BFFVariable *const newVar = FNEW( BFFVariable( **it ) ); + allMembers.Append( newVar ); + } + } + + FLOG_INFO( "Concatenated variable '%s' with %u members", dstName.Get(), allMembers.GetSize() ); + return result; + } + } + + return nullptr; +} + //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.h b/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.h index 7171aad80..5fc6d8aa6 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.h +++ b/Code/Tools/FBuild/FBuildCore/BFF/BFFVariable.h @@ -61,11 +61,14 @@ class BFFVariable inline bool IsStruct() const { return m_Type == BFFVariable::VAR_STRUCT; } inline bool IsArrayOfStructs() const { return m_Type == BFFVariable::VAR_ARRAY_OF_STRUCTS; } + BFFVariable * ConcatVarsRecurse( const AString & dstName, const BFFVariable & other ) const; + private: friend class BFFStackFrame; explicit BFFVariable( const BFFVariable & other ); + explicit BFFVariable( const AString & name, VarType type ); explicit BFFVariable( const AString & name, const AString & value ); explicit BFFVariable( const AString & name, bool value ); explicit BFFVariable( const AString & name, const Array< AString > & values ); @@ -81,6 +84,8 @@ class BFFVariable void SetValueStruct( const Array< const BFFVariable * > & members ); void SetValueArrayOfStructs( const Array< const BFFVariable * > & values ); + static const BFFVariable ** GetMemberByName( const AString & name, const Array< const BFFVariable * > & members ); + AString m_Name; // diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/Function.cpp b/Code/Tools/FBuild/FBuildCore/BFF/Functions/Function.cpp index 4bfbcf82d..78de175d0 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/Functions/Function.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/Function.cpp @@ -19,6 +19,7 @@ #include "FunctionObjectList.h" #include "FunctionPrint.h" #include "FunctionSettings.h" +#include "FunctionSLN.h" #include "FunctionTest.h" #include "FunctionUnity.h" #include "FunctionUsing.h" @@ -115,6 +116,7 @@ Function::~Function() FNEW( FunctionLibrary ); FNEW( FunctionPrint ); FNEW( FunctionSettings ); + FNEW( FunctionSLN ); FNEW( FunctionTest ); FNEW( FunctionUnity ); FNEW( FunctionUsing ); @@ -461,6 +463,25 @@ bool Function::GetDirectoryListNodeList( const BFFIterator & iter, { NodeGraph & ng = FBuild::Get().GetDependencyGraph(); + // Handle special case of excluded files beginning with ../ + // Since they can be used seinsibly by matching just the end + // of a path, assume they are relative to the working dir. + // TODO:C Move this during bff parsing when everything is using reflection + Array< AString > filesToExcludeCleaned( filesToExclude.GetSize(), true ); + for ( const AString& file : filesToExclude ) + { + if ( file.BeginsWith( ".." ) ) + { + AStackString<> fullPath; + NodeGraph::CleanPath( file, fullPath ); + filesToExcludeCleaned.Append( fullPath ); + } + else + { + filesToExcludeCleaned.Append( file ); + } + } + const AString * const end = paths.End(); for ( const AString * it = paths.Begin(); it != end; ++it ) { @@ -468,7 +489,7 @@ bool Function::GetDirectoryListNodeList( const BFFIterator & iter, // get node for the dir we depend on AStackString<> name; - DirectoryListNode::FormatName( path, pattern, recurse, excludePaths, filesToExclude, name ); + DirectoryListNode::FormatName( path, pattern, recurse, excludePaths, filesToExcludeCleaned, name ); Node * node = ng.FindNode( name ); if ( node == nullptr ) { @@ -477,7 +498,7 @@ bool Function::GetDirectoryListNodeList( const BFFIterator & iter, pattern, recurse, excludePaths, - filesToExclude ); + filesToExcludeCleaned ); } else if ( node->GetType() != Node::DIRECTORY_LIST_NODE ) { @@ -920,7 +941,7 @@ bool Function::PopulateProperties( const BFFIterator & iter, Node * node ) const default: { ASSERT( false ); // Unsupported type - break; + return false; } } } diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionLibrary.cpp b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionLibrary.cpp index 54f87648d..259eaba76 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionLibrary.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionLibrary.cpp @@ -111,9 +111,10 @@ FunctionLibrary::FunctionLibrary() // check /c or -c if ( objFlags & ObjectNode::FLAG_MSVC ) { - if ( args.Find( "/c" ) == nullptr ) + if ( args.Find( "/c" ) == nullptr && + args.Find( "-c" ) == nullptr) { - Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c" ); + Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c or -c" ); return false; } } diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionObjectList.cpp b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionObjectList.cpp index a015e8f0e..4df817931 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionObjectList.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionObjectList.cpp @@ -103,9 +103,10 @@ FunctionObjectList::FunctionObjectList() // check /c or -c if ( objFlags & ObjectNode::FLAG_MSVC ) { - if ( args.Find( "/c" ) == nullptr ) + if ( args.Find( "/c" ) == nullptr && + args.Find( "-c" ) == nullptr) { - Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c" ); + Error::Error_1106_MissingRequiredToken( funcStartIter, this, ".CompilerOptions", "/c or -c" ); return false; } } diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.cpp b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.cpp index e514cb541..1ef3c52c9 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.cpp +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.cpp @@ -9,6 +9,7 @@ #include "Tools/FBuild/FBuildCore/FLog.h" #include "Tools/FBuild/FBuildCore/BFF/BFFIterator.h" #include "Tools/FBuild/FBuildCore/BFF/BFFParser.h" +#include "Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h" // CONSTRUCTOR //------------------------------------------------------------------------------ @@ -59,31 +60,122 @@ FunctionPrint::FunctionPrint() // a quoted string? const char c = *start; - if ( ( c != '"' ) && ( c != '\'' ) ) + if ( ( c == '"' ) || ( c == '\'' ) ) + { + // find end of string + BFFIterator stop( start ); + stop.SkipString( c ); + ASSERT( stop.GetCurrent() <= functionHeaderStopToken->GetCurrent() ); // should not be in this function if strings are not validly terminated + + // perform variable substitutions + AStackString< 1024 > tmp; + + start++; // skip past opening quote + if ( BFFParser::PerformVariableSubstitutions( start, stop, tmp ) == false ) + { + return false; // substitution will have emitted an error + } + tmp += '\n'; + + FLOG_BUILD_DIRECT( tmp.Get() ); + } + else if ( c == '.' ) + { + // find end of var name + BFFIterator stop( start ); + stop++; // skip past '.' + stop.SkipVariableName(); + + // get the var + AStackString<> varName( start.GetCurrent(), stop.GetCurrent() ); + const BFFVariable * var = BFFStackFrame::GetVar( varName ); + if ( !var ) + { + Error::Error_1009_UnknownVariable( start, this ); + return false; + } + + // dump the contents + PrintVarRecurse( *var, 0 ); + } + else { Error::Error_1001_MissingStringStartToken( start, this ); return false; } + } - // find end of string - BFFIterator stop( start ); - stop.SkipString( c ); - ASSERT( stop.GetCurrent() <= functionHeaderStopToken->GetCurrent() ); // should not be in this function if strings are not validly terminated + return true; +} - // perform variable substitutions - AStackString< 1024 > tmp; +// PrintVarRecurse +//------------------------------------------------------------------------------ +/*static*/ void FunctionPrint::PrintVarRecurse( const BFFVariable & var, uint32_t indent ) +{ + AStackString<> indentStr; + for ( uint32_t i=0; i value( var.GetString() ); + value.Replace( "'", "^'" ); // escape single quotes + FLOG_BUILD( "%s = '%s'\n", var.GetName().Get(), value.Get() ); + break; } - tmp += '\n'; - - FLOG_BUILD_DIRECT( tmp.Get() ); + case BFFVariable::VAR_BOOL: + { + FLOG_BUILD( "%s = %s\n", var.GetName().Get(), var.GetBool() ? "true" : "false" ); + break; + } + case BFFVariable::VAR_ARRAY_OF_STRINGS: + { + const auto & strings = var.GetArrayOfStrings(); + FLOG_BUILD( "%s = // ArrayOfStrings, size: %u\n%s{\n", var.GetName().Get(), (uint32_t)strings.GetSize(), indentStr.Get() ); + for ( const AString & string : strings ) + { + AStackString<> value( string ); + value.Replace( "'", "^'" ); // escape single quotes + FLOG_BUILD( "%s '%s'\n", indentStr.Get(), value.Get() ); + } + FLOG_BUILD( "%s}\n", indentStr.Get() ); + break; + } + case BFFVariable::VAR_INT: + { + FLOG_BUILD( "%s = %i\n", var.GetName().Get(), var.GetInt() ); + break; + } + case BFFVariable::VAR_STRUCT: + { + FLOG_BUILD( "%s = // Struct\n%s[\n", var.GetName().Get(), indentStr.Get() ); + for ( const BFFVariable * subVar : var.GetStructMembers() ) + { + PrintVarRecurse( *subVar, indent ); + } + FLOG_BUILD( "%s]\n", indentStr.Get() ); + break; + } + case BFFVariable::VAR_ARRAY_OF_STRUCTS: + { + const auto & structs = var.GetArrayOfStructs(); + FLOG_BUILD( "%s = // ArrayOfStructs, size: %u\n%s{\n", var.GetName().Get(), (uint32_t)structs.GetSize(), indentStr.Get() ); + for ( const BFFVariable * subVar : structs ) + { + PrintVarRecurse( *subVar, indent ); + } + FLOG_BUILD( "%s}\n", indentStr.Get() ); + break; + } + case BFFVariable::MAX_VAR_TYPES: ASSERT( false ); break; // Something is terribly wrong } - - return true; } //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.h b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.h index 2f5131a93..2b3d51544 100644 --- a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.h +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionPrint.h @@ -26,6 +26,8 @@ class FunctionPrint : public Function const BFFIterator * functionBodyStopToken, const BFFIterator * functionHeaderStartToken, const BFFIterator * functionHeaderStopToken ) const; + + static void PrintVarRecurse( const BFFVariable & var, uint32_t indent ); }; //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.cpp b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.cpp new file mode 100644 index 000000000..a553ce93d --- /dev/null +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.cpp @@ -0,0 +1,450 @@ +// FunctionSLN +//------------------------------------------------------------------------------ + +// Includes +//------------------------------------------------------------------------------ +#include "Tools/FBuild/FBuildCore/PrecompiledHeader.h" + +// FBuild +#include "FunctionSLN.h" +#include "Tools/FBuild/FBuildCore/FBuild.h" +#include "Tools/FBuild/FBuildCore/BFF/BFFStackFrame.h" +#include "Tools/FBuild/FBuildCore/BFF/BFFVariable.h" +#include "Tools/FBuild/FBuildCore/Graph/AliasNode.h" +#include "Tools/FBuild/FBuildCore/Graph/NodeGraph.h" +#include "Tools/FBuild/FBuildCore/Graph/SLNNode.h" +#include "Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h" + +// Core +#include "Core/FileIO/PathUtils.h" +#include "Core/Strings/AStackString.h" + +// CONSTRUCTOR +//------------------------------------------------------------------------------ +FunctionSLN::FunctionSLN() +: Function( "VSSolution" ) +{ +} + +// AcceptsHeader +//------------------------------------------------------------------------------ +/*virtual*/ bool FunctionSLN::AcceptsHeader() const +{ + return true; +} + +// ResolveVCXProjectRecurse +//------------------------------------------------------------------------------ +static VCXProjectNode * ResolveVCXProjectRecurse( Node * node ) +{ + if ( node == nullptr ) + { + return nullptr; + } + + // search inside targets if this is an alias + if ( node->GetType() == Node::ALIAS_NODE ) + { + AliasNode * const alias = node->CastTo< AliasNode >(); + const Dependencies & targets = alias->GetAliasedNodes(); + + const Dependency * const end = targets.End(); + for ( const Dependency * it = targets.Begin() ; it != end ; ++it ) + { + VCXProjectNode *result = ResolveVCXProjectRecurse( it->GetNode() ); + if ( result != nullptr ) + { + return result; + } + } + } + + // check that this a vcxproject + if ( node->GetType() != Node::VCXPROJECT_NODE ) + { + // don't know how to handle this type of node + return nullptr; + } + + return node->CastTo< VCXProjectNode >(); +} + +// VSProjectConfigComp +//------------------------------------------------------------------------------ +struct VSProjectConfigComp +{ + bool operator ()( const VSProjectConfig & a, const VSProjectConfig & b ) const + { + int32_t cmpConfig = a.m_Config.CompareI( b.m_Config ); + return ( cmpConfig == 0 ) + ? a.m_Platform < b.m_Platform + : cmpConfig < 0 ; + } +}; + +// VCXProjectNodeComp +//------------------------------------------------------------------------------ +struct VCXProjectNodeComp +{ + bool operator ()( const VCXProjectNode * a, const VCXProjectNode * b ) const + { + return ( a->GetName() < b->GetName() ); + } +}; + +// Commit +//------------------------------------------------------------------------------ +/*virtual*/ bool FunctionSLN::Commit( const BFFIterator & funcStartIter ) const +{ + // required + AStackString<> solutionOutput; + Array< AString > solutionProjects( 8, true ); + if ( !GetString( funcStartIter, solutionOutput, ".SolutionOutput", true ) || + !GetStrings( funcStartIter, solutionProjects, ".SolutionProjects", true ) ) + { + 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 ( !GetArrayOfStringsFromStruct( s, ".Projects", newFolder.m_ProjectNames ) ) + { + // TODO:B custom error + Error::Error_1101_MissingProperty( funcStartIter, this, AStackString<>( ".Projects" ) ); + return false; + } + // check if this project is included in the solution + { + const AString * const end2 = newFolder.m_ProjectNames.End(); + for ( const AString * it2 = newFolder.m_ProjectNames.Begin() ; it2 != end2 ; ++it2 ) + { + if ( solutionProjects.Find( *it2 ) == false ) + { + // TODO:B custom error + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".Projects", *it2 ); + return false; + } + } + } + + 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 ) + { + Node * const node = ng.FindNode( *it ); + + if ( node == nullptr ) + { + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".SolutionProjects", *it ); + return false; + } + + VCXProjectNode * const project = ResolveVCXProjectRecurse( node ); + + if ( project == nullptr ) + { + // don't know how to handle this type of node + Error::Error_1005_UnsupportedNodeType( funcStartIter, this, ".SolutionProjects", node->GetName(), node->GetType() ); + return false; + } + + // 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 ) + { + Node * const node = ng.FindNode( *it2 ); + + if ( node == nullptr ) + { + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".Projects", *it2 ); + return false; + } + + VCXProjectNode * const project = ResolveVCXProjectRecurse( node ); + + if ( project == nullptr ) + { + // don't know how to handle this type of node + Error::Error_1005_UnsupportedNodeType( funcStartIter, this, ".Projects", node->GetName(), node->GetType() ); + return false; + } + + if ( projects.Find( project ) == false ) + { + // project referenced in a solution folder is not referenced in .SolutionProjects + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".SolutionProjects", project->GetName() ); + return false; + } + + *it2 = project->GetName(); + } + } + } + + // resolves VCXProject node referenced by solutionBuildProject + if ( solutionBuildProject.GetLength() > 0 ) + { + Node * const node = ng.FindNode( solutionBuildProject ); + + if ( node == nullptr ) + { + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".SolutionBuildProject", solutionBuildProject ); + return false; + } + + VCXProjectNode * const project = ResolveVCXProjectRecurse( node ); + + if ( project == nullptr ) + { + // don't know how to handle this type of node + Error::Error_1005_UnsupportedNodeType( funcStartIter, this, ".SolutionBuildProject", node->GetName(), node->GetType() ); + return false; + } + + if ( projects.Find( project ) == false ) + { + // project referenced in .SolutionBuildProject is not referenced in .SolutionProjects + Error::Error_1104_TargetNotDefined( funcStartIter, this, ".SolutionBuildProject", project->GetName() ); + return false; + } + + solutionBuildProject = project->GetName(); + } + + SLNNode * sln = ng.CreateSLNNode( solutionOutput, + solutionBuildProject, + solutionVisualStudioVersion, + solutionMinimumVisualStudioVersion, + configs, + projects, + folders ); + + ASSERT( sln ); + + return ProcessAlias( funcStartIter, sln ); +} + +// GetStringFromStruct +//------------------------------------------------------------------------------ +bool FunctionSLN::GetStringFromStruct( const BFFVariable * s, const char * name, AString & result ) const +{ + const Array< const BFFVariable * > & members = s->GetStructMembers(); + const BFFVariable * const * end = members.End(); + for ( const BFFVariable ** it = members.Begin(); it != end; ++it ) + { + const BFFVariable * v = *it; + if ( v->IsString() == false ) + { + continue; // ignore non-strings + } + if ( v->GetName() == name ) + { + result = v->GetString(); + return true; // found + } + } + return false; // not found - caller decides if this is an error +} + +// GetArrayOfStringsFromStruct +//------------------------------------------------------------------------------ +bool FunctionSLN::GetArrayOfStringsFromStruct( const BFFVariable * s, const char * name, Array< AString > & result ) const +{ + const Array< const BFFVariable * > & members = s->GetStructMembers(); + const BFFVariable * const * end = members.End(); + for ( const BFFVariable ** it = members.Begin(); it != end; ++it ) + { + const BFFVariable * v = *it; + if ( v->IsArrayOfStrings() == false ) + { + continue; // ignore non-strings + } + if ( v->GetName() == name ) + { + result = v->GetArrayOfStrings(); + return true; // found + } + } + return false; // not found - caller decides if this is an error +} + +//------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.h b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.h new file mode 100644 index 000000000..1bef6b559 --- /dev/null +++ b/Code/Tools/FBuild/FBuildCore/BFF/Functions/FunctionSLN.h @@ -0,0 +1,33 @@ +// FunctionSLN +//------------------------------------------------------------------------------ +#pragma once +#ifndef FBUILD_FUNCTIONS_FUNCTIONSLN_H +#define FBUILD_FUNCTIONS_FUNCTIONSLN_H + +// Includes +//------------------------------------------------------------------------------ +#include "Function.h" + +// Forward Declarations +//------------------------------------------------------------------------------ +class BFFIterator; + +// FunctionSLN +//------------------------------------------------------------------------------ +class FunctionSLN : public Function +{ +public: + explicit FunctionSLN(); + inline virtual ~FunctionSLN() {} + +protected: + virtual bool AcceptsHeader() const; + + virtual bool Commit( const BFFIterator & funcStartIter ) const; + + bool GetStringFromStruct( const BFFVariable * s, const char * name, AString & result ) const; + bool GetArrayOfStringsFromStruct( const BFFVariable * s, const char * name, Array< AString > & result ) const; +}; + +//------------------------------------------------------------------------------ +#endif // FBUILD_FUNCTIONS_FUNCTIONSLN_H diff --git a/Code/Tools/FBuild/FBuildCore/FBuild.cpp b/Code/Tools/FBuild/FBuildCore/FBuild.cpp index 7637f330e..3632dea4a 100644 --- a/Code/Tools/FBuild/FBuildCore/FBuild.cpp +++ b/Code/Tools/FBuild/FBuildCore/FBuild.cpp @@ -28,6 +28,7 @@ #include "Core/FileIO/FileIO.h" #include "Core/FileIO/FileStream.h" #include "Core/FileIO/MemoryStream.h" +#include "Core/Math/Murmur3.h" #include "Core/Process/SystemMutex.h" #include "Core/Profile/Profile.h" #include "Core/Strings/AStackString.h" @@ -48,7 +49,9 @@ // CONSTRUCTOR - FBuild //------------------------------------------------------------------------------ FBuild::FBuild( const FBuildOptions & options ) - : m_Client( nullptr ) + : m_DependencyGraph( nullptr ) + , m_JobQueue( nullptr ) + , m_Client( nullptr ) , m_Cache( nullptr ) , m_LastProgressOutputTime( 0.0f ) , m_LastProgressCalcTime( 0.0f ) @@ -57,6 +60,7 @@ FBuild::FBuild( const FBuildOptions & options ) , m_WorkerList( 0, true ) , m_EnvironmentString( nullptr ) , m_EnvironmentStringSize( 0 ) + , m_ImportedEnvironmentVars( 0, true ) { #ifdef DEBUG_CRT_MEMORY_USAGE _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | @@ -418,6 +422,48 @@ void FBuild::SetEnvironmentString( const char * envString, uint32_t size, const m_LibEnvVar = libEnvVar; } +// ImportEnvironmentVar +//------------------------------------------------------------------------------ +bool FBuild::ImportEnvironmentVar( const char * name, AString & value, uint32_t & hash ) +{ + // check if system environment contains the variable + if ( Env::GetEnvVariable( name, value ) == false ) + { + FLOG_ERROR( "Could not import environment variable '%s'", name ); + return false; + } + + // compute hash value for actual value + hash = Murmur3::Calc32( value ); + + // check if the environment var was already imported + const EnvironmentVarAndHash * it = m_ImportedEnvironmentVars.Begin(); + const EnvironmentVarAndHash * const end = m_ImportedEnvironmentVars.End(); + while ( it < end ) + { + if ( it->GetName() == name ) + { + // check if imported environment changed since last import + if ( it->GetHash() != hash ) + { + FLOG_ERROR( "Overwriting imported environment variable '%s' with a different value = '%s'", + name, value.Get() ); + return false; + } + + // skip registration when already imported with same hash value + return true; + } + it++; + } + + // import new variable name with its hash value + const EnvironmentVarAndHash var( name, hash ); + m_ImportedEnvironmentVars.Append( var ); + + return true; +} + // GetLibEnvVar //------------------------------------------------------------------------------ void FBuild::GetLibEnvVar( AString & value ) const diff --git a/Code/Tools/FBuild/FBuildCore/FBuild.h b/Code/Tools/FBuild/FBuildCore/FBuild.h index 531cfb984..bb9433820 100644 --- a/Code/Tools/FBuild/FBuildCore/FBuild.h +++ b/Code/Tools/FBuild/FBuildCore/FBuild.h @@ -69,6 +69,25 @@ class FBuild : public Singleton< FBuild > inline const char * GetEnvironmentString() const { return m_EnvironmentString; } inline uint32_t GetEnvironmentStringSize() const { return m_EnvironmentStringSize; } + class EnvironmentVarAndHash + { + public: + EnvironmentVarAndHash( const char * name, uint32_t hash ) + : m_Name( name ) + , m_Hash( hash ) + {} + + inline const AString & GetName() const { return m_Name; } + inline uint32_t GetHash() const { return m_Hash; } + + protected: + AString m_Name; + uint32_t m_Hash; + }; + + bool ImportEnvironmentVar( const char * name, AString & value, uint32_t & hash ); + const Array< EnvironmentVarAndHash > & GetImportedEnvironmentVars() const { return m_ImportedEnvironmentVars; } + void GetLibEnvVar( AString & libEnvVar ) const; // stats - read access @@ -116,6 +135,8 @@ class FBuild : public Singleton< FBuild > char * m_EnvironmentString; uint32_t m_EnvironmentStringSize; // size excluding last null AString m_LibEnvVar; // LIB= value + + Array< EnvironmentVarAndHash > m_ImportedEnvironmentVars; }; //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/FBuildCore.bff b/Code/Tools/FBuild/FBuildCore/FBuildCore.bff index e3ed61bec..1900be773 100644 --- a/Code/Tools/FBuild/FBuildCore/FBuildCore.bff +++ b/Code/Tools/FBuild/FBuildCore/FBuildCore.bff @@ -9,7 +9,7 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' } diff --git a/Code/Tools/FBuild/FBuildCore/FBuildVersion.h b/Code/Tools/FBuild/FBuildCore/FBuildVersion.h index e8efbe611..e2f594469 100644 --- a/Code/Tools/FBuild/FBuildCore/FBuildVersion.h +++ b/Code/Tools/FBuild/FBuildCore/FBuildVersion.h @@ -6,7 +6,7 @@ // Defines //------------------------------------------------------------------------------ -#define FBUILD_VERSION_STRING "v0.83" +#define FBUILD_VERSION_STRING "v0.84" #if defined( __WINDOWS__ ) #ifdef WIN64 #define FBUILD_VERSION_PLATFORM "x64" diff --git a/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.cpp b/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.cpp index a4a8789a7..24938a667 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.cpp @@ -37,6 +37,26 @@ DLLNode::~DLLNode() { } +// DoBuild +//------------------------------------------------------------------------------ +/*virtual*/ Node::BuildResult DLLNode::DoBuild( Job * job ) +{ + // Make sure the implib output directory exists + if ( m_ImportLibName.IsEmpty() == false) + { + AStackString<> cleanPath; + NodeGraph::CleanPath( m_ImportLibName, cleanPath ); + + if ( EnsurePathExistsForFile( cleanPath ) == false ) + { + // EnsurePathExistsForFile will have emitted error + return NODE_RESULT_FAILED; + } + } + + return LinkerNode::DoBuild( job ); +} + // GetImportLibName //------------------------------------------------------------------------------ void DLLNode::GetImportLibName( AString & importLibName ) const diff --git a/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.h b/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.h index 13177d124..1ae06aa91 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.h +++ b/Code/Tools/FBuild/FBuildCore/Graph/DLLNode.h @@ -34,7 +34,8 @@ class DLLNode : public LinkerNode static Node * Load( IOStream & stream ); private: - virtual void Save( IOStream & stream ) const; + virtual BuildResult DoBuild( Job * job ) override; + virtual void Save( IOStream & stream ) const override; AString m_ImportLibName; }; diff --git a/Code/Tools/FBuild/FBuildCore/Graph/Node.cpp b/Code/Tools/FBuild/FBuildCore/Graph/Node.cpp index f4d0c4408..99bd88136 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/Node.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/Node.cpp @@ -25,6 +25,7 @@ #include "Tools/FBuild/FBuildCore/Graph/NodeProxy.h" #include "Tools/FBuild/FBuildCore/Graph/ObjectListNode.h" #include "Tools/FBuild/FBuildCore/Graph/ObjectNode.h" +#include "Tools/FBuild/FBuildCore/Graph/SLNNode.h" #include "Tools/FBuild/FBuildCore/Graph/TestNode.h" #include "Tools/FBuild/FBuildCore/Graph/UnityNode.h" #include "Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h" @@ -64,7 +65,8 @@ "DLL", "VCXProj", "ObjectList", - "CopyDirNode" + "CopyDirNode", + "SLN", }; // Custom MetaData @@ -418,6 +420,7 @@ void Node::SaveNode( IOStream & fileStream, const Node * node ) const case Node::VCXPROJECT_NODE: n = VCXProjectNode::Load( stream ); break; case Node::OBJECT_LIST_NODE: n = ObjectListNode::Load( stream ); break; case Node::COPY_DIR_NODE: n = CopyDirNode::Load( stream ); break; + case Node::SLN_NODE: n = SLNNode::Load( stream ); break; case Node::NUM_NODE_TYPES: ASSERT( false ); break; } @@ -464,14 +467,8 @@ void Node::SaveNode( IOStream & fileStream, const Node * node ) const } // read contents - Node * n = nullptr; - switch ( (Node::Type)nodeType ) - { - case Node::OBJECT_NODE: n = ObjectNode::LoadRemote( stream ); break; - default: ASSERT( false ); break; - } - - return n; + ASSERT( (Node::Type)nodeType == Node::OBJECT_NODE ); + return ObjectNode::LoadRemote( stream ); } // SaveRemote diff --git a/Code/Tools/FBuild/FBuildCore/Graph/Node.h b/Code/Tools/FBuild/FBuildCore/Graph/Node.h index ef70a0571..4e9d4397d 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/Node.h +++ b/Code/Tools/FBuild/FBuildCore/Graph/Node.h @@ -73,6 +73,7 @@ class Node : public Object VCXPROJECT_NODE = 14, OBJECT_LIST_NODE = 15, COPY_DIR_NODE = 16, + SLN_NODE = 17, // Make sure you update 's_NodeTypeNames' in the cpp NUM_NODE_TYPES // leave this last }; diff --git a/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.cpp b/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.cpp index fd564dffb..fca227b84 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.cpp @@ -26,6 +26,7 @@ #include "LibraryNode.h" #include "ObjectListNode.h" #include "ObjectNode.h" +#include "SLNNode.h" #include "TestNode.h" #include "UnityNode.h" #include "VCXProjectNode.h" @@ -234,6 +235,46 @@ bool NodeGraph::Load( IOStream & stream, bool & needReparsing ) FBuild::Get().SetEnvironmentString( envString.Get(), envStringSize, libEnvVar ); } + // imported environment variables + uint32_t importedEnvironmentsVarsSize = 0; + if ( stream.Read( importedEnvironmentsVarsSize ) == false ) + { + return false; + } + if ( importedEnvironmentsVarsSize > 0 ) + { + AStackString<> varName; + AStackString<> varValue; + uint32_t savedVarHash = 0; + uint32_t importedVarHash = 0; + + for ( uint32_t i = 0; i < importedEnvironmentsVarsSize; ++i ) + { + if ( stream.Read( varName ) == false ) + { + return false; + } + if ( stream.Read( savedVarHash ) == false ) + { + return false; + } + if ( FBuild::Get().ImportEnvironmentVar( varName.Get(), varValue, importedVarHash ) == false ) + { + // make sure the user knows why some things might re-build + FLOG_WARN( "'%s' Environment variable was not found - BFF will be re-parsed\n", varName.Get() ); + needReparsing = true; + return true; + } + if ( importedVarHash != savedVarHash ) + { + // make sure the user knows why some things might re-build + FLOG_WARN( "'%s' Environment variable has changed - BFF will be re-parsed\n", varName.Get() ); + needReparsing = true; + return true; + } + } + } + // check if 'LIB' env variable has changed uint32_t libEnvVarHashInDB( 0 ); if ( stream.Read( libEnvVarHashInDB ) == false ) @@ -368,6 +409,18 @@ void NodeGraph::Save( IOStream & stream ) const stream.Write( libEnvVar ); } + // imported environment variables + const Array< FBuild::EnvironmentVarAndHash > & importedEnvironmentsVars = FBuild::Get().GetImportedEnvironmentVars(); + const uint32_t importedEnvironmentsVarsSize = static_cast( importedEnvironmentsVars.GetSize() ); + ASSERT( importedEnvironmentsVarsSize == importedEnvironmentsVars.GetSize() ); + stream.Write( importedEnvironmentsVarsSize ); + for ( uint32_t i = 0; i < importedEnvironmentsVarsSize; ++i ) + { + const FBuild::EnvironmentVarAndHash & varAndHash = importedEnvironmentsVars[i]; + stream.Write( varAndHash.GetName() ); + stream.Write( varAndHash.GetHash() ); + } + // 'LIB' env var hash const uint32_t libEnvVarHash = GetLibEnvVarHash(); stream.Write( libEnvVarHash ); @@ -853,6 +906,33 @@ VCXProjectNode * NodeGraph::CreateVCXProjectNode( const AString & projectOutput, return node; } +// CreateSLNNode +//------------------------------------------------------------------------------ +SLNNode * NodeGraph::CreateSLNNode( const AString & solutionOutput, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ) +{ + ASSERT( Thread::IsMainThread() ); + + AStackString< 1024 > fullPath; + CleanPath( solutionOutput, fullPath ); + + SLNNode * node = FNEW( SLNNode( fullPath, + solutionBuildProject, + solutionVisualStudioVersion, + solutionMinimumVisualStudioVersion, + configs, + projects, + folders ) ); + AddNode( node ); + return node; +} + + // CreateObjectListNode //------------------------------------------------------------------------------ ObjectListNode * NodeGraph::CreateObjectListNode( const AString & listName, diff --git a/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.h b/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.h index 15a793e71..8d713fbc1 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.h +++ b/Code/Tools/FBuild/FBuildCore/Graph/NodeGraph.h @@ -6,6 +6,7 @@ // Includes //------------------------------------------------------------------------------ +#include "Tools/FBuild/FBuildCore/Helpers/SLNGenerator.h" #include "Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h" #include "Tools/FBuild/FBuildCore/Graph/Node.h" // TODO:C remove when USE_NODE_REFLECTION is removed @@ -33,6 +34,7 @@ class LinkerNode; class Node; class ObjectListNode; class ObjectNode; +class SLNNode; class TestNode; class UnityNode; class VCXProjectNode; @@ -51,7 +53,7 @@ class NodeGraphHeader } inline ~NodeGraphHeader() {} - enum { NODE_GRAPH_CURRENT_VERSION = 59 }; + enum { NODE_GRAPH_CURRENT_VERSION = 62 }; bool IsValid() const { @@ -193,6 +195,13 @@ class NodeGraph const Array< VSProjectFileType > & fileTypes, const Array< AString > & references, const Array< AString > & projectReferences ); + SLNNode * CreateSLNNode( const AString & solutionOutput, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ); ObjectListNode * CreateObjectListNode( const AString & listName, const Dependencies & inputNodes, CompilerNode * compiler, diff --git a/Code/Tools/FBuild/FBuildCore/Graph/ObjectListNode.cpp b/Code/Tools/FBuild/FBuildCore/Graph/ObjectListNode.cpp index 115c9e81d..fd2812937 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/ObjectListNode.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/ObjectListNode.cpp @@ -487,7 +487,11 @@ const char * ObjectListNode::GetObjExtension() const { if ( m_ObjExtensionOverride.IsEmpty() ) { - return ".obj"; + #if defined( __WINDOWS__ ) + return ".obj"; + #else + return ".o"; + #endif } return m_ObjExtensionOverride.Get(); } diff --git a/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.cpp b/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.cpp index 66c011a7e..4f200335f 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.cpp @@ -244,8 +244,24 @@ ObjectNode::~ObjectNode() return NODE_RESULT_FAILED; // SpawnCompiler has logged error } + const char *output = nullptr; + uint32_t outputSize = 0; + + // MSVC will write /ShowIncludes output on stderr sometimes (ex: /Fi) + if ( GetFlag( FLAG_INCLUDES_IN_STDERR ) == true ) + { + output = ch.GetErr().Get(); + outputSize = ch.GetErrSize(); + } + else + { + // but most of the time it will be on stdout + output = ch.GetOut().Get(); + outputSize = ch.GetOutSize(); + } + // compiled ok, try to extract includes - if ( ProcessIncludesMSCL( ch.GetOut().Get(), ch.GetOutSize() ) == false ) + if ( ProcessIncludesMSCL( output, outputSize ) == false ) { return NODE_RESULT_FAILED; // ProcessIncludesMSCL will have emitted an error } @@ -441,10 +457,10 @@ bool ObjectNode::ProcessIncludesMSCL( const char * output, uint32_t outputSize ) Timer t; { - ASSERT( output && outputSize ); - CIncludeParser parser; - bool result = parser.ParseMSCL_Output( output, outputSize ); + bool result = ( output && outputSize ) ? parser.ParseMSCL_Output( output, outputSize ) + : false; + if ( result == false ) { FLOG_ERROR( "Failed to process includes for '%s'", GetName().Get() ); @@ -620,6 +636,7 @@ bool ObjectNode::ProcessIncludesWithPreProcessor( Job * job ) { bool usingCLR = false; bool usingWinRT = false; + bool usingPreprocessorOnly = false; Array< AString > tokens; args.Tokenize( tokens ); @@ -649,13 +666,19 @@ bool ObjectNode::ProcessIncludesWithPreProcessor( Job * job ) { flags |= ObjectNode::FLAG_CREATING_PCH; } + else if ( ( token == "/P" ) || ( token == "-P" ) ) + { + usingPreprocessorOnly = true; + flags |= ObjectNode::FLAG_INCLUDES_IN_STDERR; + } } // 1) clr code cannot be distributed due to a compiler bug where the preprocessed using // statements are truncated // 2) code consuming the windows runtime cannot be cached due to preprocessing weirdness // 3) pch files are machine specific - if ( !usingWinRT && !usingCLR && !( flags & ObjectNode::FLAG_CREATING_PCH ) ) + // 4) user only wants preprocessor step executed + if ( !usingWinRT && !usingCLR && !usingPreprocessorOnly && !( flags & ObjectNode::FLAG_CREATING_PCH ) ) { if ( isDistributableCompiler ) { @@ -787,7 +810,11 @@ const char * ObjectNode::GetObjExtension() const { if ( m_ObjExtensionOverride.IsEmpty() ) { - return ".obj"; + #if defined( __WINDOWS__ ) + return ".obj"; + #else + return ".o"; + #endif } return m_ObjExtensionOverride.Get(); } @@ -1150,6 +1177,12 @@ void ObjectNode::BuildFullArgs( const Job * job, AString & fullArgs, Pass pass, { continue; } + + // "Minimal Rebuild" is not compatible with FASTBuild + if ( StripToken( "/Gm", token ) ) + { + continue; + } } // remove includes for second pass @@ -1344,20 +1377,6 @@ bool ObjectNode::WriteTmpFile( Job * job, AString & tmpFileName ) const FileStream tmpFile; AStackString<> fileName( sourceFile->GetName().FindLast( NATIVE_SLASH ) + 1 ); - // TODO:B This code doesn't seem to do anything. Looks like the extension - // logic was broken, but GCC support is still working, so perhaps this - // can be removed - if ( GetFlag( FLAG_GCC ) || GetFlag( CODEWARRIOR_WII ) || GetFlag( GREENHILLS_WIIU ) ) - { - // GCC requires preprocessed output to be named a certain way - // SNC & MSVC like the extension left alone - // C code (.c) -> .i (NOTE: lower case only) - // C++ code (.C, .cc, .cp, .cpp, .cxx) -> .ii - const char * tmpFileExt = fileName.FindLast( '.' ); - tmpFileExt = tmpFileExt ? ( tmpFileExt + 1 ) : fileName.Get(); - tmpFileExt = ( strcmp( tmpFileExt, "c" ) == 0 ) ? "i" : "ii"; - } - void const * dataToWrite = job->GetData(); size_t dataToWriteSize = job->GetDataSize(); diff --git a/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.h b/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.h index 4424b8d51..91def98fa 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.h +++ b/Code/Tools/FBuild/FBuildCore/Graph/ObjectNode.h @@ -61,6 +61,7 @@ class ObjectNode : public FileNode CODEWARRIOR_WII = 0x2000, GREENHILLS_WIIU = 0x4000, FLAG_CUDA_NVCC = 0x10000, + FLAG_INCLUDES_IN_STDERR = 0x20000, }; static uint32_t DetermineFlags( const Node * compilerNode, const AString & args ); diff --git a/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.cpp b/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.cpp new file mode 100644 index 000000000..2cbc1db00 --- /dev/null +++ b/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.cpp @@ -0,0 +1,207 @@ +// SLNNode.cpp +//------------------------------------------------------------------------------ + +// Includes +//------------------------------------------------------------------------------ +#include "Tools/FBuild/FBuildCore/PrecompiledHeader.h" + +#include "SLNNode.h" + +#include "Tools/FBuild/FBuildCore/FBuild.h" +#include "Tools/FBuild/FBuildCore/FLog.h" +#include "Tools/FBuild/FBuildCore/Graph/NodeGraph.h" +#include "Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h" +#include "Tools/FBuild/FBuildCore/Helpers/SLNGenerator.h" +#include "Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h" + +// Core +#include "Core/Env/Env.h" +#include "Core/FileIO/FileIO.h" +#include "Core/FileIO/FileStream.h" +#include "Core/FileIO/PathUtils.h" +#include "Core/Process/Process.h" +#include "Core/Strings/AStackString.h" + +// system +#include // for memcmp + +// CONSTRUCTOR +//------------------------------------------------------------------------------ +SLNNode::SLNNode( const AString & solutionOuput, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ) +: FileNode( solutionOuput, Node::FLAG_NONE ) +, m_SolutionBuildProject( solutionBuildProject ) +, m_SolutionVisualStudioVersion( solutionVisualStudioVersion ) +, m_SolutionMinimumVisualStudioVersion( solutionMinimumVisualStudioVersion ) +, m_Configs( configs ) +, m_Folders( folders ) +{ + m_LastBuildTimeMs = 100; // higher default than a file node + m_Type = Node::SLN_NODE; + + // depend on the input nodes + VCXProjectNode * const * projectsEnd = projects.End(); + for( VCXProjectNode ** it = projects.Begin() ; it != projectsEnd ; ++it ) + { + m_StaticDependencies.Append( Dependency( *it ) ); + } +} + +// DESTRUCTOR +//------------------------------------------------------------------------------ +SLNNode::~SLNNode() +{ +} + +// DoBuild +//------------------------------------------------------------------------------ +/*virtual*/ Node::BuildResult SLNNode::DoBuild( Job * UNUSED( job ) ) +{ + SLNGenerator sg; + + // projects + Array< VCXProjectNode * > projects( m_StaticDependencies.GetSize(), false ); + const Dependency * const end = m_StaticDependencies.End(); + for ( const Dependency * it = m_StaticDependencies.Begin() ; it != end ; ++it ) + { + projects.Append( it->GetNode()->CastTo< VCXProjectNode >() ); + } + + // .sln solution file + const AString & sln = sg.GenerateSLN( m_Name, + m_SolutionBuildProject, + m_SolutionVisualStudioVersion, + m_SolutionMinimumVisualStudioVersion, + m_Configs, + projects, + m_Folders ); + if ( Save( sln, m_Name ) == false ) + { + return NODE_RESULT_FAILED; // Save will have emitted an error + } + + return NODE_RESULT_OK; +} + +// 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; +} + +// Load +//------------------------------------------------------------------------------ +/*static*/ Node * SLNNode::Load( IOStream & stream ) +{ + NODE_LOAD( AStackString<>, name ); + NODE_LOAD( AStackString<>, buildProject ); + NODE_LOAD( AStackString<>, visualStudioVersion ); + NODE_LOAD( AStackString<>, minimumVisualStudioVersion ); + NODE_LOAD_DEPS( 1, staticDeps ); + + Array< VSProjectConfig > configs; + VSProjectConfig::Load( stream, configs ); + + Array< SLNSolutionFolder > folders; + SLNSolutionFolder::Load( stream, folders ); + + Array< VCXProjectNode * > projects( staticDeps.GetSize(), false ); + const Dependency * const end = staticDeps.End(); + for ( const Dependency * it = staticDeps.Begin() ; it != end ; ++it ) + { + projects.Append( it->GetNode()->CastTo< VCXProjectNode >() ); + } + + NodeGraph & ng = FBuild::Get().GetDependencyGraph(); + SLNNode * n = ng.CreateSLNNode( name, + buildProject, + visualStudioVersion, + minimumVisualStudioVersion, + configs, + projects, + folders ); + return n; +} + +// Save +//------------------------------------------------------------------------------ +/*virtual*/ void SLNNode::Save( IOStream & stream ) const +{ + NODE_SAVE( m_Name ); + NODE_SAVE( m_SolutionBuildProject ); + NODE_SAVE( m_SolutionVisualStudioVersion ); + NODE_SAVE( m_SolutionMinimumVisualStudioVersion ); + NODE_SAVE_DEPS( m_StaticDependencies ); + VSProjectConfig::Save( stream, m_Configs ); + SLNSolutionFolder::Save( stream, m_Folders ); +} + +//------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.h b/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.h new file mode 100644 index 000000000..bc8c82887 --- /dev/null +++ b/Code/Tools/FBuild/FBuildCore/Graph/SLNNode.h @@ -0,0 +1,54 @@ +// SLNNode.h - a node that builds a sln file +//------------------------------------------------------------------------------ +#pragma once +#ifndef FBUILD_GRAPH_SLNNODE_H +#define FBUILD_GRAPH_SLNNODE_H + +// Includes +//------------------------------------------------------------------------------ +#include "FileNode.h" + +#include "Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h" +#include "Tools/FBuild/FBuildCore/Helpers/SLNGenerator.h" + +#include "Core/Containers/Array.h" +#include "Core/FileIO/FileIO.h" +#include "Core/Strings/AString.h" + +// Forward Declarations +//------------------------------------------------------------------------------ +class VCXProjectNode; + +// SLNNode +//------------------------------------------------------------------------------ +class SLNNode : public FileNode +{ +public: + explicit SLNNode( const AString & solutionOutput, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ); + virtual ~SLNNode(); + + static inline Node::Type GetTypeS() { return Node::SLN_NODE; } + + static Node * Load( IOStream & stream ); + virtual void Save( IOStream & stream ) const; +private: + virtual BuildResult DoBuild( Job * job ); + + bool Save( const AString & content, const AString & fileName ) const; + + AString m_SolutionBuildProject; + AString m_SolutionVisualStudioVersion; + AString m_SolutionMinimumVisualStudioVersion; + + Array< VSProjectConfig > m_Configs; + Array< SLNSolutionFolder > m_Folders; +}; + +//------------------------------------------------------------------------------ +#endif // FBUILD_GRAPH_SLNNODE_H diff --git a/Code/Tools/FBuild/FBuildCore/Graph/UnityNode.cpp b/Code/Tools/FBuild/FBuildCore/Graph/UnityNode.cpp index b9348a6e3..4d40a64d8 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/UnityNode.cpp +++ b/Code/Tools/FBuild/FBuildCore/Graph/UnityNode.cpp @@ -67,18 +67,6 @@ UnityNode::UnityNode() //------------------------------------------------------------------------------ bool UnityNode::Initialize( const BFFIterator & iter, const Function * function ) { - if ( m_PrecompiledHeader.IsEmpty() == false ) - { - // automatically exclude the associated CPP file for a PCH (if there is one) - if ( m_PrecompiledHeader.EndsWithI( ".h" ) ) - { - AStackString<> pchCPP( m_PrecompiledHeader.Get(), - m_PrecompiledHeader.Get() + m_PrecompiledHeader.GetLength() - 2 ); - pchCPP += ".cpp"; - m_FilesToExclude.Append( pchCPP ); - } - } - Dependencies dirNodes( m_InputPaths.GetSize() ); if ( !function->GetDirectoryListNodeList( iter, m_InputPaths, m_PathsToExclude, m_FilesToExclude, m_InputPathRecurse, m_InputPattern, "UnityInputPath", dirNodes ) ) { @@ -366,6 +354,17 @@ bool UnityNode::GetFiles( Array< FileAndOrigin > & files ) { bool ok = true; + // automatically exclude the associated CPP file for a PCH (if there is one) + AStackString<> pchCPP; + if ( m_PrecompiledHeader.IsEmpty() == false ) + { + if ( m_PrecompiledHeader.EndsWithI( ".h" ) ) + { + pchCPP.Assign( m_PrecompiledHeader.Get(), m_PrecompiledHeader.GetEnd() - 1 ); + pchCPP += "cpp"; + } + } + const Dependency * const sEnd = m_StaticDependencies.End(); for ( const Dependency * sIt = m_StaticDependencies.Begin(); sIt != sEnd; ++sIt ) { @@ -393,6 +392,14 @@ bool UnityNode::GetFiles( Array< FileAndOrigin > & files ) } } + if ( keep && ( pchCPP.IsEmpty() == false ) ) + { + if ( PathUtils::PathEndsWithFile( filesIt->m_Name, pchCPP ) ) + { + keep = false; + } + } + if ( keep ) { files.Append( FileAndOrigin( filesIt, dirNode ) ); diff --git a/Code/Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h b/Code/Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h index 6a547ad21..17c08565f 100644 --- a/Code/Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h +++ b/Code/Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h @@ -40,7 +40,10 @@ class VCXProjectNode : public FileNode const Array< AString > & projectReferences ); virtual ~VCXProjectNode(); - static inline Node::Type GetType() { return Node::VCXPROJECT_NODE; } + static inline Node::Type GetTypeS() { return Node::VCXPROJECT_NODE; } + + const AString & GetProjectGuid() const { return m_ProjectGuid; } + const Array< VSProjectConfig > & GetConfigs() const { return m_Configs; } static Node * Load( IOStream & stream ); virtual void Save( IOStream & stream ) const; diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/CIncludeParser.cpp b/Code/Tools/FBuild/FBuildCore/Helpers/CIncludeParser.cpp index c05c3e4e5..d693510d0 100644 --- a/Code/Tools/FBuild/FBuildCore/Helpers/CIncludeParser.cpp +++ b/Code/Tools/FBuild/FBuildCore/Helpers/CIncludeParser.cpp @@ -50,32 +50,89 @@ bool CIncludeParser::ParseMSCL_Output( const char * compilerOutput, //const char * end = pos + compilerOutputSize; for (;;) { - // find next include note - const char * token = strstr( pos, "\nNote: including file: " ); - if ( !token ) + const char * lineStart = pos; + + // find end of the line + pos = strchr( pos, '\r' ); + if ( !pos ) { - break; + break; // end of output } - pos = token + 23; - // skip whitespace (alwways spaces) - while ( *pos == ' ' ) + const char * lineEnd = pos; + + ASSERT( *pos == '\r' ); + ++pos; // skip \r for next line + + const char * ch = lineStart; + + // count colons in the line + const char * colon1 = nullptr; + for ( ; ch < lineEnd ; ++ch ) { - ++pos; + if ( *ch == ':' ) + { + if ( colon1 == nullptr ) + { + colon1 = ch; + } + else + { + break; + } + } } - const char * lineStart = pos; + // check that we have two colons separated by at least one char + if ( colon1 == nullptr || colon1 == lineStart || + *ch != ':' || (ch - colon1) < 2 ) + { + continue; // next line + } - // find end of line - pos = strchr( pos, '\r' ); - if ( !pos ) + ASSERT( *ch == ':' ); + const char * colon2 = ch; + + // skip whitespace (always spaces) + do { - return false; + ++ch; } + while ( *ch == ' ' ); - const char * lineEnd = pos; + // must have whitespaces + if ( ch == colon2 ) + { + continue; // next line + } - AddInclude( lineStart, lineEnd ); + const char * includeStart = ch; + const char * includeEnd = lineEnd; + + // validates the windows path + bool validated = ( includeStart < includeEnd ); + for ( ; validated && ( ch < includeEnd ); ++ch ) + { + switch ( *ch ) + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + case '<': + case '>': + case '"': + case '|': + case '?': + case '*': + validated = false; + break; + default: + break; + } + } + + if ( validated ) + { + AddInclude( includeStart, includeEnd ); + } } return true; diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/Report.cpp b/Code/Tools/FBuild/FBuildCore/Helpers/Report.cpp index 3104d3d26..1c2db0e5c 100644 --- a/Code/Tools/FBuild/FBuildCore/Helpers/Report.cpp +++ b/Code/Tools/FBuild/FBuildCore/Helpers/Report.cpp @@ -43,7 +43,9 @@ uint32_t g_ReportNodeColors[] = { 0x000000, // PROXY_NODE (never seen) 0xFFCC88, // DLL_NODE 0xFFFFFF, // VCXPROJ_NODE 0x444444, // OBJECT_LIST_NODE - 0x000000 }; // COPY_DIR_NODE (never seen) + 0x000000, // COPY_DIR_NODE (never seen) + 0x77DDAA, // SLN_NODE + }; // CONSTRUCTOR //------------------------------------------------------------------------------ @@ -271,9 +273,14 @@ void Report::CreateOverview( const FBuildStats & stats ) Write( "ItemDetails\n" ); // Full command line - const char * commandLine = Env::GetCmdLine(); - const char * exeExtension = strstr( commandLine, ".exe\"" ); - commandLine = exeExtension ? ( exeExtension + 5 ) : commandLine; // skip .exe + closing quote + AStackString<> commandLineBuffer; + Env::GetCmdLine( commandLineBuffer ); + #if defined( __WINDOWS__ ) + const char * exeExtension = strstr( commandLineBuffer.Get(), ".exe\"" ); + const char * commandLine = exeExtension ? ( exeExtension + 5 ) : commandLineBuffer.Get(); // skip .exe + closing quote + #else + const char * commandLine = commandLineBuffer.Get(); + #endif Write( "Cmd Line Options%s", commandLine ); // Target @@ -410,6 +417,10 @@ void Report::DoCacheStats( const FBuildStats & stats ) // out of date items const uint32_t outOfDateItems = ls.objectCount_OutOfDate; + if ( outOfDateItems == 0 ) + { + continue; // skip library if nothing was done + } const float outOfDateItemsPerc = ( (float)outOfDateItems / (float)items ) * 100.0f; // cacheable @@ -427,12 +438,6 @@ void Report::DoCacheStats( const FBuildStats & stats ) // stores const uint32_t cStores = ls.objectCount_CacheStores; - // skip library if nothing was done - if ( outOfDateItems == 0 ) - { - continue; - } - // start collapsable section if ( numOutput == 10 ) { diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/SLNGenerator.cpp b/Code/Tools/FBuild/FBuildCore/Helpers/SLNGenerator.cpp new file mode 100644 index 000000000..7ad07f135 --- /dev/null +++ b/Code/Tools/FBuild/FBuildCore/Helpers/SLNGenerator.cpp @@ -0,0 +1,428 @@ +// SLNGenerator +//------------------------------------------------------------------------------ + +// Includes +//------------------------------------------------------------------------------ +#include "Tools/FBuild/FBuildCore/PrecompiledHeader.h" + +#include "SLNGenerator.h" + +#include "Tools/FBuild/FBuildCore/Graph/VCXProjectNode.h" +#include "Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h" + +// Core +#include "Core/FileIO/IOStream.h" +#include "Core/FileIO/PathUtils.h" +#include "Core/Math/CRC32.h" +#include "Core/Strings/AStackString.h" + +// system +#include // for va_args + +// CONSTRUCTOR +//------------------------------------------------------------------------------ +SLNGenerator::SLNGenerator() +{ +} + +// DESTRUCTOR +//------------------------------------------------------------------------------ +SLNGenerator::~SLNGenerator() +{ +} + +// GenerateVCXProj +//------------------------------------------------------------------------------ +const AString & SLNGenerator::GenerateSLN( const AString & solutionFile, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ) +{ + // preallocate to avoid re-allocations + m_Output.SetReserved( MEGABYTE ); + m_Output.SetLength( 0 ); + + // determine folder for project + const char * lastSlash = solutionFile.FindLast( NATIVE_SLASH ); + AStackString<> solutionBasePath( solutionFile.Get(), lastSlash ? lastSlash + 1 : solutionFile.Get() ); + + AStackString<> solutionBuildProjectGuid; + Array< AString > projectGuids( projects.GetSize(), false ); + Array< AString > solutionProjectsToFolder( projects.GetSize(), true ); + Array< AString > solutionFolderPaths( folders.GetSize(), true ); + + // Create solution configs (solves Visual Studio weirdness) + const size_t configCount = configs.GetSize(); + Array< SolutionConfig > solutionConfigs( configCount, false ); + solutionConfigs.SetSize( configCount ); + for ( size_t i = 0 ; i < configCount ; ++i ) + { + const VSProjectConfig & projectConfig = configs[ i ]; + SolutionConfig & solutionConfig = solutionConfigs[ i ]; + + solutionConfig.m_Config = projectConfig.m_Config; + solutionConfig.m_Platform = projectConfig.m_Platform; + + if ( projectConfig.m_Platform.MatchesI( "Win32" ) ) + { + solutionConfig.m_SolutionPlatform = "x86"; + } + else + { + solutionConfig.m_SolutionPlatform = projectConfig.m_Platform; + } + } + + // Sort again with substituted solution platforms + solutionConfigs.Sort(); + + // construct sln file + WriteHeader( solutionVisualStudioVersion, solutionMinimumVisualStudioVersion ); + WriteProjectListings( solutionBasePath, solutionBuildProject, projects, folders, solutionBuildProjectGuid, projectGuids, solutionProjectsToFolder ); + WriteSolutionFolderListings( folders, solutionFolderPaths ); + Write( "Global\r\n" ); + WriteSolutionConfigurationPlatforms( solutionConfigs ); + WriteProjectConfigurationPlatforms( solutionBuildProjectGuid, solutionConfigs, projectGuids ); + WriteNestedProjects( solutionProjectsToFolder, solutionFolderPaths ); + WriteFooter(); + + return m_Output; +} + +// 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 ); +} + +// WriteProjectListings +//------------------------------------------------------------------------------ +void SLNGenerator::WriteProjectListings( const AString& solutionBasePath, + const AString& solutionBuildProject, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders, + 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 ); + + // project relative path + AStackString<> projectPath( (*it)->GetName() ); + projectPath.Replace( solutionBasePath.Get(), "" ); + + // get project base name only + const char * p1 = projectPath.FindLast( NATIVE_SLASH ); + const char * p2 = projectPath.FindLast( '.' ); + AStackString<> projectName( p1 ? p1 + 1 : projectPath.Get(), + p2 ? p2 : projectPath.GetEnd() ); + + // retrieve projectGuid + AStackString<> projectGuid; + if ( (*it)->GetProjectGuid().GetLength() == 0 ) + { + AStackString<> projectNameForGuid( p1 ? p1 : projectName.Get() ); + VSProjectGenerator::FormatDeterministicProjectGUID( projectGuid, projectNameForGuid ); + } + else + { + projectGuid = (*it)->GetProjectGuid(); + } + + // 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() ); + + 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 ); + } + } + } +} + +// WriteSolutionConfigs +//------------------------------------------------------------------------------ +void SLNGenerator::WriteSolutionFolderListings( const Array< SLNSolutionFolder > & folders, + Array< AString > & solutionFolderPaths ) +{ + // Create every intermediate path + const SLNSolutionFolder * const foldersEnd = folders.End(); + for( const SLNSolutionFolder * it = folders.Begin() ; it != foldersEnd ; ++it ) + { + if ( solutionFolderPaths.Find( it->m_Path ) == false ) + { + solutionFolderPaths.Append( it->m_Path ); + } + + const char * pathEnd = it->m_Path.Find( NATIVE_SLASH ); + while ( pathEnd ) + { + AStackString<> solutionFolderPath( it->m_Path.Get(), pathEnd ); + if ( solutionFolderPaths.Find( solutionFolderPath ) == false ) + { + solutionFolderPaths.Append( solutionFolderPath ); + } + + pathEnd = it->m_Path.Find( NATIVE_SLASH, pathEnd + 1 ); + } + } + + solutionFolderPaths.Sort(); + + // Solution Folders Listings + + const AString * const solutionFolderPathsEnd = solutionFolderPaths.End(); + for( const AString * it = solutionFolderPaths.Begin() ; it != solutionFolderPathsEnd ; ++it ) + { + // parse solution folder name + const char * solutionFolderName = it->FindLast( NATIVE_SLASH ); + solutionFolderName = solutionFolderName ? solutionFolderName + 1 : it->Get(); + + // generate a guid for the solution folder + AStackString<> solutionFolderGuid; + VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderGuid, *it ); + + // Guid must be uppercase (like visual) + solutionFolderGuid.ToUpper(); + + Write( "Project(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"%s\", \"%s\", \"%s\"\r\n", + solutionFolderName, solutionFolderName, solutionFolderGuid.Get() ); + + Write( "EndProject\r\n" ); + } +} + +// WriteSolutionConfigurationPlatforms +//------------------------------------------------------------------------------ +void SLNGenerator::WriteSolutionConfigurationPlatforms( const Array< SolutionConfig > & solutionConfigs ) +{ + Write( "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n" ); + + // Solution Configurations + const SolutionConfig * const end = solutionConfigs.End(); + for( const SolutionConfig * it = solutionConfigs.Begin() ; it != end ; ++it ) + { + Write( "\t\t%s|%s = %s|%s\r\n", + it->m_Config.Get(), it->m_SolutionPlatform.Get(), + it->m_Config.Get(), it->m_SolutionPlatform.Get() ); + } + + Write( "\tEndGlobalSection\r\n" ); +} + +// WriteProjectConfigurationPlatforms +//------------------------------------------------------------------------------ +void SLNGenerator::WriteProjectConfigurationPlatforms( const AString & solutionBuildProjectGuid, + const Array< SolutionConfig > & solutionConfigs, + const Array< AString > & projectGuids ) +{ + Write( "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n" ); + + // Solution Configuration Mappings to Projects + const AString * const projectGuidsEnd = projectGuids.End(); + for( const AString * it = projectGuids.Begin() ; it != projectGuidsEnd ; ++it ) + { + // only one project active in the solution build + const bool projectIsActive = ( solutionBuildProjectGuid == *it ); + + const SolutionConfig * const solutionConfigsEnd = solutionConfigs.End(); + for( const SolutionConfig * it2 = solutionConfigs.Begin() ; it2 != solutionConfigsEnd ; ++it2 ) + { + Write( "\t\t%s.%s|%s.ActiveCfg = %s|%s\r\n", + it->Get(), + it2->m_Config.Get(), it2->m_SolutionPlatform.Get(), + it2->m_Config.Get(), it2->m_Platform.Get() ); + + if ( projectIsActive ) + { + Write( "\t\t%s.%s|%s.Build.0 = %s|%s\r\n", + it->Get(), + it2->m_Config.Get(), it2->m_SolutionPlatform.Get(), + it2->m_Config.Get(), it2->m_Platform.Get() ); + } + } + } + + Write( "\tEndGlobalSection\r\n" ); + Write( "\tGlobalSection(SolutionProperties) = preSolution\r\n" ); + Write( "\t\tHideSolutionNode = FALSE\r\n" ); + Write( "\tEndGlobalSection\r\n" ); +} + +// WriteNestedProjects +//------------------------------------------------------------------------------ +void SLNGenerator::WriteNestedProjects( const Array< AString > & solutionProjectsToFolder, + const Array< AString > & solutionFolderPaths ) +{ + if ( solutionProjectsToFolder.GetSize() == 0 && + solutionFolderPaths.GetSize() == 0 ) + { + return; // skip global section + } + + Write( "\tGlobalSection(NestedProjects) = preSolution\r\n" ); + + // Write every project to solution folder relationships + const AString * const solutionProjectsToFolderEnd = solutionProjectsToFolder.End(); + for( const AString * it = solutionProjectsToFolder.Begin() ; it != solutionProjectsToFolderEnd ; ++it ) + { + Write( it->Get() ); + } + + // Write every intermediate path + const AString * const solutionFolderPathsEnd = solutionFolderPaths.End(); + for( const AString * it = solutionFolderPaths.Begin() ; it != solutionFolderPathsEnd ; ++it ) + { + // parse solution folder parent path + AStackString<> solutionFolderParentGuid; + const char * lastSlash = it->FindLast( NATIVE_SLASH ); + if ( lastSlash ) + { + AStackString<> solutionFolderParentPath( it->Get(), lastSlash ); + VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderParentGuid, solutionFolderParentPath ); + } + + if ( solutionFolderParentGuid.GetLength() > 0 ) + { + // generate a guid for the solution folder + AStackString<> solutionFolderGuid; + VSProjectGenerator::FormatDeterministicProjectGUID( solutionFolderGuid, *it ); + + solutionFolderGuid.ToUpper(); + solutionFolderParentGuid.ToUpper(); + + // write parent solution folder relationship + Write( "\t\t%s = %s\r\n", solutionFolderGuid.Get(), solutionFolderParentGuid.Get() ); + } + } + + Write( "\tEndGlobalSection\r\n" ); +} + +// WriteFooter +//------------------------------------------------------------------------------ +void SLNGenerator::WriteFooter() +{ + // footer + Write( "EndGlobal\r\n" ); +} + +// Write +//------------------------------------------------------------------------------ +void SLNGenerator::Write( const char * fmtString, ... ) +{ + AStackString< 1024 > tmp; + + va_list args; + va_start(args, fmtString); + tmp.VFormat( fmtString, args ); + va_end( args ); + + // resize output buffer in large chunks to prevent re-sizing + if ( m_Output.GetLength() + tmp.GetLength() > m_Output.GetReserved() ) + { + m_Output.SetReserved( m_Output.GetReserved() + MEGABYTE ); + } + + m_Output += tmp; +} + + +// SLNSolutionFolder::Save +//------------------------------------------------------------------------------ +/*static*/ void SLNSolutionFolder::Save( IOStream & stream, const Array< SLNSolutionFolder > & solutionFolders ) +{ + uint32_t numSolutionFolders = (uint32_t)solutionFolders.GetSize(); + stream.Write( numSolutionFolders ); + for ( uint32_t i=0; i & solutionFolders ) +{ + ASSERT( solutionFolders.IsEmpty() ); + + uint32_t numSolutionFolders( 0 ); + if ( !stream.Read( numSolutionFolders ) ) + { + return false; + } + solutionFolders.SetSize( numSolutionFolders ); + for ( uint32_t i=0; i m_ProjectNames; + + static bool Load( IOStream & stream, Array< SLNSolutionFolder > & solutionFolders ); + static void Save( IOStream & stream, const Array< SLNSolutionFolder > & solutionFolders ); +}; + +// SolutionConfig +//------------------------------------------------------------------------------ +struct SolutionConfig +{ + AString m_Config; + AString m_Platform; + AString m_SolutionPlatform; + + bool operator < ( const SolutionConfig& other ) const + { + int32_t cmpConfig = m_Config.CompareI( other.m_Config ); + return ( cmpConfig == 0 ) + ? m_SolutionPlatform < other.m_SolutionPlatform + : cmpConfig < 0 ; + } +}; + +// SLNGenerator +//------------------------------------------------------------------------------ +class SLNGenerator +{ +public: + SLNGenerator(); + ~SLNGenerator(); + + const AString & GenerateSLN( const AString & solutionFile, + const AString & solutionBuildProject, + const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion, + const Array< VSProjectConfig > & configs, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders ); + +private: + void WriteHeader( const AString & solutionVisualStudioVersion, + const AString & solutionMinimumVisualStudioVersion ); + void WriteProjectListings( const AString& solutionBasePath, + const AString & solutionBuildProject, + const Array< VCXProjectNode * > & projects, + const Array< SLNSolutionFolder > & folders, + AString & solutionBuildProjectGuid, + Array< AString > & projectGuids, + Array< AString > & solutionProjectsToFolder ); + void WriteSolutionFolderListings( const Array< SLNSolutionFolder > & folders, + Array< AString > & solutionFolderPaths ); + void WriteSolutionConfigurationPlatforms( const Array< SolutionConfig > & solutionConfigs ); + void WriteProjectConfigurationPlatforms( const AString & solutionBuildProjectGuid, + const Array< SolutionConfig > & solutionConfigs, + const Array< AString > & projectGuids ); + void WriteNestedProjects( const Array< AString > & solutionProjectsToFolder, + const Array< AString > & solutionFolderPaths ); + void WriteFooter(); + + // Helper to format some text + void Write( const char * fmtString, ... ); + + // working buffer + AString m_Output; +}; + +//------------------------------------------------------------------------------ +#endif // FBUILD_HELPERS_SLNGENERATOR_H diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/ToolManifest.cpp b/Code/Tools/FBuild/FBuildCore/Helpers/ToolManifest.cpp index f8592576b..f6ac19798 100644 --- a/Code/Tools/FBuild/FBuildCore/Helpers/ToolManifest.cpp +++ b/Code/Tools/FBuild/FBuildCore/Helpers/ToolManifest.cpp @@ -477,14 +477,14 @@ bool ToolManifest::LoadFile( const AString & fileName, void * & content, uint32_ FileStream fs; if ( fs.Open( fileName.Get(), FileStream::READ_ONLY ) == false ) { - FLOG_ERROR( "Error opening file '%s' in Compiler ToolManifest\n", fileName.Get() ); + FLOG_ERROR( "Error: opening file '%s' in Compiler ToolManifest\n", fileName.Get() ); return false; } contentSize = (uint32_t)fs.GetFileSize(); AutoPtr< void > mem( ALLOC( contentSize ) ); if ( fs.Read( mem.Get(), contentSize ) != contentSize ) { - FLOG_ERROR( "Error reading file '%s' in Compiler ToolManifest\n", fileName.Get() ); + FLOG_ERROR( "Error: reading file '%s' in Compiler ToolManifest\n", fileName.Get() ); return false; } diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.cpp b/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.cpp index 495b9330b..a9cdb00f2 100644 --- a/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.cpp +++ b/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.cpp @@ -82,6 +82,13 @@ void VSProjectGenerator::AddFiles( const Array< AString > & files, bool filterBy } } +// GetDeterministicProjectGUID +//------------------------------------------------------------------------------ +/*static*/ void VSProjectGenerator::FormatDeterministicProjectGUID( AString & guid, const AString & projectName ) +{ + guid.Format( "{%08x-6c94-4f93-bc2a-7f5284b7d434}", CRC32::Calc( projectName ) ); +} + // GenerateVCXProj //------------------------------------------------------------------------------ const AString & VSProjectGenerator::GenerateVCXProj( const AString & projectFile, @@ -186,7 +193,7 @@ const AString & VSProjectGenerator::GenerateVCXProj( const AString & projectFile AStackString<> guid; if ( m_ProjectGuid.IsEmpty() ) { - guid.Format( "{%08x-6c94-4f93-bc2a-7f5284b7d434}", CRC32::Calc( m_ProjectName ) ); + FormatDeterministicProjectGUID( guid, m_ProjectName ); } else { diff --git a/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h b/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h index b07dbd4fc..1facf2b3d 100644 --- a/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h +++ b/Code/Tools/FBuild/FBuildCore/Helpers/VSProjectGenerator.h @@ -90,6 +90,8 @@ class VSProjectGenerator const Array< VSProjectFileType > & fileTypes ); const AString & GenerateVCXProjFilters( const AString & projectFile ); + static void FormatDeterministicProjectGUID( AString & guid, const AString & projectName ); + private: // Helper to format some text void Write( const char * fmtString, ... ); diff --git a/Code/Tools/FBuild/FBuildCore/Protocol/Client.cpp b/Code/Tools/FBuild/FBuildCore/Protocol/Client.cpp index bf59a02a1..4006d4650 100644 --- a/Code/Tools/FBuild/FBuildCore/Protocol/Client.cpp +++ b/Code/Tools/FBuild/FBuildCore/Protocol/Client.cpp @@ -731,6 +731,7 @@ const ToolManifest * Client::FindManifest( const ConnectionInfo * connection, ui Client::ServerState::ServerState() : m_Connection( nullptr ) , m_CurrentMessage( nullptr ) + , m_NumJobsAvailable( 0 ) , m_Jobs( 16, true ) , m_Blacklisted( false ) { diff --git a/Code/Tools/FBuild/FBuildCore/Protocol/Server.cpp b/Code/Tools/FBuild/FBuildCore/Protocol/Server.cpp index dfc00298b..9899762e0 100644 --- a/Code/Tools/FBuild/FBuildCore/Protocol/Server.cpp +++ b/Code/Tools/FBuild/FBuildCore/Protocol/Server.cpp @@ -16,6 +16,7 @@ #include "Core/FileIO/ConstMemoryStream.h" #include "Core/FileIO/MemoryStream.h" +#include "Core/Profile/Profile.h" #include "Core/Strings/AStackString.h" // Defines @@ -500,13 +501,13 @@ void Server::ThreadFunc() { while ( m_ShouldExit == false ) { - FindNeedyClients(); - FinalizeCompletedJobs(); + FindNeedyClients(); + SendServerStatus(); - Thread::Sleep( 16 ); + JobQueueRemote::Get().MainThreadWait( 100 ); } m_Exited = true; @@ -521,10 +522,17 @@ void Server::FindNeedyClients() return; } + PROFILE_FUNCTION + MutexHolder mh( m_ClientListMutex ); // determine job availability int availableJobs = (int)WorkerThreadRemote::GetNumCPUsToUse(); + if ( availableJobs == 0 ) + { + return; + } + ++availableJobs; // over request to parallelize building/network transfers ClientState ** iter = m_ClientList.Begin(); const ClientState * const * end = m_ClientList.End(); @@ -588,6 +596,8 @@ void Server::FindNeedyClients() //------------------------------------------------------------------------------ void Server::FinalizeCompletedJobs() { + PROFILE_FUNCTION + JobQueueRemote & jcr = JobQueueRemote::Get(); while ( Job * job = jcr.GetCompletedJob() ) { @@ -635,6 +645,8 @@ void Server::FinalizeCompletedJobs() //------------------------------------------------------------------------------ void Server::SendServerStatus() { + PROFILE_FUNCTION + MutexHolder mh( m_ClientListMutex ); const ClientState * const * end = m_ClientList.End(); diff --git a/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.cpp b/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.cpp index b1a1a5cf6..828b5a241 100644 --- a/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.cpp +++ b/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.cpp @@ -93,14 +93,47 @@ void JobQueueRemote::GetWorkerStatus( size_t index, AString & hostName, AString ( (WorkerThreadRemote *)m_Workers[ index ] )->GetStatus( hostName, status, isIdle ); } +// MainThreadWait +//------------------------------------------------------------------------------ +void JobQueueRemote::MainThreadWait( uint32_t timeoutMS ) +{ + PROFILE_SECTION( "MainThreadWait" ) + m_MainThreadSemaphore.Wait( timeoutMS ); +} + +// WakeMainThread +//------------------------------------------------------------------------------ +void JobQueueRemote::WakeMainThread() +{ + m_MainThreadSemaphore.Signal(); +} + +// WorkerThreadWait +//------------------------------------------------------------------------------ +void JobQueueRemote::WorkerThreadWait( uint32_t timeoutMS ) +{ + PROFILE_SECTION( "WorkerThreadWait" ) + m_WorkerThreadSemaphore.Wait( timeoutMS ); +} + +// WakeWorkers +//------------------------------------------------------------------------------ +void JobQueueRemote::WakeWorkers() +{ + m_WorkerThreadSemaphore.Signal(); +} + // QueueJob (Main Thread) //------------------------------------------------------------------------------ void JobQueueRemote::QueueJob( Job * job ) { - MutexHolder m( m_PendingJobsMutex ); - m_PendingJobs.Append( job ); -} + { + MutexHolder m( m_PendingJobsMutex ); + m_PendingJobs.Append( job ); + } + WakeWorkers(); +} // GetCompletedJob //------------------------------------------------------------------------------ @@ -184,6 +217,8 @@ void JobQueueRemote::CancelJobsWithUserData( void * userData ) //------------------------------------------------------------------------------ Job * JobQueueRemote::GetJobToProcess() { + WorkerThreadWait( 100 ); + MutexHolder m( m_PendingJobsMutex ); if ( m_PendingJobs.IsEmpty() ) { @@ -222,15 +257,19 @@ void JobQueueRemote::FinishedProcessingJob( Job * job, bool success ) } // push to appropriate completion queue - MutexHolder m( m_CompletedJobsMutex ); - if ( success ) - { - m_CompletedJobs.Append( job ); - } - else { - m_CompletedJobsFailed.Append( job ); + MutexHolder m( m_CompletedJobsMutex ); + if ( success ) + { + m_CompletedJobs.Append( job ); + } + else + { + m_CompletedJobsFailed.Append( job ); + } } + + WakeMainThread(); } // DoBuild diff --git a/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.h b/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.h index 1ecfe6bab..9c445807c 100644 --- a/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.h +++ b/Code/Tools/FBuild/FBuildCore/WorkerPool/JobQueueRemote.h @@ -11,6 +11,7 @@ #include "Tools/FBuild/FBuildCore/Graph/Node.h" #include "Core/Process/Mutex.h" +#include "Core/Process/Semaphore.h" // Forward Declarations //------------------------------------------------------------------------------ @@ -37,6 +38,12 @@ class JobQueueRemote : public Singleton< JobQueueRemote > inline size_t GetNumWorkers() const { return m_Workers.GetSize(); } void GetWorkerStatus( size_t index, AString & hostName, AString & status, bool & isIdle ) const; + + void MainThreadWait( uint32_t timeoutMS ); + void WakeMainThread(); + + void WorkerThreadWait( uint32_t timeoutMS ); + void WakeWorkers(); private: // worker threads call these friend class WorkerThread; @@ -56,6 +63,9 @@ class JobQueueRemote : public Singleton< JobQueueRemote > Array< Job * > m_CompletedJobs; Array< Job * > m_CompletedJobsFailed; + Semaphore m_MainThreadSemaphore; + Semaphore m_WorkerThreadSemaphore; + Array< WorkerThread * > m_Workers; }; diff --git a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.cpp b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.cpp index e9a353199..f69b6b25c 100644 --- a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.cpp +++ b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.cpp @@ -38,12 +38,20 @@ WorkerBrokerage::WorkerBrokerage() m_BrokerageRoot.Format( "%s/main/%u/", root.Get(), protocolVersion ); #endif } + + Network::GetHostName(m_HostName); + + AStackString<> filePath; + m_BrokerageFilePath.Format( "%s%s", m_BrokerageRoot.Get(), m_HostName.Get() ); + m_TimerLastUpdate.Start(); } // DESTRUCTOR //------------------------------------------------------------------------------ WorkerBrokerage::~WorkerBrokerage() { + // Ensure the file disapears when closing + FileIO::FileDelete( m_BrokerageFilePath.Get() ); } // FindWorkers @@ -72,10 +80,6 @@ void WorkerBrokerage::FindWorkers( Array< AString > & workerList ) workerList.SetCapacity( workerList.GetSize() + results.GetSize() ); } - // we'll filter our own host if found - AStackString<> hostName; - Network::GetHostName( hostName ); - // convert worker strings const AString * const end = results.End(); for ( AString * it = results.Begin(); it != end; ++it ) @@ -83,7 +87,7 @@ void WorkerBrokerage::FindWorkers( Array< AString > & workerList ) const AString & fileName = *it; const char * lastSlash = fileName.FindLast( NATIVE_SLASH ); AStackString<> workerName( lastSlash + 1 ); - if ( workerName.CompareI( hostName ) != 0 ) + if ( workerName.CompareI( m_HostName ) != 0 ) { workerList.Append( workerName ); } @@ -92,41 +96,45 @@ void WorkerBrokerage::FindWorkers( Array< AString > & workerList ) // SetAvailability //------------------------------------------------------------------------------ -void WorkerBrokerage::SetAvailability( bool available ) +void WorkerBrokerage::SetAvailability(bool available) { - // ignore if brokerage not configured - if ( m_BrokerageRoot.IsEmpty() ) - { - return; - } - - if ( m_Availability == available ) - { - return; // avoid doing network IO when nothing has changed - } - m_Availability = available; - - FileIO::EnsurePathExists( m_BrokerageRoot ); - - // Host Name - TODO:C Would it be better to use the IP here? - AStackString<> hostName; - Network::GetHostName( hostName ); - - // construct filename : "ip.username.fastbuild" - AStackString<> filePath; - filePath.Format( "%s\\%s", m_BrokerageRoot.Get(), hostName.Get() ); + // ignore if brokerage not configured + if ( m_BrokerageRoot.IsEmpty() ) + { + return; + } if ( available ) { - // create file to signify availability - FileStream fs; - fs.Open( filePath.Get(), FileStream::WRITE_ONLY ); + // Check the last update time to avoid too much File IO. + float elapsedTime = m_TimerLastUpdate.GetElapsedMS(); + if ( elapsedTime >= 10000.0f ) + { + // + // Ensure that the file will be recreated if cleanup is done on the brokerage path. + // + if ( !FileIO::FileExists( m_BrokerageFilePath.Get() ) ) + { + FileIO::EnsurePathExists( m_BrokerageRoot ); + + // create file to signify availability + FileStream fs; + fs.Open( m_BrokerageFilePath.Get(), FileStream::WRITE_ONLY ); + + // Restart the timer + m_TimerLastUpdate.Start(); + } + } } - else - { - // remove file to redact availability - FileIO::FileDelete( filePath.Get() ); + else if ( m_Availability != available ) + { + // remove file to remove availability + FileIO::FileDelete( m_BrokerageFilePath.Get() ); + + // Restart the timer + m_TimerLastUpdate.Start(); } + m_Availability = available; } //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.h b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.h index 6854dde27..ce37a3918 100644 --- a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.h +++ b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerBrokerage.h @@ -7,6 +7,7 @@ // Includes //------------------------------------------------------------------------------ #include "Core/Strings/AString.h" +#include "Core/Time/Timer.h" // Forward Declarations //------------------------------------------------------------------------------ @@ -27,6 +28,9 @@ class WorkerBrokerage private: AString m_BrokerageRoot; bool m_Availability; + AString m_HostName; + AString m_BrokerageFilePath; + Timer m_TimerLastUpdate; // Throttle network access }; //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerThreadRemote.cpp b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerThreadRemote.cpp index e1e175242..14d08a0f1 100644 --- a/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerThreadRemote.cpp +++ b/Code/Tools/FBuild/FBuildCore/WorkerPool/WorkerThreadRemote.cpp @@ -69,9 +69,6 @@ WorkerThreadRemote::~WorkerThreadRemote() // loop again to get another job continue; } - - // no work to do right now - Thread::Sleep( 16 ); // wait and try again later } m_Exited = true; diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestBFFParsing/struct_concatenation.bff b/Code/Tools/FBuild/FBuildTest/Data/TestBFFParsing/struct_concatenation.bff new file mode 100644 index 000000000..e59aab71c --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestBFFParsing/struct_concatenation.bff @@ -0,0 +1,92 @@ +// Both Empty +//-------------------------------------------------------------------- +Print( "---- Both Empty" ) +{ + .StructA = + [ + ] + .StructB = + [ + ] + .StructA + .StructB + Print( .StructA ) +} + +// Empty Left +//-------------------------------------------------------------------- +Print( "---- Empty Left" ) +{ + .StructA = + [ + ] + .StructB = + [ + .String = 'Hello' + .Int = 1 + .Bool = false + .ArrayOfStrings = { 'A', 'B' } + ] + .StructA + .StructB + Print( .StructA ) +} + +// Empty Right +//-------------------------------------------------------------------- +Print( "---- Empty Right" ) +{ + .StructA = + [ + .String = 'Hello' + .Int = 1 + .Bool = false + .ArrayOfStrings = { 'A', 'B' } + ] + .StructB = + [ + ] + .StructA + .StructB + Print( .StructA ) +} + +// Concat +//-------------------------------------------------------------------- +Print( "---- Concat" ) +{ + .StructA = + [ + .String = 'Hello' + .Int = 1 + .Bool = false + .ArrayOfStrings = { 'A', 'B' } + ] + .StructB = + [ + .String = ' There' + .Int = 2 + .Bool = true + .ArrayOfStrings = { 'C', 'D' } + ] + .StructA + .StructB + Print( .StructA ) +} + +// ArrayOfStructs Recursion +//-------------------------------------------------------------------- +Print( "---- ArrayOfStructs Recursion" ) +{ + .Struct1 = [ .String = '1' ] + .Struct2 = [ .String = '2' ] + .Struct3 = [ .String = '3' ] + .Struct4 = [ .String = '4' ] + .StructA = + [ + .ArrayOfStructs = { .Struct1, .Struct2 } + ] + .StructB = + [ + .ArrayOfStructs = { .Struct3, .Struct4 } + ] + .StructC = .StructA + + .StructB + Print( .StructC ) +} diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestCLR/clr.bff b/Code/Tools/FBuild/FBuildTest/Data/TestCLR/clr.bff index 43d7a26db..42e16b07c 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestCLR/clr.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestCLR/clr.bff @@ -2,25 +2,14 @@ ; Test the compilation of Managed C (/clr) code ; #include "..\testcommon.bff" - -Settings -{ - .Environment = { "PATH=$VisualStudioPath$\Common7\IDE\", - "TMP=C:\Windows\Temp", - "SystemRoot=C:\Windows" } - .CachePath = "C:\.fbuild.cache" -} - -.Librarian = "$VisualStudioPath$\VC\bin\lib.exe" -.Compiler = "$VisualStudioPath$\VC\bin\cl.exe" -.CompilerOptions = '/WX /nologo %1 /Fo%2 /c /Z7 /I"./" /clr' -.LibrarianOptions = "/NODEFAULTLIB /WX /NOLOGO /OUT:%2 %1" -.Linker = "$VisualStudioPath$\VC\bin\link.exe" +Using( .StandardEnvironment ) +Settings {} // Activate standard settings .Out = "..\..\..\..\ftmp" Library( "CLR-Target" ) { + .CompilerOptions + ' /clr' .CompilerInputFiles = "Data/TestCLR/a.cpp" .CompilerOutputPath = "$Out$/Test/CLR/" .LibrarianOutput = "$Out$/Test/CLR/clr.lib" @@ -28,6 +17,7 @@ Library( "CLR-Target" ) Library( "CLR-Parallel-Target" ) { + .CompilerOptions + ' /clr' .CompilerInputPath = "Data/TestCLR/" .CompilerOutputPath = "$Out$/Test/CLR/" .LibrarianOutput = "$Out$/Test/CLR/clrmulti.lib" @@ -38,16 +28,14 @@ Library( "CLR-Parallel-Target" ) // Library( 'BrigeTest-CLR' ) { + .CompilerOptions + ' /clr' .CompilerInputFiles = { 'Data/TestCLR/a.cpp', 'Data/TestCLR/b.cpp' } .CompilerOutputPath = '$Out$/Test/CLR/Bridge/' .LibrarianOutput = '$Out$/Test/CLR/Bridge/clr.lib' } -Library( 'BridgeTest-CPP' ) +ObjectList( 'BridgeTest-CPP' ) { - // regular C++ - .CompilerOptions = '/WX /nologo %1 /Fo%2 /c /Z7 /I"./"' - .CompilerInputFiles = 'Data/TestCLR/exe.cxx' .CompilerOutputPath = '$Out$/Test/CLR/Bridge/' .LibrarianOutput = '$Out$/Test/CLR/Bridge/exe.lib' @@ -55,11 +43,12 @@ Library( 'BridgeTest-CPP' ) Executable( 'BridgeTest-Exe' ) { - .LinkerOptions = "/NOLOGO /OUT:%2 %1 /MACHINE:X86" - + " /IGNORE:4001" ; don't complain about linking libs only - + " /SUBSYSTEM:CONSOLE" + .LinkerOptions + ' /SUBSYSTEM:CONSOLE' + ' /ENTRY:ExeMain' - + ' MSVCRT.lib' + + ' kernel32.lib' + + ' MSCOREE.lib' + + ' msvcmrt.lib' + + ' msvcrt.LIB' + ' /LIBPATH:"$WindowsSDK$\Lib\winv6.3\um\x86"' + ' /LIBPATH:"$VisualStudioPath$\VC\lib"' diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/Plugin.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/Plugin.cpp index c293b5249..3d7eeee9f 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/Plugin.cpp +++ b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/Plugin.cpp @@ -12,7 +12,7 @@ // CacheInit //------------------------------------------------------------------------------ -bool CacheInit( const char * settings ) +bool __stdcall CacheInit( const char * settings ) { printf( "Init : %s\n", settings ); return true; @@ -20,14 +20,14 @@ bool CacheInit( const char * settings ) // CacheShutdown //------------------------------------------------------------------------------ -void CacheShutdown() +void __stdcall CacheShutdown() { printf( "Shutdown\n" ); } // CachePublish //------------------------------------------------------------------------------ -bool CachePublish( const char * cacheId, const void * data, unsigned long long dataSize ) +bool __stdcall CachePublish( const char * cacheId, const void * data, unsigned long long dataSize ) { printf( "Publish : %s, %p, %llu\n", cacheId, data, dataSize ); return true; @@ -35,7 +35,7 @@ bool CachePublish( const char * cacheId, const void * data, unsigned long long d // CacheRetrieve //------------------------------------------------------------------------------ -bool CacheRetrieve( const char * cacheId, void * & data, unsigned long long & dataSize ) +bool __stdcall CacheRetrieve( const char * cacheId, void * & data, unsigned long long & dataSize ) { (void)data; (void)dataSize; @@ -45,7 +45,7 @@ bool CacheRetrieve( const char * cacheId, void * & data, unsigned long long & da // CacheFreeMemory //------------------------------------------------------------------------------ -void CacheFreeMemory( void * data, unsigned long long dataSize ) +void __stdcall CacheFreeMemory( void * data, unsigned long long dataSize ) { printf( "FreeMemory : %p, %llu\n", data, dataSize ); } diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/buildplugin.bff b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/buildplugin.bff index 3c2b16615..6b393b54e 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/buildplugin.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/buildplugin.bff @@ -2,32 +2,13 @@ // Build an external cache plugin // //------------------------------------------------------------------------------ - - -// Toolchain Root -//------------------------------------------------------------------------------ -.VSBasePath = '../../../../External/SDK/VS12.0' -.WindowsSDKBasePath = '../../../../External/SDK/Windows8.1' - -// General Setting -//------------------------------------------------------------------------------ -Settings -{ - .Environment = { "PATH=$VSBasePath$/Common7/IDE/;$VSBasePath$/VC/bin/", - "TMP=C:\Windows\Temp", - "SystemRoot=C:\Windows" } -} +#include "..\testcommon.bff" +Using( .StandardEnvironment ) +Settings {} // Activate standard settings // Toolchain //------------------------------------------------------------------------------ -.Compiler = "$VSBasePath$\VC\bin\x86_amd64\cl.exe" -.Linker = "$VSBasePath$\VC\bin\x86_amd64\link.exe" - -// Toolchain Options -//------------------------------------------------------------------------------ -.CompilerOptions = '"%1" /Fo"%2" /c /Z7 /WX /nologo /I"./" /MT /Od' - + ' /I"$VSBasePath$/VC/include"' -.LinkerOptions = '/OUT:"%2" "%1" /NOLOGO /WX /NODEFAULTLIB /DEBUG /PDB:"%2.pdb"' +.Compiler = .CompilerX64 // Build for X64 so we can load the DLL // Base compilation output //------------------------------------------------------------------------------ @@ -37,6 +18,7 @@ Settings //------------------------------------------------------------------------------ ObjectList( "CachePlugin-Lib" ) { + .CompilerOptions + .VisualStudioIncludePaths .CompilerInputFiles = "Data/TestCachePlugin/Plugin.cpp" .CompilerOutputPath = "$Out$/" } @@ -49,8 +31,8 @@ DLL( 'Plugin-DLL' ) + ' LIBCMT.lib' + ' OLDNAMES.LIB' + ' kernel32.lib' - + ' /LIBPATH:"$VSBasePath$\VC\lib\amd64"' - + ' /LIBPATH:"$WindowsSDKBasePath$\Lib\winv6.3\um\x64"' + + ' /LIBPATH:"$VisualStudioPath$\VC\lib\amd64"' + + ' /LIBPATH:"$WindowsSDK$\Lib\winv6.3\um\x64"' .LinkerOutput = '$Out$/CachePlugin.dll' .Libraries = { 'CachePlugin-Lib' } diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/useplugin.bff b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/useplugin.bff index b95b833bb..bd1763f31 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/useplugin.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestCachePlugin/useplugin.bff @@ -1,31 +1,12 @@ // -// Build an external cache plugin +// Use the previously built cache plugin // //------------------------------------------------------------------------------ - - -// Toolchain Root -//------------------------------------------------------------------------------ -.VSBasePath = '../../../../External/SDK/VS12.0' - -// General Setting -//------------------------------------------------------------------------------ -Settings -{ - .Environment = { 'PATH=$VSBasePath$/Common7/IDE/;$VSBasePath$/VC/bin/', - 'TMP=C:\Windows\Temp', - 'SystemRoot=C:\Windows' } - .CachePluginDLL = '../../../../ftmp/Test/CachePlugin/CachePlugin.dll' - .CachePath = 'X:\' // passed to cache plugin -} - -// Toolchain -//------------------------------------------------------------------------------ -.Compiler = '$VSBasePath$\VC\bin\x86_amd64\cl.exe' - -// Toolchain Options -//------------------------------------------------------------------------------ -.CompilerOptions = '"%1" /Fo"%2" /c /Z7 /WX /nologo /I"./" /MT' +#include "..\testcommon.bff" +Using( .StandardEnvironment ) +.CachePluginDLL = '../../../../ftmp/Test/CachePlugin/CachePlugin.dll' +.CachePath = 'X:\' // passed to cache plugin +Settings {} // Activate standard settings // Base compilation output //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestDLL/fbuild.bff b/Code/Tools/FBuild/FBuildTest/Data/TestDLL/fbuild.bff index 568436d2e..664b81b14 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestDLL/fbuild.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestDLL/fbuild.bff @@ -1,24 +1,15 @@ - +// DLL +//------------------------------------------------------------------------------ #include "..\testcommon.bff" +Using( .StandardEnvironment ) +Settings {} // Activate standard settings -Settings -{ - .Environment = { "PATH=$VisualStudioPath$\Common7\IDE\", - "TMP=C:\Windows\Temp", - "SystemRoot=C:\Windows" } -} - -.Compiler = "$VisualStudioPath$\VC\bin\cl.exe" -.Librarian = "$VisualStudioPath$\VC\bin\lib.exe" -.Linker = "$VisualStudioPath$\VC\bin\link.exe" -.CompilerOptions = "%1 /Fo%2 /nologo /c /DLL" -.LibrarianOptions = "/NOLOGO /OUT:%2 %1" -.LinkerOptions = "/NOLOGO /DLL /OUT:%2 %1 /MACHINE:X86 /WX" - + " /IGNORE:4001" ; don't complain about linking libs only - + ' /NODEFAULTLIB' +// DLL Settings +.CompilerOptions + ' /DLL' +.LinkerOptions + ' /DLL' + ' /ENTRY:DllMain' -.Out = "..\..\..\..\ftmp" +.Out = "..\..\..\..\ftmp" .CompilerOutputPath = "$Out$\Test\DLL\" // One DLL @@ -69,7 +60,7 @@ Library( "DLLPCHTest" ) { .PCHInputFile = "Data\TestDLL\PrecompiledHeader.cpp" .PCHOutputFile = "$Out$\Test\DLL\PrecompiledHeader.pch" - .PCHOptions = '%1 /Fo"%3" /Yc"PrecompiledHeader.h" /Fp"%2" /nologo /c' + .PCHOptions = '%1 /Fo"%3" /Yc"PrecompiledHeader.h" /Z7 /Fp"%2" /nologo /c' .LibrarianOutput = "$Out$\Test\DLL\contentsPCH.lib" .CompilerInputFiles = "Data\TestDLL\a.cpp" diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestDistributed/fbuild.bff b/Code/Tools/FBuild/FBuildTest/Data/TestDistributed/fbuild.bff index b67ba8b24..8baa3bc0c 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestDistributed/fbuild.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestDistributed/fbuild.bff @@ -79,7 +79,7 @@ Library( "forceinclude" ) // Make sure we link to check that debug info is correctly retained ObjectList( 'remoteZiLib' ) { - .CompilerOptions = "%1 /Fo%2 /c /Zi /Od /nologo" + .CompilerOptions = "%1 /Fo%2 /c /Zi /Od /nologo /Fd$Out$\Test\Distributed\Zi\RemoteZi_Lib.pdb" .CompilerInputPath = "Data\TestDistributed\Zi" .CompilerOutputPath = "$Out$\Test\Distributed\Zi\" } @@ -88,5 +88,7 @@ DLL( 'remoteZi' ) .Libraries = 'remoteZiLib' .LinkerOptions + ' /DLL' + ' /ENTRY:DllMain' + + ' /DEBUG' + + ' /PDB:$Out$\Test\Distributed\Zi\RemoteZi.pdb' .LinkerOutput = '$Out$\Test\Distributed\Zi\RemoteZi.dll' } diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestGraph/DeepGraph.bff b/Code/Tools/FBuild/FBuildTest/Data/TestGraph/DeepGraph.bff index bb98fba28..d90a4d50b 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/TestGraph/DeepGraph.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/TestGraph/DeepGraph.bff @@ -23,11 +23,11 @@ Using( .VisualStudioOptionsDebug ) .CompilerOptions + ' /I"../../.."' + .Defines + .WindowsIncludePaths - + .VisualStudionIncludePaths + + .VisualStudioIncludePaths // Common options //------------------------------------------------------------------------------ -.CompilerOutputPath = '../ftmp/Test/TestGraph/DeepGraph' +.CompilerOutputPath = '../../../../ftmp/Test/TestGraph/DeepGraph' .PreBuildDependencies = {} // Create a node which depends on all the other nodes before it diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestIncludeParser/MSVC-P/fbuild.bff b/Code/Tools/FBuild/FBuildTest/Data/TestIncludeParser/MSVC-P/fbuild.bff new file mode 100644 index 000000000..a7f45ac07 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestIncludeParser/MSVC-P/fbuild.bff @@ -0,0 +1,22 @@ +// Test MSVC -P option +//------------------------------------------------------------------------------ +#include "../../testcommon.bff" +Using( .StandardEnvironment ) +Settings {} // Activate standard settings + +// MSVC /P (-P) option +//------------------------------------------------------------------------------ +ObjectList( 'MSVC-P' ) +{ + // MSVC option to write preprocessed output to file + .CompilerOptions + ' /P' + + .CompilerInputFiles = { 'Data/TestIncludeParser/MSVC-P/test.cpp' } + .CompilerOutputPath = "../../../../ftmp/Test/IncludeParser/MSVC-P/" + + // USe the /Fi option to make sure the Compiler and FASTBuild agree on the output location + .CompilerOptions + ' /Fi"%2"' + .CompilerOutputExtension= '.i' +} + +//------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestIncludeParser/MSVC-P/test.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestIncludeParser/MSVC-P/test.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/fbuild.bff b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/fbuild.bff new file mode 100644 index 000000000..186d5a6c3 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/fbuild.bff @@ -0,0 +1,39 @@ +// +// ObjectList - Exclusions +// +#include "../../testcommon.bff" +Using( .StandardEnvironment ) +Settings {} + +// Common settings +.Out = "../../../../ftmp" +.CompilerInputPath = "Data/TestObjectList/Exclusions/" +.UnityOutputPath = '$Out$/Test/ObjectList/Exclusions/' +.CompilerOutputPath = '$Out$/Test/ObjectList/Exclusions/' + +// +// Exclude by file name +//------------------------------------------------------------------------------ +ObjectList( 'ExcludeFileName' ) +{ + .CompilerInputExcludedFiles = 'ignore.cpp' + .CompilerOutputPath + 'ExcludeFileName/' +} + +// +// Exclude by file path +//------------------------------------------------------------------------------ +ObjectList( 'ExcludeFilePath' ) +{ + .CompilerInputExcludedFiles = 'Exclusions/ignore.cpp' + .CompilerOutputPath + 'ExcludeFilePath/' +} + +// +// Exclude by file path (relative) +//------------------------------------------------------------------------------ +ObjectList( 'ExcludeFilePathRelative' ) +{ + .CompilerInputExcludedFiles = "../FBuildTest/Data/TestObjectList/Exclusions/ignore.cpp" + .CompilerOutputPath + 'ExcludeFilePathRelative/' +} diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ignore.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ignore.cpp new file mode 100644 index 000000000..b82b1b6e3 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ignore.cpp @@ -0,0 +1,2 @@ + +#error This file should not be compiled diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ok.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ok.cpp new file mode 100644 index 000000000..31678b63e --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestObjectList/Exclusions/ok.cpp @@ -0,0 +1,4 @@ +// simple valid file +void Function() +{ +} \ No newline at end of file diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/fbuild.bff b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/fbuild.bff new file mode 100644 index 000000000..82bb5a9d3 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/fbuild.bff @@ -0,0 +1,54 @@ +// +// Unity - Exclusions +// +#include "../../testcommon.bff" +Using( .StandardEnvironment ) +Settings {} + +// Common settings +.Out = "../../../../ftmp" +.UnityInputPath = "Data/TestUnity/Exclusions/" +.UnityOutputPath = '$Out$/Test/Unity/Exclusions/' +.CompilerOutputPath = '$Out$/Test/Unity/Exclusions/' + +// +// Exclude by file name +//------------------------------------------------------------------------------ +Unity( 'ExcludeFileName-Unity' ) +{ + .UnityInputExcludedFiles = "ignore.cpp" + .UnityOutputPath + 'ExcludeFileName/' +} +ObjectList( 'ExcludeFileName' ) +{ + .CompilerInputUnity = 'ExcludeFileName-Unity' + .CompilerOutputPath + 'ExcludeFileName/' +} + +// +// Exclude by file path +//------------------------------------------------------------------------------ +Unity( 'ExcludeFilePath-Unity' ) +{ + .UnityInputExcludedFiles = "Exclusions/ignore.cpp" + .UnityOutputPath + 'ExcludeFilePath/' +} +ObjectList( 'ExcludeFilePath' ) +{ + .CompilerInputUnity = 'ExcludeFilePath-Unity' + .CompilerOutputPath + 'ExcludeFilePath/' +} + +// +// Exclude by file path (relative) +//------------------------------------------------------------------------------ +Unity( 'ExcludeFilePathRelative-Unity' ) +{ + .UnityInputExcludedFiles = "../FBuildTest/Data/TestUnity/Exclusions/ignore.cpp" + .UnityOutputPath + 'ExcludeFilePathRelative/' +} +ObjectList( 'ExcludeFilePathRelative' ) +{ + .CompilerInputUnity = 'ExcludeFilePathRelative-Unity' + .CompilerOutputPath + 'ExcludeFilePathRelative/' +} diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ignore.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ignore.cpp new file mode 100644 index 000000000..b82b1b6e3 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ignore.cpp @@ -0,0 +1,2 @@ + +#error This file should not be compiled diff --git a/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ok.cpp b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ok.cpp new file mode 100644 index 000000000..31678b63e --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Data/TestUnity/Exclusions/ok.cpp @@ -0,0 +1,4 @@ +// simple valid file +void Function() +{ +} \ No newline at end of file diff --git a/Code/Tools/FBuild/FBuildTest/Data/testcommon.bff b/Code/Tools/FBuild/FBuildTest/Data/testcommon.bff index fea823040..100fb93e9 100644 --- a/Code/Tools/FBuild/FBuildTest/Data/testcommon.bff +++ b/Code/Tools/FBuild/FBuildTest/Data/testcommon.bff @@ -11,6 +11,7 @@ .VisualStudioToolChain = [ .Compiler = '$VisualStudioPath$\VC\bin\cl.exe' + .CompilerX64 = '$VisualStudioPath$\VC\bin\x86_amd64\cl.exe' .Librarian = '$VisualStudioPath$\VC\bin\lib.exe' .Linker = '$VisualStudioPath$\VC\bin\link.exe' @@ -29,7 +30,7 @@ [ .Defines = ' /DDEBUG /D_DEBUG' ] -.VisualStudionIncludePaths = ' /I"$VisualStudioPath$\VC\include"' +.VisualStudioIncludePaths = ' /I"$VisualStudioPath$\VC\include"' //============================================================================== // WindowsSDK diff --git a/Code/Tools/FBuild/FBuildTest/FBuildTest.bff b/Code/Tools/FBuild/FBuildTest/FBuildTest.bff index b734c80ef..fad603cb1 100644 --- a/Code/Tools/FBuild/FBuildTest/FBuildTest.bff +++ b/Code/Tools/FBuild/FBuildTest/FBuildTest.bff @@ -8,11 +8,12 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' - .LocalDebuggerCommand = '^$(SolutionDir)..\..\..\tmp\^$(Configuration)\Tools\FBuild\FBuildTest\FBuildTest.exe' + .LocalDebuggerCommand = '^$(SolutionDir)..\^$(Configuration)\Tools\FBuild\FBuildTest\FBuildTest.exe' + .LocalDebuggerWorkingDirectory = '^$(SolutionDir)..\..\Code\Tools\FBuild\FBuildTest' } // Unity diff --git a/Code/Tools/FBuild/FBuildTest/TestMain.cpp b/Code/Tools/FBuild/FBuildTest/TestMain.cpp index f0434d3a1..4555316f0 100644 --- a/Code/Tools/FBuild/FBuildTest/TestMain.cpp +++ b/Code/Tools/FBuild/FBuildTest/TestMain.cpp @@ -31,6 +31,7 @@ int main(int , char * []) REGISTER_TESTGROUP( TestGraph ) REGISTER_TESTGROUP( TestIncludeParser ) #if defined( __WINDOWS__ ) + REGISTER_TESTGROUP( TestObjectList ) // TODO:LINUX TODO:MAC Enable REGISTER_TESTGROUP( TestPrecompiledHeaders ) // TODO:LINUX TODO:MAC Enable #endif REGISTER_TESTGROUP( TestProjectGeneration ) diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestBFFParsing.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestBFFParsing.cpp index 8514aad5d..defcfe9d5 100644 --- a/Code/Tools/FBuild/FBuildTest/Tests/TestBFFParsing.cpp +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestBFFParsing.cpp @@ -30,6 +30,7 @@ class TestBFFParsing : public UnitTest void IncludeDirective() const; void OnceDirective() const; void Structs() const; + void Struct_Concatenation() const; void Struct_Unterminated() const; void IncludeScope() const; void IfDirective() const; @@ -52,6 +53,7 @@ REGISTER_TESTS_BEGIN( TestBFFParsing ) REGISTER_TEST( IncludeDirective ) REGISTER_TEST( OnceDirective ) REGISTER_TEST( Structs ) + REGISTER_TEST( Struct_Concatenation ) REGISTER_TEST( Struct_Unterminated ) REGISTER_TEST( IncludeScope ) REGISTER_TEST( IfDirective ) @@ -147,6 +149,13 @@ void TestBFFParsing::Structs() const Parse( "Data/TestBFFParsing/structs.bff" ); } +// Struct_Concatenation +//------------------------------------------------------------------------------ +void TestBFFParsing::Struct_Concatenation() const +{ + Parse( "Data/TestBFFParsing/struct_concatenation.bff" ); +} + // Struct_Unterminated //------------------------------------------------------------------------------ void TestBFFParsing::Struct_Unterminated() const diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestBuildAndLinkLibrary.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestBuildAndLinkLibrary.cpp index 9ce11eda3..5cfa5ce01 100644 --- a/Code/Tools/FBuild/FBuildTest/Tests/TestBuildAndLinkLibrary.cpp +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestBuildAndLinkLibrary.cpp @@ -60,9 +60,15 @@ void TestBuildAndLinkLibrary::TestBuildLib() const fBuild.Initialize(); const AStackString<> lib( "../../../../ftmp/Test/BuildAndLinkLibrary/test.lib" ); - const AStackString<> obj1( "../../../../ftmp/Test/BuildAndLinkLibrary/a.obj" ); - const AStackString<> obj2( "../../../../ftmp/Test/BuildAndLinkLibrary/b.obj" ); - const AStackString<> obj3( "../../../../ftmp/Test/BuildAndLinkLibrary/c.obj" ); + #if defined( __WINDOWS__ ) + const AStackString<> obj1( "../../../../ftmp/Test/BuildAndLinkLibrary/a.obj" ); + const AStackString<> obj2( "../../../../ftmp/Test/BuildAndLinkLibrary/b.obj" ); + const AStackString<> obj3( "../../../../ftmp/Test/BuildAndLinkLibrary/c.obj" ); + #else + const AStackString<> obj1( "../../../../ftmp/Test/BuildAndLinkLibrary/a.o" ); + const AStackString<> obj2( "../../../../ftmp/Test/BuildAndLinkLibrary/b.o" ); + const AStackString<> obj3( "../../../../ftmp/Test/BuildAndLinkLibrary/c.o" ); + #endif // clean up anything left over from previous runs EnsureFileDoesNotExist( lib ); diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestIncludeParser.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestIncludeParser.cpp index b37b6806b..6a8bad7c4 100644 --- a/Code/Tools/FBuild/FBuildTest/Tests/TestIncludeParser.cpp +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestIncludeParser.cpp @@ -3,7 +3,7 @@ // Includes //------------------------------------------------------------------------------ -#include "TestFramework/UnitTest.h" +#include "FBuildTest.h" #include "Tools/FBuild/FBuildCore/FBuild.h" #include "Tools/FBuild/FBuildCore/Helpers/CIncludeParser.h" @@ -17,13 +17,14 @@ // TestIncludeParser //------------------------------------------------------------------------------ -class TestIncludeParser : public UnitTest +class TestIncludeParser : public FBuildTest { private: DECLARE_TESTS void TestMSVCPreprocessedOutput() const; void TestMSVCShowIncludesOutput() const; + void TestMSVC_P() const; void TestGCCPreprocessedOutput() const; void TestClangPreprocessedOutput() const; void TestClangMSExtensionsPreprocessedOutput() const; @@ -36,6 +37,7 @@ REGISTER_TESTS_BEGIN( TestIncludeParser ) #if defined( __WINDOWS__ ) REGISTER_TEST( TestMSVCPreprocessedOutput ); REGISTER_TEST( TestMSVCShowIncludesOutput ); + REGISTER_TEST( TestMSVC_P ); #endif REGISTER_TEST( TestGCCPreprocessedOutput ); REGISTER_TEST( TestClangPreprocessedOutput ); @@ -105,6 +107,37 @@ void TestIncludeParser::TestMSVCShowIncludesOutput() const OUTPUT( "MSVC /showincludes : %2.3fs (%2.1f MiB/sec)\n", time, ( (float)( fileSize * repeatCount / ( 1024.0f * 1024.0f ) ) / time ) ); } +// TestMSVC_P +//------------------------------------------------------------------------------ +void TestIncludeParser::TestMSVC_P() const +{ + FBuildOptions options; + options.m_ShowSummary = true; // required to generate stats for node count checks + options.m_ConfigFile = "Data/TestIncludeParser/MSVC-P/fbuild.bff"; + + FBuild fBuild( options ); + fBuild.Initialize(); + + const AStackString<> file( "../../../../ftmp/Test/IncludeParser/MSVC-P/test.i" ); + + // clean up anything left over from previous runs + EnsureFileDoesNotExist( file ); + + // Build + TEST_ASSERT( fBuild.Build( AStackString<>( "MSVC-P" ) ) ); + + // make sure all output files are as expected + EnsureFileExists( file ); + + // Check stats + // Seen, Built, Type + CheckStatsNode ( 1, 1, Node::OBJECT_LIST_NODE ); + CheckStatsNode ( 1, 1, Node::FILE_NODE ); + CheckStatsNode ( 1, 1, Node::COMPILER_NODE ); + CheckStatsNode ( 1, 1, Node::OBJECT_NODE ); + CheckStatsTotal( 4, 4 ); +} + // TestGCCPreprocessedOutput //------------------------------------------------------------------------------ void TestIncludeParser::TestGCCPreprocessedOutput() const diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestObjectList.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestObjectList.cpp new file mode 100644 index 000000000..320549f41 --- /dev/null +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestObjectList.cpp @@ -0,0 +1,58 @@ +// TestObjectList.cpp +//------------------------------------------------------------------------------ + +// Includes +//------------------------------------------------------------------------------ +#include "FBuildTest.h" + +#include "Tools/FBuild/FBuildCore/FBuild.h" +#include "Tools/FBuild/FBuildCore/BFF/BFFParser.h" +#include "Core/Strings/AStackString.h" + +// TestObjectList +//------------------------------------------------------------------------------ +class TestObjectList : public FBuildTest +{ +private: + DECLARE_TESTS + + // Tests + void TestExcludedFiles() const; +}; + +// Register Tests +//------------------------------------------------------------------------------ +REGISTER_TESTS_BEGIN( TestObjectList ) + REGISTER_TEST( TestExcludedFiles ) // Ensure files are correctly excluded +REGISTER_TESTS_END + +// TestExcludedFiles +//------------------------------------------------------------------------------ +void TestObjectList::TestExcludedFiles() const +{ + FBuildOptions options; + options.m_ConfigFile = "Data/TestObjectList/Exclusions/fbuild.bff"; + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFileName" ) ) ); + } + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFilePath" ) ) ); + } + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFilePathRelative" ) ) ); + } +} + +//------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestPrecompiledHeaders.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestPrecompiledHeaders.cpp index 1a8d0c5f5..1917b8b67 100644 --- a/Code/Tools/FBuild/FBuildTest/Tests/TestPrecompiledHeaders.cpp +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestPrecompiledHeaders.cpp @@ -73,7 +73,11 @@ void TestPrecompiledHeaders::TestPCH() const options.m_UseCacheWrite = true; options.m_ShowSummary = true; // required to generate stats for node count checks - AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.obj" ); + #if defined( __WINDOWS__ ) + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.obj" ); + #else + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.o" ); + #endif AStackString<> pch( "../../../../ftmp/Test/PrecompiledHeaders/PrecompiledHeader.pch" ); AStackString<> lib( "../../../../ftmp/Test/PrecompiledHeaders/TestPCH.lib" ); EnsureFileDoesNotExist( obj ); @@ -128,7 +132,11 @@ void TestPrecompiledHeaders::TestPCHWithCache() const options.m_UseCacheRead = true; options.m_ShowSummary = true; // required to generate stats for node count checks - AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.obj" ); + #if defined( __WINDOWS__ ) + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.obj" ); + #else + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/PCHUser.o" ); + #endif AStackString<> pch( "../../../../ftmp/Test/PrecompiledHeaders/PrecompiledHeader.pch" ); AStackString<> lib( "../../../../ftmp/Test/PrecompiledHeaders/TestPCH.lib" ); EnsureFileDoesNotExist( obj ); @@ -184,7 +192,11 @@ void TestPrecompiledHeaders::TestPCHClang() const options.m_UseCacheWrite = true; options.m_ShowSummary = true; // required to generate stats for node count checks - AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/Clang/PCHUser.obj" ); + #if defined( __WINDOWS__ ) + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/Clang/PCHUser.obj" ); + #else + AStackString<> obj( "../../../../ftmp/Test/PrecompiledHeaders/Clang/PCHUser.o" ); + #endif AStackString<> pch( "../../../../ftmp/Test/PrecompiledHeaders/Clang/PrecompiledHeader.pch" ); EnsureFileDoesNotExist( obj ); EnsureFileDoesNotExist( pch ); diff --git a/Code/Tools/FBuild/FBuildTest/Tests/TestUnity.cpp b/Code/Tools/FBuild/FBuildTest/Tests/TestUnity.cpp index 936711584..d687f79d7 100644 --- a/Code/Tools/FBuild/FBuildTest/Tests/TestUnity.cpp +++ b/Code/Tools/FBuild/FBuildTest/Tests/TestUnity.cpp @@ -33,6 +33,7 @@ class TestUnity : public FBuildTest void TestCompile() const; void TestCompile_NoRebuild() const; void TestGenerateFromExplicitList() const; + void TestExcludedFiles() const; }; // Register Tests @@ -43,6 +44,7 @@ REGISTER_TESTS_BEGIN( TestUnity ) REGISTER_TEST( TestCompile ) // compile a library using unity inputs REGISTER_TEST( TestCompile_NoRebuild ) // check nothing rebuilds REGISTER_TEST( TestGenerateFromExplicitList ) // create a unity with manually provided files + REGISTER_TEST( TestExcludedFiles ) // Ensure files are correctly excluded REGISTER_TESTS_END // BuildGenerate @@ -197,4 +199,33 @@ void TestUnity::TestGenerateFromExplicitList() const CheckStatsTotal( 4, 4 ); } +// TestExcludedFiles +//------------------------------------------------------------------------------ +void TestUnity::TestExcludedFiles() const +{ + FBuildOptions options; + options.m_ConfigFile = "Data/TestUnity/Exclusions/fbuild.bff"; + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFileName" ) ) ); + } + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFilePath" ) ) ); + } + + { + FBuild fBuild( options ); + TEST_ASSERT( fBuild.Initialize() ); + + TEST_ASSERT( fBuild.Build( AStackString<>( "ExcludeFilePathRelative" ) ) ); + } +} + //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/FBuildWorker/FBuildWorker.bff b/Code/Tools/FBuild/FBuildWorker/FBuildWorker.bff index d4455ba96..c06f62025 100644 --- a/Code/Tools/FBuild/FBuildWorker/FBuildWorker.bff +++ b/Code/Tools/FBuild/FBuildWorker/FBuildWorker.bff @@ -8,11 +8,11 @@ //-------------------------------------------------------------------------- VCXProject( '$ProjectName$-proj' ) { - .ProjectOutput = '$ProjectPath$\$ProjectName$.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/$ProjectName$.vcxproj' .ProjectInputPaths = '$ProjectPath$\' .ProjectBasePath = '$ProjectPath$\' - .LocalDebuggerCommand = '^$(SolutionDir)..\..\..\tmp\^$(Configuration)\Tools\FBuild\FBuildWorker\FBuildWorker.exe' + .LocalDebuggerCommand = '^$(SolutionDir)..\^$(Configuration)\Tools\FBuild\FBuildWorker\FBuildWorker.exe' } // Unity diff --git a/Code/Tools/FBuild/FBuildWorker/Worker/Worker.cpp b/Code/Tools/FBuild/FBuildWorker/Worker/Worker.cpp index 25257f4d9..9e6f0ff59 100644 --- a/Code/Tools/FBuild/FBuildWorker/Worker/Worker.cpp +++ b/Code/Tools/FBuild/FBuildWorker/Worker/Worker.cpp @@ -24,6 +24,9 @@ #include "Core/Profile/Profile.h" #include "Core/Strings/AStackString.h" +// system +#include + // CONSTRUCTOR //------------------------------------------------------------------------------ Worker::Worker( void * hInstance, const AString & args ) @@ -33,6 +36,9 @@ Worker::Worker( void * hInstance, const AString & args ) , m_BaseArgs( args ) , m_LastWriteTime( 0 ) , m_RestartNeeded( false ) + #if defined( __WINDOWS__ ) + , m_LastDiskSpaceResult( -1 ) + #endif { m_WorkerSettings = FNEW( WorkerSettings ); m_NetworkStartupHelper = FNEW( NetworkStartupHelper ); @@ -129,10 +135,85 @@ int Worker::Work() return 0; } +// HasEnoughDiskSpace +//------------------------------------------------------------------------------ +bool Worker::HasEnoughDiskSpace() +{ + #if defined( __WINDOWS__ ) + // Only check disk space every few seconds + float elapsedTime = m_TimerLastDiskSpaceCheck.GetElapsedMS(); + if ( ( elapsedTime < 15000.0f ) && ( m_LastDiskSpaceResult != -1 ) ) + { + return ( m_LastDiskSpaceResult != 0 ); + } + m_TimerLastDiskSpaceCheck.Start(); + + static const uint64_t MIN_DISK_SPACE = 1024 * 1024 * 1024; // 1 GiB + + DWORD logicalDrives = GetLogicalDrives(); + DWORD driveMask = 1; + char driveLetter = 'a'; + char drivePath[32]; + int validDriveCount = 0; // This is to insure we find at least one drive... + + // Enumerate all drive letters. + for ( uint32_t i = 0; i < 26; ++i, ++driveLetter, driveMask *= 2 ) + { + if ( (logicalDrives & driveMask) != 0 ) + { + // This letter is used. + sprintf_s( drivePath, sizeof(drivePath), "%c:\\", driveLetter ); + + UINT driveType = GetDriveType( drivePath ); + if ( driveType == DRIVE_FIXED ) + { + // This is a fixed frive. + unsigned __int64 freeBytesAvailable = 0; + unsigned __int64 totalNumberOfBytes = 0; + unsigned __int64 totalNumberOfFreeBytes = 0; + + // Check available disk space + BOOL result = GetDiskFreeSpaceExA( drivePath, (PULARGE_INTEGER)&freeBytesAvailable, (PULARGE_INTEGER)&totalNumberOfBytes, (PULARGE_INTEGER)&totalNumberOfFreeBytes ); + if ( result ) + { + if ( freeBytesAvailable < MIN_DISK_SPACE ) + { + // The drive doesn't have enough free space. Exclude this machine from workers. + // It is simpler to exclude the machine from the build when any of its drive has not enough space + // than trying to figure out which drives are really used by Fastbuild especially in the context of symlinks, hardlinks, etc... + m_LastDiskSpaceResult = 0; + return false; + } + + // At least one drive found. + ++validDriveCount; + } + else + { + // There is something wrong with the drives... + m_LastDiskSpaceResult = 0; + return false; + } + } + } + } + + // return true if we've found at least one disk drive. + // This is paranoid but I like being paranoid. If we reach this line it is pretty much impossible for validDriveCount to be 0. + m_LastDiskSpaceResult = validDriveCount > 0; + return m_LastDiskSpaceResult != 0; + #else + return true; // TODO:MAC TODO:LINUX Implement disk space checks + #endif +} + // UpdateAvailability //------------------------------------------------------------------------------ void Worker::UpdateAvailability() { + // Check disk space + bool hasEnoughDiskSpace = HasEnoughDiskSpace(); + m_IdleDetection.Update(); WorkerSettings & ws = WorkerSettings::Get(); @@ -166,7 +247,7 @@ void Worker::UpdateAvailability() WorkerThreadRemote::SetNumCPUsToUse( numCPUsToUse ); - m_WorkerBrokerage.SetAvailability( numCPUsToUse > 0 ); + m_WorkerBrokerage.SetAvailability( ( numCPUsToUse > 0 ) && hasEnoughDiskSpace ); } // UpdateUI diff --git a/Code/Tools/FBuild/FBuildWorker/Worker/Worker.h b/Code/Tools/FBuild/FBuildWorker/Worker/Worker.h index 8ea4f53f3..bc09c955c 100644 --- a/Code/Tools/FBuild/FBuildWorker/Worker/Worker.h +++ b/Code/Tools/FBuild/FBuildWorker/Worker/Worker.h @@ -36,6 +36,7 @@ class Worker void UpdateAvailability(); void UpdateUI(); void CheckForExeUpdate(); + bool HasEnoughDiskSpace(); static void ShowMessageBox( const char * fmtString, ... ); @@ -52,6 +53,10 @@ class Worker bool m_RestartNeeded; Timer m_UIUpdateTimer; FileStream m_TargetIncludeFolderLock; + #if defined( __WINDOWS__ ) + Timer m_TimerLastDiskSpaceCheck; + int32_t m_LastDiskSpaceResult; // -1 : No check done yet. 0=Not enough space right now. 1=OK for now. + #endif }; //------------------------------------------------------------------------------ diff --git a/Code/Tools/FBuild/Integration/notepad++markup.xml b/Code/Tools/FBuild/Integration/notepad++markup.xml index d06e480f3..5801859b6 100644 --- a/Code/Tools/FBuild/Integration/notepad++markup.xml +++ b/Code/Tools/FBuild/Integration/notepad++markup.xml @@ -14,7 +14,7 @@ + = - + = endif if in include once + + = endif if import in include once @@ -25,7 +25,7 @@ Alias Compiler Copy CopyDir CSAssembly DLL Exec Executable ForEach Library ObjectList Print Settings Test Unity Using VCXProject - .AdditionalOptions .ApplicationEnvironment .AssemblySearchPath .AumidOverride .CachePath .Compiler .CompilerForceUsing .CompilerInputExcludedFiles .CompilerInputExcludePath .CompilerInputFiles .CompilerInputPath .CompilerInputPathRecurse .CompilerInputPattern .CompilerInputUnity .CompilerOptions .CompilerOutput .CompilerOutputExtension .CompilerOutputPath .CompilerOutputPrefix .CompilerReferences .DebuggerFlavor .DefaultLanguage .Dest .Environment .ExecArguments .ExecExecutable .ExecInput .ExecOutput .Executable .ExecWorkingDir .ExtraFiles .FileType .ForcedIncludes .ForcedUsingAssemblies .IncludeSearchPath .IntermediateDirectory .LayoutDir .LayoutExtensionFilter .Librarian .LibrarianAdditionalInputs .LibrarianOptions .LibrarianOutput .Libraries .Linker .LinkerAssemblyResources .LinkerLinkObjects .LinkerOptions .LinkerOutput .LinkerStampExe .LinkerStampExeArgs .LocalDebuggerCommand .LocalDebuggerCommandArguments .LocalDebuggerEnvironment .LocalDebuggerWorkingDirectory .Output .OutputDirectory .Pattern .PCHInputFile .PCHOutputFile .PCHOptions .PlatformToolset .PreBuildDependencies .Preprocessor .PreprocessorDefinitions .PreprocessorOptions .ProjectBuildCommand .ProjectRebuildCommand .ProjectCleanCommand .ProjectConfigs .ProjectOutput .ProjectInputPaths .ProjectInputPathsExcluded .ProjectFiles .ProjectFilesToExclude .ProjectGuid .ProjectBasePath .ProjectFileTypes .ProjectReferences .ProjectProjectReferences .RootNamespace .Source .SourceExcludePaths .SourcePathsPattern .SourcePathsRecurse .Targets .TestArguments .TestExecutable .TestOutput .TestWorkingDir .UnityInputExcludedFiles .UnityInputExcludePath .UnityInputExcludePattern .UnityInputFiles .UnityInputIsolateWritableFiles .UnityInputObjectLists .UnityInputPath .UnityInputPathRecurse .UnityInputPattern .UnityNumFiles .UnityOutputPath .UnityOutputPattern .UnityPCH .Workers .X360DebuggerCommand + .AdditionalOptions .ApplicationEnvironment .AssemblySearchPath .AumidOverride .CachePath .Compiler .CompilerForceUsing .CompilerInputExcludedFiles .CompilerInputExcludePath .CompilerInputFiles .CompilerInputPath .CompilerInputPathRecurse .CompilerInputPattern .CompilerInputUnity .CompilerOptions .CompilerOutput .CompilerOutputExtension .CompilerOutputPath .CompilerOutputPrefix .CompilerReferences .DebuggerFlavor .DefaultLanguage .Dest .Environment .ExecArguments .ExecExecutable .ExecInput .ExecOutput .Executable .ExecWorkingDir .ExtraFiles .FileType .ForcedIncludes .ForcedUsingAssemblies .IncludeSearchPath .IntermediateDirectory .LayoutDir .LayoutExtensionFilter .Librarian .LibrarianAdditionalInputs .LibrarianOptions .LibrarianOutput .Libraries .Linker .LinkerAssemblyResources .LinkerLinkObjects .LinkerOptions .LinkerOutput .LinkerStampExe .LinkerStampExeArgs .LocalDebuggerCommand .LocalDebuggerCommandArguments .LocalDebuggerEnvironment .LocalDebuggerWorkingDirectory .Output .OutputDirectory .Pattern .PCHInputFile .PCHOutputFile .PCHOptions .PlatformToolset .PreBuildDependencies .Preprocessor .PreprocessorDefinitions .PreprocessorOptions .ProjectBuildCommand .ProjectRebuildCommand .ProjectCleanCommand .ProjectConfigs .ProjectOutput .ProjectInputPaths .ProjectInputPathsExcluded .ProjectFiles .ProjectFilesToExclude .ProjectGuid .ProjectBasePath .ProjectFileTypes .ProjectReferences .ProjectProjectReferences .RootNamespace .SolutionBuildProject .SolutionConfigs .SolutionFolders .SolutionOutput .SolutionProjects .SolutionVisualStudioVersion .SolutionMinimumVisualStudioVersion .Source .SourceExcludePaths .SourcePathsPattern .SourcePathsRecurse .Targets .TestArguments .TestExecutable .TestOutput .TestWorkingDir .UnityInputExcludedFiles .UnityInputExcludePath .UnityInputExcludePattern .UnityInputFiles .UnityInputIsolateWritableFiles .UnityInputObjectLists .UnityInputPath .UnityInputPathRecurse .UnityInputPattern .UnityNumFiles .UnityOutputPath .UnityOutputPattern .UnityPCH .Workers .X360DebuggerCommand ) %1 %2 %3 diff --git a/Code/Tools/FBuild/Integration/usertype.dat b/Code/Tools/FBuild/Integration/usertype.dat index 75ffda373..4c8fba39f 100644 --- a/Code/Tools/FBuild/Integration/usertype.dat +++ b/Code/Tools/FBuild/Integration/usertype.dat @@ -1,5 +1,6 @@ endif if +import in include once @@ -21,6 +22,7 @@ Test Unity Using VCXProject +VSSolution AdditionalOptions ApplicationEnvironment @@ -102,6 +104,13 @@ ProjectFileTypes ProjectReferences ProjectProjectReferences RootNamespace +SolutionBuildProject +SolutionConfigs +SolutionFolders +SolutionOutput +SolutionProjects +SolutionVisualStudioVersion +SolutionMinimumVisualStudioVersion Source SourceExcludePaths SourcePaths diff --git a/Code/fbuild.bff b/Code/fbuild.bff index fcd9b1e44..f1b31e898 100644 --- a/Code/fbuild.bff +++ b/Code/fbuild.bff @@ -291,6 +291,9 @@ Compiler( 'Compiler-x64-OSX' ) + ' -Wno-ignored-attributes' // __declspec attribute 'restrict' is not supported [-Werror,-Wignored-attributes] + ' -Wno-incompatible-ms-struct' // ms_struct may not produce MSVC-compatible layouts for classes with base classes or virtual functions [-Wincompatible-ms-struct] + // Extra warnings + + ' -Wshadow' + + ' -fno-exceptions' + ' -fno-rtti' @@ -463,8 +466,8 @@ Compiler( 'Compiler-x64-OSX' ) //------------------------------------------------------------------------------ .ProjectCommon = [ - .ProjectBuildCommand = 'cd ^$(SolutionDir)\..\..\ & fbuild -vs -dist -cache ^$(ProjectName)-^$(Configuration)' - .ProjectRebuildCommand = 'cd ^$(SolutionDir)\..\..\ & fbuild -vs -dist -cache -clean ^$(ProjectName)-^$(Configuration)' + .ProjectBuildCommand = 'cd ^$(SolutionDir)\..\..\Code\ & fbuild -vs -dist -cache ^$(ProjectName)-^$(Configuration)' + .ProjectRebuildCommand = 'cd ^$(SolutionDir)\..\..\Code\ & fbuild -vs -dist -cache -clean ^$(ProjectName)-^$(Configuration)' .OutputDirectory = '^$(SolutionDir)\..\tmp2' .IntermediateDirectory = '^$(SolutionDir)\..\tmp2' .Platform = 'Win32' // unused @@ -489,11 +492,38 @@ Compiler( 'Compiler-x64-OSX' ) .ProjectX64Debug, .ProjectX64Profile, .ProjectX64Release, .ProjectX86ClangDebug } +// Project to compile everything VCXProject( 'All-proj' ) { - .ProjectOutput = '.\All.vcxproj' + .ProjectOutput = '../tmp/VisualStudio/Projects/All.vcxproj' + .ProjectFiles = { '.\fbuild.bff' } + .ProjectBasePath = '.\' +} + +// Project to regenerate projects/solutions +VCXProject( 'UpdateSolution-proj' ) +{ + .ProjectOutput = '../tmp/VisualStudio/Projects/UpdateSolution.vcxproj' .ProjectFiles = { '.\fbuild.bff' } .ProjectBasePath = '.\' + + .GenerateProjectsCommands = + [ + .ProjectBuildCommand = 'cd ^$(SolutionDir)\..\..\Code\ & fbuild solution' + .ProjectRebuildCommand = 'cd ^$(SolutionDir)\..\..\Code\ & fbuild solution -clean' + ] + + .ProjectX86Debug = [ Using( .ProjectX86Debug ) Using( .GenerateProjectsCommands ) ] + .ProjectX86Profile = [ Using( .ProjectX86Profile ) Using( .GenerateProjectsCommands ) ] + .ProjectX86Release = [ Using( .ProjectX86Release ) Using( .GenerateProjectsCommands ) ] + .ProjectX64Debug = [ Using( .ProjectX64Debug ) Using( .GenerateProjectsCommands ) ] + .ProjectX64Profile = [ Using( .ProjectX64Profile ) Using( .GenerateProjectsCommands ) ] + .ProjectX64Release = [ Using( .ProjectX64Release ) Using( .GenerateProjectsCommands ) ] + .ProjectX86ClangDebug = [ Using( .ProjectX86ClangDebug ) Using( .GenerateProjectsCommands ) ] + + .ProjectConfigs = { .ProjectX86Debug, .ProjectX86Profile, .ProjectX86Release, + .ProjectX64Debug, .ProjectX64Profile, .ProjectX64Release, + .ProjectX86ClangDebug } } // Configurations @@ -630,20 +660,46 @@ Alias( 'All+Tests' ) .Targets = { 'All', 'Tests' } } -// Aliases : proj (all projects) +// Visual Studio Solution //------------------------------------------------------------------------------ -Alias( 'proj' ) +VSSolution( 'solution' ) { - .Targets = { 'All-proj', - 'Core-proj', - 'CoreTest-proj', - 'FBuildApp-proj' - 'FBuildCore-proj', - 'FBuildTest-proj', - 'FBuildWorker-proj', - 'LZ4-proj', - 'TestFramework-proj' - } + .SolutionOutput = '../tmp/VisualStudio/FASTBuild.sln' + .SolutionProjects = { 'All-proj', + 'Core-proj', + 'CoreTest-proj', + 'FBuildApp-proj' + 'FBuildCore-proj', + 'FBuildTest-proj', + 'FBuildWorker-proj', + 'LZ4-proj', + 'TestFramework-proj', + 'UpdateSolution-proj', + } + .SolutionBuildProject = 'All-proj' + .SolutionConfigs = .ProjectConfigs + + .Folder_0_External = + [ + .Path = '0. External' + .Projects = { 'LZ4-proj' } + ] + .Folder_1_Test = + [ + .Path = '1. Test' + .Projects = { 'CoreTest-proj', 'FBuildTest-proj', 'TestFramework-proj' } + ] + .Folder_2_Libs = + [ + .Path = '2. Libs' + .Projects = { 'Core-proj', 'FBuildCore-proj' } + ] + .Folder_3_Apps = + [ + .Path = '3. Apps' + .Projects = { 'FBuildApp-proj', 'FBuildWorker-proj' } + ] + .SolutionFolders = { .Folder_0_External, .Folder_1_Test, .Folder_2_Libs, .Folder_3_Apps } } //------------------------------------------------------------------------------