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.

935 lines
30KB

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