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.

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