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.

355 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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_JUCELIBRARYMODULE_JUCEHEADER__
  19. #define __JUCER_JUCELIBRARYMODULE_JUCEHEADER__
  20. //==============================================================================
  21. class JuceLibraryModule : public LibraryModule
  22. {
  23. public:
  24. JuceLibraryModule (const File& file)
  25. : moduleInfo (JSON::parse (file)),
  26. moduleFile (file),
  27. moduleFolder (file.getParentDirectory())
  28. {
  29. jassert (isValid());
  30. }
  31. String getID() const { return moduleInfo ["id"].toString(); };
  32. bool isValid() const { return getID().isNotEmpty(); }
  33. void writeIncludes (Project& project, OutputStream& out)
  34. {
  35. File header (getInclude());
  36. StringArray paths, guards;
  37. createMultipleIncludes (project, getPathToModuleFile (header),
  38. paths, guards);
  39. ProjectSaver::writeGuardedInclude (out, paths, guards);
  40. }
  41. void prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
  42. {
  43. Project& project = exporter.getProject();
  44. {
  45. Array<File> compiled;
  46. findAndAddCompiledCode (exporter, projectSaver, compiled);
  47. if (project.shouldShowAllModuleFilesInProject (getID()).getValue())
  48. addIncludedCode (exporter, compiled);
  49. }
  50. if (isVSTPluginHost (project))
  51. VSTHelpers::addVSTFolderToPath (exporter, exporter.extraSearchPaths);
  52. if (isAUPluginHost (project))
  53. exporter.xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
  54. if (isPluginClient())
  55. {
  56. if (shouldBuildVST (project).getValue()) VSTHelpers::prepareExporter (exporter, projectSaver);
  57. if (shouldBuildRTAS (project).getValue()) RTASHelpers::prepareExporter (exporter, projectSaver, moduleFolder);
  58. if (shouldBuildAU (project).getValue()) AUHelpers::prepareExporter (exporter, projectSaver);
  59. }
  60. }
  61. void createPropertyEditors (const ProjectExporter& exporter, Array <PropertyComponent*>& props) const
  62. {
  63. if (isVSTPluginHost (exporter.getProject()))
  64. VSTHelpers::createVSTPathEditor (exporter, props);
  65. if (isPluginClient())
  66. {
  67. if (shouldBuildVST (exporter.getProject()).getValue()) VSTHelpers::createPropertyEditors (exporter, props);
  68. if (shouldBuildRTAS (exporter.getProject()).getValue()) RTASHelpers::createPropertyEditors (exporter, props);
  69. }
  70. }
  71. void getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
  72. {
  73. const File header (getInclude());
  74. jassert (header.exists());
  75. StringArray lines;
  76. header.readLines (lines);
  77. for (int i = 0; i < lines.size(); ++i)
  78. {
  79. String line (lines[i].trim());
  80. if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
  81. {
  82. ScopedPointer <Project::ConfigFlag> config (new Project::ConfigFlag());
  83. config->sourceModuleID = getID();
  84. config->symbol = line.fromFirstOccurrenceOf (":", false, false).trim();
  85. if (config->symbol.length() > 2)
  86. {
  87. ++i;
  88. while (! (lines[i].contains ("*/") || lines[i].contains ("@see")))
  89. {
  90. if (lines[i].trim().isNotEmpty())
  91. config->description = config->description.trim() + " " + lines[i].trim();
  92. ++i;
  93. }
  94. config->description = config->description.upToFirstOccurrenceOf ("*/", false, false);
  95. config->value.referTo (project.getConfigFlag (config->symbol));
  96. flags.add (config.release());
  97. }
  98. }
  99. }
  100. }
  101. var moduleInfo;
  102. File moduleFile, moduleFolder;
  103. private:
  104. File getInclude() const
  105. {
  106. return moduleFolder.getChildFile (moduleInfo ["include"]);
  107. }
  108. String getPathToModuleFile (const File& file) const
  109. {
  110. return file.getRelativePathFrom (moduleFolder.getParentDirectory().getParentDirectory());
  111. }
  112. static bool fileTargetMatches (ProjectExporter& exporter, const String& target)
  113. {
  114. if (target == "xcode" && ! exporter.isXcode())
  115. return false;
  116. if (target == "msvc" && ! exporter.isVisualStudio())
  117. return false;
  118. return true;
  119. }
  120. struct FileSorter
  121. {
  122. static int compareElements (const File& f1, const File& f2)
  123. {
  124. return f1.getFileName().compareIgnoreCase (f2.getFileName());
  125. }
  126. };
  127. void findWildcardMatches (const String& wildcardPath, Array<File>& result) const
  128. {
  129. String path (wildcardPath.upToLastOccurrenceOf ("/", false, false));
  130. String wildCard (wildcardPath.fromLastOccurrenceOf ("/", false, false));
  131. DirectoryIterator iter (moduleFolder.getChildFile (path), false, wildCard);
  132. Array<File> tempList;
  133. while (iter.next())
  134. tempList.add (iter.getFile());
  135. FileSorter sorter;
  136. tempList.sort (sorter);
  137. result.addArray (tempList);
  138. }
  139. void getAllSourceFiles (Array<File>& result) const
  140. {
  141. const var filesArray (moduleInfo ["browse"]);
  142. const Array<var>* const files = filesArray.getArray();
  143. for (int i = 0; i < files->size(); ++i)
  144. findWildcardMatches (files->getReference(i), result);
  145. }
  146. void addFileWithGroups (Project::Item group, const File& file, const String& path) const
  147. {
  148. const int slash = path.indexOfChar ('/');
  149. if (slash >= 0)
  150. {
  151. const String topLevelGroup (path.substring (0, slash));
  152. const String remainingPath (path.substring (slash + 1));
  153. addFileWithGroups (group.getOrCreateSubGroup (topLevelGroup), file, remainingPath);
  154. }
  155. else
  156. {
  157. if (! group.findItemForFile (file).isValid())
  158. group.addFile (file, -1, false);
  159. }
  160. }
  161. Project::Item addCompiledFile (const File& compiledFile, ProjectExporter& exporter, ProjectSaver& projectSaver) const
  162. {
  163. if (compiledFile.hasFileExtension ("cpp;cc;cxx;mm;m"))
  164. {
  165. MemoryOutputStream mem;
  166. writeSourceWrapper (mem, exporter.getProject(),
  167. getPathToModuleFile (compiledFile));
  168. String wrapperName (compiledFile.getFileNameWithoutExtension());
  169. wrapperName << "_wrapper" << (exporter.usesMMFiles() ? ".mm" : ".cpp");
  170. return projectSaver.saveGeneratedFile (wrapperName, mem);
  171. }
  172. return projectSaver.addFileToGeneratedGroup (compiledFile);
  173. }
  174. void findAndAddCompiledCode (ProjectExporter& exporter, ProjectSaver& projectSaver, Array<File>& result) const
  175. {
  176. const var compileArray (moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
  177. const Array<var>* const files = compileArray.getArray();
  178. if (files != nullptr)
  179. {
  180. for (int i = 0; i < files->size(); ++i)
  181. {
  182. const var& file = files->getReference(i);
  183. const String filename (file ["file"].toString());
  184. if (filename.isNotEmpty()
  185. && fileTargetMatches (exporter, file ["target"].toString()))
  186. {
  187. const File compiledFile (moduleFolder.getChildFile (filename));
  188. result.add (compiledFile);
  189. Project::Item item (addCompiledFile (compiledFile, exporter, projectSaver));
  190. if (file ["warnings"].toString().equalsIgnoreCase ("disabled"))
  191. item.getShouldInhibitWarningsValue() = true;
  192. if (file ["stdcall"])
  193. item.getShouldUseStdCallValue() = true;
  194. }
  195. }
  196. }
  197. }
  198. void addIncludedCode (ProjectExporter& exporter, const Array<File>& compiled) const
  199. {
  200. Array<File> files;
  201. getAllSourceFiles (files);
  202. Project::Item sourceGroup (Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID()));
  203. int i;
  204. for (i = 0; i < files.size(); ++i)
  205. addFileWithGroups (sourceGroup, files.getReference(i),
  206. files.getReference(i).getRelativePathFrom (moduleFolder));
  207. sourceGroup.addFile (moduleFile, -1, false);
  208. sourceGroup.addFile (getInclude(), -1, false);
  209. for (i = 0; i < compiled.size(); ++i)
  210. addFileWithGroups (sourceGroup, compiled.getReference(i),
  211. compiled.getReference(i).getRelativePathFrom (moduleFolder));
  212. exporter.getModulesGroup().getNode().addChild (sourceGroup.getNode().createCopy(), -1, nullptr);
  213. }
  214. static void writeSourceWrapper (OutputStream& out, Project& project, const String& pathFromJuceFolder)
  215. {
  216. const String appConfigFileName (project.getAppConfigFilename());
  217. ProjectSaver::writeAutoGenWarningComment (out);
  218. out << " This file pulls in a module's source code, and builds it using the settings" << newLine
  219. << " defined in " << appConfigFileName << "." << newLine
  220. << newLine
  221. << "*/"
  222. << newLine
  223. << newLine
  224. << "#define JUCE_WRAPPED_FILE 1" << newLine
  225. << newLine
  226. << CodeHelpers::createIncludeStatement (appConfigFileName) << newLine;
  227. writeInclude (project, out, pathFromJuceFolder);
  228. }
  229. static void createMultipleIncludes (Project& project, const String& pathFromLibraryFolder,
  230. StringArray& paths, StringArray& guards)
  231. {
  232. for (int i = project.getNumExporters(); --i >= 0;)
  233. {
  234. ScopedPointer <ProjectExporter> exporter (project.createExporter (i));
  235. if (exporter != nullptr)
  236. {
  237. paths.add (exporter->getIncludePathForFileInJuceFolder (pathFromLibraryFolder, project.getAppIncludeFile()));
  238. guards.add ("defined (" + exporter->getExporterIdentifierMacro() + ")");
  239. }
  240. }
  241. }
  242. static void writeInclude (Project& project, OutputStream& out, const String& pathFromJuceFolder)
  243. {
  244. StringArray paths, guards;
  245. createMultipleIncludes (project, pathFromJuceFolder, paths, guards);
  246. StringArray uniquePaths (paths);
  247. uniquePaths.removeDuplicates (false);
  248. if (uniquePaths.size() == 1)
  249. {
  250. out << "#include " << paths[0] << newLine;
  251. }
  252. else
  253. {
  254. int i = paths.size();
  255. for (; --i >= 0;)
  256. {
  257. for (int j = i; --j >= 0;)
  258. {
  259. if (paths[i] == paths[j] && guards[i] == guards[j])
  260. {
  261. paths.remove (i);
  262. guards.remove (i);
  263. }
  264. }
  265. }
  266. for (i = 0; i < paths.size(); ++i)
  267. {
  268. out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
  269. << " #include " << paths[i] << newLine;
  270. }
  271. out << "#endif" << newLine;
  272. }
  273. }
  274. bool isPluginClient() const { return getID() == "juce_audio_plugin_client"; }
  275. bool isAUPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); }
  276. bool isVSTPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); }
  277. };
  278. #endif // __JUCER_JUCELIBRARYMODULE_JUCEHEADER__