You're breathtaking!
This commit is contained in:
662
Source/Editor/Scripting/ScriptsBuilder.cpp
Normal file
662
Source/Editor/Scripting/ScriptsBuilder.cpp
Normal file
@@ -0,0 +1,662 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ScriptsBuilder.h"
|
||||
#include "CodeEditor.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
#include "Engine/Debug/Exceptions/FileNotFoundException.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/FileSystemWatcher.h"
|
||||
#include "Engine/Threading/ThreadPool.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Scripting/MainThreadManagedInvokeAction.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
#include "Engine/Scripting/BinaryModule.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
|
||||
enum class EventType
|
||||
{
|
||||
Unknown = -1,
|
||||
CompileBegin = 0,
|
||||
CompileStarted = 1,
|
||||
CompileEndGood = 2,
|
||||
CompileEndFailed = 3,
|
||||
ReloadCalled = 4,
|
||||
ReloadBegin = 5,
|
||||
Reload = 6,
|
||||
ReloadEnd = 7,
|
||||
};
|
||||
|
||||
struct EventData
|
||||
{
|
||||
EventType Type;
|
||||
};
|
||||
|
||||
namespace ScriptsBuilderImpl
|
||||
{
|
||||
CriticalSection _locker;
|
||||
bool _isInited = false;
|
||||
bool _isCompileRequested = false;
|
||||
bool _isCompileRunning = false;
|
||||
bool _wasProjectStructureChanged = false;
|
||||
bool _lastCompilationFailed = false;
|
||||
int32 _compilationsCount = 0;
|
||||
DateTime _lastSourceCodeEdited = 0;
|
||||
DateTime _lastCompileAction = 0;
|
||||
|
||||
Array<FileSystemWatcher*> SourceFoldersWatchers;
|
||||
|
||||
CriticalSection _compileEventsLocker;
|
||||
Array<EventData> _compileEvents;
|
||||
|
||||
MMethod* Internal_OnEvent = nullptr;
|
||||
MMethod* Internal_OnCompileEvent = nullptr;
|
||||
MMethod* Internal_OnCodeEditorEvent = nullptr;
|
||||
|
||||
void CallEvent(EventType type);
|
||||
void CallCompileEvent(EventData& data);
|
||||
void CallCodeEditorEvent(bool isEnd);
|
||||
|
||||
void sourceDirEvent(const String& path, FileSystemAction action);
|
||||
void onEditorAssemblyUnloading(MAssembly* assembly);
|
||||
void onScriptsReloadStart();
|
||||
void onScriptsReload();
|
||||
void onScriptsReloadEnd();
|
||||
|
||||
void GetClassName(const MString& fullname, MString& className);
|
||||
|
||||
void onCodeEditorAsyncOpenBegin()
|
||||
{
|
||||
CallCodeEditorEvent(false);
|
||||
}
|
||||
|
||||
void onCodeEditorAsyncOpenEnd()
|
||||
{
|
||||
CallCodeEditorEvent(true);
|
||||
}
|
||||
|
||||
bool compileGameScriptsAsyncInner();
|
||||
bool compileGameScriptsAsync();
|
||||
}
|
||||
|
||||
using namespace ScriptsBuilderImpl;
|
||||
|
||||
class ScriptsBuilderService : public EngineService
|
||||
{
|
||||
public:
|
||||
|
||||
ScriptsBuilderService()
|
||||
: EngineService(TEXT("Scripts Builder"))
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Update() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
ScriptsBuilderService ScriptsBuilderServiceInstance;
|
||||
|
||||
Delegate<bool> ScriptsBuilder::OnCompilationEnd;
|
||||
Action ScriptsBuilder::OnCompilationSuccess;
|
||||
Action ScriptsBuilder::OnCompilationFailed;
|
||||
|
||||
void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction action)
|
||||
{
|
||||
// Discard non-source files or generated files
|
||||
if (!path.EndsWith(TEXT(".cs")) &&
|
||||
!path.EndsWith(TEXT(".cpp")) &&
|
||||
!path.EndsWith(TEXT(".h")) ||
|
||||
path.EndsWith(TEXT(".Gen.cs")))
|
||||
return;
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
}
|
||||
|
||||
int32 ScriptsBuilder::GetCompilationsCount()
|
||||
{
|
||||
Platform::MemoryBarrier();
|
||||
return _compilationsCount;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::LastCompilationFailed()
|
||||
{
|
||||
return _lastCompilationFailed;
|
||||
}
|
||||
|
||||
void ScriptsBuilder::FilterNamespaceText(String& value)
|
||||
{
|
||||
value.Replace(TEXT(" "), TEXT(""), StringSearchCase::CaseSensitive);
|
||||
value.Replace(TEXT("."), TEXT(""), StringSearchCase::CaseSensitive);
|
||||
value.Replace(TEXT("-"), TEXT(""), StringSearchCase::CaseSensitive);
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsSourceDirty()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _lastSourceCodeEdited > _lastCompileAction;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsSourceWorkspaceDirty()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _wasProjectStructureChanged;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsSourceDirty(const TimeSpan& timeout)
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _lastSourceCodeEdited > (_lastCompileAction + timeout);
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsCompiling()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _isCompileRunning;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsReady()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return !IsSourceDirty() && !_isCompileRequested && !_isCompileRunning;
|
||||
}
|
||||
|
||||
void ScriptsBuilder::MarkWorkspaceDirty()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
_wasProjectStructureChanged = true;
|
||||
}
|
||||
|
||||
void ScriptsBuilder::CheckForCompile()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
if (IsSourceDirty())
|
||||
Compile();
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::onScriptsReloadStart()
|
||||
{
|
||||
CallEvent(EventType::ReloadBegin);
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::onScriptsReload()
|
||||
{
|
||||
CallEvent(EventType::Reload);
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::onScriptsReloadEnd()
|
||||
{
|
||||
CallEvent(EventType::ReloadEnd);
|
||||
}
|
||||
|
||||
void ScriptsBuilder::Compile()
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
|
||||
// Request compile job
|
||||
_isCompileRequested = true;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::RunBuildTool(const StringView& args)
|
||||
{
|
||||
const String buildToolPath = Globals::StartupFolder / TEXT("Binaries/Tools/Flax.Build.exe");
|
||||
if (!FileSystem::FileExists(buildToolPath))
|
||||
{
|
||||
Log::FileNotFoundException(buildToolPath).SetLevel(LogType::Fatal);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
// Prepare build options
|
||||
StringBuilder cmdLine(args.Length() + buildToolPath.Length() + 30);
|
||||
cmdLine.Append(TEXT("\""));
|
||||
cmdLine.Append(buildToolPath);
|
||||
cmdLine.Append(TEXT("\" "));
|
||||
cmdLine.Append(args.Get(), args.Length());
|
||||
cmdLine.Append(TEXT('\0'));
|
||||
#else
|
||||
// Use mono to run the build tool
|
||||
const String monoPath = Globals::MonoPath / TEXT("bin/mono.exe");
|
||||
if (!FileSystem::FileExists(monoPath))
|
||||
{
|
||||
Log::FileNotFoundException(monoPath).SetLevel(LogType::Fatal);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare build options
|
||||
StringBuilder cmdLine(monoPath.Length() + args.Length() + buildToolPath.Length() + 30);
|
||||
cmdLine.Append(TEXT("\""));
|
||||
cmdLine.Append(monoPath);
|
||||
cmdLine.Append(TEXT("\" \""));
|
||||
cmdLine.Append(buildToolPath);
|
||||
cmdLine.Append(TEXT("\" "));
|
||||
cmdLine.Append(args.Get(), args.Length());
|
||||
cmdLine.Append(TEXT('\0'));
|
||||
|
||||
// TODO: Set env var for the mono MONO_GC_PARAMS=nursery-size64m to boost build performance
|
||||
#endif
|
||||
|
||||
// Call build tool
|
||||
const int32 result = Platform::RunProcess(StringView(*cmdLine, cmdLine.Length()), StringView::Empty);
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::GenerateProject(const StringView& customArgs)
|
||||
{
|
||||
String args(TEXT("-log -genproject "));
|
||||
args += customArgs;
|
||||
_wasProjectStructureChanged = false;
|
||||
return RunBuildTool(args);
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::GetClassName(const MString& fullname, MString& className)
|
||||
{
|
||||
const auto lastDotIndex = fullname.FindLast('.');
|
||||
if (lastDotIndex != -1)
|
||||
{
|
||||
//namespaceName = fullname.Substring(0, lastDotIndex);
|
||||
className = fullname.Substring(lastDotIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
className = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
MClass* ScriptsBuilder::FindScript(const StringView& scriptName)
|
||||
{
|
||||
const MString scriptNameStd = scriptName.ToStringAnsi();
|
||||
|
||||
const ScriptingTypeHandle scriptingType = Scripting::FindScriptingType(scriptNameStd);
|
||||
if (scriptingType)
|
||||
{
|
||||
MClass* mclass = scriptingType.GetType().ManagedClass;
|
||||
if (mclass)
|
||||
{
|
||||
return mclass;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all assemblies (ignoring the typename namespace)
|
||||
auto& modules = BinaryModule::GetModules();
|
||||
MString className;
|
||||
GetClassName(scriptNameStd, className);
|
||||
MClass* scriptClass = Script::GetStaticClass();
|
||||
for (int32 j = 0; j < modules.Count(); j++)
|
||||
{
|
||||
auto managedModule = dynamic_cast<ManagedBinaryModule*>(modules[j]);
|
||||
if (!managedModule)
|
||||
continue;
|
||||
auto assembly = managedModule->Assembly;
|
||||
auto& classes = assembly->GetClasses();
|
||||
for (auto i = classes.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
MClass* mclass = i->Value;
|
||||
|
||||
// Managed scripts
|
||||
if (mclass->IsSubClassOf(scriptClass) && !mclass->IsStatic() && !mclass->IsAbstract() && !mclass->IsInterface())
|
||||
{
|
||||
MString mclassName;
|
||||
GetClassName(mclass->GetFullName(), mclassName);
|
||||
if (className == mclassName)
|
||||
{
|
||||
LOG(Info, "Found {0} type for type {1} (assembly {2})", mclass->ToString(), String(scriptName.Get(), scriptName.Length()), assembly->ToString());
|
||||
return mclass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG(Warning, "Failed to find script class of name {0}", String(scriptNameStd));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptsBuilder::GetExistingEditors(int32* result, int32 count)
|
||||
{
|
||||
auto& editors = CodeEditingManager::GetEditors();
|
||||
for (int32 i = 0; i < editors.Count() && i < count; i++)
|
||||
{
|
||||
result[static_cast<int32>(editors[i]->GetType())] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsBuilder::GetBinariesConfiguration(StringView& target, StringView& platform, StringView& architecture, StringView& configuration)
|
||||
{
|
||||
const Char *targetPtr, *platformPtr, *architecturePtr, *configurationPtr;
|
||||
GetBinariesConfiguration(targetPtr, platformPtr, architecturePtr, configurationPtr);
|
||||
target = targetPtr;
|
||||
platform = platformPtr;
|
||||
architecture = architecturePtr;
|
||||
configuration = configurationPtr;
|
||||
}
|
||||
|
||||
void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*& platform, const Char*& architecture, const Char*& configuration)
|
||||
{
|
||||
// Special case when opening engine project
|
||||
if (Editor::Project->ProjectFolderPath == Globals::StartupFolder)
|
||||
{
|
||||
target = platform = architecture = configuration = nullptr;
|
||||
return;
|
||||
}
|
||||
target = Editor::Project->EditorTarget.GetText();
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
platform = TEXT("Windows");
|
||||
#endif
|
||||
|
||||
#if PLATFORM_ARCH_X64
|
||||
architecture = TEXT("x64");
|
||||
#elif PLATFORM_ARCH_X86
|
||||
architecture = TEXT("x86");
|
||||
#endif
|
||||
|
||||
#if BUILD_DEBUG
|
||||
configuration = TEXT("Debug");
|
||||
#elif BUILD_DEVELOPMENT
|
||||
configuration = TEXT("Development");
|
||||
#elif BUILD_RELEASE
|
||||
configuration = TEXT("Release");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ScriptsBuilderImpl::compileGameScriptsAsyncInner()
|
||||
{
|
||||
LOG(Info, "Starting scripts compilation...");
|
||||
CallEvent(EventType::CompileStarted);
|
||||
|
||||
// Call compilation
|
||||
const Char *target, *platform, *architecture, *configuration;
|
||||
ScriptsBuilder::GetBinariesConfiguration(target, platform, architecture, configuration);
|
||||
if (!target)
|
||||
{
|
||||
LOG(Info, "Missing EditorTarget in project. Skipping compilation.");
|
||||
CallEvent(EventType::ReloadCalled);
|
||||
Scripting::Reload();
|
||||
return false;
|
||||
}
|
||||
auto args = String::Format(
|
||||
TEXT("-log -logfile= -build -mutex -buildtargets={0} -skiptargets=FlaxEditor -platform={1} -arch={2} -configuration={3}"),
|
||||
target, platform, architecture, configuration);
|
||||
if (Scripting::HasGameModulesLoaded())
|
||||
{
|
||||
// Add postfix to output binaries to prevent file locking collisions when doing hot-reload in Editor
|
||||
args += String::Format(TEXT(" -hotreload=\".HotReload.{0}\""), _compilationsCount - 1);
|
||||
}
|
||||
if (ScriptsBuilder::RunBuildTool(args))
|
||||
return true;
|
||||
|
||||
// Reload scripts
|
||||
CallEvent(EventType::ReloadCalled);
|
||||
Scripting::Reload();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::CallEvent(EventType type)
|
||||
{
|
||||
ScopeLock lock(_compileEventsLocker);
|
||||
|
||||
const int32 index = _compileEvents.Count();
|
||||
_compileEvents.AddDefault(1);
|
||||
auto& data = _compileEvents[index];
|
||||
data.Type = type;
|
||||
|
||||
// Flush events on a main tread
|
||||
if (IsInMainThread())
|
||||
{
|
||||
for (int32 i = 0; i < _compileEvents.Count(); i++)
|
||||
CallCompileEvent(_compileEvents[i]);
|
||||
_compileEvents.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::CallCompileEvent(EventData& data)
|
||||
{
|
||||
ASSERT(IsInMainThread());
|
||||
|
||||
// Special case for a single events with no data
|
||||
if (data.Type != EventType::Unknown)
|
||||
{
|
||||
// Call C# event
|
||||
if (Internal_OnEvent == nullptr)
|
||||
{
|
||||
auto scriptsBuilderClass = ScriptsBuilder::GetStaticClass();
|
||||
if (scriptsBuilderClass)
|
||||
Internal_OnEvent = scriptsBuilderClass->GetMethod("Internal_OnEvent", 1);
|
||||
if (Internal_OnEvent == nullptr)
|
||||
{
|
||||
LOG(Fatal, "Invalid Editor assembly!");
|
||||
}
|
||||
}
|
||||
/*MonoObject* exception = nullptr;
|
||||
void* args[1];
|
||||
args[0] = &data.Type;
|
||||
Internal_OnEvent->Invoke(nullptr, args, &exception);
|
||||
if (exception)
|
||||
{
|
||||
DebugLog::LogException(exception);
|
||||
}*/
|
||||
|
||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||
params.AddParam(data.Type);
|
||||
MainThreadManagedInvokeAction::Invoke(Internal_OnEvent, params);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::CallCodeEditorEvent(bool isEnd)
|
||||
{
|
||||
if (Internal_OnCodeEditorEvent == nullptr)
|
||||
{
|
||||
auto scriptsBuilderClass = ScriptsBuilder::GetStaticClass();
|
||||
if (scriptsBuilderClass)
|
||||
Internal_OnCodeEditorEvent = scriptsBuilderClass->GetMethod("Internal_OnCodeEditorEvent", 1);
|
||||
ASSERT(Internal_OnCodeEditorEvent);
|
||||
}
|
||||
|
||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||
params.AddParam(isEnd);
|
||||
MainThreadManagedInvokeAction::Invoke(Internal_OnCodeEditorEvent, params);
|
||||
}
|
||||
|
||||
void ScriptsBuilderImpl::onEditorAssemblyUnloading(MAssembly* assembly)
|
||||
{
|
||||
Internal_OnEvent = nullptr;
|
||||
Internal_OnCompileEvent = nullptr;
|
||||
Internal_OnCodeEditorEvent = nullptr;
|
||||
}
|
||||
|
||||
bool ScriptsBuilderImpl::compileGameScriptsAsync()
|
||||
{
|
||||
// Start
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
|
||||
_isCompileRequested = false;
|
||||
_lastCompileAction = DateTime::Now();
|
||||
_compilationsCount++;
|
||||
_isCompileRunning = true;
|
||||
|
||||
ScriptsBuilderServiceInstance.Init();
|
||||
|
||||
CallEvent(EventType::CompileBegin);
|
||||
}
|
||||
|
||||
// Do work
|
||||
const bool success = !compileGameScriptsAsyncInner();
|
||||
|
||||
// End
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
|
||||
_lastCompilationFailed = !success;
|
||||
|
||||
ScriptsBuilder::OnCompilationEnd(success);
|
||||
if (success)
|
||||
ScriptsBuilder::OnCompilationSuccess();
|
||||
else
|
||||
ScriptsBuilder::OnCompilationFailed();
|
||||
if (success)
|
||||
CallEvent(EventType::CompileEndGood);
|
||||
else
|
||||
CallEvent(EventType::CompileEndFailed);
|
||||
_isCompileRunning = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptsBuilderService::Init()
|
||||
{
|
||||
// Check flag
|
||||
if (_isInited)
|
||||
return false;
|
||||
_isInited = true;
|
||||
|
||||
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
|
||||
auto editorAssembly = GetBinaryModuleFlaxEngine()->Assembly;
|
||||
editorAssembly->Unloading.Bind(onEditorAssemblyUnloading);
|
||||
|
||||
// Listen to scripts reloading events and forward them to c#
|
||||
Level::ScriptsReloadStart.Bind(onScriptsReloadStart);
|
||||
Level::ScriptsReload.Bind(onScriptsReload);
|
||||
Level::ScriptsReloadEnd.Bind(onScriptsReloadEnd);
|
||||
|
||||
// Listen to code editors manager events
|
||||
CodeEditingManager::AsyncOpenBegin.Bind(onCodeEditorAsyncOpenBegin);
|
||||
CodeEditingManager::AsyncOpenEnd.Bind(onCodeEditorAsyncOpenEnd);
|
||||
|
||||
// Create source folder watcher to handle scripts modification events (create/delete scripts events are handled by the editor itself)
|
||||
auto project = Editor::Project;
|
||||
HashSet<ProjectInfo*> projects;
|
||||
project->GetAllProjects(projects);
|
||||
for (auto e : projects)
|
||||
{
|
||||
ProjectInfo* project = e.Item;
|
||||
if (project->Name == TEXT("Flax"))
|
||||
continue;
|
||||
auto watcher = New<FileSystemWatcher>(project->ProjectFolderPath / TEXT("Source"), true);
|
||||
watcher->OnEvent.Bind(sourceDirEvent);
|
||||
SourceFoldersWatchers.Add(watcher);
|
||||
}
|
||||
|
||||
// Verify project
|
||||
if (project->EditorTarget.IsEmpty() || project->GameTarget.IsEmpty())
|
||||
{
|
||||
const String& name = project->Name;
|
||||
String codeName;
|
||||
for (int32 i = 0; i < name.Length(); i++)
|
||||
{
|
||||
Char c = name[i];
|
||||
if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
|
||||
codeName += c;
|
||||
}
|
||||
project->GameTarget = codeName + TEXT("Target");
|
||||
project->EditorTarget = codeName + TEXT("EditorTarget");
|
||||
LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget);
|
||||
}
|
||||
|
||||
// Remove any reaming files from previous Editor run hot-reloads
|
||||
const Char *target, *platform, *architecture, *configuration;
|
||||
ScriptsBuilder::GetBinariesConfiguration(target, platform, architecture, configuration);
|
||||
if (target)
|
||||
{
|
||||
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
||||
Array<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, targetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
||||
if (files.HasItems())
|
||||
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
||||
for (auto& file : files)
|
||||
{
|
||||
FileSystem::DeleteFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
bool forceRecompile = false;
|
||||
|
||||
// Check last Editor version that was using a project is different from current
|
||||
if (Editor::IsOldProjectOpened)
|
||||
forceRecompile = true;
|
||||
|
||||
// Check if need to force recompile game scripts
|
||||
if (forceRecompile)
|
||||
{
|
||||
LOG(Warning, "Forcing scripts recompilation");
|
||||
FileSystem::DeleteDirectory(Globals::ProjectCacheFolder / TEXT("Intermediate"));
|
||||
ScriptsBuilder::Compile();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptsBuilderService::Update()
|
||||
{
|
||||
// Send compilation events
|
||||
{
|
||||
ScopeLock scopeLock(_compileEventsLocker);
|
||||
|
||||
for (int32 i = 0; i < _compileEvents.Count(); i++)
|
||||
CallCompileEvent(_compileEvents[i]);
|
||||
_compileEvents.Clear();
|
||||
}
|
||||
|
||||
// Check if compile code (if has been edited)
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50);
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (ScriptsBuilder::IsSourceDirty(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
|
||||
{
|
||||
// Check if auto reload is enabled
|
||||
if (Editor::Managed->CanAutoReloadScripts())
|
||||
ScriptsBuilder::Compile();
|
||||
}
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
|
||||
// Check if compilation has been requested (some time ago since we want to batch calls to reduce amount of compilations)
|
||||
if (_isCompileRequested)
|
||||
{
|
||||
// Check if compilation isn't running
|
||||
if (!_isCompileRunning)
|
||||
{
|
||||
// Check if editor state can perform scripts reloading
|
||||
if (Editor::Managed->CanReloadScripts())
|
||||
{
|
||||
// Call compilation (and switch flags)
|
||||
_isCompileRequested = false;
|
||||
_isCompileRunning = true;
|
||||
Function<bool()> action(compileGameScriptsAsync);
|
||||
Task::StartNew(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptsBuilderService::Dispose()
|
||||
{
|
||||
// Don't exit while scripts compilation is still running
|
||||
if (ScriptsBuilder::IsCompiling())
|
||||
{
|
||||
LOG(Warning, "Scripts compilation is running, waiting for the end...");
|
||||
int32 timeOutMilliseconds = 5000;
|
||||
while (ScriptsBuilder::IsCompiling() && timeOutMilliseconds > 0)
|
||||
{
|
||||
const int sleepTimeMilliseconds = 50;
|
||||
timeOutMilliseconds -= sleepTimeMilliseconds;
|
||||
Platform::Sleep(sleepTimeMilliseconds);
|
||||
}
|
||||
LOG(Warning, "Scripts compilation wait ended");
|
||||
}
|
||||
|
||||
SourceFoldersWatchers.ClearDelete();
|
||||
}
|
||||
Reference in New Issue
Block a user