mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-01 03:10:06 +00:00
Projucer: Re-factored source code groups and general code clean-up
This commit is contained in:
parent
980361e54b
commit
54029e776d
268 changed files with 8838 additions and 8682 deletions
|
|
@ -0,0 +1,968 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
#include "../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "jucer_MessageIDs.h"
|
||||
#include "jucer_CppHelpers.h"
|
||||
#include "jucer_SourceCodeRange.h"
|
||||
#include "jucer_ClassDatabase.h"
|
||||
#include "jucer_DiagnosticMessage.h"
|
||||
#include "jucer_ProjectBuildInfo.h"
|
||||
#include "jucer_ClientServerMessages.h"
|
||||
#include "jucer_CompileEngineClient.h"
|
||||
#include "../LiveBuildEngine/jucer_CompileEngineServer.h"
|
||||
|
||||
#ifndef RUN_CLANG_IN_CHILD_PROCESS
|
||||
#error
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace ProjectProperties
|
||||
{
|
||||
const Identifier liveSettingsType ("LIVE_SETTINGS");
|
||||
#if JUCE_MAC
|
||||
const Identifier liveSettingsSubtype ("OSX");
|
||||
#elif JUCE_WINDOWS
|
||||
const Identifier liveSettingsSubtype ("WINDOWS");
|
||||
#elif JUCE_LINUX
|
||||
const Identifier liveSettingsSubtype ("LINUX");
|
||||
#endif
|
||||
|
||||
static ValueTree getLiveSettings (Project& project)
|
||||
{
|
||||
return project.getProjectRoot().getOrCreateChildWithName (liveSettingsType, nullptr)
|
||||
.getOrCreateChildWithName (liveSettingsSubtype, nullptr);
|
||||
}
|
||||
|
||||
static const ValueTree getLiveSettingsConst (Project& project)
|
||||
{
|
||||
return project.getProjectRoot().getChildWithName (liveSettingsType)
|
||||
.getChildWithName (liveSettingsSubtype);
|
||||
}
|
||||
|
||||
static Value getLiveSetting (Project& p, const Identifier& i) { return getLiveSettings (p).getPropertyAsValue (i, p.getUndoManagerFor (getLiveSettings (p))); }
|
||||
static var getLiveSettingVar (Project& p, const Identifier& i) { return getLiveSettingsConst (p) [i]; }
|
||||
|
||||
static Value getUserHeaderPathValue (Project& p) { return getLiveSetting (p, Ids::headerPath); }
|
||||
static String getUserHeaderPathString (Project& p) { return getLiveSettingVar (p, Ids::headerPath); }
|
||||
static Value getSystemHeaderPathValue (Project& p) { return getLiveSetting (p, Ids::systemHeaderPath); }
|
||||
static String getSystemHeaderPathString (Project& p) { return getLiveSettingVar (p, Ids::systemHeaderPath); }
|
||||
static Value getExtraDLLsValue (Project& p) { return getLiveSetting (p, Ids::extraDLLs); }
|
||||
static String getExtraDLLsString (Project& p) { return getLiveSettingVar (p, Ids::extraDLLs); }
|
||||
static Value getExtraCompilerFlagsValue (Project& p) { return getLiveSetting (p, Ids::extraCompilerFlags); }
|
||||
static String getExtraCompilerFlagsString (Project& p) { return getLiveSettingVar (p, Ids::extraCompilerFlags); }
|
||||
static Value getExtraPreprocessorDefsValue (Project& p) { return getLiveSetting (p, Ids::defines); }
|
||||
static String getExtraPreprocessorDefsString (Project& p) { return getLiveSettingVar (p, Ids::defines); }
|
||||
static Value getWindowsTargetPlatformVersionValue (Project& p) { return getLiveSetting (p, Ids::liveWindowsTargetPlatformVersion); }
|
||||
static String getWindowsTargetPlatformVersionString (Project& p) { return getLiveSettingVar (p, Ids::liveWindowsTargetPlatformVersion); }
|
||||
|
||||
static File getProjucerTempFolder()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
return File ("~/Library/Caches/com.juce.projucer");
|
||||
#else
|
||||
return File::getSpecialLocation (File::tempDirectory).getChildFile ("com.juce.projucer");
|
||||
#endif
|
||||
}
|
||||
|
||||
static File getCacheLocation (Project& project)
|
||||
{
|
||||
String cacheFolderName = project.getProjectFilenameRoot() + "_" + project.getProjectUID();
|
||||
|
||||
#if JUCE_DEBUG
|
||||
cacheFolderName += "_debug";
|
||||
#endif
|
||||
|
||||
return getProjucerTempFolder()
|
||||
.getChildFile ("Intermediate Files")
|
||||
.getChildFile (cacheFolderName);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LiveBuildProjectSettings::getLiveSettings (Project& project, PropertyListBuilder& props)
|
||||
{
|
||||
using namespace ProjectProperties;
|
||||
|
||||
props.addSearchPathProperty (getUserHeaderPathValue (project), "User header paths", "User header search paths.");
|
||||
props.addSearchPathProperty (getSystemHeaderPathValue (project), "System header paths", "System header search paths.");
|
||||
|
||||
props.add (new TextPropertyComponent (getExtraPreprocessorDefsValue (project), "Preprocessor Definitions", 32768, true),
|
||||
"Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace or commas "
|
||||
"to separate the items - to include a space or comma in a definition, precede it with a backslash.");
|
||||
|
||||
props.add (new TextPropertyComponent (getExtraCompilerFlagsValue (project), "Extra compiler flags", 2048, true),
|
||||
"Extra command-line flags to be passed to the compiler. This string can contain references to preprocessor"
|
||||
" definitions in the form ${NAME_OF_DEFINITION}, which will be replaced with their values.");
|
||||
|
||||
props.add (new TextPropertyComponent (getExtraDLLsValue (project), "Extra dynamic libraries", 2048, true),
|
||||
"Extra dynamic libs that the running code may require. Use new-lines or commas to separate the items");
|
||||
|
||||
static const char* targetPlatformNames[] = { "(default)", "8.1", "10.0.10240.0", "10.0.10586.0", "10.0.14393.0", "10.0.15063.0", nullptr };
|
||||
const var targetPlatforms[] = { var(), "8.1", "10.0.10240.0", "10.0.10586.0", "10.0.14393.0", "10.0.15063.0" };
|
||||
|
||||
props.add (new ChoicePropertyComponent (getWindowsTargetPlatformVersionValue (project), "Windows Target Platform",
|
||||
StringArray (targetPlatformNames), Array<var> (targetPlatforms, numElementsInArray (targetPlatforms))),
|
||||
"The Windows target platform to use");
|
||||
}
|
||||
|
||||
void LiveBuildProjectSettings::updateNewlyOpenedProject (Project&) { /* placeholder */ }
|
||||
|
||||
bool LiveBuildProjectSettings::isBuildDisabled (Project& p)
|
||||
{
|
||||
const bool defaultBuildDisabled = true;
|
||||
return p.getStoredProperties().getBoolValue ("buildDisabled", defaultBuildDisabled);
|
||||
}
|
||||
|
||||
void LiveBuildProjectSettings::setBuildDisabled (Project& p, bool b) { p.getStoredProperties().setValue ("buildDisabled", b); }
|
||||
bool LiveBuildProjectSettings::areWarningsDisabled (Project& p) { return p.getStoredProperties().getBoolValue ("warningsDisabled"); }
|
||||
void LiveBuildProjectSettings::setWarningsDisabled (Project& p, bool b) { p.getStoredProperties().setValue ("warningsDisabled", b); }
|
||||
|
||||
//==============================================================================
|
||||
class ClientIPC : public MessageHandler,
|
||||
private InterprocessConnection,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ClientIPC (CompileEngineChildProcess& cp)
|
||||
: InterprocessConnection (true), owner (cp)
|
||||
{
|
||||
launchServer();
|
||||
}
|
||||
|
||||
~ClientIPC()
|
||||
{
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
if (childProcess.isRunning())
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
killServerPolitely();
|
||||
#else
|
||||
// in release builds we don't want to wait
|
||||
// for the server to clean up and shut down
|
||||
killServerWithoutMercy();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void launchServer()
|
||||
{
|
||||
DBG ("Client: Launching Server...");
|
||||
const String pipeName ("ipc_" + String::toHexString (Random().nextInt64()));
|
||||
|
||||
const String command (createCommandLineForLaunchingServer (pipeName,
|
||||
owner.project.getProjectUID(),
|
||||
ProjectProperties::getCacheLocation (owner.project)));
|
||||
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
if (! childProcess.start (command))
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
#else
|
||||
server = createClangServer (command);
|
||||
#endif
|
||||
|
||||
bool ok = connectToPipe (pipeName, 10000);
|
||||
jassert (ok);
|
||||
|
||||
if (ok)
|
||||
MessageTypes::sendPing (*this);
|
||||
|
||||
startTimer (serverKeepAliveTimeout);
|
||||
}
|
||||
|
||||
void killServerPolitely()
|
||||
{
|
||||
DBG ("Client: Killing Server...");
|
||||
MessageTypes::sendQuit (*this);
|
||||
|
||||
disconnect();
|
||||
stopTimer();
|
||||
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
childProcess.waitForProcessToFinish (5000);
|
||||
#endif
|
||||
|
||||
killServerWithoutMercy();
|
||||
}
|
||||
|
||||
void killServerWithoutMercy()
|
||||
{
|
||||
disconnect();
|
||||
stopTimer();
|
||||
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
childProcess.kill();
|
||||
#else
|
||||
destroyClangServer (server);
|
||||
server = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void connectionMade()
|
||||
{
|
||||
DBG ("Client: connected");
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
void connectionLost()
|
||||
{
|
||||
DBG ("Client: disconnected");
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
bool sendMessage (const ValueTree& m)
|
||||
{
|
||||
return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
|
||||
}
|
||||
|
||||
void messageReceived (const MemoryBlock& message)
|
||||
{
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
startTimer (serverKeepAliveTimeout);
|
||||
#else
|
||||
stopTimer();
|
||||
#endif
|
||||
MessageTypes::dispatchToClient (owner, MessageHandler::convertMessage (message));
|
||||
}
|
||||
|
||||
enum { serverKeepAliveTimeout = 10000 };
|
||||
|
||||
private:
|
||||
CompileEngineChildProcess& owner;
|
||||
|
||||
#if RUN_CLANG_IN_CHILD_PROCESS
|
||||
ChildProcess childProcess;
|
||||
#else
|
||||
void* server;
|
||||
#endif
|
||||
|
||||
void timerCallback() { owner.handleCrash (String()); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class CompileEngineChildProcess::ChildProcess : private ValueTree::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ChildProcess (CompileEngineChildProcess& proc, Project& p)
|
||||
: owner (proc), project (p)
|
||||
{
|
||||
projectRoot = project.getProjectRoot();
|
||||
|
||||
restartServer();
|
||||
projectRoot.addListener (this);
|
||||
openedOk = true;
|
||||
}
|
||||
|
||||
~ChildProcess()
|
||||
{
|
||||
projectRoot.removeListener (this);
|
||||
|
||||
if (isRunningApp && server != nullptr)
|
||||
server->killServerWithoutMercy();
|
||||
|
||||
server = nullptr;
|
||||
}
|
||||
|
||||
void restartServer()
|
||||
{
|
||||
server = nullptr;
|
||||
server = new ClientIPC (owner);
|
||||
sendRebuild();
|
||||
}
|
||||
|
||||
void sendRebuild()
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
ProjectBuildInfo build;
|
||||
|
||||
if (! doesProjectMatchSavedHeaderState (project))
|
||||
{
|
||||
MessageTypes::sendNewBuild (*server, build);
|
||||
|
||||
owner.errorList.resetToError ("Project structure does not match the saved headers! "
|
||||
"Please re-save your project to enable compilation");
|
||||
return;
|
||||
}
|
||||
|
||||
if (areAnyModulesMissing (project))
|
||||
{
|
||||
MessageTypes::sendNewBuild (*server, build);
|
||||
|
||||
owner.errorList.resetToError ("Some of your JUCE modules can't be found! "
|
||||
"Please check that all the module paths are correct");
|
||||
return;
|
||||
}
|
||||
|
||||
build.setSystemIncludes (getSystemIncludePaths());
|
||||
build.setUserIncludes (getUserIncludes());
|
||||
|
||||
build.setGlobalDefs (getGlobalDefs (project));
|
||||
build.setCompileFlags (ProjectProperties::getExtraCompilerFlagsString (project).trim());
|
||||
build.setExtraDLLs (getExtraDLLs());
|
||||
build.setJuceModulesFolder (EnabledModuleList::findDefaultModulesFolder (project).getFullPathName());
|
||||
|
||||
build.setUtilsCppInclude (project.getAppIncludeFile().getFullPathName());
|
||||
|
||||
build.setWindowsTargetPlatformVersion (ProjectProperties::getWindowsTargetPlatformVersionString (project));
|
||||
|
||||
scanForProjectFiles (project, build);
|
||||
|
||||
owner.updateAllEditors();
|
||||
|
||||
MessageTypes::sendNewBuild (*server, build);
|
||||
}
|
||||
|
||||
void cleanAll()
|
||||
{
|
||||
MessageTypes::sendCleanAll (*server);
|
||||
sendRebuild();
|
||||
}
|
||||
|
||||
void reinstantiatePreviews()
|
||||
{
|
||||
MessageTypes::sendReinstantiate (*server);
|
||||
}
|
||||
|
||||
bool launchApp()
|
||||
{
|
||||
MessageTypes::sendLaunchApp (*server);
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopedPointer<ClientIPC> server;
|
||||
|
||||
bool openedOk = false;
|
||||
bool isRunningApp = false;
|
||||
|
||||
private:
|
||||
CompileEngineChildProcess& owner;
|
||||
Project& project;
|
||||
ValueTree projectRoot;
|
||||
|
||||
void projectStructureChanged()
|
||||
{
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
sendRebuild();
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { projectStructureChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { projectStructureChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { projectStructureChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { projectStructureChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
|
||||
|
||||
static String getGlobalDefs (Project& proj)
|
||||
{
|
||||
String defs (ProjectProperties::getExtraPreprocessorDefsString (proj));
|
||||
|
||||
for (Project::ExporterIterator exporter (proj); exporter.next();)
|
||||
if (exporter->canLaunchProject())
|
||||
defs << " " << exporter->getExporterIdentifierMacro() << "=1";
|
||||
|
||||
// Use the JUCE implementation of std::function until the live build
|
||||
// engine can compile the one from the standard library
|
||||
defs << " _LIBCPP_FUNCTIONAL=1";
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
static void scanProjectItem (const Project::Item& projectItem, Array<File>& compileUnits, Array<File>& userFiles)
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
scanProjectItem (projectItem.getChild(i), compileUnits, userFiles);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectItem.shouldBeCompiled())
|
||||
{
|
||||
const File f (projectItem.getFile());
|
||||
|
||||
if (f.exists())
|
||||
compileUnits.add (f);
|
||||
}
|
||||
|
||||
if (projectItem.shouldBeAddedToTargetProject() && ! projectItem.shouldBeAddedToBinaryResources())
|
||||
{
|
||||
const File f (projectItem.getFile());
|
||||
|
||||
if (f.exists())
|
||||
userFiles.add (f);
|
||||
}
|
||||
}
|
||||
|
||||
void scanForProjectFiles (Project& proj, ProjectBuildInfo& build)
|
||||
{
|
||||
Array<File> compileUnits, userFiles;
|
||||
scanProjectItem (proj.getMainGroup(), compileUnits, userFiles);
|
||||
|
||||
{
|
||||
auto isVST3Host = project.getModules().isModuleEnabled ("juce_audio_processors")
|
||||
&& project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3");
|
||||
|
||||
auto isPluginProject = proj.getProjectType().isAudioPlugin();
|
||||
|
||||
OwnedArray<LibraryModule> modules;
|
||||
proj.getModules().createRequiredModules (modules);
|
||||
|
||||
for (Project::ExporterIterator exporter (proj); exporter.next();)
|
||||
{
|
||||
if (exporter->canLaunchProject())
|
||||
{
|
||||
for (const LibraryModule* m : modules)
|
||||
{
|
||||
const File localModuleFolder = proj.getModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue()
|
||||
? proj.getLocalModuleFolder (m->moduleInfo.getID())
|
||||
: m->moduleInfo.getFolder();
|
||||
|
||||
|
||||
m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits,
|
||||
isPluginProject || isVST3Host ? ProjectType::Target::SharedCodeTarget
|
||||
: ProjectType::Target::unspecified);
|
||||
|
||||
if (isPluginProject || isVST3Host)
|
||||
m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits, ProjectType::Target::StandalonePlugIn);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; ; ++i)
|
||||
{
|
||||
const File binaryDataCpp (proj.getBinaryDataCppFile (i));
|
||||
if (! binaryDataCpp.exists())
|
||||
break;
|
||||
|
||||
compileUnits.add (binaryDataCpp);
|
||||
}
|
||||
|
||||
for (int i = compileUnits.size(); --i >= 0;)
|
||||
if (compileUnits.getReference(i).hasFileExtension (".r"))
|
||||
compileUnits.remove (i);
|
||||
|
||||
build.setFiles (compileUnits, userFiles);
|
||||
}
|
||||
|
||||
static bool doesProjectMatchSavedHeaderState (Project& project)
|
||||
{
|
||||
ValueTree liveModules (project.getProjectRoot().getChildWithName (Ids::MODULES));
|
||||
|
||||
ScopedPointer<XmlElement> xml (XmlDocument::parse (project.getFile()));
|
||||
|
||||
if (xml == nullptr || ! xml->hasTagName (Ids::JUCERPROJECT.toString()))
|
||||
return false;
|
||||
|
||||
ValueTree diskModules (ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES));
|
||||
|
||||
return liveModules.isEquivalentTo (diskModules);
|
||||
}
|
||||
|
||||
static bool areAnyModulesMissing (Project& project)
|
||||
{
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getModules().createRequiredModules (modules);
|
||||
|
||||
for (auto* module : modules)
|
||||
if (! module->getFolder().isDirectory())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
StringArray getUserIncludes()
|
||||
{
|
||||
StringArray paths;
|
||||
paths.add (project.getGeneratedCodeFolder().getFullPathName());
|
||||
paths.addArray (getSearchPathsFromString (ProjectProperties::getUserHeaderPathString (project)));
|
||||
return convertSearchPathsToAbsolute (paths);
|
||||
}
|
||||
|
||||
StringArray getSystemIncludePaths()
|
||||
{
|
||||
StringArray paths;
|
||||
paths.addArray (getSearchPathsFromString (ProjectProperties::getSystemHeaderPathString (project)));
|
||||
|
||||
auto isVST3Host = project.getModules().isModuleEnabled ("juce_audio_processors")
|
||||
&& project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3");
|
||||
|
||||
if (project.getProjectType().isAudioPlugin() || isVST3Host)
|
||||
paths.add (getAppSettings().getStoredPath (Ids::vst3Path).toString());
|
||||
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getModules().createRequiredModules (modules);
|
||||
|
||||
for (auto* module : modules)
|
||||
paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
|
||||
|
||||
return convertSearchPathsToAbsolute (paths);
|
||||
}
|
||||
|
||||
StringArray convertSearchPathsToAbsolute (const StringArray& paths) const
|
||||
{
|
||||
StringArray s;
|
||||
const File root (project.getProjectFolder());
|
||||
|
||||
for (String p : paths)
|
||||
s.add (root.getChildFile (p).getFullPathName());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray getExtraDLLs()
|
||||
{
|
||||
StringArray dlls;
|
||||
dlls.addTokens (ProjectProperties::getExtraDLLsString (project), "\n\r,", StringRef());
|
||||
dlls.trim();
|
||||
dlls.removeEmptyStrings();
|
||||
return dlls;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
|
||||
: project (p),
|
||||
continuousRebuild (false)
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.addListener (this);
|
||||
|
||||
createProcess();
|
||||
|
||||
errorList.setWarningsEnabled (! LiveBuildProjectSettings::areWarningsDisabled (project));
|
||||
}
|
||||
|
||||
CompileEngineChildProcess::~CompileEngineChildProcess()
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
|
||||
|
||||
process = nullptr;
|
||||
lastComponentList.clear();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::createProcess()
|
||||
{
|
||||
jassert (process == nullptr);
|
||||
process = new ChildProcess (*this, project);
|
||||
|
||||
if (! process->openedOk)
|
||||
process = nullptr;
|
||||
|
||||
updateAllEditors();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::cleanAll()
|
||||
{
|
||||
if (process != nullptr)
|
||||
process->cleanAll();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
|
||||
{
|
||||
if (process != nullptr)
|
||||
{
|
||||
MainWindow* projectWindow = nullptr;
|
||||
OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
|
||||
|
||||
for (int i = 0; i < windows.size(); ++i)
|
||||
{
|
||||
if (MainWindow* w = windows[i])
|
||||
{
|
||||
if (w->getProject() == &project)
|
||||
{
|
||||
projectWindow = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<int> mainWindowRect;
|
||||
|
||||
if (projectWindow != nullptr)
|
||||
mainWindowRect = projectWindow->getBounds();
|
||||
|
||||
MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
|
||||
}
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::reinstantiatePreviews()
|
||||
{
|
||||
if (process != nullptr)
|
||||
process->reinstantiatePreviews();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::processActivationChanged (bool isForeground)
|
||||
{
|
||||
if (process != nullptr)
|
||||
MessageTypes::sendProcessActivationState (*process->server, isForeground);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool CompileEngineChildProcess::canLaunchApp() const
|
||||
{
|
||||
return process != nullptr
|
||||
&& runningAppProcess == nullptr
|
||||
&& activityList.getNumActivities() == 0
|
||||
&& errorList.getNumErrors() == 0
|
||||
&& project.getProjectType().isGUIApplication();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::launchApp()
|
||||
{
|
||||
if (process != nullptr)
|
||||
process->launchApp();
|
||||
}
|
||||
|
||||
bool CompileEngineChildProcess::canKillApp() const
|
||||
{
|
||||
return runningAppProcess != nullptr;
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::killApp()
|
||||
{
|
||||
runningAppProcess = nullptr;
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleAppLaunched()
|
||||
{
|
||||
runningAppProcess = process;
|
||||
runningAppProcess->isRunningApp = true;
|
||||
createProcess();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleAppQuit()
|
||||
{
|
||||
DBG ("handleAppQuit");
|
||||
runningAppProcess = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
|
||||
private Timer
|
||||
{
|
||||
Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
|
||||
: owner (ccp), file (f), document (doc), transactionTimer (doc)
|
||||
{
|
||||
sendFullUpdate();
|
||||
document.addListener (this);
|
||||
}
|
||||
|
||||
~Editor()
|
||||
{
|
||||
document.removeListener (this);
|
||||
}
|
||||
|
||||
void codeDocumentTextInserted (const String& newText, int insertIndex) override
|
||||
{
|
||||
CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
|
||||
startEditorChangeTimer();
|
||||
transactionTimer.stopTimer();
|
||||
|
||||
owner.lastComponentList.globalNamespace
|
||||
.nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
|
||||
}
|
||||
|
||||
void codeDocumentTextDeleted (int start, int end) override
|
||||
{
|
||||
CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
|
||||
startEditorChangeTimer();
|
||||
transactionTimer.stopTimer();
|
||||
|
||||
owner.lastComponentList.globalNamespace
|
||||
.nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
|
||||
}
|
||||
|
||||
void sendFullUpdate()
|
||||
{
|
||||
reset();
|
||||
|
||||
if (owner.process != nullptr)
|
||||
MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
|
||||
}
|
||||
|
||||
bool flushEditorChanges()
|
||||
{
|
||||
if (pendingChanges.size() > 0)
|
||||
{
|
||||
if (owner.process != nullptr && owner.process->server != nullptr)
|
||||
MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
|
||||
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
stopTimer();
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
stopTimer();
|
||||
pendingChanges.clear();
|
||||
}
|
||||
|
||||
void startTransactionTimer()
|
||||
{
|
||||
transactionTimer.startTimer (1000);
|
||||
}
|
||||
|
||||
void startEditorChangeTimer()
|
||||
{
|
||||
startTimer (200);
|
||||
}
|
||||
|
||||
CompileEngineChildProcess& owner;
|
||||
File file;
|
||||
CodeDocument& document;
|
||||
|
||||
private:
|
||||
Array<CodeChange> pendingChanges;
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (owner.continuousRebuild)
|
||||
flushEditorChanges();
|
||||
else
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
struct TransactionTimer : public Timer
|
||||
{
|
||||
TransactionTimer (CodeDocument& doc) : document (doc) {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
document.newTransaction();
|
||||
}
|
||||
|
||||
CodeDocument& document;
|
||||
};
|
||||
|
||||
TransactionTimer transactionTimer;
|
||||
};
|
||||
|
||||
void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
|
||||
{
|
||||
editors.add (new Editor (*this, file, document));
|
||||
}
|
||||
|
||||
bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
|
||||
{
|
||||
for (int i = editors.size(); --i >= 0;)
|
||||
{
|
||||
if (document->getFile() == editors.getUnchecked(i)->file)
|
||||
{
|
||||
const File f (editors.getUnchecked(i)->file);
|
||||
editors.remove (i);
|
||||
|
||||
if (process != nullptr)
|
||||
MessageTypes::sendHandleFileReset (*process->server, f);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::updateAllEditors()
|
||||
{
|
||||
for (int i = editors.size(); --i >= 0;)
|
||||
editors.getUnchecked(i)->sendFullUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CompileEngineChildProcess::handleCrash (const String& message)
|
||||
{
|
||||
Logger::writeToLog ("*** Child process crashed: " + message);
|
||||
|
||||
if (crashHandler != nullptr)
|
||||
crashHandler (message);
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
|
||||
void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
|
||||
|
||||
void CompileEngineChildProcess::handleCloseIDE()
|
||||
{
|
||||
if (JUCEApplication* app = JUCEApplication::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleMissingSystemHeaders()
|
||||
{
|
||||
if (ProjectContentComponent* p = findProjectContentComponent())
|
||||
p->handleMissingSystemHeaders();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
|
||||
{
|
||||
ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
|
||||
|
||||
CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
|
||||
|
||||
if (command == StandardApplicationCommandIDs::undo)
|
||||
{
|
||||
handleUndoInEditor (className);
|
||||
}
|
||||
else if (command == StandardApplicationCommandIDs::redo)
|
||||
{
|
||||
handleRedoInEditor (className);
|
||||
}
|
||||
else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
|
||||
{
|
||||
commandManager.setFirstCommandTarget (target);
|
||||
commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
|
||||
commandManager.setFirstCommandTarget (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
|
||||
{
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
|
||||
{
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
|
||||
{
|
||||
lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
|
||||
activityList.sendClassListChangedMessage (lastComponentList);
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleBuildFailed()
|
||||
{
|
||||
auto* mcm = ModalComponentManager::getInstance();
|
||||
auto* pcc = findProjectContentComponent();
|
||||
|
||||
if (mcm->getNumModalComponents() > 0 || pcc == nullptr || pcc->getCurrentTabIndex() == 1)
|
||||
return;
|
||||
|
||||
if (errorList.getNumErrors() > 0)
|
||||
ProjucerApplication::getCommandManager().invokeDirectly (CommandIDs::showBuildTab, true);
|
||||
|
||||
ProjucerApplication::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
|
||||
{
|
||||
if (Editor* ed = getOrOpenEditorFor (location.file))
|
||||
{
|
||||
if (ed->flushEditorChanges())
|
||||
return; // client-side editor changes were pending, so deal with them first, and discard
|
||||
// the incoming change, whose position may now be wrong.
|
||||
|
||||
ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
|
||||
ed->document.insertText (location.range.getStart(), newText);
|
||||
|
||||
// deliberately clear the messages that we just added, to avoid these changes being
|
||||
// sent to the server (which will already have processed the same ones locally)
|
||||
ed->reset();
|
||||
ed->startTransactionTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handlePing()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CompileEngineChildProcess::setContinuousRebuild (bool b)
|
||||
{
|
||||
continuousRebuild = b;
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::flushEditorChanges()
|
||||
{
|
||||
for (Editor* ed : editors)
|
||||
ed->flushEditorChanges();
|
||||
}
|
||||
|
||||
ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
|
||||
{
|
||||
for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
|
||||
if (mw->getProject() == &project)
|
||||
return mw->getProjectContentComponent();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
|
||||
{
|
||||
for (Editor* ed : editors)
|
||||
if (ed->file == file)
|
||||
return ed;
|
||||
|
||||
if (ProjectContentComponent* pcc = findProjectContentComponent())
|
||||
if (pcc->showEditorForFile (file, false))
|
||||
return getOrOpenEditorFor (file);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
|
||||
{
|
||||
ProjectContentComponent* pcc = findProjectContentComponent();
|
||||
|
||||
if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
|
||||
{
|
||||
SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
|
||||
|
||||
if (sce != nullptr && sce->editor != nullptr)
|
||||
{
|
||||
sce->highlight (location.range, true);
|
||||
|
||||
Process::makeForegroundProcess();
|
||||
|
||||
CodeEditorComponent& ed = *sce->editor;
|
||||
ed.getTopLevelComponent()->toFront (false);
|
||||
ed.grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
|
||||
{
|
||||
File cacheFolder (ProjectProperties::getCacheLocation (p));
|
||||
|
||||
if (cacheFolder.isDirectory())
|
||||
cacheFolder.deleteRecursively();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue