You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ============================ MAJOR FEATURES & CHANGES ============================ Change 3623004 by Ben.Marsh Fix RemoteExecutor not taking the remote machine specs into account. Change 3623172 by Ben.Marsh UGS: Fix "More Info..." button not using P4 server override. Change 3628820 by Ben.Marsh PR #3979: Get working directory from task element, not tool node (Contributed by nullbus) Change 3630424 by Graeme.Thornton Make the AES key parameter const in FAES::EncryptData() Change 3632786 by Steve.Robb FString constructor fixed to not take an ignored void* parameter, which can be misleading. Change 3639534 by Ben.Marsh Remove old P4.NET library. Doesn't seem to be used by anything. Change 3640536 by Steve.Robb GitHub #4007 : Delete unnecessary specialization of MakeArrayView #jira UE-49617 Change 3641155 by Gil.Gribb UE4 - Speculative fix for problem with summary reading in FAsyncArchive2. Change 3643932 by Ben.Marsh Add an example build script for updating the version number, then compiling and staging the editor and tools to an output directory. Optionally submits at the end (requires -Submit argument). Change 3644825 by Ben.Marsh Use VSWHERE to find the location of MsBuild.exe, if available. https://github.com/EpicGames/UnrealEngine/pull/3879#issuecomment-329688645 Change 3647395 by Ben.Marsh Allow compiling of monolithic binaries from BuildEditorAndTools.xml, using the -set:GameTarget=FooGame -set:TargetPlatforms=Win32;Win64 options. Change 3650300 by Ben.Marsh UAT: Remove code that deletes cooked data on a failed cook. The engine should write packages out transactionally now (by writing to a temporary file and moving into place), and deleting the cooked data just prevents post-mortem analysis. Change 3650856 by Robert.Manuszewski Adding checks to prevent FlushAsyncLoading and LoadObject/LoadPackage from being called from any threads other than the game thread Change 3651022 by Gil.Gribb UE4 - Possible fix for mysterious ensure indicating problematic recursion in the pak precacher. Change 3658331 by Steve.Robb Fix for the parsing of large integer values. Change 3661958 by Gil.Gribb UE4 - Fixed rare hang in task graph. Change 3664021 by Robert.Manuszewski Fix for a potential GC crash caused by stale pointer in AnimInstanceProxy See https://udn.unrealengine.com/questions/392432/gc-issue-uaniminstancemontageinstances-empty-but-u.html Change 3664254 by Steve.Robb Use ANSI allocator when thread sanitizer is enabled. This allows the generation of more accurate and meaningful reports. Change 3664436 by Steve.Robb Use TUniquePtr instead of a thread-unsafe TSharedPtr to move data between threads. Change 3666461 by Graeme.Thornton Improvements to signing/encryption key embedding and runtime access - Changed method of embedding key into the executable to make it more secure - Added FAESKey class to wrap a 32 byte key Change 3666462 by Graeme.Thornton Cut ShooterGame AES key down to 32 characters Change 3677560 by Ben.Marsh PR #4074: UBT: Add include and library-related fields to module JSON output (Contributed by adamrehn) Change 3683534 by Steve.Robb Refactoring of enum/struct lookup during hot reload. Change 3683754 by Steve.Robb Alignment fixes to allow int64 on 32-bit platforms Support for integral types in IsAligned. Static asserts so that alignment functions will no longer be called with non-intergal, non-pointer types. Some fixes to existing code. Change 3686670 by Steve.Robb Fix for thread-unsafe modification of static array in FString::ParseIntoArrayWS. Change 3687540 by Ben.Marsh Fix all UBT/UAT output going to stderr rather than stdout. Change 3688931 by Gil.Gribb UE4 - Critical fix for a rare race condition in the pak file async IO layer. Change 3690000 by Graeme.Thornton Manual copy of 4.18 CL 3687869 Make UBT include the destination INI file for a given hierarchy if it exists Renamed VSCode enum value to VisualStudioCode, so it matches the source accessor plugin name Change 3690030 by Graeme.Thornton VSCode fixes - Source Code Accessor plugin changes. Add new interface method to open a solution at a given path - GameProjectUtils now uses the source navigation API to open solutions rather than hardcoding which solution file types to look for - Various fixes for vscode project file generation #jira UE-50554 Change 3690885 by Steve.Robb Atomic reads in FReferenceControllerOps<ESPMode::ThreadSafe>. Change 3691052 by Steve.Robb Free stats thread on shutdown. Change 3695138 by Steve.Robb AsConst helper function added. Change 3696627 by James.Hopkin Changed player controller iterator typedefs to use TWeakObjectPtr rather than the deprecated TAutoWeakObjectPtr (review-3606695) Change 3697099 by Steve.Robb GitHub #4105 : Removed redundant class access modifier Change 3697154 by Steve.Robb Removal of deprecated functions in delegates. Mutable lambdas to can now be bound to delegates. Change 3697180 by Steve.Robb GitHub #4115 : Incorrect CPPMacroType used for USoftClassProperty Change 3697239 by Steve.Robb Allow TArray::Insert to take an array with any allocator type. Change 3697269 by Steve.Robb RelocateConstructItems instead of MoveConstructItems. Change 3697558 by Steve.Robb New _GetRef functions for TArray, which return a reference to the newly-added element. Unit tests for these functions. Change3699776by Steve.Robb TSAN warning suppression around IAsyncReadRequest::bCompleteAndCallbackCalled. Change 3702397 by Steve.Robb TIsTrivial type trait. Change 3702569 by Steve.Robb Allow a TGuardValue to be assigned to a different type from the one being guarded. Change 3706644 by Robert.Manuszewski Different stack ingore count for development builds for FArchiveStackTrace Change 3709272 by Steve.Robb Removal of redundant UpdateVertices, which causes a race condition on the renderer thread. Change 3709452 by Robert.Manuszewski Fixed a bug where with async time limit set to a low value the async loading could hang because the linker would keep reloading the preload dependencies Change 3709454 by Robert.Manuszewski Added command line option -NOEDL to disable EDL Change 3709487 by Steve.Robb Remove use of PLATFORM_HAS_64BIT_ATOMICS, which is always 1. Change 3709645 by Ben.Marsh Fix race condition between multiple instances of UBT trying to write out the XML config cache. Change 3711193 by Ben.Marsh Add an editor setting for the shared DDC location to use. #jira UE-51487 Change 3713811 by Steve.Robb Update .modules files after a hot reload. Don't check for directory timestamp changes as a way of detecting new files if hot reloading with a makefile, as this is already done during makefile invalidation checks. Pass hotreload flags around in UBT instead of relying on global state. This fixes the hot reload iteration speed regression without also regressing the fix to UE-42205. #jira UE-51472 Change 3715654 by Steve.Robb GitHub #4156 : Fixed not compiling template function Algo::UpperBoundBy. Change 3718782 by Steve.Robb TSharedPtr, TSharedRef and TWeakPtr assignment are now implemented as copy-and-swap to avoid an invalid smart pointer state being visible to any destructors being called. Change 3720830 by Steve.Robb Initial import of TAtomic object wrapper, which guarantees atomic access to an object. Change 3720881 by Steve.Robb FCompression ThreadSanitizer data race fixes. Change 3722640 by Graeme.Thornton Guard network platform file heartbeat function with the socket critical section. Stop heartbeat from causing a crash when firing during async loading. #jira UE-51463 Change 3722655 by Steve.Robb Don't null name table because it's already zeroed at startup. Some tidy-ups. Change 3722754 by Steve.Robb Thread sanitizer fix. Small typo fix. Change 3722849 by Graeme.Thornton Improve "caching file" message in networkplatformfile so it says "Requesting file..." and is only output when we actually request the file from the server Change 3723081 by Steve.Robb TAtomic is now aligned to the underlying integer type. TAtomic will now static assert with a better error message when given an unsupported type. Define added for the maximum platform-supported atomic type, and used instead of a (wrong) hardcoded number. Misc renames. Change 3723270 by Ben.Marsh Include /d2cgsummary argument when running UBT with -Timing. Change 3723683 by Ben.Marsh Do not include documentation in the generated project files by default. Suspect that the 30,000 UDN files that get added to the solution take up memory and degrate performance. Change 3725422 by Robert.Manuszewski When serializing compressed archive with multithreaded compression enabled, wait for the oldest async task instead of spinning. Change 3725735 by Robert.Manuszewski Making all CheckDefaultSubobjects related functions const Change 3726167 by Steve.Robb FMinimalName::IsNone added. Change 3726458 by Steve.Robb TAtomic will no longer instantiate for types which are not exactly a size supported by the platform layer. Change 3726542 by Ben.Marsh UGS: Always include the project filename in the editor build command. The project may not be in one of the .uprojectdirs paths. Change 3726595 by Ben.Marsh Allow building multiple game targets in the example BuildEditorAndTools.xml script. Change 3726724 by Ben.Marsh Fix ambiguities in calculating root directory. (GitHub #4172) Change 3726959 by Ben.Marsh Make sure that AutomationTool uses the same list of preprocessor definitions when compiling *.target.cs files as UnrealBuildTool does. Change 3728437 by Steve.Robb VisitTupleElements now supports invocation of a functor taking arguments from multiple tuples in parallel. Some improved documentation. NOTE: This is a backward-incompatible change to VisitTupleElements. Any existing calls will need their arguments swapping. Change 3732262 by Gil.Gribb UE4 - Fixed rare hangs in the task graph. Change 3732755 by Steve.Robb Stats TSAN fixes. Optimizations to FCycleCounter::Start() to only read the stat name once. Change 3735000 by Robert.Manuszewski Always preload the AssetRegistry module on startup. even if EDL is disabled. Even without EDL, if the async loading thread is enabled the AssetRegistryModule will otherwise be loaded from the ASL thread and that will assert. Change 3735292 by Robert.Manuszewski Made sure component visualizer is removed from VisualizersForSelection when UnregisterComponentVisualizer() is called otherwise it may cause crashes when the engine terminates. Change3735332by Steve.Robb Refactoring of UDelegateProperty::Identical() to clarify logic. Fixed UMulticastDelegateProperty::Identical() to compare the bound function names. PPF_DeltaComparison removed, as it doesn't seem useful. Change 3737960 by Graeme.Thornton VSCode - Add launch task for generating project files for the given folder Change 3738398 by Graeme.Thornton Make Visual Studio source code accessor's module hotreload handler pass the 'save all files' message to the current accesor, rather than direct to the visual studio accessor #jira UE-51451 Change 3738405 by Graeme.Thornton VSCode: Format c/cpp settings strings using comment path formatting function Change 3738928 by Steve.Robb Fix for lack of null conditional operators in some older Monos. (replicated from CL# 3729574 in Release-4.18) #jira UE-51842 Change 3739135 by Ben.Marsh Fix being unable to package projects in a folder called "Wolf". This is only a restricted folder for Epic's Perforce history. #jira UE-51855 Change 3739360 by Ben.Marsh UAT: Fix issue with P4PORT setting not being parsed correctly. Change 3745959 by James.Hopkin #core Added ImplicitConv for safe upcasts to a specific required type, e.g. deduced delegate payload types Change 3746125 by Steve.Robb FName ThreadSanitizer fixes. Change 3747274 by Steve.Robb TSAN fix for FMediaTicker::Stopping. Change 3747618 by Steve.Robb ThreadSanitizer data race fix for FShaderCompileThreadRunnableBase::bForceFinish. Change3747720by Steve.Robb ThreadSanitizer fix for FMessageRouter::Stopping. Change3749207by Graeme.Thornton First pass of CryptoKeys plugin. Allows creation/editing/cycling of AES/RSA keys. Change 3749323 by Graeme.Thornton Fix UAT crash when only -targetplatform is specifiied Change 3749349 by Steve.Robb TSAN_SAFE guards around LockFreeList to silence ThreadSanitizer. Change 3749617 by Steve.Robb Logf static_assert for formatting string enabled. Change 3749897 by Steve.Robb FDebug::LogAssertFailedMessage static assert for formatting string enabled. Change 3754011 by Steve.Robb Static asserts that the allocator supports move. Move-enabled our allocators which don't support move. Change 3754227 by Ben.Marsh Fix build command line in generated projects missing a space before the compiler version override. #jira UE-52226 Change 3754562 by Ben.Marsh PR #4206: Replace deprecated wsprintf with secure swprintf for Bootstrap executable (Contributed by jessicafalk) Change 3755616 by Graeme.Thornton Runtime code for using the new crypto ini files to define signing/encryption keys #jira UE-46580 Change 3755666 by James.Hopkin Used ImplicitConv to remove Casts being used for up-casts #review-3745965 Change 3755671 by Graeme.Thornton Add log message in unrealpak to say which config file system it is using for crypto keys Change 3755672 by Graeme.Thornton Updating ShooterGame with new CryptoKeys based security setup Change 3756778 by Ben.Marsh Add support for running multiple jobs simultaneously on a single builder. When running job or agent setup, the --num-slots=X parameter defines the number of steps that can run simultaneously (EC procedures pass in the resource step limit). A lock file is created under the workspace root (D:\Build) and a reservation file is created for the first slot that can be allocated (slot-1, slot-2, etc...). The slot number is used to define the workspace name that should be used. Change 3758498 by Ben.Marsh Re-throw exceptions when a file cannot be deleted when cleaning a target. Change 3758921 by Steve.Robb ThreadSanitizer fix to FThreadSafeStaticStatBase::HighPerformanceEnable to do a relaxed atomic load on access. DoSetup() now returns the newly-allocated pointer, instead of reloading it from memory. Change 3760599 by Graeme.Thornton Added missing epic header comment to some new source files Change 3760642 by Steve.Robb ThreadSanitizer fix for concurrent access to GMainThreadBlockedOnRenderThread. Change 3760669 by Graeme.Thornton Improvement to OpenSSL based signing key generator. Generate a full RSA key then steal the primes from it, rather than generating the primes manually. Added a test mode to the cryptokeys commandlet to test signing key generation Change 3760711 by Steve.Robb ThreadSanitizer fixes to GIsRenderingThreadSuspended. Change 3760739 by Steve.Robb ThreadSanitizer fix for FQueuedThread::TimeToDie. Change 3760763 by Steve.Robb ThreadSanitizer fix for GRunRenderingThreadHeartbeat. Removal of unnecessary/dangerous initializer for GMainThreadBlockedOnRenderThread. Change 3760793 by Steve.Robb Some simple refactoring to remove some volatile reads of BufferStartPos and BufferEndPos. Change 3760817 by Steve.Robb ThreadSanitizer fixes for FAsyncWriter::BufferStartPos and BufferEndPos. Change 3761331 by Josh.Engebretson UnrealBuildTool enforcement of Development and Debug configurations in existing .csproj #jira UE-52416 Change 3761521 by Steve.Robb ThreadSanitizer fixes for FEvent::EventStartCycles and EventUniqueId. Change 3763117 by Graeme.Thornton PR #3722: Optimising FPaths::IsRelative() (Contributed by jovisgCL) Change 3763358 by Graeme.Thornton Ensure that all branches within FGenericPlatformMisc::RootDir() produce an absolute path with no duplicate slashes Remove relative->abs conversion of root dir from FPaths::MakeStandardFilename(), now that we know RootDir() always returns an absolute path Derived from the content of this PR: PR #3742: Treat RootDirectory the same way as Standardized (Contributed by TroutZhang) Change 3764058 by Graeme.Thornton Generate a .code-workspace file for the current workspace. Allows foreign projects to "mount" the UE4 folder so that the engine tasks are avaible, and all engine source is visible to VSCode for searching purposes #jira UE-52359 Change 3764705 by Steve.Robb Better handling of whitespace in ImportText_Internal() for set and map properties. Containers are now emptied upon import failure, to avoid leaving bad container states (unhashed, partial data). Fix to USetProperty's temp buffer size to avoid buffer overruns. Duplicate map keys are now skipped during import, same as USetProperty's behavior. Change 3764731 by Steve.Robb Don't re-run UHT if only source files have changed in the same folder as headers. This was already done for hot reload, but there's no reason why it should be limited to that. Change 3765923 by Graeme.Thornton VSCode - "taskName" -> "label" for C# build tasks Change 3766018 by Steve.Robb constexpr constructor for TAtomic. Change 3766037 by Steve.Robb Misc tidyings in HotReload.cpp. Change3766046by Steve.Robb ThreadSanitizer fixes to ENamedThreads::RenderThread and ENamedThreads::ENamedThreads_Local. Change 3766288 by Steve.Robb Improved efficiency of adding/removing elements to UGCObjectReferencer::ReferencedObjects. Change 3766374 by Josh.Engebretson Fix issue with ini quoted value comparison #jira UE-52066 Change 3766532 by Josh.Engebretson PR #3680: Added NetSerialize to FDateTime fixing UE-22533 (Contributed by druhasu) #jira UE-46156 Change3766740by Steve.Robb TMultiMap::Append added. Change3767523by Steve.Robb ThreadSanitizer fix for UE4Delegates_Private::GNextID. Change 3767601 by Steve.Robb ThreadSanitizer fix for FStats::GameThreadStatsFrame. Change3770567by Ben.Marsh Add a FAnnotatedArchiveFormatter interface which allows querying structural type information that may not be in binary archives. Change 3770826 by Ben.Marsh Move StructuredArchive implementation into Core, so primitive types can implement serialization overloads for it. Change 3770875 by Steve.Robb Redundant UScriptStruct::PostLoad removed, which was causing a race condition in async loading. This was re-establishing the CppStructOps, but that is unnecessary because native classes cannot change as a result of a load - only BP structs can, and they don't have CppStructOps. Change 3772167 by Ben.Marsh Add a context-free binary formatter that can serialize tagged data. This functions as a lower-overhead binary intermediate format for JSON data. Change 3772248 by Steve.Robb ThreadSanitizer fixes to FMalloc call counters. Change 3772383 by Ben.Marsh Separate archive metadata from FArchive into FArchiveContext, so it can be safely exposed to consumers of FStructuredArchive. Change 3772906 by Graeme.Thornton TextAssetCommandlet - Utility commandlet for testing/converting to text asset format Change 3772932 by Ben.Marsh Fix "String:" prefix not being stripped from escaped string values. Change 3772942 by Graeme.Thornton Add experimental setting to enable in-editor text asset format functionality Add "export to text" option into the content browser asset actions context menu Change 3772955 by Ben.Marsh Add a new "stream" compound type to FStructuredArchive, which allows serializing a sequence of elements similarly to an array, but without serializing an explicit size. Allows passing through data to an underlying binary archive without breaking compatibility. Change 3772963 by Ben.Marsh Allow querying record keys and stream lengths from annotated archive formatters, since these archives have markup for field boundaries. Change 3773010 by Graeme.Thornton Added CORE_API to FArchiveFromStructuredArchive Gave text asset format experimental option a slightly less random tooltip comment Change 3773057 by Ben.Marsh Add a flag to FArchive to determine whether the archive is text (IsTextFormat()). Add support for seeking within FArchiveFromStructuredArchive. For text formats, data is serialized to an in-memory buffer, with names and objects serialized as indices into an array. For non-text formats, data is serialized directly to the underlying archive. Also rename FStructuredArchive::TryEnterSlot() to TryEnterField(). Change 3773118 by Steve.Robb TSignedIntType and TUnsignedIntType type traits for getting an integer type of a given size. Change 3773122 by Steve.Robb TAtomic fixes for pointer arithmetic. TSignedIntType used instead of reimplementing its own trait. Change 3773123 by Steve.Robb Unit tests for TAtomic. Change 3773138 by Steve.Robb Run numeric tests on integer types instead of basic tests. Fix for compiler warnings when subtracting from unsigned atomics. Change 3773166 by Steve.Robb Refactoring of arithmetic operations into its own class, then basing the pointer and integral versions on that. Change 3774216 by Gil.Gribb UE4 - Fix rare crash in the pak precacher immediately after unmounting a pak file. Change 3774426 by Ben.Marsh Copy all C# tools to a staging directory before compiling them. This prevents access violations when compiling tools like iPhonePackager that reference DotNETCommon, and ensures we strip NotForLicensees folders out of them all. See: https://answers.unrealengine.com/questions/726010/418-will-not-build-from-source.html Change 3774658 by Ben.Marsh Improve error reporting while generating intellisense for project files. Include the name of the target being compiled, and allow project file generation to continue without it. Change 3775141 by Ben.Marsh Always output HTML5 diagnostics at "information" verbosity, to avoid every line being prefixed with "WARNING:" and screwing up the EC postprocessor. Change 3775459 by Ben.Marsh Removing .NET Framework Perforce DLL as runtime dependency of engine third party library. The actual library is linked statically. Change 3775522 by Ben.Marsh UGS: Treat .uproject and .uplugin files as code changes. Change 3775597 by Ben.Marsh Fix post-build steps for plugins not being executed. #jira UE-52754 Change 3777895 by Graeme.Thornton StructuredArchiveFromArchive - An adapter class for wrapping an existing FArchive with a structured archive Change 3777931 by Graeme.Thornton Refactored FArchiveUObjects serialization code into some static helpers Added FArchiveUObjectFromStructuredArchive which allows the adaption of a structured archive into an FArchive that supports the extra UObect serialization functions for weak/soft pointers Change 3777942 by Graeme.Thornton Added missing CORE_API to FStructuredArchive::FStream Added FStructuredArchive::FSlot insertion operator for char Added specialization of TArray<uint8> serializer for structured archives which serializes the contents as one value Change 3778084 by Graeme.Thornton Adding FPackageName::GetTextAssetPackageExtension() to access the file extension we use for text asset files Change 3778096 by Graeme.Thornton Add a constructor to FArchiveUObjectFromStructuredArchive that takes a slot and passes it to the base class Change 3778389 by Josh.Engebretson Fix an optimization issue with CPU benchmarking Add better support for debugging/testing local rocket builds UDN Link: https://udn.unrealengine.com/questions/400909/command-scalability-auto-gives-inaccurate-cpu-benc.html #jira UE-52192 Change 3778701 by Josh.Engebretson Ensure plugin content folders are mounted consistently. Fixes TryConvertFilenameToLongPackageName failing to work on plugin assets UDN Link: https://udn.unrealengine.com/questions/276386/tryconvertfilenametolongpackagename-fails-for-plug.html #jira UE-40317 Change 3778832 by Chad.Garyet Adding enterprise path support for PCB's for UGS Change 3780258 by Graeme.Thornton TextAssetCommandlet - Accumulate timings for loading packages and saving packages Change 3780463 by Graeme.Thornton CryptoKeys improvements - Enable CryptoKeys plugin by default - Attempt to inherit settings from the old system by default - Hide ini/index encryption settings from packaging settings and just inherit previous values into new system Minor UBT change to remove a trailing comma from the end of encryption/signing key binary strings Change 3780557 by Ben.Marsh Fix LoginFlow module not being precompiled for the binary release. Change 3780846 by Josh.Engebretson Improve filename to long package name resolution when provided a relative path Change 3780863 by Ben.Marsh UAT: Add a better error message when a C# project has an invalid reference. Change 3780911 by Ben.Marsh Update the BuildEditorAndTools.xml script to allow submitting archived binaries to Perforce. The "Submit To Perforce For UGS" node creates a zip of all the binaries that have been built, and submits it to the stream specified by the 'ArchiveStream' argument. Change 3780956 by Josh.Engebretson Add support for ! (RemoveKey) config command to UBT UDN Link: https://udn.unrealengine.com/questions/397267/index.html #jira UE-52033 Change 3782957 by Robert.Manuszewski UE4 - Fixed a linear search in EDL that caused performance problems for very large maps. Change 3784503 by Ben.Marsh Optimizations for FStructuredArchive: * Store the depth explicitly in element objects, to avoid having to loop through the scope stack to find it. * Prevent shrinking of arrays when removing elements. * Add an inline allocator to the scope and container stacks. Change 3784700 by Ben.Marsh Remove the inline allocator from FStructuredArchive; checking whether the inline or backup allocator is being used is slower than just allocating up-front. Change 3784989 by Ben.Marsh Compile out all the FStructuredArchive validation code when WITH_TEXT_ARCHIVE_SUPPORT = 0. Change 3786860 by Gil.Gribb UE4 - Remove no buffering flag from windows async IO because it disabled the disk cache entirely. Change 3787159 by Ben.Marsh Guard against UE4.0 backwards compatibility path when determining if an engine is a source distribution. Change 3787493 by Josh.Engebretson Parallel pak generation now uses MaxDegreeOfParallelism option which is now set to the number of CPU cores Moved cryptography settings parsing out of threaded CreatePak method to avoid concurrency issue in ConfigCache.TryReadFile Fix for multiple threads parsing ini keys (PR 3995) #PR 3995 #jira 52913 #jira 49503 Change 3787773 by Steve.Robb Fix for missing final values from FOREACH_ENUM_ macros. Change 3788287 by Ben.Marsh TBA: Add checks in debug builds that key names in maps and records for FStructuredArchive are unique. Change 3788678 by Ben.Marsh Fix compile error due to inability to instantiate TArray<> of forward declared struct. Convert set of key names to an array to avoid including Set.h in public header for FStructuredArchive. Change 3789353 by Graeme.Thornton Removed unused/rotten modes from TextAsset commandlet. Used existing "-iterations=n" switch to control a global iteration over the given command. Useful for performance testing. Change 3789396 by Ben.Marsh Move code to validate container keys/sizes into DO_GUARD_SLOW checks, and allocate container metadata instances dynamically to fix problems with references to things not declared in headers that can't be included from StructuredArchive.h Change 3789772 by Ben.Marsh Always strip trailing slashes from the end of paths specified by .build.cs files; they can cause quoted paths to be escaped on the command line. Change 3790003 by Ben.Marsh TBA: Rename FStructuredArchive::EElementType::Object to FStructuredArchive::EElementType::Record. Change 3790051 by Steve.Robb PIE is disabled during a hot reload. Hot reload in editor is disabled during PIE. Hot reload from IDE is deferred until after PIE is exited. Compiling multiple times before a hot reload (e.g. compiling multiple times in PIE) will now load the most recent change. #jira UE-20357 #jira UE-52137 Change 3790709 by Steve.Robb Better move support for TVariant. EVariantTypes switched over to using an enum class to aid debugger visualization. Change 3791422 by Ben.Marsh TBA: Return the type of a field from an annotated archive formatter at the point that we enter it, rather than querying all the time. Change 3791489 by Graeme.Thornton TBA: Change StructuredArchiveFromArchive adapter to use the archive.Open() result directly, now that it's a slot and not a record Change 3792344 by Ben.Marsh Improvements to base64 encoding library. * Now supports encoding and decoding with ANSICHAR and WIDECHAR implementations. * Added support for decoding base-64 blobs without padding marks. * Added support for decoding into pre-allocated buffer. * Added constexpr functions for determining the encoded and maximum decoded size of an input buffer. * Prevent writes past the end of allocated buffer (no longer need to manually remove padding bytes). Change 3792949 by Ben.Marsh TBA: Rename FAnnotatedArchiveFormatter to FAnnotatedStructuredArchiveFormatter. Change 3794078 by Robert.Manuszewski Fixing a crash that could happen when FGCObjects were constructed and destructed when shutting down the engine #jira UE-52392 Change 3794413 by Ben.Marsh TBA: Remove the element type parameter to SetScope(). It isn't really needed; we can just assume the element ID correctly identifies the item on the stack. Change3794731by Ben.Marsh TBA: Optimize creation of stack elements for empty slots in FStructuredArchive. This saves a lot of bookkeeping when serializing a large number of individual fields. Since only one slot can be active at a time (and it only exists temporarily, until we write into it), we can just store the element ID assigned to it in a member variable. Change 3795081 by Ben.Marsh UBT: Move LinuxCommon.cs into Platform/Linux folder. Change 3795137 by Ben.Marsh UBT: Allow modules to specify private compiler definitions from the build.cs file, only visible within that module (via the "PrivateDefinitions" property). Change 3795247 by Ben.Marsh Fix missing header when creating a new interface from the editor new code wizard. #jira UE-53174 Change 3796025 by Graeme.Thornton Fixed some deprecated "Definitions" warnings in OpenCV build files Change 3796103 by Graeme.Thornton Disable experimental text asset option - it does nothing useful yet. Change 3796157 by Graeme.Thornton Fix path type mismatch in visual studio source code accessor meaning that the DTE comms wouldn't identify a running instance of VS as having the current solution open. #jira UE-53206 Change 3796315 by Ben.Marsh Move Formatter to the correct position for initializer. #jira UE-53208 Change 3797082 by Ben.Marsh UAT: Work around for exception thrown by launching cook with "-platform=Android_ETC1 -targetplatform=Android -cookflavor=ETC1". Anrdoid_ETC1 is not a valid platform (it's a cook platform), and can't be parsed by UAT. #jira UE-53232 Change 3799050 by Ben.Marsh Make UnrealPak.version files writable for Mac and Linux. Change 3801012 by Graeme.Thornton VSCode - Update source accessor to use code workspace as it's target, rather than just the project directory Change 3801214 by Gil.Gribb UE4 - Remove assert to work around minor problem with lock free lists. #jira UE-49600 Change 3801219 by Steve.Robb WeakObjectPtrs now warn when casting away const. Change 3801299 by Graeme.Thornton Fix quote issue with foreign project build tasks on PC Change 3803292 by Graeme.Thornton Fix crash on startup when using cook-on-the-side. Force a flush of the asset registry background scanning when creating the cook-on-the-side platform registries Change 3803559 by Steve.Robb TSAN fix for FMalloc::MaxSingleAlloc. Change 3803735 by Graeme.Thornton Last set of cryptokeys changes - Added some comments for editor exposed settings - Split "encrypt assets" option into "encrypt uassets" and "encrypt all assets" Change 3803929 by Ben.Marsh UGS: Show an in-place error panel when a project fails to open, allowing the user to retry and have their tabs saved instead of creating a modal dialog. Change 3624590 by Steve.Robb AddReferencedObjects now generates a compile error with containers of UObject*s where the UObjectType is forward-declared, as these which won't be added to the reference collector. Tidy-up of existing calls to AddReferencedObjects. Change 3629473 by Ben.Marsh Build: Rename the option for embedding source server information in PDB files for installed engine builds. Change 3632894 by Steve.Robb VARARG* macros deprecated and usage replaced with variadic templates. Change 3640704 by Steve.Robb MakeWeakObjectPtr added, which deduces a TWeakObjectPtr type from a raw pointer type. Fix to TWeakObjectPtr's constructor which implicitly removed const. Fixes to everything which didn't compile as a result. Change 3650813 by Graeme.Thornton Removed FStartupPackages and associated code Change 3651000 by Ben.Marsh Return the stack size from FPlatformStackWalk::CaptureStackBacktrace() rather than checking for the first null pointer, to prevent truncated callstacks if parts of the stack are zeroed out. #jira UE-49980 Change 3690842 by Steve.Robb FPlatformAtomics::AtomicRead added - needs optimizing. AtomicRead() used in FThreadSafeCounter::GetValue(). Change 3699416 by Steve.Robb Fix to debugger visualization of TArray with a TInlineAllocator or TFixedAllocator. Improved readability of TSparseArray visualization. Change 3720812 by Steve.Robb Atomic functions for 8-bit and 16-bit. Android, Linux and Switch implementations now just use the Clang implementation. AtomicRead64 deprecated in favor of the int64* AtomicRead overload. Change 3722698 by Steve.Robb VS debugger visualizers for TAtomic. Change 3732270 by Steve.Robb Relaxed stores and loads. Change 3749315 by Graeme.Thornton If UAT is invoked with platforms in both the -platform and -targetplatform command line switches, build using all of them rather than just the ones in -targetplatform #jira UE-52034 Change 3750657 by Josh.Engebretson Fixed issue when debugging editor cook/package and project launch operations #jira UE-52207 Change 3758514 by Steve.Robb Fixes to FString::Printf having non-literals being passed as its formatting string. Change 3763356 by Steve.Robb ENamedThreads::RenderThread and ENamedThreads::RenderThread_Local encapsulated by getters and setters. Change 3770549 by Steve.Robb Removal of obsolete PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS and PLATFORM_COMPILER_HAS_AUTO_RETURN_TYPES. Tidy up of existing code which uses it. Change 3770553 by Ben.Marsh Adding structured serialization API to Core/CoreUObject for use with text-based assets. * FStructuredArchive abstracts an archive which is made up of compound types (records, arrays, and maps). Values are stored in slots within these types. * Records are string -> value dictionaries where the key names can be compiled out in non-editor builds or when WITH_TEXT_ARCHIVE_SUPPORT = 0. * Maps are string -> value dictionaries where the key names are present regardless of the build type. * Proxy objects are defined to express the context for serialization (FStructuredArchive::FRecord, FStructuredArchive::FArray, FStructuredArchive::FMap, FStructuredArchive::FSlot) which allows basic validation through static typing. These objects act as lightweight handles, and can be cheaply constructed and passed around on the stack. Most serialization to and from the archive is done through these objects. * Runtime checks perform additional validation to ensure that serialized data is well formed and written in a forward-only manner, regardless of the underlying archive type. * The actual input/output format is determined by a separate interface (FArchiveFormatter). Context validation (always causing matching LeaveArray for every EnterArray, etc...) is done by FStructuredArchive, so implementing these classes is fairly trivial. FArchiveFormatter can be de-virtualized in non-editor builds, where WITH_TEXT_ARCHIVE_SUPPORT = 0. * Includes implementations of FArchiveFormatter for binary and JSON formats. Change 3771105 by Steve.Robb Deprecation warnings for PLATFORM_COMPILER_HAS_AUTO_RETURN_TYPES and PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS. Fix for incorrect warning formatting on Clang platforms. Change 3771520 by Steve.Robb Start moving Clang-using platforms' pre-setup stuff into a Clang-specific header. Change 3771564 by Steve.Robb More common macros moved to the Clang pre-setup header. Change 3771613 by Steve.Robb EMIT_CUSTOM_WARNING_AT_LINE moved to ClangPlatformCompilerPreSetup.h. Change 3772881 by Ben.Marsh Add support for serializing FName and UObject through FStructuredArchive. In order to allow custom linker behavior when serializing objects: * The constructor to JSON input formatter now takes a delegate to convert a string object name into a UObject pointer. * The constructor to tagged binary formatter takes a delegate to serialize a UObject pointer into any form it chooses (likely an integer index into the import table) Object and name types are stored as strings in JSON, using an "Object:" or "Name:" prefix to differentiate them from regular strings. Any strings that already contain one of these prefixes are prepended with a "String:" prefix (as is any string that already has a "String:" prefix). Change 3772941 by Graeme.Thornton Make build work when including StructuredArchive.h from core container types Added standard header to new files Add structured archive serializer for TArray Fix bug in structured archive where containers weren't being popped from the scope stack Change 3772972 by Ben.Marsh Add an adapter which presents a legacy FArchive interface to a FStructuredArchive slot. Data is serialized into this slot as a stream of elements; raw data is buffered up into fixed size chunks, names and objects are serialized separately. When used with FBinaryArchiveFormatter, this should result in all data being passed through to the underlying archive in a backwards compatible way, wiith no additional bookkeeping fields. Change 3773006 by Ben.Marsh Rename FStructuredArchive::FRecord::EnterSlot() to EnterField(). Change 3773013 by Steve.Robb bUseInlining target rule added to UnrealBuildTool, which defaults to true, to allow inlining to be disabled for debugging purposes. Change 3774499 by Ben.Marsh Minor fixes for FStructuredArchive related classes: * Text-based archive formats are now compiled out when WITH_TEXT_ARCHIVE_SUPPORT = 0. * Fixed issue with FTaggedBinaryArchiveFormatter state becoming corrupted when looking ahead at field types. * FArchiveFieldName constructor is now explicit, to fix cases where strings were being passed directly to serialize functions. Change 3774600 by Ben.Marsh Add CopyFormattedData() function, which can copy data from one formatter to another. Add a test case to SerializationAPI that converts from data -> JSON -> binary -> JSON -> data. This function can be used to implement a generic visitor pattern, by implementing a FArchiveFormatter which receives the deserialized data. Change 3789721 by Ben.Marsh TBA: Split FTaggedBinaryArchiveFormatter into separate classes for reading and writing. Change 3789920 by Ben.Marsh TBA: Support automatic coercion between any numeric types in tagged binary archives. Also report the smallest type that can contain a value, rather than just in32/double. #jira UECORE-364 Change 3789982 by Ben.Marsh TBA: Change FStructuredArchive::Open() to return a slot, rather than a record, to make it easier to implement a raw FArchive adapter. Change 3792466 by Ben.Marsh TBA: Better handling of raw data in text based assets. Short sequences of binary data are Base64 encoded as a single string. Longer sequences are stored as an array of Base64 encoded lines, push a SHA1 hash to detect cases where the data was merged incorrectly. In order to allow inference of the correct type for a field, other fields called "Base64" will be escaped to "_Base64", and any field beginning with "_" will have an additional underscore inserted. Reading files back in reverses these transformations. Change 3792935 by Ben.Marsh TBA: Rename FArchiveFormatter to FStructuredArchiveFormatter for consistency with FStructuredArchive. Change 3795100 by Ben.Marsh UBT: Rename the ModuleRules Definitions property to PublicDefinitions, to make its semantics clearer. Change 3795106 by Ben.Marsh Replace all internal usages of ModuleRules.Definitions, and replace it with ModuleRules.PublicDefinitions. Change 3796275 by Ben.Marsh Fix paths to Version.h includes from resource files. Change 3800683 by Josh.Engebretson Remove WER from Mac and Linux crash reports in favor of unified runtime-xml format #jira UE-50073 Change 3803545 by Steve.Robb TWeakObjPtr const-dropping assignment fix. Fixes to change. [CL 3805231 by Ben Marsh in Main branch]
2673 lines
106 KiB
C#
2673 lines
106 KiB
C#
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using Microsoft.Win32;
|
|
using System.Linq;
|
|
using System.Diagnostics;
|
|
using Tools.DotNETCommon;
|
|
using System.Xml.Linq;
|
|
using System.Xml;
|
|
|
|
namespace UnrealBuildTool
|
|
{
|
|
/// <summary>
|
|
/// Represents a folder within the master project (e.g. Visual Studio solution)
|
|
/// </summary>
|
|
abstract class MasterProjectFolder
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="InitOwnerProjectFileGenerator">Project file generator that owns this object</param>
|
|
/// <param name="InitFolderName">Name for this folder</param>
|
|
public MasterProjectFolder( ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName )
|
|
{
|
|
OwnerProjectFileGenerator = InitOwnerProjectFileGenerator;
|
|
FolderName = InitFolderName;
|
|
}
|
|
|
|
/// Name of this folder
|
|
public string FolderName
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Adds a new sub-folder to this folder
|
|
/// </summary>
|
|
/// <param name="SubFolderName">Name of the new folder</param>
|
|
/// <returns>The newly-added folder</returns>
|
|
public MasterProjectFolder AddSubFolder( string SubFolderName )
|
|
{
|
|
MasterProjectFolder ResultFolder = null;
|
|
|
|
List<string> FolderNames = SubFolderName.Split(new char[2] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, 2, StringSplitOptions.RemoveEmptyEntries).ToList();
|
|
string FirstFolderName = FolderNames[0];
|
|
|
|
bool AlreadyExists = false;
|
|
foreach (MasterProjectFolder ExistingFolder in SubFolders)
|
|
{
|
|
if (ExistingFolder.FolderName.Equals(FirstFolderName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
// Already exists!
|
|
ResultFolder = ExistingFolder;
|
|
AlreadyExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!AlreadyExists)
|
|
{
|
|
ResultFolder = OwnerProjectFileGenerator.AllocateMasterProjectFolder(OwnerProjectFileGenerator, FirstFolderName);
|
|
SubFolders.Add(ResultFolder);
|
|
}
|
|
|
|
if (FolderNames.Count > 1)
|
|
{
|
|
ResultFolder = ResultFolder.AddSubFolder(FolderNames[1]);
|
|
}
|
|
|
|
return ResultFolder;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively searches for the specified project and returns the folder that it lives in, or null if not found
|
|
/// </summary>
|
|
/// <param name="Project">The project file to look for</param>
|
|
/// <returns>The found folder that the project is in, or null</returns>
|
|
public MasterProjectFolder FindFolderForProject( ProjectFile Project )
|
|
{
|
|
foreach( MasterProjectFolder CurFolder in SubFolders )
|
|
{
|
|
MasterProjectFolder FoundFolder = CurFolder.FindFolderForProject( Project );
|
|
if( FoundFolder != null )
|
|
{
|
|
return FoundFolder;
|
|
}
|
|
}
|
|
|
|
foreach( ProjectFile ChildProject in ChildProjects )
|
|
{
|
|
if( ChildProject == Project )
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// Owner project generator
|
|
readonly ProjectFileGenerator OwnerProjectFileGenerator;
|
|
|
|
/// Sub-folders
|
|
public readonly List<MasterProjectFolder> SubFolders = new List<MasterProjectFolder>();
|
|
|
|
/// Child projects
|
|
public readonly List<ProjectFile> ChildProjects = new List<ProjectFile>();
|
|
|
|
/// Files in this folder. These are files that aren't part of any project, but display in the IDE under the project folder
|
|
/// and can be browsed/opened by the user easily in the user interface
|
|
public readonly List<string> Files = new List<string>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The type of project files to generate
|
|
/// </summary>
|
|
enum ProjectFileFormat
|
|
{
|
|
Make,
|
|
CMake,
|
|
QMake,
|
|
KDevelop,
|
|
CodeLite,
|
|
VisualStudio,
|
|
VisualStudio2012,
|
|
VisualStudio2013,
|
|
VisualStudio2015,
|
|
VisualStudio2017,
|
|
XCode,
|
|
Eddie,
|
|
VisualStudioCode,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Static class containing
|
|
/// </summary>
|
|
static class ProjectFileGeneratorSettings
|
|
{
|
|
/// <summary>
|
|
/// Default list of project file formats to generate
|
|
/// </summary>
|
|
[XmlConfigFile(Category = "ProjectFileGenerator", Name = "Format")]
|
|
public static string Format = null;
|
|
|
|
/// <summary>
|
|
/// Parses a list of project file formats from a string
|
|
/// </summary>
|
|
/// <param name="Formats"></param>
|
|
/// <returns>Sequence of project file formats</returns>
|
|
public static IEnumerable<ProjectFileFormat> ParseFormatList(string Formats)
|
|
{
|
|
foreach(string FormatName in Formats.Split('+').Select(x => x.Trim()))
|
|
{
|
|
ProjectFileFormat Format;
|
|
if(Enum.TryParse(FormatName, true, out Format))
|
|
{
|
|
yield return Format;
|
|
}
|
|
else
|
|
{
|
|
Log.TraceError("Invalid project file format '{0}'", FormatName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base class for all project file generators
|
|
/// </summary>
|
|
abstract class ProjectFileGenerator
|
|
{
|
|
/// Global static that enables generation of project files. Doesn't actually compile anything.
|
|
/// This is enabled only via UnrealBuildTool command-line.
|
|
public static bool bGenerateProjectFiles = false;
|
|
|
|
/// True if we're generating lightweight project files for a single game only, excluding most engine code, documentation, etc.
|
|
public bool bGeneratingGameProjectFiles = false;
|
|
|
|
/// Optional list of platforms to generate projects for
|
|
readonly List<UnrealTargetPlatform> ProjectPlatforms = new List<UnrealTargetPlatform>();
|
|
|
|
/// When bGeneratingGameProjectFiles=true, this is the game name we're generating projects for
|
|
protected string GameProjectName = null;
|
|
|
|
/// Global static that only adds platforms that are supported when generating a given target.
|
|
/// This was the old behavior, and it resulted in scenarios where having an unsupported platform selected
|
|
/// in the platform drop-down would silently 'switch' to building Win32.
|
|
/// The new behavior is to add all platforms when generating a target, and then check if it is supported
|
|
/// at build time. If it is not, then a BuildException is thrown informing the user of an unsupported platform.
|
|
/// NOTE: This only matters when using "-AllProjects". It can increase the project file load times though, because of all
|
|
/// of the extra project configuration combinations we need to store
|
|
public static bool bCreateDummyConfigsForUnsupportedPlatforms = true;
|
|
|
|
/// Whether we should include configurations for "Test" and "Shipping" in generated projects (pass "-NoShippingConfigs" to disable this)
|
|
public static bool bIncludeTestAndShippingConfigs = true;
|
|
|
|
/// True if intellisense data should be generated (takes a while longer)
|
|
bool bGenerateIntelliSenseData = true;
|
|
|
|
/// True if we should include documentation in the generated projects
|
|
[XmlConfigFile]
|
|
protected bool bIncludeDocumentation = false;
|
|
|
|
/// True if all documentation languages should be included in generated projects, otherwise only "INT" will be included
|
|
bool bAllDocumentationLanguages = false;
|
|
|
|
/// True if build targets should pass the -useprecompiled argument
|
|
public static bool bUsePrecompiled = false;
|
|
|
|
/// True if we should include engine source in the generated solution
|
|
protected bool bIncludeEngineSource = true;
|
|
|
|
/// True if shader source files should be included in the generated projects
|
|
protected bool bIncludeShaderSource = true;
|
|
|
|
/// True if build system files should be included
|
|
bool bIncludeBuildSystemFiles = true;
|
|
|
|
/// True if we should include config files (.ini files) in the generated project
|
|
protected bool bIncludeConfigFiles = true;
|
|
|
|
/// True if we should include localization files (.int/.kor/etc files) in the generated project
|
|
bool bIncludeLocalizationFiles = false;
|
|
|
|
/// True if we should include template files (.template files) in the generated project
|
|
protected bool bIncludeTemplateFiles = true;
|
|
|
|
/// True if we should include program projects in the generated solution
|
|
protected bool IncludeEnginePrograms = true;
|
|
|
|
/// True if we should include .NET Core projects in the generated solution
|
|
bool bIncludeDotNETCoreProjects = false;
|
|
|
|
/// True if we should reflect "Source" sub-directories on disk in the master project as master project directories.
|
|
/// This arguably adds some visual clutter to the master project, but is truer to the on-disk file organization.
|
|
bool bKeepSourceSubDirectories = true;
|
|
|
|
/// Relative path to the directory where the master project file will be saved to
|
|
public static DirectoryReference MasterProjectPath = UnrealBuildTool.RootDirectory; // We'll save the master project to our "root" folder
|
|
|
|
/// Name of the UE4 engine project that contains all of the engine code, config files and other files
|
|
public static readonly string EngineProjectFileNameBase = "UE4";
|
|
|
|
/// Name of the UE4 enterprise project that contains all of the enterprise code, config files and other files
|
|
public static readonly string EnterpriseProjectFileNameBase = "Studio";
|
|
|
|
/// When ProjectsAreIntermediate is true, this is the directory to store generated project files
|
|
// @todo projectfiles: Ideally, projects for game modules/targets would be created in the game's Intermediate folder!
|
|
public static DirectoryReference IntermediateProjectFilesPath = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Intermediate", "ProjectFiles" );
|
|
|
|
/// Path to timestamp file, recording when was the last time projects were created.
|
|
public static string ProjectTimestampFile = Path.Combine(IntermediateProjectFilesPath.FullName, "Timestamp");
|
|
|
|
/// Global static new line string used by ProjectFileGenerator to generate project files.
|
|
public static readonly string NewLine = Environment.NewLine;
|
|
|
|
/// If true, we'll parse subdirectories of third-party projects to locate source and header files to include in the
|
|
/// generated projects. This can make the generated projects quite a bit bigger, but makes it easier to open files
|
|
/// directly from the IDE.
|
|
bool bGatherThirdPartySource = false;
|
|
|
|
/// Indicates whether we should process dot net core based C# projects
|
|
bool AllowDotNetCoreProjects = false;
|
|
|
|
/// Name of the master project file (e.g. base file name for the solution file for Visual Studio, or the Xcode project file on Mac)
|
|
protected string MasterProjectName = "UE4";
|
|
|
|
/// Maps all module names that were included in generated project files, to actual project file objects.
|
|
/// @todo projectfiles: Nasty global static list. This is only really used for IntelliSense, and to avoid extra folder searches for projects we've already cached source files for.
|
|
public static readonly Dictionary<string, ProjectFile> ModuleToProjectFileMap = new Dictionary<string, ProjectFile>( StringComparer.InvariantCultureIgnoreCase );
|
|
|
|
/// If generating project files for a single project, the path to its .uproject file.
|
|
public readonly FileReference OnlyGameProject;
|
|
|
|
/// When generating IntelliSense data, we may want to only generate data for a specific project file, even if other targets make use of modules
|
|
/// in this project file. This is useful to prevent unusual or hacky global definitions from Programs affecting the Editor/Engine modules. We
|
|
/// always want the most common and useful definitions to be set when working with solutions with many modules.
|
|
public static ProjectFile OnlyGenerateIntelliSenseDataForProject
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
|
|
/// File extension for project files we'll be generating (e.g. ".vcxproj")
|
|
abstract public string ProjectFileExtension
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// True if we should include IntelliSense data in the generated project files when possible
|
|
virtual public bool ShouldGenerateIntelliSenseData()
|
|
{
|
|
return bGenerateIntelliSenseData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default constructor.
|
|
/// </summary>
|
|
/// <param name="InOnlyGameProject">The project file passed in on the command line</param>
|
|
public ProjectFileGenerator(FileReference InOnlyGameProject)
|
|
{
|
|
AllowDotNetCoreProjects = Environment.CommandLine.Contains("-dotnetcore");
|
|
OnlyGameProject = InOnlyGameProject;
|
|
XmlConfig.ApplyTo(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds all .automation.csproj files to the solution.
|
|
/// </summary>
|
|
void AddAutomationModules(List<FileReference> UnrealProjectFiles, MasterProjectFolder ProgramsFolder)
|
|
{
|
|
MasterProjectFolder Folder = ProgramsFolder.AddSubFolder("Automation");
|
|
List<DirectoryReference> BuildFolders = new List<DirectoryReference>();
|
|
foreach (FileReference UnrealProjectFile in UnrealProjectFiles)
|
|
{
|
|
DirectoryReference GameBuildFolder = DirectoryReference.Combine(UnrealProjectFile.Directory, "Build");
|
|
if (DirectoryReference.Exists(GameBuildFolder))
|
|
{
|
|
BuildFolders.Add(GameBuildFolder);
|
|
}
|
|
}
|
|
|
|
// Find all the automation modules .csproj files to add
|
|
List<FileReference> ModuleFiles = RulesCompiler.FindAllRulesSourceFiles(RulesCompiler.RulesFileType.AutomationModule, null, ForeignPlugins:null, AdditionalSearchPaths: BuildFolders );
|
|
foreach (FileReference ProjectFile in ModuleFiles)
|
|
{
|
|
if (FileReference.Exists(ProjectFile))
|
|
{
|
|
VCSharpProjectFile Project = new VCSharpProjectFile(ProjectFile);
|
|
Project.ShouldBuildForAllSolutionTargets = true;
|
|
AddExistingProjectFile(Project, bForceDevelopmentConfiguration: true);
|
|
AutomationProjectFiles.Add( Project );
|
|
Folder.ChildProjects.Add( Project );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all csproj within Engine/Source/Programs, and add them if their UE4CSharp.prog file exists.
|
|
/// </summary>
|
|
void DiscoverCSharpProgramProjects(MasterProjectFolder ProgramsFolder)
|
|
{
|
|
List<FileReference> FoundProjects = new List<FileReference>();
|
|
DirectoryReference EngineProgramsSource = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Source", "Programs");
|
|
DiscoverCSharpProgramProjectsRecursively(EngineProgramsSource, FoundProjects);
|
|
foreach (FileReference FoundProject in FoundProjects)
|
|
{
|
|
VCSharpProjectFile Project = new VCSharpProjectFile(FoundProject);
|
|
|
|
if (AllowDotNetCoreProjects || !Project.IsDotNETCoreProject())
|
|
{
|
|
Project.ShouldBuildForAllSolutionTargets = true;
|
|
Project.ShouldBuildByDefaultForSolutionTargets = true;
|
|
|
|
AddExistingProjectFile(Project, bForceDevelopmentConfiguration: false);
|
|
ProgramsFolder.ChildProjects.Add(Project);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void DiscoverCSharpProgramProjectsRecursively(DirectoryReference SearchFolder, List<FileReference> FoundProjects)
|
|
{
|
|
// Scan all the files in this directory
|
|
bool bSearchSubFolders = true;
|
|
foreach (FileReference File in DirectoryLookupCache.EnumerateFiles(SearchFolder))
|
|
{
|
|
// If we find a csproj or sln, we should not recurse this directory.
|
|
bool bIsCsProj = File.HasExtension(".csproj");
|
|
bool bIsSln = File.HasExtension(".sln");
|
|
bSearchSubFolders &= !(bIsCsProj || bIsSln);
|
|
// If we found an sln, ignore completely.
|
|
if (bIsSln)
|
|
{
|
|
break;
|
|
}
|
|
// For csproj files, add them to the sln if the UE4CSharp.prog file also exists.
|
|
if (bIsCsProj && FileReference.Exists(FileReference.Combine(SearchFolder, "UE4CSharp.prog")))
|
|
{
|
|
FoundProjects.Add(File);
|
|
}
|
|
}
|
|
|
|
// If we didn't find anything to stop the search, search all the subdirectories too
|
|
if (bSearchSubFolders)
|
|
{
|
|
foreach (DirectoryReference SubDirectory in DirectoryLookupCache.EnumerateDirectories(SearchFolder))
|
|
{
|
|
DiscoverCSharpProgramProjectsRecursively(SubDirectory, FoundProjects);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the game projects that we're generating project files for
|
|
/// </summary>
|
|
/// <returns>List of project files</returns>
|
|
public List<FileReference> FindGameProjects()
|
|
{
|
|
List<FileReference> ProjectFiles = new List<FileReference>();
|
|
if(bGeneratingGameProjectFiles)
|
|
{
|
|
ProjectFiles.Add(OnlyGameProject);
|
|
}
|
|
else
|
|
{
|
|
ProjectFiles.AddRange(UProjectInfo.AllProjectFiles.Where(x => ShouldGenerateProject(x)));
|
|
}
|
|
return ProjectFiles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if we should generate IDE project files for for the given project
|
|
/// </summary>
|
|
/// <param name="ProjectFile">The project to check</param>
|
|
/// <returns>True to generate a project, false otherwise</returns>
|
|
private static bool ShouldGenerateProject(FileReference ProjectFile)
|
|
{
|
|
DirectoryReference ProjectDirectory = ProjectFile.Directory;
|
|
DirectoryReference SourceFolder = DirectoryReference.Combine(ProjectDirectory, "Source");
|
|
DirectoryReference IntermediateSourceFolder = DirectoryReference.Combine(ProjectDirectory, "Intermediate", "Source");
|
|
return DirectoryReference.Exists(SourceFolder) || DirectoryReference.Exists(IntermediateSourceFolder);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a Visual Studio solution file and Visual C++ project files for all known engine and game targets.
|
|
/// Does not actually build anything.
|
|
/// </summary>
|
|
/// <param name="Arguments">Command-line arguments</param>
|
|
public virtual bool GenerateProjectFiles( String[] Arguments )
|
|
{
|
|
bool bSuccess = true;
|
|
|
|
// Parse project generator options
|
|
bool IncludeAllPlatforms = true;
|
|
ConfigureProjectFileGeneration( Arguments, ref IncludeAllPlatforms);
|
|
|
|
if (bGeneratingGameProjectFiles || UnrealBuildTool.IsEngineInstalled())
|
|
{
|
|
Log.TraceInformation("Discovering modules, targets and source code for project...");
|
|
|
|
MasterProjectPath = OnlyGameProject.Directory;
|
|
|
|
// Set the project file name
|
|
MasterProjectName = OnlyGameProject.GetFileNameWithoutExtension();
|
|
|
|
if (!DirectoryReference.Exists(DirectoryReference.Combine(MasterProjectPath, "Source")))
|
|
{
|
|
if (!DirectoryReference.Exists(DirectoryReference.Combine(MasterProjectPath, "Intermediate", "Source")))
|
|
{
|
|
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
|
|
{
|
|
MasterProjectPath = UnrealBuildTool.EngineDirectory;
|
|
GameProjectName = "UE4Game";
|
|
}
|
|
if (!DirectoryReference.Exists(DirectoryReference.Combine(MasterProjectPath, "Source")))
|
|
{
|
|
throw new BuildException("Directory '{0}' is missing 'Source' folder.", MasterProjectPath);
|
|
}
|
|
}
|
|
}
|
|
IntermediateProjectFilesPath = DirectoryReference.Combine(MasterProjectPath, "Intermediate", "ProjectFiles");
|
|
}
|
|
|
|
// Modify the name if specific platforms were given
|
|
if (ProjectPlatforms.Count > 0)
|
|
{
|
|
// Sort the platforms names so we get consistent names
|
|
List<string> SortedPlatformNames = new List<string>();
|
|
foreach (UnrealTargetPlatform SpecificPlatform in ProjectPlatforms)
|
|
{
|
|
SortedPlatformNames.Add(SpecificPlatform.ToString());
|
|
}
|
|
SortedPlatformNames.Sort();
|
|
|
|
MasterProjectName += "_";
|
|
foreach (string SortedPlatform in SortedPlatformNames)
|
|
{
|
|
MasterProjectName += SortedPlatform;
|
|
IntermediateProjectFilesPath = new DirectoryReference(IntermediateProjectFilesPath.FullName + SortedPlatform);
|
|
}
|
|
}
|
|
|
|
// Optionally include the folder name in the project
|
|
if (Environment.GetEnvironmentVariable("UE_NAME_PROJECT_AFTER_FOLDER") == "1")
|
|
{
|
|
MasterProjectName += "_" + Path.GetFileName(MasterProjectPath.ToString());
|
|
}
|
|
|
|
bool bCleanProjectFiles = Arguments.Any(x => x.Equals("-CleanProjects", StringComparison.InvariantCultureIgnoreCase));
|
|
if (bCleanProjectFiles)
|
|
{
|
|
CleanProjectFiles(MasterProjectPath, MasterProjectName, IntermediateProjectFilesPath);
|
|
}
|
|
|
|
// Figure out which platforms we should generate project files for.
|
|
string SupportedPlatformNames;
|
|
SetupSupportedPlatformsAndConfigurations( IncludeAllPlatforms:IncludeAllPlatforms, SupportedPlatformNames:out SupportedPlatformNames );
|
|
|
|
Log.TraceVerbose( "Detected supported platforms: " + SupportedPlatformNames );
|
|
|
|
RootFolder = AllocateMasterProjectFolder( this, "<Root>" );
|
|
|
|
// Build the list of games to generate projects for
|
|
List<FileReference> AllGameProjects = FindGameProjects();
|
|
|
|
// Find all of the module files. This will filter out any modules or targets that don't belong to platforms
|
|
// we're generating project files for.
|
|
List<FileReference> AllModuleFiles = DiscoverModules(AllGameProjects);
|
|
|
|
ProjectFile EngineProject = null;
|
|
ProjectFile EnterpriseProject = null;
|
|
Dictionary<DirectoryReference, ProjectFile> GameProjects = null;
|
|
Dictionary<DirectoryReference, ProjectFile> ModProjects = null;
|
|
Dictionary<FileReference, ProjectFile> ProgramProjects = null;
|
|
Dictionary<DirectoryReference, ProjectFile> TemplateGameProjects = null;
|
|
Dictionary<DirectoryReference, ProjectFile> SampleGameProjects = null;
|
|
{
|
|
// Setup buildable projects for all targets
|
|
AddProjectsForAllTargets( AllGameProjects, out EngineProject, out EnterpriseProject, out GameProjects, out ModProjects, out ProgramProjects, out TemplateGameProjects, out SampleGameProjects );
|
|
|
|
// Add all game projects and game config files
|
|
AddAllGameProjects(GameProjects, SupportedPlatformNames, RootFolder);
|
|
|
|
// Set the game to be the default project
|
|
if(ModProjects.Count > 0)
|
|
{
|
|
DefaultProject = ModProjects.Values.First();
|
|
}
|
|
else if(bGeneratingGameProjectFiles && GameProjects.Count > 0)
|
|
{
|
|
DefaultProject = GameProjects.Values.First();
|
|
}
|
|
|
|
//Related Debug Project Files - Tuple here has the related Debug Project, SolutionFolder
|
|
List<Tuple<ProjectFile, string>> DebugProjectFiles = new List<Tuple<ProjectFile, string>>();
|
|
|
|
// Place projects into root level solution folders
|
|
if ( bIncludeEngineSource )
|
|
{
|
|
// If we're still missing an engine project because we don't have any targets for it, make one up.
|
|
if( EngineProject == null )
|
|
{
|
|
FileReference ProjectFilePath = FileReference.Combine(IntermediateProjectFilesPath, EngineProjectFileNameBase + ProjectFileExtension);
|
|
|
|
bool bAlreadyExisted;
|
|
EngineProject = FindOrAddProject(ProjectFilePath, true, out bAlreadyExisted);
|
|
|
|
EngineProject.IsForeignProject = false;
|
|
EngineProject.IsGeneratedProject = true;
|
|
EngineProject.IsStubProject = true;
|
|
}
|
|
|
|
if( EngineProject != null )
|
|
{
|
|
RootFolder.AddSubFolder( "Engine" ).ChildProjects.Add( EngineProject );
|
|
|
|
// Engine config files
|
|
if( bIncludeConfigFiles )
|
|
{
|
|
AddEngineConfigFiles( EngineProject );
|
|
if( IncludeEnginePrograms )
|
|
{
|
|
AddUnrealHeaderToolConfigFiles(EngineProject);
|
|
AddUBTConfigFilesToEngineProject(EngineProject);
|
|
}
|
|
}
|
|
|
|
// Engine Extras files
|
|
AddEngineExtrasFiles(EngineProject);
|
|
|
|
// Engine localization files
|
|
if( bIncludeLocalizationFiles )
|
|
{
|
|
AddEngineLocalizationFiles( EngineProject );
|
|
}
|
|
|
|
// Engine template files
|
|
if (bIncludeTemplateFiles)
|
|
{
|
|
AddEngineTemplateFiles( EngineProject );
|
|
}
|
|
|
|
if( bIncludeShaderSource )
|
|
{
|
|
Log.TraceVerbose( "Adding shader source code..." );
|
|
|
|
// Find shader source files and generate stub project
|
|
AddEngineShaderSource( EngineProject );
|
|
}
|
|
|
|
if( bIncludeBuildSystemFiles )
|
|
{
|
|
Log.TraceVerbose( "Adding build system files..." );
|
|
|
|
AddEngineBuildFiles( EngineProject );
|
|
}
|
|
|
|
if( bIncludeDocumentation )
|
|
{
|
|
AddEngineDocumentation( EngineProject );
|
|
}
|
|
|
|
List<Tuple<ProjectFile, string>> NewProjectFiles = EngineProject.WriteDebugProjectFiles(InPlatforms: SupportedPlatforms, InConfigurations: SupportedConfigurations);
|
|
|
|
if (NewProjectFiles != null)
|
|
{
|
|
DebugProjectFiles.AddRange(NewProjectFiles);
|
|
}
|
|
}
|
|
|
|
if (EnterpriseProject != null)
|
|
{
|
|
RootFolder.AddSubFolder(UnrealBuildTool.EnterpriseDirectory.GetDirectoryName()).ChildProjects.Add(EnterpriseProject);
|
|
}
|
|
|
|
foreach( ProjectFile CurModProject in ModProjects.Values )
|
|
{
|
|
RootFolder.AddSubFolder("Mods").ChildProjects.Add(CurModProject);
|
|
}
|
|
|
|
foreach( ProjectFile CurGameProject in GameProjects.Values )
|
|
{
|
|
// Templates go under a different solution folder than games
|
|
DirectoryReference TemplateGameDirectory = TemplateGameProjects.FirstOrDefault( FolderAndProject => FolderAndProject.Value == CurGameProject ).Key;
|
|
DirectoryReference SampleGameDirectory = SampleGameProjects.FirstOrDefault( FolderAndProject => FolderAndProject.Value == CurGameProject ).Key;
|
|
|
|
if( TemplateGameDirectory != null )
|
|
{
|
|
if( TemplateGameDirectory.IsUnderDirectory((UnrealBuildTool.EnterpriseDirectory)) )
|
|
{
|
|
RootFolder.AddSubFolder(UnrealBuildTool.EnterpriseDirectory.GetDirectoryName() + Path.DirectorySeparatorChar + "Templates").ChildProjects.Add(CurGameProject);
|
|
}
|
|
else
|
|
{
|
|
RootFolder.AddSubFolder("Templates").ChildProjects.Add(CurGameProject);
|
|
}
|
|
}
|
|
else if( SampleGameDirectory != null )
|
|
{
|
|
if( SampleGameDirectory.IsUnderDirectory((UnrealBuildTool.EnterpriseDirectory)) )
|
|
{
|
|
RootFolder.AddSubFolder(UnrealBuildTool.EnterpriseDirectory.GetDirectoryName() + Path.DirectorySeparatorChar + "Samples").ChildProjects.Add(CurGameProject);
|
|
}
|
|
else
|
|
{
|
|
RootFolder.AddSubFolder("Samples").ChildProjects.Add(CurGameProject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RootFolder.AddSubFolder( "Games" ).ChildProjects.Add( CurGameProject );
|
|
}
|
|
|
|
List<Tuple<ProjectFile, string>> NewProjectFiles = CurGameProject.WriteDebugProjectFiles(InPlatforms: SupportedPlatforms, InConfigurations: SupportedConfigurations);
|
|
|
|
if (NewProjectFiles != null)
|
|
{
|
|
DebugProjectFiles.AddRange(NewProjectFiles);
|
|
}
|
|
|
|
}
|
|
|
|
//Related Debug Project Files - Tuple has the related Debug Project, SolutionFolder
|
|
foreach (Tuple<ProjectFile, string> DebugProjectFile in DebugProjectFiles)
|
|
{
|
|
AddExistingProjectFile(DebugProjectFile.Item1, bForceDevelopmentConfiguration: false);
|
|
|
|
//add it to the Android Debug Projects folder in the solution
|
|
RootFolder.AddSubFolder(DebugProjectFile.Item2).ChildProjects.Add(DebugProjectFile.Item1);
|
|
}
|
|
|
|
|
|
foreach( KeyValuePair<FileReference, ProjectFile> CurProgramProject in ProgramProjects )
|
|
{
|
|
ProjectTarget Target = CurProgramProject.Value.ProjectTargets.FirstOrDefault(t => !String.IsNullOrEmpty(t.TargetRules.SolutionDirectory));
|
|
|
|
if (Target != null)
|
|
{
|
|
RootFolder.AddSubFolder(Target.TargetRules.SolutionDirectory).ChildProjects.Add(CurProgramProject.Value);
|
|
}
|
|
else
|
|
{
|
|
if (CurProgramProject.Key.IsUnderDirectory(UnrealBuildTool.EnterpriseDirectory))
|
|
{
|
|
RootFolder.AddSubFolder(UnrealBuildTool.EnterpriseDirectory.GetDirectoryName() + Path.DirectorySeparatorChar + "Programs").ChildProjects.Add(CurProgramProject.Value);
|
|
}
|
|
else
|
|
{
|
|
RootFolder.AddSubFolder( "Programs" ).ChildProjects.Add( CurProgramProject.Value );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add all of the config files for generated program targets
|
|
AddEngineProgramConfigFiles( ProgramProjects );
|
|
}
|
|
}
|
|
|
|
// Setup "stub" projects for all modules
|
|
AddProjectsForAllModules(AllGameProjects, ProgramProjects, ModProjects, AllModuleFiles, bGatherThirdPartySource);
|
|
|
|
{
|
|
if( IncludeEnginePrograms )
|
|
{
|
|
MasterProjectFolder ProgramsFolder = RootFolder.AddSubFolder( "Programs" );
|
|
|
|
// Add UnrealBuildTool to the master project
|
|
AddUnrealBuildToolProject( ProgramsFolder );
|
|
|
|
// Add AutomationTool to the master project
|
|
ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("AutomationTool", bShouldBuildForAllSolutionTargets: true, bForceDevelopmentConfiguration: true));
|
|
|
|
// Add automation.csproj files to the master project
|
|
AddAutomationModules(AllGameProjects, ProgramsFolder);
|
|
|
|
// Discover C# programs which should additionally be included in the solution.
|
|
DiscoverCSharpProgramProjects(ProgramsFolder);
|
|
}
|
|
|
|
|
|
// Eliminate all redundant master project folders. E.g., folders which contain only one project and that project
|
|
// has the same name as the folder itself. To the user, projects "feel like" folders already in the IDE, so we
|
|
// want to collapse them down where possible.
|
|
EliminateRedundantMasterProjectSubFolders( RootFolder, "" );
|
|
|
|
// Figure out which targets we need about IntelliSense for. We only need to worry about targets for projects
|
|
// that we're actually generating in this session.
|
|
List<Tuple<ProjectFile, ProjectTarget>> IntelliSenseTargetFiles = new List<Tuple<ProjectFile, ProjectTarget>>();
|
|
{
|
|
// Engine targets
|
|
if( EngineProject != null)
|
|
{
|
|
foreach( ProjectTarget ProjectTarget in EngineProject.ProjectTargets )
|
|
{
|
|
if( ProjectTarget.TargetFilePath != null )
|
|
{
|
|
// Only bother with the editor target. We want to make sure that definitions are setup to be as inclusive as possible
|
|
// for good quality IntelliSense. For example, we want WITH_EDITORONLY_DATA=1, so using the editor targets works well.
|
|
if( ProjectTarget.TargetRules.Type == TargetType.Editor )
|
|
{
|
|
IntelliSenseTargetFiles.Add( Tuple.Create(EngineProject, ProjectTarget) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enterprise targets
|
|
if (EnterpriseProject != null)
|
|
{
|
|
foreach (ProjectTarget ProjectTarget in EnterpriseProject.ProjectTargets)
|
|
{
|
|
if (ProjectTarget.TargetFilePath != null)
|
|
{
|
|
// Only bother with the editor target. We want to make sure that definitions are setup to be as inclusive as possible
|
|
// for good quality IntelliSense. For example, we want WITH_EDITORONLY_DATA=1, so using the editor targets works well.
|
|
if (ProjectTarget.TargetRules.Type == TargetType.Editor)
|
|
{
|
|
IntelliSenseTargetFiles.Add(Tuple.Create(EnterpriseProject, ProjectTarget));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Program targets
|
|
foreach( ProjectFile ProgramProject in ProgramProjects.Values )
|
|
{
|
|
foreach( ProjectTarget ProjectTarget in ProgramProject.ProjectTargets )
|
|
{
|
|
if( ProjectTarget.TargetFilePath != null )
|
|
{
|
|
IntelliSenseTargetFiles.Add( Tuple.Create( ProgramProject, ProjectTarget ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Game/template targets
|
|
foreach( ProjectFile GameProject in GameProjects.Values )
|
|
{
|
|
foreach( ProjectTarget ProjectTarget in GameProject.ProjectTargets )
|
|
{
|
|
if( ProjectTarget.TargetFilePath != null )
|
|
{
|
|
// Only bother with the editor target. We want to make sure that definitions are setup to be as inclusive as possible
|
|
// for good quality IntelliSense. For example, we want WITH_EDITORONLY_DATA=1, so using the editor targets works well.
|
|
if( ProjectTarget.TargetRules.Type == TargetType.Editor )
|
|
{
|
|
IntelliSenseTargetFiles.Add( Tuple.Create( GameProject, ProjectTarget ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate IntelliSense data if we need to. This involves having UBT simulate the action compilation of
|
|
// the targets so that we can extra the compiler defines, include paths, etc.
|
|
if(GenerateIntelliSenseData(Arguments, IntelliSenseTargetFiles))
|
|
{
|
|
WriteProjectFiles();
|
|
Log.TraceVerbose( "Project generation complete ({0} generated, {1} imported)", GeneratedProjectFiles.Count, OtherProjectFiles.Count );
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds detected UBT configuration files (BuildConfiguration.xml) to engine project.
|
|
/// </summary>
|
|
/// <param name="EngineProject">Engine project to add files to.</param>
|
|
private void AddUBTConfigFilesToEngineProject(ProjectFile EngineProject)
|
|
{
|
|
EngineProject.AddAliasedFileToProject(new AliasedFile(
|
|
XmlConfig.GetSchemaLocation().FullName,
|
|
Path.Combine("Programs", "UnrealBuildTool")
|
|
));
|
|
|
|
List<XmlConfig.InputFile> InputFiles = XmlConfig.FindInputFiles();
|
|
foreach(XmlConfig.InputFile InputFile in InputFiles)
|
|
{
|
|
EngineProject.AddAliasedFileToProject(
|
|
new AliasedFile(
|
|
InputFile.Location.FullName,
|
|
Path.Combine("Config", "UnrealBuildTool", InputFile.FolderName)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clean project files
|
|
/// </summary>
|
|
/// <param name="InMasterProjectDirectory">The master project directory</param>
|
|
/// <param name="InMasterProjectName">The name of the master project</param>
|
|
/// <param name="InIntermediateProjectFilesDirectory">The intermediate path of project files</param>
|
|
public abstract void CleanProjectFiles(DirectoryReference InMasterProjectDirectory, string InMasterProjectName, DirectoryReference InIntermediateProjectFilesDirectory);
|
|
|
|
/// <summary>
|
|
/// Configures project generator based on command-line options
|
|
/// </summary>
|
|
/// <param name="Arguments">Arguments passed into the program</param>
|
|
/// <param name="IncludeAllPlatforms">True if all platforms should be included</param>
|
|
protected virtual void ConfigureProjectFileGeneration( String[] Arguments, ref bool IncludeAllPlatforms )
|
|
{
|
|
bool bAlwaysIncludeEngineModules = false;
|
|
foreach( string CurArgument in Arguments )
|
|
{
|
|
if( CurArgument.StartsWith( "-" ) )
|
|
{
|
|
if (CurArgument.StartsWith( "-Platforms=", StringComparison.InvariantCultureIgnoreCase ))
|
|
{
|
|
// Parse the list... will be in Foo+Bar+New format
|
|
string PlatformList = CurArgument.Substring(11);
|
|
while (PlatformList.Length > 0)
|
|
{
|
|
string PlatformString = PlatformList;
|
|
Int32 PlusIdx = PlatformList.IndexOf("+");
|
|
if (PlusIdx != -1)
|
|
{
|
|
PlatformString = PlatformList.Substring(0, PlusIdx);
|
|
PlatformList = PlatformList.Substring(PlusIdx + 1);
|
|
}
|
|
else
|
|
{
|
|
// We are on the last platform... clear the list to exit the loop
|
|
PlatformList = "";
|
|
}
|
|
|
|
// Is the string a valid platform? If so, add it to the list
|
|
UnrealTargetPlatform SpecifiedPlatform = UnrealTargetPlatform.Unknown;
|
|
foreach (UnrealTargetPlatform PlatformParam in Enum.GetValues(typeof(UnrealTargetPlatform)))
|
|
{
|
|
if (PlatformString.Equals(PlatformParam.ToString(), StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
SpecifiedPlatform = PlatformParam;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SpecifiedPlatform != UnrealTargetPlatform.Unknown)
|
|
{
|
|
if (ProjectPlatforms.Contains(SpecifiedPlatform) == false)
|
|
{
|
|
ProjectPlatforms.Add(SpecifiedPlatform);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.TraceWarning("ProjectFiles invalid platform specified: {0}", PlatformString);
|
|
}
|
|
}
|
|
}
|
|
else switch( CurArgument.ToUpperInvariant() )
|
|
{
|
|
case "-ALLPLATFORMS":
|
|
IncludeAllPlatforms = true;
|
|
break;
|
|
|
|
case "-CURRENTPLATFORM":
|
|
IncludeAllPlatforms = false;
|
|
break;
|
|
|
|
case "-THIRDPARTY":
|
|
bGatherThirdPartySource = true;
|
|
break;
|
|
|
|
case "-GAME":
|
|
// Generates project files for a single game
|
|
bGeneratingGameProjectFiles = true;
|
|
break;
|
|
|
|
case "-ENGINE":
|
|
// Forces engine modules and targets to be included in game-specific project files
|
|
bAlwaysIncludeEngineModules = true;
|
|
break;
|
|
|
|
case "-NOINTELLISENSE":
|
|
bGenerateIntelliSenseData = false;
|
|
break;
|
|
|
|
case "-INTELLISENSE":
|
|
bGenerateIntelliSenseData = true;
|
|
break;
|
|
|
|
case "-SHIPPINGCONFIGS":
|
|
bIncludeTestAndShippingConfigs = true;
|
|
break;
|
|
|
|
case "-NOSHIPPINGCONFIGS":
|
|
bIncludeTestAndShippingConfigs = false;
|
|
break;
|
|
|
|
case "-DUMMYCONFIGS":
|
|
bCreateDummyConfigsForUnsupportedPlatforms = true;
|
|
break;
|
|
|
|
case "-NODUMMYCONFIGS":
|
|
bCreateDummyConfigsForUnsupportedPlatforms = false;
|
|
break;
|
|
|
|
case "-ALLLANGUAGES":
|
|
bAllDocumentationLanguages = true;
|
|
break;
|
|
|
|
case "-USEPRECOMPILED":
|
|
bUsePrecompiled = true;
|
|
break;
|
|
|
|
case "-VSCODE":
|
|
bIncludeDotNETCoreProjects = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bGeneratingGameProjectFiles || UnrealBuildTool.IsEngineInstalled() )
|
|
{
|
|
if (OnlyGameProject == null)
|
|
{
|
|
throw new BuildException("A game project path was not specified, which is required when generating project files using an installed build or passing -game on the command line");
|
|
}
|
|
|
|
GameProjectName = OnlyGameProject.GetFileNameWithoutExtension();
|
|
if (String.IsNullOrEmpty(GameProjectName))
|
|
{
|
|
throw new BuildException("A valid game project was not found in the specified location (" + OnlyGameProject.Directory.FullName + ")");
|
|
}
|
|
|
|
bool bInstalledEngineWithSource = UnrealBuildTool.IsEngineInstalled() && DirectoryReference.Exists(UnrealBuildTool.EngineSourceDirectory);
|
|
|
|
bIncludeEngineSource = bAlwaysIncludeEngineModules || bInstalledEngineWithSource;
|
|
bIncludeDocumentation = false;
|
|
bIncludeBuildSystemFiles = false;
|
|
bIncludeShaderSource = true;
|
|
bIncludeTemplateFiles = false;
|
|
bIncludeConfigFiles = true;
|
|
IncludeEnginePrograms = bAlwaysIncludeEngineModules;
|
|
}
|
|
else
|
|
{
|
|
// At least one extra argument was specified, but we weren't expected it. Ignored.
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Adds all game project files, including target projects and config files
|
|
/// </summary>
|
|
private void AddAllGameProjects(Dictionary<DirectoryReference, ProjectFile> GameProjects, string SupportedPlatformNames, MasterProjectFolder ProjectsFolder)
|
|
{
|
|
foreach( KeyValuePair<DirectoryReference, ProjectFile> GameFolderAndProjectFile in GameProjects )
|
|
{
|
|
DirectoryReference GameProjectDirectory = GameFolderAndProjectFile.Key;
|
|
|
|
// @todo projectfiles: We have engine localization files, but should we also add GAME localization files?
|
|
|
|
// Game config files
|
|
if( bIncludeConfigFiles )
|
|
{
|
|
DirectoryReference GameConfigDirectory = DirectoryReference.Combine(GameProjectDirectory, "Config");
|
|
if( DirectoryReference.Exists(GameConfigDirectory) )
|
|
{
|
|
ProjectFile GameProjectFile = GameFolderAndProjectFile.Value;
|
|
GameProjectFile.AddFilesToProject( SourceFileSearch.FindFiles( GameConfigDirectory ), GameProjectDirectory );
|
|
}
|
|
}
|
|
|
|
// Game build files
|
|
if( bIncludeBuildSystemFiles )
|
|
{
|
|
var GameBuildDirectory = DirectoryReference.Combine(GameProjectDirectory, "Build");
|
|
if( DirectoryReference.Exists(GameBuildDirectory) )
|
|
{
|
|
var SubdirectoryNamesToExclude = new List<string>();
|
|
SubdirectoryNamesToExclude.Add("Receipts");
|
|
SubdirectoryNamesToExclude.Add("Scripts");
|
|
|
|
var GameProjectFile = GameFolderAndProjectFile.Value;
|
|
GameProjectFile.AddFilesToProject( SourceFileSearch.FindFiles( GameBuildDirectory, SubdirectoryNamesToExclude ), GameProjectDirectory );
|
|
}
|
|
}
|
|
|
|
DirectoryReference GameShaderDirectory = DirectoryReference.Combine(GameProjectDirectory, "Shaders");
|
|
if (DirectoryReference.Exists(GameShaderDirectory))
|
|
{
|
|
ProjectFile GameProjectFile = GameFolderAndProjectFile.Value;
|
|
GameProjectFile.AddFilesToProject(SourceFileSearch.FindFiles(GameShaderDirectory), GameProjectDirectory);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine localization text files to the specified project
|
|
private void AddEngineLocalizationFiles( ProjectFile EngineProject )
|
|
{
|
|
DirectoryReference EngineLocalizationDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Content", "Localization" );
|
|
if( DirectoryReference.Exists(EngineLocalizationDirectory) )
|
|
{
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( EngineLocalizationDirectory ), UnrealBuildTool.EngineDirectory );
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine template text files to the specified project
|
|
private void AddEngineTemplateFiles( ProjectFile EngineProject )
|
|
{
|
|
DirectoryReference EngineTemplateDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Content", "Editor", "Templates");
|
|
if (DirectoryReference.Exists(EngineTemplateDirectory))
|
|
{
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( EngineTemplateDirectory ), UnrealBuildTool.EngineDirectory );
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine config files to the specified project
|
|
private void AddEngineConfigFiles( ProjectFile EngineProject )
|
|
{
|
|
DirectoryReference EngineConfigDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Config" );
|
|
if( DirectoryReference.Exists(EngineConfigDirectory) )
|
|
{
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( EngineConfigDirectory ), UnrealBuildTool.EngineDirectory );
|
|
}
|
|
}
|
|
|
|
/// Adds all engine extras files to the specified project
|
|
protected virtual void AddEngineExtrasFiles(ProjectFile EngineProject)
|
|
{
|
|
}
|
|
|
|
/// Adds UnrealHeaderTool config files to the specified project
|
|
private void AddUnrealHeaderToolConfigFiles(ProjectFile EngineProject)
|
|
{
|
|
DirectoryReference UHTConfigDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Programs", "UnrealHeaderTool", "Config");
|
|
if (DirectoryReference.Exists(UHTConfigDirectory))
|
|
{
|
|
EngineProject.AddFilesToProject(SourceFileSearch.FindFiles(UHTConfigDirectory), UnrealBuildTool.EngineDirectory);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds any additional plugin files.
|
|
/// </summary>
|
|
/// <returns>List of additional plugin files</returns>
|
|
private List<FileReference> DiscoverExtraPlugins(List<FileReference> AllGameProjects)
|
|
{
|
|
List<FileReference> AddedPlugins = new List<FileReference>();
|
|
|
|
foreach (FileReference GameProject in AllGameProjects)
|
|
{
|
|
// Check the user preference to see if they'd like to include nativized assets as a generated project.
|
|
bool bIncludeNativizedAssets = false;
|
|
ConfigHierarchy Config = ConfigCache.ReadHierarchy(ConfigHierarchyType.Game, GameProject.Directory, BuildHostPlatform.Current.Platform);
|
|
if (Config != null)
|
|
{
|
|
Config.TryGetValue("/Script/UnrealEd.ProjectPackagingSettings", "bIncludeNativizedAssetsInProjectGeneration", out bIncludeNativizedAssets);
|
|
}
|
|
|
|
// Note: Whether or not we include nativized assets here has no bearing on whether or not they actually get built.
|
|
if (bIncludeNativizedAssets)
|
|
{
|
|
AddedPlugins.AddRange(Plugins.EnumeratePlugins(DirectoryReference.Combine(GameProject.Directory, "Intermediate", "Plugins")).Where(x => x.GetFileNameWithoutExtension() == "NativizedAssets"));
|
|
}
|
|
}
|
|
return AddedPlugins;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all module files (filtering by platform)
|
|
/// </summary>
|
|
/// <returns>Filtered list of module files</returns>
|
|
protected List<FileReference> DiscoverModules(List<FileReference> AllGameProjects)
|
|
{
|
|
List<FileReference> AllModuleFiles = new List<FileReference>();
|
|
|
|
// Locate all modules (*.Build.cs files)
|
|
List<FileReference> FoundModuleFiles = RulesCompiler.FindAllRulesSourceFiles( RulesCompiler.RulesFileType.Module, GameFolders: AllGameProjects.Select(x => x.Directory).ToList(), ForeignPlugins: DiscoverExtraPlugins(AllGameProjects), AdditionalSearchPaths:null );
|
|
foreach( FileReference BuildFileName in FoundModuleFiles )
|
|
{
|
|
AllModuleFiles.Add( BuildFileName );
|
|
}
|
|
return AllModuleFiles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of non-redistributable folders
|
|
/// </summary>
|
|
private static string[] NoRedistFolders = new string[]
|
|
{
|
|
Path.DirectorySeparatorChar + "NoRedist" + Path.DirectorySeparatorChar,
|
|
Path.DirectorySeparatorChar + "NotForLicensees" + Path.DirectorySeparatorChar
|
|
};
|
|
|
|
/// <summary>
|
|
/// Checks if a module is in a non-redistributable folder
|
|
/// </summary>
|
|
private static bool IsNoRedistModule(FileReference ModulePath)
|
|
{
|
|
foreach (string NoRedistFolderName in NoRedistFolders)
|
|
{
|
|
if (ModulePath.FullName.IndexOf(NoRedistFolderName, StringComparison.InvariantCultureIgnoreCase) >= 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all target files (filtering by platform)
|
|
/// </summary>
|
|
/// <returns>Filtered list of target files</returns>
|
|
protected List<FileReference> DiscoverTargets(List<FileReference> AllGameProjects)
|
|
{
|
|
List<FileReference> AllTargetFiles = new List<FileReference>();
|
|
|
|
// Make a list of all platform name strings that we're *not* including in the project files
|
|
List<string> UnsupportedPlatformNameStrings = Utils.MakeListOfUnsupportedPlatforms( SupportedPlatforms );
|
|
|
|
// Locate all targets (*.Target.cs files)
|
|
List<FileReference> FoundTargetFiles = RulesCompiler.FindAllRulesSourceFiles( RulesCompiler.RulesFileType.Target, AllGameProjects.Select(x => x.Directory).ToList(), ForeignPlugins: DiscoverExtraPlugins(AllGameProjects), AdditionalSearchPaths:null );
|
|
foreach( FileReference CurTargetFile in FoundTargetFiles )
|
|
{
|
|
string CleanTargetFileName = Utils.CleanDirectorySeparators( CurTargetFile.FullName );
|
|
|
|
// remove the local root
|
|
string LocalRoot = UnrealBuildTool.RootDirectory.FullName;
|
|
string Search = CleanTargetFileName;
|
|
if (Search.StartsWith(LocalRoot, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
if (LocalRoot.EndsWith("\\") || LocalRoot.EndsWith("/"))
|
|
{
|
|
Search = Search.Substring(LocalRoot.Length - 1);
|
|
}
|
|
else
|
|
{
|
|
Search = Search.Substring(LocalRoot.Length);
|
|
}
|
|
}
|
|
|
|
if (OnlyGameProject != null)
|
|
{
|
|
string ProjectRoot = OnlyGameProject.Directory.FullName;
|
|
if (Search.StartsWith(ProjectRoot, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
if (ProjectRoot.EndsWith("\\") || ProjectRoot.EndsWith("/"))
|
|
{
|
|
Search = Search.Substring(ProjectRoot.Length - 1);
|
|
}
|
|
else
|
|
{
|
|
Search = Search.Substring(ProjectRoot.Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Skip targets in unsupported platform directories
|
|
bool IncludeThisTarget = true;
|
|
foreach( string CurPlatformName in UnsupportedPlatformNameStrings )
|
|
{
|
|
if (Search.IndexOf(Path.DirectorySeparatorChar + CurPlatformName + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase) != -1)
|
|
{
|
|
IncludeThisTarget = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( IncludeThisTarget )
|
|
{
|
|
AllTargetFiles.Add( CurTargetFile );
|
|
}
|
|
}
|
|
|
|
return AllTargetFiles;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively collapses all sub-folders that are redundant. Should only be called after we're done adding
|
|
/// files and projects to the master project.
|
|
/// </summary>
|
|
/// <param name="Folder">The folder whose sub-folders we should potentially collapse into</param>
|
|
/// <param name="ParentMasterProjectFolderPath"></param>
|
|
void EliminateRedundantMasterProjectSubFolders( MasterProjectFolder Folder, string ParentMasterProjectFolderPath )
|
|
{
|
|
// NOTE: This is for diagnostics output only
|
|
string MasterProjectFolderPath = String.IsNullOrEmpty( ParentMasterProjectFolderPath ) ? Folder.FolderName : ( ParentMasterProjectFolderPath + "/" + Folder.FolderName );
|
|
|
|
// We can eliminate folders that meet all of these requirements:
|
|
// 1) Have only a single project file in them
|
|
// 2) Have no files in the folder except project files, and no sub-folders
|
|
// 3) The project file matches the folder name
|
|
//
|
|
// Additionally, if KeepSourceSubDirectories==false, we can eliminate directories called "Source".
|
|
//
|
|
// Also, we can kill folders that are completely empty.
|
|
|
|
|
|
foreach( MasterProjectFolder SubFolder in Folder.SubFolders )
|
|
{
|
|
// Recurse
|
|
EliminateRedundantMasterProjectSubFolders( SubFolder, MasterProjectFolderPath );
|
|
}
|
|
|
|
List<MasterProjectFolder> SubFoldersToAdd = new List<MasterProjectFolder>();
|
|
List<MasterProjectFolder> SubFoldersToRemove = new List<MasterProjectFolder>();
|
|
foreach( MasterProjectFolder SubFolder in Folder.SubFolders )
|
|
{
|
|
bool CanCollapseFolder = false;
|
|
|
|
// 1)
|
|
if( SubFolder.ChildProjects.Count == 1 )
|
|
{
|
|
// 2)
|
|
if( SubFolder.Files.Count == 0 &&
|
|
SubFolder.SubFolders.Count == 0 )
|
|
{
|
|
// 3)
|
|
if (SubFolder.FolderName.Equals(SubFolder.ChildProjects[0].ProjectFilePath.GetFileNameWithoutAnyExtensions(), StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
CanCollapseFolder = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bKeepSourceSubDirectories )
|
|
{
|
|
if( SubFolder.FolderName.Equals( "Source", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
// Avoid collapsing the Engine's Source directory, since there are so many other solution folders in
|
|
// the parent directory.
|
|
if( !Folder.FolderName.Equals( "Engine", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
CanCollapseFolder = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( SubFolder.ChildProjects.Count == 0 && SubFolder.Files.Count == 0 && SubFolder.SubFolders.Count == 0 )
|
|
{
|
|
// Folder is totally empty
|
|
CanCollapseFolder = true;
|
|
}
|
|
|
|
if( CanCollapseFolder )
|
|
{
|
|
// OK, this folder is redundant and can be collapsed away.
|
|
|
|
SubFoldersToAdd.AddRange( SubFolder.SubFolders );
|
|
SubFolder.SubFolders.Clear();
|
|
|
|
Folder.ChildProjects.AddRange( SubFolder.ChildProjects );
|
|
SubFolder.ChildProjects.Clear();
|
|
|
|
Folder.Files.AddRange( SubFolder.Files );
|
|
SubFolder.Files.Clear();
|
|
|
|
SubFoldersToRemove.Add( SubFolder );
|
|
}
|
|
}
|
|
|
|
foreach( MasterProjectFolder SubFolderToRemove in SubFoldersToRemove )
|
|
{
|
|
Folder.SubFolders.Remove( SubFolderToRemove );
|
|
}
|
|
Folder.SubFolders.AddRange( SubFoldersToAdd );
|
|
|
|
// After everything has been collapsed, do a bit of data validation
|
|
Validate(Folder, ParentMasterProjectFolderPath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validate the specified Folder. Default implementation requires
|
|
/// for project file names to be unique!
|
|
/// </summary>
|
|
/// <param name="Folder">Folder.</param>
|
|
/// <param name="MasterProjectFolderPath">Parent master project folder path.</param>
|
|
protected virtual void Validate(MasterProjectFolder Folder, string MasterProjectFolderPath)
|
|
{
|
|
foreach (ProjectFile CurChildProject in Folder.ChildProjects)
|
|
{
|
|
foreach (ProjectFile OtherChildProject in Folder.ChildProjects)
|
|
{
|
|
if (CurChildProject != OtherChildProject)
|
|
{
|
|
if (CurChildProject.ProjectFilePath.GetFileNameWithoutAnyExtensions().Equals(OtherChildProject.ProjectFilePath.GetFileNameWithoutAnyExtensions(), StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw new BuildException("Detected collision between two project files with the same path in the same master project folder, " + OtherChildProject.ProjectFilePath.FullName + " and " + CurChildProject.ProjectFilePath.FullName + " (master project folder: " + MasterProjectFolderPath + ")");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (MasterProjectFolder SubFolder in Folder.SubFolders)
|
|
{
|
|
// If the parent folder already has a child project or file item with the same name as this sub-folder, then
|
|
// that's considered an error (it should never have been allowed to have a folder name that collided
|
|
// with project file names or file items, as that's not supported in Visual Studio.)
|
|
foreach (ProjectFile CurChildProject in Folder.ChildProjects)
|
|
{
|
|
if (CurChildProject.ProjectFilePath.GetFileNameWithoutAnyExtensions().Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a project within the outer folder " + CurChildProject.ProjectFilePath + " (master project folder: " + MasterProjectFolderPath + ")");
|
|
}
|
|
}
|
|
foreach (string CurFile in Folder.Files)
|
|
{
|
|
if (Path.GetFileName(CurFile).Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a file within the outer folder " + CurFile + " (master project folder: " + MasterProjectFolderPath + ")");
|
|
}
|
|
}
|
|
foreach (MasterProjectFolder CurFolder in Folder.SubFolders)
|
|
{
|
|
if (CurFolder != SubFolder)
|
|
{
|
|
if (CurFolder.FolderName.Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a sibling folder " + CurFolder.FolderName + " (master project folder: " + MasterProjectFolderPath + ")");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds UnrealBuildTool to the master project
|
|
/// </summary>
|
|
private void AddUnrealBuildToolProject(MasterProjectFolder ProgramsFolder)
|
|
{
|
|
List<string> ProjectDirectoryNames = new List<string>();
|
|
ProjectDirectoryNames.Add("UnrealBuildTool");
|
|
|
|
if (AllowDotNetCoreProjects)
|
|
{
|
|
ProjectDirectoryNames.Add("UnrealBuildTool_NETCore");
|
|
}
|
|
|
|
foreach (string ProjectDirectoryName in ProjectDirectoryNames)
|
|
{
|
|
DirectoryReference ProjectDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Programs", ProjectDirectoryName);
|
|
if (DirectoryReference.Exists(ProjectDirectory))
|
|
{
|
|
FileReference ProjectFileName = FileReference.Combine(ProjectDirectory, "UnrealBuildTool.csproj");
|
|
|
|
if (FileReference.Exists(ProjectFileName))
|
|
{
|
|
VCSharpProjectFile UnrealBuildToolProject = new VCSharpProjectFile(ProjectFileName);
|
|
UnrealBuildToolProject.ShouldBuildForAllSolutionTargets = true;
|
|
|
|
if (bIncludeDotNETCoreProjects || !UnrealBuildToolProject.IsDotNETCoreProject())
|
|
{
|
|
if (!UnrealBuildToolProject.IsDotNETCoreProject())
|
|
{
|
|
// Store it off as we need it when generating target projects.
|
|
UBTProject = UnrealBuildToolProject;
|
|
}
|
|
|
|
// Add the project
|
|
AddExistingProjectFile(UnrealBuildToolProject, bNeedsAllPlatformAndConfigurations: true, bForceDevelopmentConfiguration: true);
|
|
|
|
// Put this in a solution folder
|
|
ProgramsFolder.ChildProjects.Add(UnrealBuildToolProject);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a C# project to the master project
|
|
/// </summary>
|
|
/// <param name="ProjectName">Name of project file to add</param>
|
|
/// <param name="bShouldBuildForAllSolutionTargets"></param>
|
|
/// <param name="bForceDevelopmentConfiguration"></param>
|
|
/// <param name="bShouldBuildByDefaultForSolutionTargets"></param>
|
|
/// <returns>ProjectFile if the operation was successful, otherwise null.</returns>
|
|
private VCSharpProjectFile AddSimpleCSharpProject(string ProjectName, bool bShouldBuildForAllSolutionTargets = false, bool bForceDevelopmentConfiguration = false, bool bShouldBuildByDefaultForSolutionTargets = true)
|
|
{
|
|
VCSharpProjectFile Project = null;
|
|
|
|
FileReference ProjectFileName = FileReference.Combine( UnrealBuildTool.EngineSourceDirectory, "Programs", ProjectName, Path.GetFileName( ProjectName ) + ".csproj" );
|
|
FileInfo Info = new FileInfo( ProjectFileName.FullName );
|
|
if( Info.Exists )
|
|
{
|
|
Project = new VCSharpProjectFile(ProjectFileName);
|
|
Project.ShouldBuildForAllSolutionTargets = bShouldBuildForAllSolutionTargets;
|
|
Project.ShouldBuildByDefaultForSolutionTargets = bShouldBuildByDefaultForSolutionTargets;
|
|
AddExistingProjectFile(Project, bForceDevelopmentConfiguration: bForceDevelopmentConfiguration);
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException( ProjectFileName.FullName + " doesn't exist!" );
|
|
}
|
|
|
|
return Project;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check the registry for MVC3 project support
|
|
/// </summary>
|
|
/// <param name="RootKey"></param>
|
|
/// <param name="VisualStudioVersion"></param>
|
|
/// <returns></returns>
|
|
private bool CheckRegistryKey( RegistryKey RootKey, string VisualStudioVersion )
|
|
{
|
|
bool bInstalled = false;
|
|
RegistryKey VSSubKey = RootKey.OpenSubKey( "SOFTWARE\\Microsoft\\VisualStudio\\" + VisualStudioVersion + "\\Projects\\{E53F8FEA-EAE0-44A6-8774-FFD645390401}" );
|
|
if( VSSubKey != null )
|
|
{
|
|
bInstalled = true;
|
|
VSSubKey.Close();
|
|
}
|
|
|
|
return bInstalled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if a Visual Studio Extension is installed
|
|
/// </summary>
|
|
/// <param name="VisualStudioFolder"></param>
|
|
/// <param name="VisualStudioVersion"></param>
|
|
/// <param name="Extension"></param>
|
|
/// <returns></returns>
|
|
private bool CheckVisualStudioExtensionPackage( string VisualStudioFolder, string VisualStudioVersion, string Extension )
|
|
{
|
|
DirectoryInfo DirInfo = new DirectoryInfo( Path.Combine( VisualStudioFolder, VisualStudioVersion, "Extensions" ) );
|
|
if( DirInfo.Exists )
|
|
{
|
|
List<FileInfo> PackageDefs = DirInfo.GetFiles( "*.pkgdef", SearchOption.AllDirectories ).ToList();
|
|
List<string> PackageDefNames = PackageDefs.Select( x => x.Name ).ToList();
|
|
if( PackageDefNames.Contains( Extension ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds all of the config files for program targets to their project files
|
|
/// </summary>
|
|
private void AddEngineProgramConfigFiles( Dictionary<FileReference, ProjectFile> ProgramProjects )
|
|
{
|
|
if( bIncludeConfigFiles )
|
|
{
|
|
foreach( KeyValuePair<FileReference, ProjectFile> FileAndProject in ProgramProjects )
|
|
{
|
|
string ProgramName = FileAndProject.Key.GetFileNameWithoutAnyExtensions();
|
|
ProjectFile ProgramProjectFile = FileAndProject.Value;
|
|
|
|
// @todo projectfiles: The config folder for programs is kind of weird -- you end up going UP a few directories to get to it. This stuff is not great.
|
|
// @todo projectfiles: Fragile assumption here about Programs always being under /Engine/Programs
|
|
|
|
DirectoryReference ProgramDirectory;
|
|
|
|
if ( FileAndProject.Key.IsUnderDirectory(UnrealBuildTool.EnterpriseDirectory) )
|
|
{
|
|
ProgramDirectory = DirectoryReference.Combine( UnrealBuildTool.EnterpriseDirectory, "Programs", ProgramName );
|
|
}
|
|
else
|
|
{
|
|
ProgramDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Programs", ProgramName );
|
|
}
|
|
|
|
DirectoryReference ProgramConfigDirectory = DirectoryReference.Combine( ProgramDirectory, "Config" );
|
|
if( DirectoryReference.Exists(ProgramConfigDirectory) )
|
|
{
|
|
ProgramProjectFile.AddFilesToProject( SourceFileSearch.FindFiles( ProgramConfigDirectory ), ProgramDirectory );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates data for IntelliSense (compile definitions, include paths)
|
|
/// </summary>
|
|
/// <param name="Arguments">Incoming command-line arguments to UBT</param>
|
|
/// <param name="Targets">Targets to build for intellisense</param>
|
|
/// <return>Whether the process was successful or not</return>
|
|
private bool GenerateIntelliSenseData( String[] Arguments, List<Tuple<ProjectFile, ProjectTarget>> Targets )
|
|
{
|
|
bool bSuccess = true;
|
|
if( ShouldGenerateIntelliSenseData() && Targets.Count > 0 )
|
|
{
|
|
BuildConfiguration BuildConfiguration = new BuildConfiguration();
|
|
|
|
string ProgressInfoText = Utils.IsRunningOnMono ? "Generating data for project indexing..." : "Binding IntelliSense data...";
|
|
using(ProgressWriter Progress = new ProgressWriter(ProgressInfoText, true))
|
|
{
|
|
for(int TargetIndex = 0; TargetIndex < Targets.Count; ++TargetIndex)
|
|
{
|
|
ProjectFile TargetProjectFile = Targets[ TargetIndex ].Item1;
|
|
ProjectTarget CurTarget = Targets[ TargetIndex ].Item2;
|
|
|
|
Log.TraceVerbose( "Found target: " + CurTarget.TargetFilePath.GetFileNameWithoutAnyExtensions() );
|
|
|
|
string[] NewArguments = new string[ Arguments.Length + 4 ];
|
|
NewArguments[ 0 ] = CurTarget.TargetFilePath.GetFileNameWithoutAnyExtensions();
|
|
NewArguments[ 1 ] = BuildHostPlatform.Current.Platform.ToString();
|
|
NewArguments[ 2 ] = UnrealTargetConfiguration.Development.ToString();
|
|
NewArguments[ 3 ] = "-precompile";
|
|
Array.Copy(Arguments, 0, NewArguments, 4, Arguments.Length);
|
|
|
|
// We only want to update definitions and include paths for modules that are part of this target's project file.
|
|
ProjectFileGenerator.OnlyGenerateIntelliSenseDataForProject = TargetProjectFile;
|
|
|
|
// Clear the CachedIncludePaths field on every FileItem. It's faster to keep the cache of file items around, but we need to wipe
|
|
// out any state.
|
|
FileItem.ClearCachedIncludePaths();
|
|
|
|
try
|
|
{
|
|
// Run UnrealBuildTool, pretending to build this target but instead only gathering data for IntelliSense (include paths and definitions).
|
|
// No actual compiling or linking will happen because we early out using the ProjectFileGenerator.bGenerateProjectFiles global
|
|
bSuccess = UnrealBuildTool.RunUBT( BuildConfiguration, NewArguments, CurTarget.UnrealProjectFilePath, false ) == ECompilationResult.Succeeded;
|
|
}
|
|
catch(Exception Ex)
|
|
{
|
|
Progress.LogMessage(LogEventType.Warning, "Exception while generating include data for {0}: {1}", CurTarget.TargetFilePath.GetFileNameWithoutAnyExtensions(), Ex.ToString());
|
|
}
|
|
|
|
ProjectFileGenerator.OnlyGenerateIntelliSenseDataForProject = null;
|
|
|
|
if( !bSuccess )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Display progress
|
|
Progress.Write(TargetIndex + 1, Targets.Count);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Selects which platforms and build configurations we want in the project file
|
|
/// </summary>
|
|
/// <param name="IncludeAllPlatforms">True if we should include ALL platforms that are supported on this machine. Otherwise, only desktop platforms will be included.</param>
|
|
/// <param name="SupportedPlatformNames">Output string for supported platforms, returned as comma-separated values.</param>
|
|
protected virtual void SetupSupportedPlatformsAndConfigurations(bool IncludeAllPlatforms, out string SupportedPlatformNames)
|
|
{
|
|
StringBuilder SupportedPlatformsString = new StringBuilder();
|
|
|
|
System.Array PlatformEnums = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform Platform in PlatformEnums)
|
|
{
|
|
// project is in the explicit platform list or we include them all, we add the valid desktop platforms as they are required
|
|
bool bInProjectPlatformsList = (ProjectPlatforms.Count > 0) ? (IsValidDesktopPlatform(Platform) || ProjectPlatforms.Contains(Platform)) : true;
|
|
|
|
// project is a desktop platform or we have specified some platforms explicitly
|
|
bool IsRequiredPlatform = (IsValidDesktopPlatform(Platform) || ProjectPlatforms.Count > 0);
|
|
|
|
// Only include desktop platforms unless we were explicitly asked to include all platforms or restricted to a single platform.
|
|
if (bInProjectPlatformsList && (IncludeAllPlatforms || IsRequiredPlatform))
|
|
{
|
|
// If there is a build platform present, add it to the SupportedPlatforms list
|
|
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform( Platform, true );
|
|
if( BuildPlatform != null )
|
|
{
|
|
if (UnrealBuildTool.IsValidPlatform(Platform))
|
|
{
|
|
SupportedPlatforms.Add(Platform);
|
|
|
|
if (SupportedPlatformsString.Length > 0)
|
|
{
|
|
SupportedPlatformsString.Append(", ");
|
|
}
|
|
SupportedPlatformsString.Append(Platform.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add all configurations
|
|
foreach( UnrealTargetConfiguration CurConfiguration in Enum.GetValues( typeof(UnrealTargetConfiguration) ) )
|
|
{
|
|
if( CurConfiguration != UnrealTargetConfiguration.Unknown )
|
|
{
|
|
if (UnrealBuildTool.IsValidConfiguration(CurConfiguration))
|
|
{
|
|
SupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
}
|
|
|
|
SupportedPlatformNames = SupportedPlatformsString.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is this a valid platform. Used primarily for Installed vs non-Installed builds.
|
|
/// </summary>
|
|
/// <param name="InPlatform"></param>
|
|
/// <returns>true if valid, false if not</returns>
|
|
static public bool IsValidDesktopPlatform(UnrealTargetPlatform InPlatform)
|
|
{
|
|
switch (BuildHostPlatform.Current.Platform)
|
|
{
|
|
case UnrealTargetPlatform.Linux:
|
|
return InPlatform == UnrealTargetPlatform.Linux;
|
|
case UnrealTargetPlatform.Mac:
|
|
return InPlatform == UnrealTargetPlatform.Mac;
|
|
case UnrealTargetPlatform.Win64:
|
|
return ((InPlatform == UnrealTargetPlatform.Win32) || (InPlatform == UnrealTargetPlatform.Win64));
|
|
default:
|
|
throw new BuildException("Invalid RuntimePlatform:" + BuildHostPlatform.Current.Platform);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find the game which contains a given input file.
|
|
/// </summary>
|
|
/// <param name="AllGames">All game folders</param>
|
|
/// <param name="File">Full path of the file to search for</param>
|
|
protected FileReference FindGameContainingFile(List<FileReference> AllGames, FileReference File)
|
|
{
|
|
foreach (FileReference Game in AllGames)
|
|
{
|
|
if (File.IsUnderDirectory(Game.Directory))
|
|
{
|
|
return Game;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all modules and code files, given a list of games to process
|
|
/// </summary>
|
|
/// <param name="AllGames">All game folders</param>
|
|
/// <param name="ProgramProjects">All program projects</param>
|
|
/// <param name="ModProjects">All mod projects</param>
|
|
/// <param name="AllModuleFiles">List of *.Build.cs files for all engine programs and games</param>
|
|
/// <param name="bGatherThirdPartySource">True to gather source code from third party projects too</param>
|
|
protected void AddProjectsForAllModules( List<FileReference> AllGames, Dictionary<FileReference, ProjectFile> ProgramProjects, Dictionary<DirectoryReference, ProjectFile> ModProjects, List<FileReference> AllModuleFiles, bool bGatherThirdPartySource )
|
|
{
|
|
HashSet<ProjectFile> ProjectsWithPlugins = new HashSet<ProjectFile>();
|
|
foreach( FileReference CurModuleFile in AllModuleFiles )
|
|
{
|
|
Log.TraceVerbose("AddProjectsForAllModules " + CurModuleFile);
|
|
|
|
// The module's "base directory" is simply the directory where its xxx.Build.cs file is stored. We'll always
|
|
// harvest source files for this module in this base directory directory and all of its sub-directories.
|
|
string ModuleName = CurModuleFile.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ".Build"
|
|
|
|
bool WantProjectFileForModule = true;
|
|
|
|
// We'll keep track of whether this is an "engine" or "external" module. This is determined below while loading module rules.
|
|
bool IsEngineModule = UnrealBuildTool.IsUnderAnEngineDirectory(CurModuleFile.Directory);
|
|
bool IsThirdPartyModule = CurModuleFile.IsUnderDirectory(UnrealBuildTool.EngineSourceThirdPartyDirectory);
|
|
|
|
if( IsEngineModule && !bIncludeEngineSource )
|
|
{
|
|
// We were asked to exclude engine modules from the generated projects
|
|
WantProjectFileForModule = false;
|
|
}
|
|
|
|
if( WantProjectFileForModule )
|
|
{
|
|
DirectoryReference BaseFolder;
|
|
ProjectFile ProjectFile = FindProjectForModule(CurModuleFile, AllGames, ProgramProjects, ModProjects, out BaseFolder);
|
|
|
|
// Update our module map
|
|
ModuleToProjectFileMap[ ModuleName ] = ProjectFile;
|
|
ProjectFile.IsGeneratedProject = true;
|
|
|
|
// Only search subdirectories for non-external modules. We don't want to add all of the source and header files
|
|
// for every third-party module, unless we were configured to do so.
|
|
bool SearchSubdirectories = !IsThirdPartyModule || bGatherThirdPartySource;
|
|
|
|
if( bGatherThirdPartySource )
|
|
{
|
|
Log.TraceInformation( "Searching for third-party source files..." );
|
|
}
|
|
|
|
|
|
// Find all of the source files (and other files) and add them to the project
|
|
List<FileReference> FoundFiles = SourceFileSearch.FindModuleSourceFiles( CurModuleFile, SearchSubdirectories:SearchSubdirectories );
|
|
ProjectFile.AddFilesToProject( FoundFiles, BaseFolder );
|
|
|
|
// Check if there's a plugin directory here
|
|
if(!ProjectsWithPlugins.Contains(ProjectFile))
|
|
{
|
|
DirectoryReference PluginFolder = DirectoryReference.Combine(BaseFolder, "Plugins");
|
|
if(DirectoryReference.Exists(PluginFolder))
|
|
{
|
|
// Add all the plugin files for this project
|
|
foreach(FileReference PluginFileName in Plugins.EnumeratePlugins(PluginFolder))
|
|
{
|
|
if(!ModProjects.ContainsKey(PluginFileName.Directory))
|
|
{
|
|
AddPluginFilesToProject(PluginFileName, BaseFolder, ProjectFile);
|
|
}
|
|
}
|
|
}
|
|
ProjectsWithPlugins.Add(ProjectFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddPluginFilesToProject(FileReference PluginFileName, DirectoryReference BaseFolder, ProjectFile ProjectFile)
|
|
{
|
|
// Add the .uplugin file
|
|
ProjectFile.AddFileToProject(PluginFileName, BaseFolder);
|
|
|
|
// Add plugin config files if we have any
|
|
if( bIncludeConfigFiles )
|
|
{
|
|
DirectoryReference PluginConfigFolder = DirectoryReference.Combine(PluginFileName.Directory, "Config");
|
|
if(DirectoryReference.Exists(PluginConfigFolder))
|
|
{
|
|
ProjectFile.AddFilesToProject(SourceFileSearch.FindFiles(PluginConfigFolder), BaseFolder );
|
|
}
|
|
}
|
|
|
|
// Add plugin "resource" files if we have any
|
|
DirectoryReference PluginResourcesFolder = DirectoryReference.Combine(PluginFileName.Directory, "Resources");
|
|
if(DirectoryReference.Exists(PluginResourcesFolder))
|
|
{
|
|
ProjectFile.AddFilesToProject(SourceFileSearch.FindFiles(PluginResourcesFolder), BaseFolder );
|
|
}
|
|
|
|
// Add plugin shader files if we have any
|
|
DirectoryReference PluginShadersFolder = DirectoryReference.Combine(PluginFileName.Directory, "Shaders");
|
|
if (DirectoryReference.Exists(PluginShadersFolder))
|
|
{
|
|
ProjectFile.AddFilesToProject(SourceFileSearch.FindFiles(PluginShadersFolder), BaseFolder);
|
|
}
|
|
}
|
|
|
|
private ProjectFile FindProjectForModule(FileReference CurModuleFile, List<FileReference> AllGames, Dictionary<FileReference, ProjectFile> ProgramProjects, Dictionary<DirectoryReference, ProjectFile> ModProjects, out DirectoryReference BaseFolder)
|
|
{
|
|
string ProjectFileNameBase = null;
|
|
|
|
string PossibleProgramTargetName = CurModuleFile.GetFileNameWithoutAnyExtensions();
|
|
|
|
// @todo projectfiles: This works fine for now, but is pretty busted. It assumes only one module per program and that it matches the program target file name. (see TTP 307091)
|
|
if( ProgramProjects != null && ProgramProjects.Any( ProgramProject => PossibleProgramTargetName.Equals( ProgramProject.Key.GetFileNameWithoutAnyExtensions() ) ) ) // @todo projectfiles: When building (in mem projects), ProgramProjects will be null so we are using the UE4 project instead
|
|
{
|
|
ProjectFileNameBase = PossibleProgramTargetName;
|
|
BaseFolder = CurModuleFile.Directory;
|
|
}
|
|
else if( CurModuleFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory) )
|
|
{
|
|
ProjectFileNameBase = EngineProjectFileNameBase;
|
|
BaseFolder = UnrealBuildTool.EngineDirectory;
|
|
}
|
|
else if( CurModuleFile.IsUnderDirectory(UnrealBuildTool.EnterpriseSourceDirectory) ||
|
|
CurModuleFile.IsUnderDirectory(DirectoryReference.Combine(UnrealBuildTool.EnterpriseDirectory, "Plugins")) )
|
|
{
|
|
ProjectFileNameBase = EnterpriseProjectFileNameBase;
|
|
BaseFolder = UnrealBuildTool.EnterpriseDirectory;
|
|
}
|
|
else
|
|
{
|
|
// Check if it's a mod
|
|
foreach(KeyValuePair<DirectoryReference, ProjectFile> ModProject in ModProjects)
|
|
{
|
|
if(CurModuleFile.IsUnderDirectory(ModProject.Key))
|
|
{
|
|
BaseFolder = ModProject.Key;
|
|
return ModProject.Value;
|
|
}
|
|
}
|
|
|
|
// Figure out which game project this target belongs to
|
|
FileReference ProjectInfo = FindGameContainingFile(AllGames, CurModuleFile);
|
|
if(ProjectInfo == null)
|
|
{
|
|
throw new BuildException( "Found a non-engine module file (" + CurModuleFile + ") that did not exist within any of the known game folders" );
|
|
}
|
|
BaseFolder = ProjectInfo.Directory;
|
|
ProjectFileNameBase = ProjectInfo.GetFileNameWithoutExtension();
|
|
}
|
|
|
|
// Setup a project file entry for this module's project. Remember, some projects may host multiple modules!
|
|
FileReference ProjectFileName = FileReference.Combine( IntermediateProjectFilesPath, ProjectFileNameBase + ProjectFileExtension );
|
|
bool bProjectAlreadyExisted;
|
|
return FindOrAddProject( ProjectFileName, IncludeInGeneratedProjects:true, bAlreadyExisted:out bProjectAlreadyExisted );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates project entries for all known targets (*.Target.cs files)
|
|
/// </summary>
|
|
/// <param name="AllGames">All game folders</param>
|
|
/// <param name="EngineProject">The engine project we created</param>
|
|
/// <param name="EnterpriseProject">The enterprise project we created</param>
|
|
/// <param name="GameProjects">Map of game folder name to all of the game projects we created</param>
|
|
/// <param name="ModProjects">Map of mod folder name to all the mod projects we created</param>
|
|
/// <param name="ProgramProjects">Map of program names to all of the program projects we created</param>
|
|
/// <param name="TemplateGameProjects">Set of template game projects we found. These will also be in the GameProjects map</param>
|
|
/// <param name="SampleGameProjects">Set of sample game projects that were found</param>
|
|
private void AddProjectsForAllTargets( List<FileReference> AllGames, out ProjectFile EngineProject, out ProjectFile EnterpriseProject,
|
|
out Dictionary<DirectoryReference, ProjectFile> GameProjects, out Dictionary<DirectoryReference, ProjectFile> ModProjects,
|
|
out Dictionary<FileReference, ProjectFile> ProgramProjects, out Dictionary<DirectoryReference, ProjectFile> TemplateGameProjects, out Dictionary<DirectoryReference, ProjectFile> SampleGameProjects )
|
|
{
|
|
// As we're creating project files, we'll also keep track of whether we created an "engine" project and return that if we have one
|
|
EngineProject = null;
|
|
EnterpriseProject = null;
|
|
GameProjects = new Dictionary<DirectoryReference, ProjectFile>();
|
|
ProgramProjects = new Dictionary<FileReference, ProjectFile>();
|
|
TemplateGameProjects = new Dictionary<DirectoryReference, ProjectFile>();
|
|
SampleGameProjects = new Dictionary<DirectoryReference, ProjectFile>();
|
|
|
|
// Get some standard directories
|
|
DirectoryReference EngineSourceProgramsDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Programs");
|
|
DirectoryReference TemplatesDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "..", "Templates");
|
|
DirectoryReference SamplesDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "..", "Samples");
|
|
DirectoryReference EnterpriseSourceProgramsDirectory = DirectoryReference.Combine(UnrealBuildTool.EnterpriseSourceDirectory, "Programs");
|
|
DirectoryReference EnterpriseTemplatesDirectory = DirectoryReference.Combine(UnrealBuildTool.EnterpriseDirectory, "Templates");
|
|
DirectoryReference EnterpriseSamplesDirectory = DirectoryReference.Combine(UnrealBuildTool.EnterpriseDirectory, "Samples");
|
|
|
|
// Find all of the target files. This will filter out any modules or targets that don't
|
|
// belong to platforms we're generating project files for.
|
|
List<FileReference> AllTargetFiles = DiscoverTargets(AllGames);
|
|
foreach( FileReference TargetFilePath in AllTargetFiles )
|
|
{
|
|
string TargetName = TargetFilePath.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ".Target"
|
|
|
|
// Check to see if this is an Engine target. That is, the target is located under the "Engine" folder
|
|
bool IsEngineTarget = false;
|
|
bool IsEnterpriseTarget = false;
|
|
bool WantProjectFileForTarget = true;
|
|
if(TargetFilePath.IsUnderDirectory(UnrealBuildTool.EngineDirectory))
|
|
{
|
|
// This is an engine target
|
|
IsEngineTarget = true;
|
|
|
|
if(TargetFilePath.IsUnderDirectory(EngineSourceProgramsDirectory))
|
|
{
|
|
WantProjectFileForTarget = IncludeEnginePrograms;
|
|
}
|
|
else if(TargetFilePath.IsUnderDirectory(UnrealBuildTool.EngineSourceDirectory))
|
|
{
|
|
WantProjectFileForTarget = bIncludeEngineSource;
|
|
}
|
|
}
|
|
else if (TargetFilePath.IsUnderDirectory(UnrealBuildTool.EnterpriseSourceDirectory))
|
|
{
|
|
// This is an enterprise target
|
|
IsEnterpriseTarget = true;
|
|
|
|
if(TargetFilePath.IsUnderDirectory(EnterpriseSourceProgramsDirectory))
|
|
{
|
|
WantProjectFileForTarget = IncludeEnginePrograms;
|
|
}
|
|
else if(TargetFilePath.IsUnderDirectory(UnrealBuildTool.EnterpriseSourceDirectory))
|
|
{
|
|
WantProjectFileForTarget = bIncludeEngineSource;
|
|
}
|
|
}
|
|
|
|
if (WantProjectFileForTarget)
|
|
{
|
|
RulesAssembly RulesAssembly;
|
|
|
|
FileReference CheckProjectFile;
|
|
if(!UProjectInfo.TryGetProjectForTarget(TargetName, out CheckProjectFile))
|
|
{
|
|
if(TargetFilePath.IsUnderDirectory(UnrealBuildTool.EnterpriseDirectory))
|
|
{
|
|
RulesAssembly = RulesCompiler.CreateEnterpriseRulesAssembly();
|
|
}
|
|
else
|
|
{
|
|
RulesAssembly = RulesCompiler.CreateEngineRulesAssembly();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RulesAssembly = RulesCompiler.CreateProjectRulesAssembly(CheckProjectFile);
|
|
}
|
|
|
|
// Create target rules for all of the platforms and configuration combinations that we want to enable support for.
|
|
// Just use the current platform as we only need to recover the target type and both should be supported for all targets...
|
|
TargetRules TargetRulesObject = RulesAssembly.CreateTargetRules(TargetName, BuildHostPlatform.Current.Platform, UnrealTargetConfiguration.Development, "", CheckProjectFile, false);
|
|
|
|
bool IsProgramTarget = false;
|
|
|
|
DirectoryReference GameFolder = null;
|
|
string ProjectFileNameBase = null;
|
|
if (TargetRulesObject.Type == TargetType.Program)
|
|
{
|
|
IsProgramTarget = true;
|
|
ProjectFileNameBase = TargetName;
|
|
}
|
|
else if (IsEngineTarget)
|
|
{
|
|
ProjectFileNameBase = EngineProjectFileNameBase;
|
|
}
|
|
else if (IsEnterpriseTarget)
|
|
{
|
|
ProjectFileNameBase = EnterpriseProjectFileNameBase;
|
|
}
|
|
else
|
|
{
|
|
// Figure out which game project this target belongs to
|
|
FileReference ProjectInfo = FindGameContainingFile(AllGames, TargetFilePath);
|
|
if (ProjectInfo == null)
|
|
{
|
|
throw new BuildException("Found a non-engine target file (" + TargetFilePath + ") that did not exist within any of the known game folders");
|
|
}
|
|
GameFolder = ProjectInfo.Directory;
|
|
ProjectFileNameBase = ProjectInfo.GetFileNameWithoutExtension();
|
|
}
|
|
|
|
// @todo projectfiles: We should move all of the Target.cs files out of sub-folders to clean up the project directories a bit (e.g. GameUncooked folder)
|
|
|
|
FileReference ProjectFilePath = FileReference.Combine(IntermediateProjectFilesPath, ProjectFileNameBase + ProjectFileExtension);
|
|
|
|
if (TargetRulesObject.Type == TargetType.Game || TargetRulesObject.Type == TargetType.Client || TargetRulesObject.Type == TargetType.Server)
|
|
{
|
|
// Allow platforms to generate stub projects here...
|
|
UEPlatformProjectGenerator.GenerateGameProjectStubs(
|
|
InGenerator: this,
|
|
InTargetName: TargetName,
|
|
InTargetFilepath: TargetFilePath.FullName,
|
|
InTargetRules: TargetRulesObject,
|
|
InPlatforms: SupportedPlatforms,
|
|
InConfigurations: SupportedConfigurations);
|
|
}
|
|
|
|
bool bProjectAlreadyExisted;
|
|
ProjectFile ProjectFile = FindOrAddProject(ProjectFilePath, IncludeInGeneratedProjects: true, bAlreadyExisted: out bProjectAlreadyExisted);
|
|
ProjectFile.IsForeignProject = bGeneratingGameProjectFiles && OnlyGameProject != null && TargetFilePath.IsUnderDirectory(OnlyGameProject.Directory);
|
|
ProjectFile.IsGeneratedProject = true;
|
|
ProjectFile.IsStubProject = UnrealBuildTool.IsProjectInstalled();
|
|
if (TargetRulesObject.bBuildInSolutionByDefault.HasValue)
|
|
{
|
|
ProjectFile.ShouldBuildByDefaultForSolutionTargets = TargetRulesObject.bBuildInSolutionByDefault.Value;
|
|
}
|
|
|
|
// Check to see if this is a template target. That is, the target is located under the "Templates" folder
|
|
bool IsTemplateTarget = TargetFilePath.IsUnderDirectory(TemplatesDirectory) || TargetFilePath.IsUnderDirectory(EnterpriseTemplatesDirectory);
|
|
bool IsSampleTarget = TargetFilePath.IsUnderDirectory(SamplesDirectory) || TargetFilePath.IsUnderDirectory(EnterpriseSamplesDirectory);
|
|
|
|
DirectoryReference BaseFolder = null;
|
|
if (IsProgramTarget)
|
|
{
|
|
ProgramProjects[TargetFilePath] = ProjectFile;
|
|
BaseFolder = TargetFilePath.Directory;
|
|
}
|
|
else if (IsEngineTarget)
|
|
{
|
|
EngineProject = ProjectFile;
|
|
BaseFolder = UnrealBuildTool.EngineDirectory;
|
|
if (UnrealBuildTool.IsEngineInstalled())
|
|
{
|
|
// Allow engine projects to be created but not built for Installed Engine builds
|
|
EngineProject.IsForeignProject = false;
|
|
EngineProject.IsGeneratedProject = true;
|
|
EngineProject.IsStubProject = true;
|
|
}
|
|
}
|
|
else if (IsEnterpriseTarget)
|
|
{
|
|
EnterpriseProject = ProjectFile;
|
|
BaseFolder = UnrealBuildTool.EnterpriseDirectory;
|
|
if (UnrealBuildTool.IsEngineInstalled())
|
|
{
|
|
// Allow enterprise projects to be created but not built for Installed Engine builds
|
|
EnterpriseProject.IsForeignProject = false;
|
|
EnterpriseProject.IsGeneratedProject = true;
|
|
EnterpriseProject.IsStubProject = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GameProjects[GameFolder] = ProjectFile;
|
|
if (IsTemplateTarget)
|
|
{
|
|
TemplateGameProjects[GameFolder] = ProjectFile;
|
|
}
|
|
else if (IsSampleTarget)
|
|
{
|
|
SampleGameProjects[GameFolder] = ProjectFile;
|
|
}
|
|
BaseFolder = GameFolder;
|
|
|
|
if (!bProjectAlreadyExisted)
|
|
{
|
|
// Add the .uproject file for this game/template
|
|
FileReference UProjectFilePath = FileReference.Combine(BaseFolder, ProjectFileNameBase + ".uproject");
|
|
if (FileReference.Exists(UProjectFilePath))
|
|
{
|
|
ProjectFile.AddFileToProject(UProjectFilePath, BaseFolder);
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException("Not expecting to find a game with no .uproject file. File '{0}' doesn't exist", UProjectFilePath);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
foreach (ProjectTarget ExistingProjectTarget in ProjectFile.ProjectTargets)
|
|
{
|
|
if (ExistingProjectTarget.TargetRules.Type == TargetRulesObject.Type)
|
|
{
|
|
throw new BuildException("Not expecting project {0} to already have a target rules of with configuration name {1} ({2}) while trying to add: {3}", ProjectFilePath, TargetRulesObject.Type.ToString(), ExistingProjectTarget.TargetRules.ToString(), TargetRulesObject.ToString());
|
|
}
|
|
|
|
// Not expecting to have both a game and a program in the same project. These would alias because we share the project and solution configuration names (just because it makes sense to)
|
|
if ((ExistingProjectTarget.TargetRules.Type == TargetType.Game && TargetRulesObject.Type == TargetType.Program) ||
|
|
(ExistingProjectTarget.TargetRules.Type == TargetType.Program && TargetRulesObject.Type == TargetType.Game))
|
|
{
|
|
throw new BuildException("Not expecting project {0} to already have a Game/Program target ({1}) associated with it while trying to add: {2}", ProjectFilePath, ExistingProjectTarget.TargetRules.ToString(), TargetRulesObject.ToString());
|
|
}
|
|
}
|
|
|
|
ProjectTarget ProjectTarget = new ProjectTarget()
|
|
{
|
|
TargetRules = TargetRulesObject,
|
|
TargetFilePath = TargetFilePath,
|
|
ProjectFilePath = ProjectFilePath,
|
|
UnrealProjectFilePath = CheckProjectFile,
|
|
SupportedPlatforms = UEBuildTarget.GetSupportedPlatforms(TargetRulesObject).Where(x => UEBuildPlatform.GetBuildPlatform(x, true) != null).ToArray(),
|
|
CreateRulesDelegate = (Platform, Configuration) => RulesAssembly.CreateTargetRules(TargetName, Platform, Configuration, "", CheckProjectFile, false)
|
|
};
|
|
|
|
if (TargetName == "ShaderCompileWorker") // @todo projectfiles: Ideally, the target rules file should set this
|
|
{
|
|
ProjectTarget.ForceDevelopmentConfiguration = true;
|
|
}
|
|
|
|
ProjectFile.ProjectTargets.Add(ProjectTarget);
|
|
|
|
// Make sure the *.Target.cs file is in the project.
|
|
ProjectFile.AddFileToProject(TargetFilePath, BaseFolder);
|
|
|
|
// We special case ShaderCompileWorker. It needs to always be compiled in Development mode.
|
|
Log.TraceVerbose("Generating target {0} for {1}", TargetRulesObject.Type.ToString(), ProjectFilePath);
|
|
}
|
|
}
|
|
|
|
// Find all the mods for game projects
|
|
ModProjects = new Dictionary<DirectoryReference, ProjectFile>();
|
|
if(GameProjects.Count == 1)
|
|
{
|
|
KeyValuePair<DirectoryReference, ProjectFile> GameProject = GameProjects.First();
|
|
foreach(PluginInfo PluginInfo in Plugins.ReadProjectPlugins(GameProject.Key))
|
|
{
|
|
if(PluginInfo.Descriptor.Modules != null && PluginInfo.Descriptor.Modules.Length > 0 && PluginInfo.Type == PluginType.Mod)
|
|
{
|
|
FileReference ModProjectFilePath = FileReference.Combine(PluginInfo.Directory, "Mods", PluginInfo.Name + ProjectFileExtension);
|
|
|
|
bool bProjectAlreadyExisted;
|
|
ProjectFile ModProjectFile = FindOrAddProject(ModProjectFilePath, IncludeInGeneratedProjects: true, bAlreadyExisted: out bProjectAlreadyExisted);
|
|
ModProjectFile.IsForeignProject = GameProject.Value.IsForeignProject;
|
|
ModProjectFile.IsGeneratedProject = true;
|
|
ModProjectFile.IsStubProject = false;
|
|
ModProjectFile.PluginFilePath = PluginInfo.File;
|
|
ModProjectFile.ProjectTargets.AddRange(GameProject.Value.ProjectTargets);
|
|
|
|
AddPluginFilesToProject(PluginInfo.File, PluginInfo.Directory, ModProjectFile);
|
|
|
|
ModProjects.Add(PluginInfo.Directory, ModProjectFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds shader source code to the specified project
|
|
protected void AddEngineShaderSource( ProjectFile EngineProject )
|
|
{
|
|
// Setup a project file entry for this module's project. Remember, some projects may host multiple modules!
|
|
DirectoryReference ShadersDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Shaders" );
|
|
|
|
List<string> SubdirectoryNamesToExclude = new List<string>();
|
|
{
|
|
// Don't include binary shaders in the project file.
|
|
SubdirectoryNamesToExclude.Add( "Binaries" );
|
|
// We never want shader intermediate files in our project file
|
|
SubdirectoryNamesToExclude.Add( "PDBDump" );
|
|
SubdirectoryNamesToExclude.Add( "WorkingDirectory" );
|
|
}
|
|
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( ShadersDirectory, SubdirectoryNamesToExclude ), UnrealBuildTool.EngineDirectory );
|
|
}
|
|
|
|
|
|
/// Adds engine build infrastructure files to the specified project
|
|
protected void AddEngineBuildFiles( ProjectFile EngineProject )
|
|
{
|
|
DirectoryReference BuildDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Build" );
|
|
|
|
List<string> SubdirectoryNamesToExclude = new List<string>();
|
|
SubdirectoryNamesToExclude.Add("Receipts");
|
|
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( BuildDirectory, SubdirectoryNamesToExclude ), UnrealBuildTool.EngineDirectory );
|
|
}
|
|
|
|
|
|
|
|
/// Adds engine documentation to the specified project
|
|
protected void AddEngineDocumentation( ProjectFile EngineProject )
|
|
{
|
|
// NOTE: The project folder added here will actually be collapsed away later if not needed
|
|
DirectoryReference DocumentationProjectDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Documentation" );
|
|
DirectoryReference DocumentationSourceDirectory = DirectoryReference.Combine( UnrealBuildTool.EngineDirectory, "Documentation", "Source" );
|
|
DirectoryInfo DirInfo = new DirectoryInfo( DocumentationProjectDirectory.FullName );
|
|
if( DirInfo.Exists && DirectoryReference.Exists(DocumentationSourceDirectory) )
|
|
{
|
|
Log.TraceVerbose( "Adding documentation files..." );
|
|
|
|
List<string> SubdirectoryNamesToExclude = new List<string>();
|
|
{
|
|
// We never want any of the images or attachment files included in our generated project
|
|
SubdirectoryNamesToExclude.Add( "Images" );
|
|
SubdirectoryNamesToExclude.Add( "Attachments" );
|
|
|
|
// The API directory is huge, so don't include any of it
|
|
SubdirectoryNamesToExclude.Add("API");
|
|
|
|
// Omit Javascript source because it just confuses the Visual Studio IDE
|
|
SubdirectoryNamesToExclude.Add( "Javascript" );
|
|
}
|
|
|
|
List<FileReference> DocumentationFiles = SourceFileSearch.FindFiles( DocumentationSourceDirectory, SubdirectoryNamesToExclude );
|
|
|
|
// Filter out non-English documentation files if we were configured to do so
|
|
if( !bAllDocumentationLanguages )
|
|
{
|
|
List<FileReference> FilteredDocumentationFiles = new List<FileReference>();
|
|
foreach( FileReference DocumentationFile in DocumentationFiles )
|
|
{
|
|
bool bPassesFilter = true;
|
|
if( DocumentationFile.FullName.EndsWith( ".udn", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
string LanguageSuffix = Path.GetExtension( Path.GetFileNameWithoutExtension( DocumentationFile.FullName ) );
|
|
if( !String.IsNullOrEmpty( LanguageSuffix ) &&
|
|
!LanguageSuffix.Equals( ".int", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
bPassesFilter = false;
|
|
}
|
|
}
|
|
|
|
if( bPassesFilter )
|
|
{
|
|
FilteredDocumentationFiles.Add( DocumentationFile );
|
|
}
|
|
}
|
|
DocumentationFiles = FilteredDocumentationFiles;
|
|
}
|
|
|
|
EngineProject.AddFilesToProject( DocumentationFiles, UnrealBuildTool.EngineDirectory );
|
|
}
|
|
else
|
|
{
|
|
Log.TraceVerbose("Skipping documentation project... directory not found");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Adds a new project file and returns an object that represents that project file (or if the project file is already known, returns that instead.)
|
|
/// </summary>
|
|
/// <param name="FilePath">Full path to the project file</param>
|
|
/// <param name="IncludeInGeneratedProjects">True if this project should be included in the set of generated projects. Only matters when actually generating project files.</param>
|
|
/// <param name="bAlreadyExisted">True if we already had this project file</param>
|
|
/// <returns>Object that represents this project file in Unreal Build Tool</returns>
|
|
public ProjectFile FindOrAddProject( FileReference FilePath, bool IncludeInGeneratedProjects, out bool bAlreadyExisted )
|
|
{
|
|
if( FilePath == null )
|
|
{
|
|
throw new BuildException( "Not valid to call FindOrAddProject() with an empty file path!" );
|
|
}
|
|
|
|
// Do we already have this project?
|
|
ProjectFile ExistingProjectFile;
|
|
if( ProjectFileMap.TryGetValue( FilePath, out ExistingProjectFile ) )
|
|
{
|
|
bAlreadyExisted = true;
|
|
return ExistingProjectFile;
|
|
}
|
|
|
|
// Add a new project file for the specified path
|
|
ProjectFile NewProjectFile = AllocateProjectFile( FilePath );
|
|
ProjectFileMap[ FilePath ] = NewProjectFile;
|
|
|
|
if( IncludeInGeneratedProjects )
|
|
{
|
|
GeneratedProjectFiles.Add( NewProjectFile );
|
|
}
|
|
|
|
bAlreadyExisted = false;
|
|
return NewProjectFile;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Allocates a generator-specific project file object
|
|
/// </summary>
|
|
/// <param name="InitFilePath">Path to the project file</param>
|
|
/// <returns>The newly allocated project file object</returns>
|
|
protected abstract ProjectFile AllocateProjectFile( FileReference InitFilePath );
|
|
|
|
|
|
/// <summary>
|
|
/// Allocates a generator-specific master project folder object
|
|
/// </summary>
|
|
/// <param name="OwnerProjectFileGenerator">Project file generator that owns this object</param>
|
|
/// <param name="FolderName">Name for this folder</param>
|
|
/// <returns>The newly allocated project folder object</returns>
|
|
public abstract MasterProjectFolder AllocateMasterProjectFolder(ProjectFileGenerator OwnerProjectFileGenerator, string FolderName );
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a list of all the known project files
|
|
/// </summary>
|
|
/// <returns>Project file list</returns>
|
|
public List<ProjectFile> AllProjectFiles
|
|
{
|
|
get
|
|
{
|
|
List<ProjectFile> CombinedList = new List<ProjectFile>();
|
|
CombinedList.AddRange( GeneratedProjectFiles );
|
|
CombinedList.AddRange( OtherProjectFiles );
|
|
return CombinedList;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Writes the project files to disk
|
|
/// </summary>
|
|
/// <returns>True if successful</returns>
|
|
protected virtual bool WriteProjectFiles()
|
|
{
|
|
using(ProgressWriter Progress = new ProgressWriter("Writing project files...", true))
|
|
{
|
|
int TotalProjectFileCount = GeneratedProjectFiles.Count + 1; // +1 for the master project file, which we'll save next
|
|
|
|
for(int ProjectFileIndex = 0 ; ProjectFileIndex < GeneratedProjectFiles.Count; ++ProjectFileIndex )
|
|
{
|
|
ProjectFile CurProject = GeneratedProjectFiles[ ProjectFileIndex ];
|
|
if( !CurProject.WriteProjectFile(
|
|
InPlatforms: SupportedPlatforms,
|
|
InConfigurations: SupportedConfigurations ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Progress.Write(ProjectFileIndex + 1, TotalProjectFileCount);
|
|
}
|
|
|
|
WriteMasterProjectFile( UBTProject: UBTProject );
|
|
Progress.Write(TotalProjectFileCount, TotalProjectFileCount);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the master project file (e.g. Visual Studio Solution file)
|
|
/// </summary>
|
|
/// <param name="UBTProject">The UnrealBuildTool project</param>
|
|
/// <returns>True if successful</returns>
|
|
protected abstract bool WriteMasterProjectFile( ProjectFile UBTProject );
|
|
|
|
|
|
/// <summary>
|
|
/// Writes the specified string content to a file. Before writing to the file, it loads the existing file (if present) to see if the contents have changed
|
|
/// </summary>
|
|
/// <param name="FileName">File to write</param>
|
|
/// <param name="NewFileContents">File content</param>
|
|
/// <param name="InEncoding"></param>
|
|
/// <returns>True if the file was saved, or if it didn't need to be overwritten because the content was unchanged</returns>
|
|
public static bool WriteFileIfChanged( string FileName, string NewFileContents, Encoding InEncoding = null )
|
|
{
|
|
// Check to see if the file already exists, and if so, load it up
|
|
string LoadedFileContent = null;
|
|
bool FileAlreadyExists = File.Exists( FileName );
|
|
if( FileAlreadyExists )
|
|
{
|
|
try
|
|
{
|
|
LoadedFileContent = File.ReadAllText( FileName );
|
|
}
|
|
catch( Exception )
|
|
{
|
|
Log.TraceInformation( "Error while trying to load existing file {0}. Ignored.", FileName );
|
|
}
|
|
}
|
|
|
|
|
|
// Don't bother saving anything out if the new file content is the same as the old file's content
|
|
bool FileNeedsSave = true;
|
|
if( LoadedFileContent != null )
|
|
{
|
|
bool bIgnoreProjectFileWhitespaces = true;
|
|
if (ProjectFileComparer.CompareOrdinalIgnoreCase(LoadedFileContent, NewFileContents, bIgnoreProjectFileWhitespaces) == 0)
|
|
{
|
|
// Exact match!
|
|
FileNeedsSave = false;
|
|
}
|
|
|
|
if( !FileNeedsSave )
|
|
{
|
|
Log.TraceVerbose( "Skipped saving {0} because contents haven't changed.", Path.GetFileName( FileName ) );
|
|
}
|
|
}
|
|
|
|
if( FileNeedsSave )
|
|
{
|
|
// Save the file
|
|
try
|
|
{
|
|
Directory.CreateDirectory( Path.GetDirectoryName( FileName ) );
|
|
// When WriteAllText is passed Encoding.UTF8 it likes to write a BOM marker
|
|
// at the start of the file (adding two bytes to the file length). For most
|
|
// files this is only mildly annoying but for Makefiles it can actually make
|
|
// them un-useable.
|
|
// TODO(sbc): See if we can just drop the Encoding.UTF8 argument on all
|
|
// platforms. In this case UTF8 encoding will still be used but without the
|
|
// BOM, which is, AFAICT, desirable in almost all cases.
|
|
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux || BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
|
|
File.WriteAllText(FileName, NewFileContents, new UTF8Encoding());
|
|
else
|
|
File.WriteAllText(FileName, NewFileContents, InEncoding != null ? InEncoding : Encoding.UTF8);
|
|
Log.TraceVerbose("Saved {0}", Path.GetFileName(FileName));
|
|
}
|
|
catch( Exception ex )
|
|
{
|
|
// Unable to write to the project file.
|
|
string Message = string.Format("Error while trying to write file {0}. The file is probably read-only.", FileName);
|
|
Console.WriteLine();
|
|
Log.TraceError(Message);
|
|
throw new BuildException(ex, Message);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the given project to the OtherProjects list
|
|
/// </summary>
|
|
/// <param name="InProject">The project to add</param>
|
|
/// <param name="bNeedsAllPlatformAndConfigurations"></param>
|
|
/// <param name="bForceDevelopmentConfiguration"></param>
|
|
/// <param name="bProjectDeploys"></param>
|
|
/// <param name="InSupportedPlatforms"></param>
|
|
/// <param name="InSupportedConfigurations"></param>
|
|
/// <returns>True if successful</returns>
|
|
public void AddExistingProjectFile(ProjectFile InProject, bool bNeedsAllPlatformAndConfigurations = false, bool bForceDevelopmentConfiguration = false, bool bProjectDeploys = false, List<UnrealTargetPlatform> InSupportedPlatforms = null, List<UnrealTargetConfiguration> InSupportedConfigurations = null)
|
|
{
|
|
if( InProject.ProjectTargets.Count != 0 )
|
|
{
|
|
throw new BuildException( "Expecting existing project to not have any ProjectTargets defined yet." );
|
|
}
|
|
|
|
ProjectTarget ProjectTarget = new ProjectTarget();
|
|
ProjectTarget.SupportedPlatforms = new UnrealTargetPlatform[0];
|
|
if( bForceDevelopmentConfiguration )
|
|
{
|
|
ProjectTarget.ForceDevelopmentConfiguration = true;
|
|
}
|
|
ProjectTarget.ProjectDeploys = bProjectDeploys;
|
|
|
|
if (bNeedsAllPlatformAndConfigurations)
|
|
{
|
|
// Add all platforms
|
|
Array AllPlatforms = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform CurPlatfrom in AllPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
|
|
// Add all configurations
|
|
Array AllConfigurations = Enum.GetValues(typeof(UnrealTargetConfiguration));
|
|
foreach (UnrealTargetConfiguration CurConfiguration in AllConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add( CurConfiguration );
|
|
}
|
|
}
|
|
else if (InSupportedPlatforms != null || InSupportedConfigurations != null)
|
|
{
|
|
if (InSupportedPlatforms != null)
|
|
{
|
|
// Add all explicitly specified platforms
|
|
foreach (UnrealTargetPlatform CurPlatfrom in InSupportedPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, add all platforms
|
|
Array AllPlatforms = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform CurPlatfrom in AllPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
}
|
|
|
|
if (InSupportedConfigurations != null)
|
|
{
|
|
// Add all explicitly specified configurations
|
|
foreach (UnrealTargetConfiguration CurConfiguration in InSupportedConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, add all configurations
|
|
Array AllConfigurations = Enum.GetValues(typeof(UnrealTargetConfiguration));
|
|
foreach (UnrealTargetConfiguration CurConfiguration in AllConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool bFoundDevelopmentConfig = false;
|
|
bool bFoundDebugConfig = false;
|
|
|
|
try
|
|
{
|
|
|
|
// Parse the project and ensure both Development and Debug configurations are present
|
|
foreach (var Config in XElement.Load(InProject.ProjectFilePath.FullName).Elements("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup")
|
|
.Where(node => node.Attribute("Condition") != null)
|
|
.Select(node => node.Attribute("Condition").ToString())
|
|
.ToList())
|
|
{
|
|
if (Config.Contains("Development|"))
|
|
{
|
|
bFoundDevelopmentConfig = true;
|
|
}
|
|
else if (Config.Contains("Debug|"))
|
|
{
|
|
bFoundDebugConfig = true;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Trace.TraceError("Unable to parse existing project file {0}", InProject.ProjectFilePath.FullName);
|
|
}
|
|
|
|
if (!bFoundDebugConfig || !bFoundDevelopmentConfig)
|
|
{
|
|
throw new BuildException("Existing C# project {0} must contain a {1} configuration", InProject.ProjectFilePath.FullName, bFoundDebugConfig ? "Development" : "Debug");
|
|
}
|
|
|
|
// For existing project files, just support the default desktop platforms and configurations
|
|
ProjectTarget.ExtraSupportedPlatforms.AddRange(Utils.GetPlatformsInClass(UnrealPlatformClass.Desktop));
|
|
// Debug and Development only
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(UnrealTargetConfiguration.Debug);
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(UnrealTargetConfiguration.Development);
|
|
}
|
|
|
|
InProject.ProjectTargets.Add( ProjectTarget );
|
|
|
|
// Existing projects must always have a GUID. This will throw an exception if one isn't found.
|
|
InProject.LoadGUIDFromExistingProject();
|
|
|
|
OtherProjectFiles.Add( InProject );
|
|
}
|
|
|
|
/// The default project to be built for the solution.
|
|
protected ProjectFile DefaultProject;
|
|
|
|
/// The project for UnrealBuildTool. Note that when generating project files for installed builds, we won't have
|
|
/// an UnrealBuildTool project at all.
|
|
protected ProjectFile UBTProject;
|
|
|
|
/// List of platforms that we'll support in the project files
|
|
protected List<UnrealTargetPlatform> SupportedPlatforms = new List<UnrealTargetPlatform>();
|
|
|
|
/// List of build configurations that we'll support in the project files
|
|
protected List<UnrealTargetConfiguration> SupportedConfigurations = new List<UnrealTargetConfiguration>();
|
|
|
|
/// Map of project file names to their project files. This includes every single project file in memory or otherwise that
|
|
/// we know about so far. Note that when generating project files, this map may even include project files that we won't
|
|
/// be including in the generated projects.
|
|
protected readonly Dictionary<FileReference, ProjectFile> ProjectFileMap = new Dictionary<FileReference, ProjectFile>();
|
|
|
|
/// List of project files that we'll be generating
|
|
protected readonly List<ProjectFile> GeneratedProjectFiles = new List<ProjectFile>();
|
|
|
|
/// List of other project files that we want to include in a generated solution file, even though we
|
|
/// aren't generating them ourselves. Note that these may *not* always be C++ project files (e.g. C#)
|
|
protected readonly List<ProjectFile> OtherProjectFiles = new List<ProjectFile>();
|
|
|
|
protected readonly List<ProjectFile> AutomationProjectFiles = new List<ProjectFile>();
|
|
|
|
/// List of top-level folders in the master project file
|
|
protected MasterProjectFolder RootFolder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class used for comparing the existing and generated project files.
|
|
/// </summary>
|
|
class ProjectFileComparer
|
|
{
|
|
//static readonly string GUIDRegexPattern = "(\\{){0,1}[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}(\\}){0,1}";
|
|
//static readonly string GUIDReplaceString = "GUID";
|
|
|
|
/// <summary>
|
|
/// Used by CompareOrdinalIgnoreWhitespaceAndCase to determine if a whitespace can be ignored.
|
|
/// </summary>
|
|
/// <param name="Whitespace">Whitespace character.</param>
|
|
/// <returns>true if the character can be ignored, false otherwise.</returns>
|
|
static bool CanIgnoreWhitespace(char Whitespace)
|
|
{
|
|
// Only ignore spaces and tabs.
|
|
return Whitespace == ' ' || Whitespace == '\t';
|
|
}
|
|
|
|
/*
|
|
/// <summary>
|
|
/// Replaces all GUIDs in the project file with "GUID" text.
|
|
/// </summary>
|
|
/// <param name="ProjectFileContents">Contents of the project file to remove GUIDs from.</param>
|
|
/// <returns>String with all GUIDs replaced with "GUID" text.</returns>
|
|
static string StripGUIDs(string ProjectFileContents)
|
|
{
|
|
// Replace all GUIDs with "GUID" text.
|
|
return System.Text.RegularExpressions.Regex.Replace(ProjectFileContents, GUIDRegexPattern, GUIDReplaceString);
|
|
}
|
|
*/
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring whitespaces, case and GUIDs.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Compares two specified String objects by evaluating the numeric values of the corresponding Char objects in each string.
|
|
/// Only space and tabulation characters are ignored. Ignores leading whitespaces at the beginning of each line and
|
|
/// differences in whitespace sequences between matching non-whitespace sub-strings.
|
|
/// </remarks>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreWhitespaceAndCase(string StrA, string StrB)
|
|
{
|
|
// Remove GUIDs before processing the strings.
|
|
//StrA = StripGUIDs(StrA);
|
|
//StrB = StripGUIDs(StrB);
|
|
|
|
int IndexA = 0;
|
|
int IndexB = 0;
|
|
while (IndexA < StrA.Length && IndexB < StrB.Length)
|
|
{
|
|
char A = Char.ToLowerInvariant(StrA[IndexA]);
|
|
char B = Char.ToLowerInvariant(StrB[IndexB]);
|
|
if (Char.IsWhiteSpace(A) && Char.IsWhiteSpace(B) && CanIgnoreWhitespace(A) && CanIgnoreWhitespace(B))
|
|
{
|
|
// Skip whitespaces in both strings
|
|
for (IndexA++; IndexA < StrA.Length && Char.IsWhiteSpace(StrA[IndexA]) == true; IndexA++) ;
|
|
for (IndexB++; IndexB < StrB.Length && Char.IsWhiteSpace(StrB[IndexB]) == true; IndexB++) ;
|
|
}
|
|
else if (Char.IsWhiteSpace(A) && IndexA > 0 && StrA[IndexA - 1] == '\n')
|
|
{
|
|
// Skip whitespaces in StrA at the beginning of each line
|
|
for (IndexA++; IndexA < StrA.Length && Char.IsWhiteSpace(StrA[IndexA]) == true; IndexA++) ;
|
|
}
|
|
else if (Char.IsWhiteSpace(B) && IndexB > 0 && StrB[IndexB - 1] == '\n')
|
|
{
|
|
// Skip whitespaces in StrA at the beginning of each line
|
|
for (IndexB++; IndexB < StrB.Length && Char.IsWhiteSpace(StrB[IndexB]) == true; IndexB++) ;
|
|
}
|
|
else if (A != B)
|
|
{
|
|
return A - B;
|
|
}
|
|
else
|
|
{
|
|
IndexA++;
|
|
IndexB++;
|
|
}
|
|
}
|
|
// Check if we reached the end in both strings
|
|
return (StrA.Length - IndexA) - (StrB.Length - IndexB);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring case and GUIDs.
|
|
/// </summary>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreCase(string StrA, string StrB)
|
|
{
|
|
// Remove GUIDs before processing the strings.
|
|
//StrA = StripGUIDs(StrA);
|
|
//StrB = StripGUIDs(StrB);
|
|
|
|
// Use simple ordinal comparison.
|
|
return String.Compare(StrA, StrB, StringComparison.InvariantCultureIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring case and GUIDs.
|
|
/// </summary>
|
|
/// <see cref="CompareOrdinalIgnoreWhitespaceAndCase"/>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <param name="bIgnoreWhitespace">True if whitsapces should be ignored.</param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreCase(string StrA, string StrB, bool bIgnoreWhitespace)
|
|
{
|
|
if (bIgnoreWhitespace)
|
|
{
|
|
return CompareOrdinalIgnoreWhitespaceAndCase(StrA, StrB);
|
|
}
|
|
else
|
|
{
|
|
return CompareOrdinalIgnoreCase(StrA, StrB);
|
|
}
|
|
}
|
|
}
|
|
}
|