The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

926 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "../Application/jucer_Application.h"
  19. #include "../Project Saving/jucer_ProjectExporter.h"
  20. #include "projucer_MessageIDs.h"
  21. #include "projucer_CppHelpers.h"
  22. #include "projucer_SourceCodeRange.h"
  23. #include "projucer_ClassDatabase.h"
  24. #include "projucer_DiagnosticMessage.h"
  25. #include "projucer_ProjectBuildInfo.h"
  26. #include "projucer_ClientServerMessages.h"
  27. #include "projucer_CompileEngineClient.h"
  28. #include "../LiveBuildEngine/projucer_CompileEngineServer.h"
  29. #ifndef RUN_CLANG_IN_CHILD_PROCESS
  30. #error
  31. #endif
  32. //==============================================================================
  33. namespace ProjectProperties
  34. {
  35. const Identifier liveSettingsType ("LIVE_SETTINGS");
  36. #if JUCE_MAC
  37. const Identifier liveSettingsSubtype ("OSX");
  38. #elif JUCE_WINDOWS
  39. const Identifier liveSettingsSubtype ("WINDOWS");
  40. #elif JUCE_LINUX
  41. const Identifier liveSettingsSubtype ("LINUX");
  42. #endif
  43. static ValueTree getLiveSettings (Project& project)
  44. {
  45. return project.getProjectRoot().getOrCreateChildWithName (liveSettingsType, nullptr)
  46. .getOrCreateChildWithName (liveSettingsSubtype, nullptr);
  47. }
  48. static const ValueTree getLiveSettingsConst (Project& project)
  49. {
  50. return project.getProjectRoot().getChildWithName (liveSettingsType)
  51. .getChildWithName (liveSettingsSubtype);
  52. }
  53. static Value getLiveSetting (Project& p, const Identifier& i) { return getLiveSettings (p).getPropertyAsValue (i, p.getUndoManagerFor (getLiveSettings (p))); }
  54. static var getLiveSettingVar (Project& p, const Identifier& i) { return getLiveSettingsConst (p) [i]; }
  55. static Value getUserHeaderPathValue (Project& p) { return getLiveSetting (p, Ids::headerPath); }
  56. static String getUserHeaderPathString (Project& p) { return getLiveSettingVar (p, Ids::headerPath); }
  57. static Value getSystemHeaderPathValue (Project& p) { return getLiveSetting (p, Ids::systemHeaderPath); }
  58. static String getSystemHeaderPathString (Project& p) { return getLiveSettingVar (p, Ids::systemHeaderPath); }
  59. static Value getExtraDLLsValue (Project& p) { return getLiveSetting (p, Ids::extraDLLs); }
  60. static String getExtraDLLsString (Project& p) { return getLiveSettingVar (p, Ids::extraDLLs); }
  61. static Value getExtraCompilerFlagsValue (Project& p) { return getLiveSetting (p, Ids::extraCompilerFlags); }
  62. static String getExtraCompilerFlagsString (Project& p) { return getLiveSettingVar (p, Ids::extraCompilerFlags); }
  63. static Value getExtraPreprocessorDefsValue (Project& p) { return getLiveSetting (p, Ids::defines); }
  64. static String getExtraPreprocessorDefsString (Project& p) { return getLiveSettingVar (p, Ids::defines); }
  65. static File getProjucerTempFolder()
  66. {
  67. #if JUCE_MAC
  68. return File ("~/Library/Caches/com.juce.projucer");
  69. #else
  70. return File::getSpecialLocation (File::tempDirectory).getChildFile ("com.juce.projucer");
  71. #endif
  72. }
  73. static File getCacheLocation (Project& project)
  74. {
  75. String cacheFolderName = project.getProjectFilenameRoot() + "_" + project.getProjectUID();
  76. #if JUCE_DEBUG
  77. cacheFolderName += "_debug";
  78. #endif
  79. return getProjucerTempFolder()
  80. .getChildFile ("Intermediate Files")
  81. .getChildFile (cacheFolderName);
  82. }
  83. }
  84. //==============================================================================
  85. void LiveBuildProjectSettings::getLiveSettings (Project& project, PropertyListBuilder& props)
  86. {
  87. using namespace ProjectProperties;
  88. props.addSearchPathProperty (getUserHeaderPathValue (project), "User header paths", "User header search paths.");
  89. props.addSearchPathProperty (getSystemHeaderPathValue (project), "System header paths", "System header search paths.");
  90. props.add (new TextPropertyComponent (getExtraPreprocessorDefsValue (project), "Preprocessor Definitions", 32768, true),
  91. "Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace or commas "
  92. "to separate the items - to include a space or comma in a definition, precede it with a backslash.");
  93. props.add (new TextPropertyComponent (getExtraCompilerFlagsValue (project), "Extra compiler flags", 2048, true),
  94. "Extra command-line flags to be passed to the compiler. This string can contain references to preprocessor"
  95. " definitions in the form ${NAME_OF_DEFINITION}, which will be replaced with their values.");
  96. props.add (new TextPropertyComponent (getExtraDLLsValue (project), "Extra dynamic libraries", 2048, true),
  97. "Extra dynamic libs that the running code may require. Use new-lines or commas to separate the items");
  98. }
  99. void LiveBuildProjectSettings::updateNewlyOpenedProject (Project&) { /* placeholder */ }
  100. bool LiveBuildProjectSettings::isBuildDisabled (Project& p)
  101. {
  102. const bool defaultBuildDisabled = true;
  103. return p.getStoredProperties().getBoolValue ("buildDisabled", defaultBuildDisabled);
  104. }
  105. void LiveBuildProjectSettings::setBuildDisabled (Project& p, bool b) { p.getStoredProperties().setValue ("buildDisabled", b); }
  106. bool LiveBuildProjectSettings::areWarningsDisabled (Project& p) { return p.getStoredProperties().getBoolValue ("warningsDisabled"); }
  107. void LiveBuildProjectSettings::setWarningsDisabled (Project& p, bool b) { p.getStoredProperties().setValue ("warningsDisabled", b); }
  108. //==============================================================================
  109. class ClientIPC : public MessageHandler,
  110. private InterprocessConnection,
  111. private Timer
  112. {
  113. public:
  114. ClientIPC (CompileEngineChildProcess& cp)
  115. : InterprocessConnection (true), owner (cp)
  116. {
  117. launchServer();
  118. }
  119. ~ClientIPC()
  120. {
  121. #if RUN_CLANG_IN_CHILD_PROCESS
  122. if (childProcess.isRunning())
  123. killServerPolitely();
  124. #endif
  125. }
  126. void launchServer()
  127. {
  128. DBG ("Client: Launching Server...");
  129. const String pipeName ("ipc_" + String::toHexString (Random().nextInt64()));
  130. const String command (createCommandLineForLaunchingServer (pipeName,
  131. owner.project.getProjectUID(),
  132. ProjectProperties::getCacheLocation (owner.project)));
  133. #if RUN_CLANG_IN_CHILD_PROCESS
  134. if (! childProcess.start (command))
  135. {
  136. jassertfalse;
  137. }
  138. #else
  139. server = createClangServer (command);
  140. #endif
  141. bool ok = connectToPipe (pipeName, 10000);
  142. jassert (ok);
  143. if (ok)
  144. MessageTypes::sendPing (*this);
  145. startTimer (serverKeepAliveTimeout);
  146. }
  147. void killServerPolitely()
  148. {
  149. DBG ("Client: Killing Server...");
  150. MessageTypes::sendQuit (*this);
  151. disconnect();
  152. stopTimer();
  153. #if RUN_CLANG_IN_CHILD_PROCESS
  154. childProcess.waitForProcessToFinish (5000);
  155. #endif
  156. killServerWithoutMercy();
  157. }
  158. void killServerWithoutMercy()
  159. {
  160. disconnect();
  161. stopTimer();
  162. #if RUN_CLANG_IN_CHILD_PROCESS
  163. childProcess.kill();
  164. #else
  165. destroyClangServer (server);
  166. server = nullptr;
  167. #endif
  168. }
  169. void connectionMade()
  170. {
  171. DBG ("Client: connected");
  172. stopTimer();
  173. }
  174. void connectionLost()
  175. {
  176. DBG ("Client: disconnected");
  177. startTimer (100);
  178. }
  179. bool sendMessage (const ValueTree& m)
  180. {
  181. return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
  182. }
  183. void messageReceived (const MemoryBlock& message)
  184. {
  185. #if RUN_CLANG_IN_CHILD_PROCESS
  186. startTimer (serverKeepAliveTimeout);
  187. #else
  188. stopTimer();
  189. #endif
  190. MessageTypes::dispatchToClient (owner, MessageHandler::convertMessage (message));
  191. }
  192. enum { serverKeepAliveTimeout = 10000 };
  193. private:
  194. CompileEngineChildProcess& owner;
  195. #if RUN_CLANG_IN_CHILD_PROCESS
  196. ChildProcess childProcess;
  197. #else
  198. void* server;
  199. #endif
  200. void timerCallback() { owner.handleCrash (String()); }
  201. };
  202. //==============================================================================
  203. class CompileEngineChildProcess::ChildProcess : private ValueTree::Listener,
  204. private Timer
  205. {
  206. public:
  207. ChildProcess (CompileEngineChildProcess& proc, Project& p)
  208. : owner (proc), project (p)
  209. {
  210. projectRoot = project.getProjectRoot();
  211. restartServer();
  212. projectRoot.addListener (this);
  213. openedOk = true;
  214. }
  215. ~ChildProcess()
  216. {
  217. projectRoot.removeListener (this);
  218. if (isRunningApp && server != nullptr)
  219. server->killServerWithoutMercy();
  220. server = nullptr;
  221. }
  222. void restartServer()
  223. {
  224. server = nullptr;
  225. server = new ClientIPC (owner);
  226. sendRebuild();
  227. }
  228. void sendRebuild()
  229. {
  230. stopTimer();
  231. ProjectBuildInfo build;
  232. if (! doesProjectMatchSavedHeaderState (project))
  233. {
  234. MessageTypes::sendNewBuild (*server, build);
  235. owner.errorList.resetToError ("Project structure does not match the saved headers! "
  236. "Please re-save your project to enable compilation");
  237. return;
  238. }
  239. if (areAnyModulesMissing (project))
  240. {
  241. MessageTypes::sendNewBuild (*server, build);
  242. owner.errorList.resetToError ("Some of your JUCE modules can't be found! "
  243. "Please check that all the module paths are correct");
  244. return;
  245. }
  246. build.setSystemIncludes (getSystemIncludePaths());
  247. build.setUserIncludes (getUserIncludes());
  248. build.setGlobalDefs (getGlobalDefs (project));
  249. build.setCompileFlags (ProjectProperties::getExtraCompilerFlagsString (project).trim());
  250. build.setExtraDLLs (getExtraDLLs());
  251. build.setJuceModulesFolder (EnabledModuleList::findDefaultModulesFolder (project).getFullPathName());
  252. build.setUtilsCppInclude (project.getAppIncludeFile().getFullPathName());
  253. scanForProjectFiles (project, build);
  254. owner.updateAllEditors();
  255. MessageTypes::sendNewBuild (*server, build);
  256. }
  257. void cleanAll()
  258. {
  259. MessageTypes::sendCleanAll (*server);
  260. sendRebuild();
  261. }
  262. void reinstantiatePreviews()
  263. {
  264. MessageTypes::sendReinstantiate (*server);
  265. }
  266. bool launchApp()
  267. {
  268. MessageTypes::sendLaunchApp (*server);
  269. return true;
  270. }
  271. ScopedPointer<ClientIPC> server;
  272. bool openedOk = false;
  273. bool isRunningApp = false;
  274. private:
  275. CompileEngineChildProcess& owner;
  276. Project& project;
  277. ValueTree projectRoot;
  278. void projectStructureChanged()
  279. {
  280. startTimer (100);
  281. }
  282. void timerCallback() override
  283. {
  284. sendRebuild();
  285. }
  286. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { projectStructureChanged(); }
  287. void valueTreeChildAdded (ValueTree&, ValueTree&) override { projectStructureChanged(); }
  288. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { projectStructureChanged(); }
  289. void valueTreeParentChanged (ValueTree&) override { projectStructureChanged(); }
  290. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  291. static String getGlobalDefs (Project& proj)
  292. {
  293. String defs (ProjectProperties::getExtraPreprocessorDefsString (proj));
  294. for (Project::ExporterIterator exporter (proj); exporter.next();)
  295. if (exporter->canLaunchProject())
  296. defs << " " << exporter->getExporterIdentifierMacro() << "=1";
  297. return defs;
  298. }
  299. static void scanProjectItem (const Project::Item& projectItem, Array<File>& compileUnits, Array<File>& userFiles)
  300. {
  301. if (projectItem.isGroup())
  302. {
  303. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  304. scanProjectItem (projectItem.getChild(i), compileUnits, userFiles);
  305. return;
  306. }
  307. if (projectItem.shouldBeCompiled())
  308. {
  309. const File f (projectItem.getFile());
  310. if (f.exists())
  311. compileUnits.add (f);
  312. }
  313. if (projectItem.shouldBeAddedToTargetProject() && ! projectItem.shouldBeAddedToBinaryResources())
  314. {
  315. const File f (projectItem.getFile());
  316. if (f.exists())
  317. userFiles.add (f);
  318. }
  319. }
  320. void scanForProjectFiles (Project& proj, ProjectBuildInfo& build)
  321. {
  322. Array<File> compileUnits, userFiles;
  323. scanProjectItem (proj.getMainGroup(), compileUnits, userFiles);
  324. {
  325. OwnedArray<LibraryModule> modules;
  326. proj.getModules().createRequiredModules (modules);
  327. for (Project::ExporterIterator exporter (proj); exporter.next();)
  328. {
  329. if (exporter->canLaunchProject())
  330. {
  331. for (const LibraryModule* m : modules)
  332. {
  333. const File localModuleFolder = proj.getModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue()
  334. ? proj.getLocalModuleFolder (m->moduleInfo.getID())
  335. : m->moduleInfo.getFolder();
  336. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits);
  337. }
  338. break;
  339. }
  340. }
  341. }
  342. for (int i = 0; ; ++i)
  343. {
  344. const File binaryDataCpp (proj.getBinaryDataCppFile (i));
  345. if (! binaryDataCpp.exists())
  346. break;
  347. compileUnits.add (binaryDataCpp);
  348. }
  349. for (int i = compileUnits.size(); --i >= 0;)
  350. if (compileUnits.getReference(i).hasFileExtension (".r"))
  351. compileUnits.remove (i);
  352. build.setFiles (compileUnits, userFiles);
  353. }
  354. static bool doesProjectMatchSavedHeaderState (Project& project)
  355. {
  356. ValueTree liveModules (project.getProjectRoot().getChildWithName (Ids::MODULES));
  357. ScopedPointer<XmlElement> xml (XmlDocument::parse (project.getFile()));
  358. if (xml == nullptr || ! xml->hasTagName (Ids::JUCERPROJECT.toString()))
  359. return false;
  360. ValueTree diskModules (ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES));
  361. return liveModules.isEquivalentTo (diskModules);
  362. }
  363. static bool areAnyModulesMissing (Project& project)
  364. {
  365. OwnedArray<LibraryModule> modules;
  366. project.getModules().createRequiredModules (modules);
  367. for (auto* module : modules)
  368. if (! module->getFolder().isDirectory())
  369. return true;
  370. return false;
  371. }
  372. StringArray getUserIncludes()
  373. {
  374. StringArray paths;
  375. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  376. paths.addArray (getSearchPathsFromString (ProjectProperties::getUserHeaderPathString (project)));
  377. return convertSearchPathsToAbsolute (paths);
  378. }
  379. StringArray getSystemIncludePaths()
  380. {
  381. StringArray paths;
  382. paths.addArray (getSearchPathsFromString (ProjectProperties::getSystemHeaderPathString (project)));
  383. if (project.getProjectType().isAudioPlugin())
  384. {
  385. paths.add (getAppSettings().getGlobalPath (Ids::vst3Path, TargetOS::getThisOS()).toString());
  386. }
  387. OwnedArray<LibraryModule> modules;
  388. project.getModules().createRequiredModules (modules);
  389. for (auto* module : modules)
  390. paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
  391. return convertSearchPathsToAbsolute (paths);
  392. }
  393. StringArray convertSearchPathsToAbsolute (const StringArray& paths) const
  394. {
  395. StringArray s;
  396. const File root (project.getProjectFolder());
  397. for (String p : paths)
  398. s.add (root.getChildFile (p).getFullPathName());
  399. return s;
  400. }
  401. StringArray getExtraDLLs()
  402. {
  403. StringArray dlls;
  404. dlls.addTokens (ProjectProperties::getExtraDLLsString (project), "\n\r,", StringRef());
  405. dlls.trim();
  406. dlls.removeEmptyStrings();
  407. return dlls;
  408. }
  409. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
  410. };
  411. //==============================================================================
  412. CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
  413. : project (p),
  414. continuousRebuild (false)
  415. {
  416. ProjucerApplication::getApp().openDocumentManager.addListener (this);
  417. createProcess();
  418. errorList.setWarningsEnabled (! LiveBuildProjectSettings::areWarningsDisabled (project));
  419. }
  420. CompileEngineChildProcess::~CompileEngineChildProcess()
  421. {
  422. ProjucerApplication::getApp().openDocumentManager.removeListener (this);
  423. process = nullptr;
  424. lastComponentList.clear();
  425. }
  426. void CompileEngineChildProcess::createProcess()
  427. {
  428. jassert (process == nullptr);
  429. process = new ChildProcess (*this, project);
  430. if (! process->openedOk)
  431. process = nullptr;
  432. updateAllEditors();
  433. }
  434. void CompileEngineChildProcess::cleanAll()
  435. {
  436. if (process != nullptr)
  437. process->cleanAll();
  438. }
  439. void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
  440. {
  441. if (process != nullptr)
  442. {
  443. MainWindow* projectWindow = nullptr;
  444. OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
  445. for (int i = 0; i < windows.size(); ++i)
  446. {
  447. if (MainWindow* w = windows[i])
  448. {
  449. if (w->getProject() == &project)
  450. {
  451. projectWindow = w;
  452. break;
  453. }
  454. }
  455. }
  456. Rectangle<int> mainWindowRect;
  457. if (projectWindow != nullptr)
  458. mainWindowRect = projectWindow->getBounds();
  459. MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
  460. }
  461. }
  462. void CompileEngineChildProcess::reinstantiatePreviews()
  463. {
  464. if (process != nullptr)
  465. process->reinstantiatePreviews();
  466. }
  467. void CompileEngineChildProcess::processActivationChanged (bool isForeground)
  468. {
  469. if (process != nullptr)
  470. MessageTypes::sendProcessActivationState (*process->server, isForeground);
  471. }
  472. //==============================================================================
  473. bool CompileEngineChildProcess::canLaunchApp() const
  474. {
  475. return process != nullptr
  476. && runningAppProcess == nullptr
  477. && activityList.getNumActivities() == 0
  478. && errorList.getNumErrors() == 0;
  479. }
  480. void CompileEngineChildProcess::launchApp()
  481. {
  482. if (process != nullptr)
  483. process->launchApp();
  484. }
  485. bool CompileEngineChildProcess::canKillApp() const
  486. {
  487. return runningAppProcess != nullptr;
  488. }
  489. void CompileEngineChildProcess::killApp()
  490. {
  491. runningAppProcess = nullptr;
  492. }
  493. void CompileEngineChildProcess::handleAppLaunched()
  494. {
  495. runningAppProcess = process;
  496. runningAppProcess->isRunningApp = true;
  497. createProcess();
  498. }
  499. void CompileEngineChildProcess::handleAppQuit()
  500. {
  501. DBG ("handleAppQuit");
  502. runningAppProcess = nullptr;
  503. }
  504. //==============================================================================
  505. struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
  506. private Timer
  507. {
  508. Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
  509. : owner (ccp), file (f), document (doc), transactionTimer (doc)
  510. {
  511. sendFullUpdate();
  512. document.addListener (this);
  513. }
  514. ~Editor()
  515. {
  516. document.removeListener (this);
  517. }
  518. void codeDocumentTextInserted (const String& newText, int insertIndex) override
  519. {
  520. CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
  521. startEditorChangeTimer();
  522. transactionTimer.stopTimer();
  523. owner.lastComponentList.globalNamespace
  524. .nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
  525. }
  526. void codeDocumentTextDeleted (int start, int end) override
  527. {
  528. CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
  529. startEditorChangeTimer();
  530. transactionTimer.stopTimer();
  531. owner.lastComponentList.globalNamespace
  532. .nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
  533. }
  534. void sendFullUpdate()
  535. {
  536. reset();
  537. if (owner.process != nullptr)
  538. MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
  539. }
  540. bool flushEditorChanges()
  541. {
  542. if (pendingChanges.size() > 0)
  543. {
  544. if (owner.process != nullptr && owner.process->server != nullptr)
  545. MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
  546. reset();
  547. return true;
  548. }
  549. stopTimer();
  550. return false;
  551. }
  552. void reset()
  553. {
  554. stopTimer();
  555. pendingChanges.clear();
  556. }
  557. void startTransactionTimer()
  558. {
  559. transactionTimer.startTimer (1000);
  560. }
  561. void startEditorChangeTimer()
  562. {
  563. startTimer (200);
  564. }
  565. CompileEngineChildProcess& owner;
  566. File file;
  567. CodeDocument& document;
  568. private:
  569. Array<CodeChange> pendingChanges;
  570. void timerCallback() override
  571. {
  572. if (owner.continuousRebuild)
  573. flushEditorChanges();
  574. else
  575. stopTimer();
  576. }
  577. struct TransactionTimer : public Timer
  578. {
  579. TransactionTimer (CodeDocument& doc) : document (doc) {}
  580. void timerCallback() override
  581. {
  582. stopTimer();
  583. document.newTransaction();
  584. }
  585. CodeDocument& document;
  586. };
  587. TransactionTimer transactionTimer;
  588. };
  589. void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
  590. {
  591. editors.add (new Editor (*this, file, document));
  592. }
  593. bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
  594. {
  595. for (int i = editors.size(); --i >= 0;)
  596. {
  597. if (document->getFile() == editors.getUnchecked(i)->file)
  598. {
  599. const File f (editors.getUnchecked(i)->file);
  600. editors.remove (i);
  601. if (process != nullptr)
  602. MessageTypes::sendHandleFileReset (*process->server, f);
  603. }
  604. }
  605. return true;
  606. }
  607. void CompileEngineChildProcess::updateAllEditors()
  608. {
  609. for (int i = editors.size(); --i >= 0;)
  610. editors.getUnchecked(i)->sendFullUpdate();
  611. }
  612. //==============================================================================
  613. void CompileEngineChildProcess::handleCrash (const String& message)
  614. {
  615. Logger::writeToLog ("*** Child process crashed: " + message);
  616. if (crashHandler != nullptr)
  617. crashHandler (message);
  618. }
  619. void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
  620. void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
  621. void CompileEngineChildProcess::handleCloseIDE()
  622. {
  623. if (JUCEApplication* app = JUCEApplication::getInstance())
  624. app->systemRequestedQuit();
  625. }
  626. void CompileEngineChildProcess::handleMissingSystemHeaders()
  627. {
  628. if (ProjectContentComponent* p = findProjectContentComponent())
  629. p->handleMissingSystemHeaders();
  630. }
  631. void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
  632. {
  633. ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
  634. CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
  635. if (command == StandardApplicationCommandIDs::undo)
  636. {
  637. handleUndoInEditor (className);
  638. }
  639. else if (command == StandardApplicationCommandIDs::redo)
  640. {
  641. handleRedoInEditor (className);
  642. }
  643. else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
  644. {
  645. commandManager.setFirstCommandTarget (target);
  646. commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
  647. commandManager.setFirstCommandTarget (nullptr);
  648. }
  649. }
  650. void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
  651. {
  652. }
  653. void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
  654. {
  655. }
  656. void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
  657. {
  658. lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
  659. activityList.sendClassListChangedMessage (lastComponentList);
  660. }
  661. void CompileEngineChildProcess::handleBuildFailed()
  662. {
  663. if (errorList.getNumErrors() > 0)
  664. ProjucerApplication::getCommandManager().invokeDirectly (CommandIDs::showBuildTab, true);
  665. ProjucerApplication::getCommandManager().commandStatusChanged();
  666. }
  667. void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
  668. {
  669. if (Editor* ed = getOrOpenEditorFor (location.file))
  670. {
  671. if (ed->flushEditorChanges())
  672. return; // client-side editor changes were pending, so deal with them first, and discard
  673. // the incoming change, whose position may now be wrong.
  674. ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
  675. ed->document.insertText (location.range.getStart(), newText);
  676. // deliberately clear the messages that we just added, to avoid these changes being
  677. // sent to the server (which will already have processed the same ones locally)
  678. ed->reset();
  679. ed->startTransactionTimer();
  680. }
  681. }
  682. void CompileEngineChildProcess::handlePing()
  683. {
  684. }
  685. //==============================================================================
  686. void CompileEngineChildProcess::setContinuousRebuild (bool b)
  687. {
  688. continuousRebuild = b;
  689. }
  690. void CompileEngineChildProcess::flushEditorChanges()
  691. {
  692. for (Editor* ed : editors)
  693. ed->flushEditorChanges();
  694. }
  695. ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
  696. {
  697. for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
  698. if (mw->getProject() == &project)
  699. return mw->getProjectContentComponent();
  700. return nullptr;
  701. }
  702. CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
  703. {
  704. for (Editor* ed : editors)
  705. if (ed->file == file)
  706. return ed;
  707. if (ProjectContentComponent* pcc = findProjectContentComponent())
  708. if (pcc->showEditorForFile (file, false))
  709. return getOrOpenEditorFor (file);
  710. return nullptr;
  711. }
  712. void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
  713. {
  714. ProjectContentComponent* pcc = findProjectContentComponent();
  715. if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
  716. {
  717. SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
  718. if (sce != nullptr && sce->editor != nullptr)
  719. {
  720. sce->highlight (location.range, true);
  721. Process::makeForegroundProcess();
  722. CodeEditorComponent& ed = *sce->editor;
  723. ed.getTopLevelComponent()->toFront (false);
  724. ed.grabKeyboardFocus();
  725. }
  726. }
  727. }
  728. void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
  729. {
  730. File cacheFolder (ProjectProperties::getCacheLocation (p));
  731. if (cacheFolder.isDirectory())
  732. cacheFolder.deleteRecursively();
  733. }