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.

364 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCER_PROJECTSAVER_JUCEHEADER__
  19. #define __JUCER_PROJECTSAVER_JUCEHEADER__
  20. #include "jucer_ResourceFile.h"
  21. //==============================================================================
  22. class ProjectSaver
  23. {
  24. public:
  25. ProjectSaver (Project& project_, const File& projectFile_)
  26. : project (project_), projectFile (projectFile_),
  27. generatedFilesGroup (Project::Item::createGroup (project, project.getJuceCodeGroupName()))
  28. {
  29. generatedFilesGroup.setID (getGeneratedGroupID());
  30. }
  31. Project& getProject() noexcept { return project; }
  32. String save()
  33. {
  34. jassert (generatedFilesGroup.getNumChildren() == 0); // this method can't be called more than once!
  35. const File oldFile (project.getFile());
  36. project.setFile (projectFile);
  37. writeMainProjectFile();
  38. if (errors.size() == 0)
  39. writeAppConfigFile();
  40. if (errors.size() == 0)
  41. writeBinaryDataFiles();
  42. if (errors.size() == 0)
  43. writeAppHeader();
  44. if (errors.size() == 0)
  45. writeProjects();
  46. if (errors.size() > 0)
  47. project.setFile (oldFile);
  48. return errors[0];
  49. }
  50. bool saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData)
  51. {
  52. if (! project.getGeneratedCodeFolder().createDirectory())
  53. {
  54. errors.add ("Couldn't create folder: " + project.getGeneratedCodeFolder().getFullPathName());
  55. return false;
  56. }
  57. const File file (project.getGeneratedCodeFolder().getChildFile (filePath));
  58. if (replaceFileIfDifferent (file, newData))
  59. {
  60. if (! generatedFilesGroup.findItemForFile (file).isValid())
  61. generatedFilesGroup.addFile (file, -1);
  62. return true;
  63. }
  64. return false;
  65. }
  66. static void writeAutoGenWarningComment (OutputStream& out)
  67. {
  68. out << "/*" << newLine << newLine
  69. << " IMPORTANT! This file is auto-generated each time you save your" << newLine
  70. << " project - if you alter its contents, your changes may be overwritten!" << newLine
  71. << newLine;
  72. }
  73. static const char* getGeneratedGroupID() noexcept { return "__jucelibfiles"; }
  74. private:
  75. Project& project;
  76. const File projectFile;
  77. Project::Item generatedFilesGroup;
  78. StringArray errors;
  79. File appConfigFile, binaryDataCpp;
  80. void writeMainProjectFile()
  81. {
  82. ScopedPointer <XmlElement> xml (project.getProjectRoot().createXml());
  83. jassert (xml != nullptr);
  84. if (xml != nullptr)
  85. {
  86. #if JUCE_DEBUG
  87. {
  88. MemoryOutputStream mo;
  89. project.getProjectRoot().writeToStream (mo);
  90. MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
  91. ValueTree v = ValueTree::readFromStream (mi);
  92. ScopedPointer <XmlElement> xml2 (v.createXml());
  93. // This bit just tests that ValueTree save/load works reliably.. Let me know if this asserts for you!
  94. jassert (xml->isEquivalentTo (xml2, true));
  95. }
  96. #endif
  97. MemoryOutputStream mo;
  98. xml->writeToStream (mo, String::empty);
  99. replaceFileIfDifferent (projectFile, mo);
  100. }
  101. }
  102. bool writeAppConfig (OutputStream& out)
  103. {
  104. writeAutoGenWarningComment (out);
  105. out << " If you want to change any of these values, use the Introjucer to do so, rather than" << newLine
  106. << " editing this file directly!" << newLine
  107. << newLine
  108. << " Any commented-out settings will fall back to using the default values that" << newLine
  109. << " they are given in juce_Config.h" << newLine
  110. << newLine
  111. << "*/" << newLine << newLine;
  112. bool notActive = project.getJuceLinkageMode() == Project::useLinkedJuce
  113. || project.getJuceLinkageMode() == Project::notLinkedToJuce;
  114. if (notActive)
  115. out << "/* NOTE: These configs aren't available when you're linking to the juce library statically!" << newLine
  116. << " If you need to set a configuration that differs from the default, you'll need" << newLine
  117. << " to include the amalgamated Juce files." << newLine << newLine;
  118. OwnedArray <Project::ConfigFlag> flags;
  119. project.getAllConfigFlags (flags);
  120. for (int i = 0; i < flags.size(); ++i)
  121. {
  122. const Project::ConfigFlag* const f = flags[i];
  123. const String value (f->value.toString());
  124. if (value != Project::configFlagEnabled && value != Project::configFlagDisabled)
  125. out << "//#define ";
  126. else
  127. out << "#define ";
  128. out << f->symbol;
  129. if (value == Project::configFlagEnabled)
  130. out << " 1";
  131. else if (value == Project::configFlagDisabled)
  132. out << " 0";
  133. out << newLine;
  134. }
  135. if (notActive)
  136. out << newLine << "*/" << newLine;
  137. return flags.size() > 0;
  138. }
  139. void writeAppConfigFile()
  140. {
  141. appConfigFile = project.getGeneratedCodeFolder().getChildFile (project.getAppConfigFilename());
  142. MemoryOutputStream mem;
  143. if (writeAppConfig (mem))
  144. saveGeneratedFile (project.getAppConfigFilename(), mem);
  145. else
  146. appConfigFile.deleteFile();
  147. }
  148. void writeAppHeader (OutputStream& out)
  149. {
  150. writeAutoGenWarningComment (out);
  151. out << " This is the header file that your files should include in order to get all the" << newLine
  152. << " Juce library headers. You should NOT include juce.h or juce_amalgamated.h directly in" << newLine
  153. << " your own source files, because that wouldn't pick up the correct Juce configuration" << newLine
  154. << " options for your app." << newLine
  155. << newLine
  156. << "*/" << newLine << newLine;
  157. String headerGuard ("__APPHEADERFILE_" + project.getProjectUID().toUpperCase() + "__");
  158. out << "#ifndef " << headerGuard << newLine
  159. << "#define " << headerGuard << newLine << newLine;
  160. if (appConfigFile.exists())
  161. out << CodeHelpers::createIncludeStatement (project.getAppConfigFilename()) << newLine;
  162. {
  163. OwnedArray<LibraryModule> modules;
  164. project.getProjectType().createRequiredModules (project, modules);
  165. StringArray paths, guards;
  166. for (int i = 0; i < modules.size(); ++i)
  167. modules.getUnchecked(i)->getHeaderFiles (project, paths, guards);
  168. StringArray uniquePaths (paths);
  169. uniquePaths.removeDuplicates (false);
  170. if (uniquePaths.size() == 1)
  171. {
  172. out << "#include " << paths[0] << newLine;
  173. }
  174. else
  175. {
  176. int i = paths.size();
  177. for (; --i >= 0;)
  178. {
  179. for (int j = i; --j >= 0;)
  180. {
  181. if (paths[i] == paths[j] && guards[i] == guards[j])
  182. {
  183. paths.remove (i);
  184. guards.remove (i);
  185. }
  186. }
  187. }
  188. for (i = 0; i < paths.size(); ++i)
  189. {
  190. out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
  191. << " #include " << paths[i] << newLine;
  192. }
  193. out << "#endif" << newLine;
  194. }
  195. }
  196. if (binaryDataCpp.exists())
  197. out << CodeHelpers::createIncludeStatement (binaryDataCpp.withFileExtension (".h"), appConfigFile) << newLine;
  198. out << newLine
  199. << "namespace ProjectInfo" << newLine
  200. << "{" << newLine
  201. << " const char* const projectName = " << CodeHelpers::addEscapeChars (project.getProjectName().toString()).quoted() << ";" << newLine
  202. << " const char* const versionString = " << CodeHelpers::addEscapeChars (project.getVersion().toString()).quoted() << ";" << newLine
  203. << " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine
  204. << "}" << newLine
  205. << newLine
  206. << "#endif // " << headerGuard << newLine;
  207. }
  208. void writeAppHeader()
  209. {
  210. if (project.getJuceLinkageMode() != Project::notLinkedToJuce
  211. || ! project.getProjectType().isLibrary())
  212. {
  213. MemoryOutputStream mem;
  214. writeAppHeader (mem);
  215. saveGeneratedFile (project.getJuceSourceHFilename(), mem);
  216. }
  217. else
  218. {
  219. project.getAppIncludeFile().deleteFile();
  220. }
  221. }
  222. void writeBinaryDataFiles()
  223. {
  224. binaryDataCpp = project.getGeneratedCodeFolder().getChildFile ("BinaryData.cpp");
  225. ResourceFile resourceFile (project);
  226. if (resourceFile.getNumFiles() > 0)
  227. {
  228. resourceFile.setClassName ("BinaryData");
  229. if (resourceFile.write (binaryDataCpp))
  230. {
  231. generatedFilesGroup.addFile (binaryDataCpp, -1);
  232. generatedFilesGroup.addFile (binaryDataCpp.withFileExtension (".h"), -1);
  233. }
  234. else
  235. {
  236. errors.add ("Can't create binary resources file: " + binaryDataCpp.getFullPathName());
  237. }
  238. }
  239. else
  240. {
  241. binaryDataCpp.deleteFile();
  242. binaryDataCpp.withFileExtension ("h").deleteFile();
  243. }
  244. }
  245. void writeProjects()
  246. {
  247. for (int i = project.getNumExporters(); --i >= 0;)
  248. {
  249. ScopedPointer <ProjectExporter> exporter (project.createExporter (i));
  250. std::cout << "Writing files for: " << exporter->getName() << std::endl;
  251. if (exporter->getTargetFolder().createDirectory())
  252. {
  253. project.getProjectType().prepareExporter (*exporter);
  254. // start with a copy of the basic files, as each exporter may modify it.
  255. const ValueTree generatedGroupCopy (generatedFilesGroup.getNode().createCopy());
  256. for (int j = 0; j < exporter->libraryModules.size(); ++j)
  257. exporter->libraryModules.getUnchecked(j)->prepareExporter (*exporter, *this);
  258. exporter->groups.add (generatedFilesGroup);
  259. try
  260. {
  261. exporter->create();
  262. }
  263. catch (ProjectExporter::SaveError& error)
  264. {
  265. errors.add (error.message);
  266. }
  267. generatedFilesGroup.getNode() = generatedGroupCopy;
  268. }
  269. else
  270. {
  271. errors.add ("Can't create folder: " + exporter->getTargetFolder().getFullPathName());
  272. }
  273. }
  274. }
  275. bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData)
  276. {
  277. if (! FileHelpers::overwriteFileWithNewDataIfDifferent (f, newData))
  278. {
  279. errors.add ("Can't write to file: " + f.getFullPathName());
  280. return false;
  281. }
  282. return true;
  283. }
  284. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver);
  285. };
  286. #endif