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.

336 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "juce_AppConfig.h"
  19. #include "../../juce_amalgamated.h"
  20. //==============================================================================
  21. static bool matchesWildcard (const String& filename, const StringArray& wildcards)
  22. {
  23. for (int i = wildcards.size(); --i >= 0;)
  24. if (filename.matchesWildcard (wildcards[i], true))
  25. return true;
  26. return false;
  27. }
  28. static bool canFileBeReincluded (const File& f)
  29. {
  30. String content (f.loadFileAsString());
  31. for (;;)
  32. {
  33. content = content.trimStart();
  34. if (content.startsWith (T("//")))
  35. content = content.fromFirstOccurrenceOf (T("\n"), false, false);
  36. else if (content.startsWith (T("/*")))
  37. content = content.fromFirstOccurrenceOf (T("*/"), false, false);
  38. else
  39. break;
  40. }
  41. StringArray lines;
  42. lines.addLines (content);
  43. lines.trim();
  44. lines.removeEmptyStrings();
  45. const String l1 (lines[0].removeCharacters (T(" \t")).trim());
  46. const String l2 (lines[1].removeCharacters (T(" \t")).trim());
  47. if (l1.replace (T("#ifndef"), T("#define")) == l2)
  48. return false;
  49. return true;
  50. }
  51. //==============================================================================
  52. static bool parseFile (const File& rootFolder,
  53. const File& newTargetFile,
  54. StringArray& dest,
  55. const File& file,
  56. StringArray& alreadyIncludedFiles,
  57. const StringArray& includesToIgnore,
  58. const StringArray& wildcards,
  59. const bool isOuterFile,
  60. const bool stripUnnecessaryStuff)
  61. {
  62. printf ("reading: " + file.getFileName() + "\n");
  63. if (! file.exists())
  64. {
  65. printf ("!! ERROR - file doesn't exist!");
  66. return false;
  67. }
  68. String content (file.loadFileAsString());
  69. if (stripUnnecessaryStuff && ! isOuterFile)
  70. {
  71. if (content.startsWith (T("/*")))
  72. content = content.fromFirstOccurrenceOf (T("*/"), false, false).trimStart();
  73. content = content.replace (T("\r\n\r\n\r\n"), T("\r\n\r\n"));
  74. }
  75. StringArray lines;
  76. lines.addLines (content);
  77. while (lines.size() > 0 && lines[0].trim().isEmpty())
  78. lines.remove (0);
  79. for (int i = 0; i < lines.size(); ++i)
  80. {
  81. String line (lines[i]);
  82. if ((! isOuterFile) && line.contains (T("//================================================================")))
  83. line = String::empty;
  84. if (line.trimStart().startsWithChar (T('#'))
  85. && line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\"")))
  86. {
  87. const int endOfInclude = line.indexOfChar (line.indexOfChar (T('\"')) + 1, T('\"')) + 1;
  88. const String lineUpToEndOfInclude (line.substring (0, endOfInclude));
  89. const String lineAfterInclude (line.substring (endOfInclude));
  90. const String filename (line.fromFirstOccurrenceOf (T("\""), false, false)
  91. .upToLastOccurrenceOf (T("\""), false, false));
  92. const File targetFile (file.getSiblingFile (filename));
  93. if (targetFile.exists()
  94. && targetFile.isAChildOf (rootFolder))
  95. {
  96. if (matchesWildcard (filename.replaceCharacter (T('\\'), T('/')), wildcards)
  97. && ! includesToIgnore.contains (targetFile.getFileName()))
  98. {
  99. if (line.containsIgnoreCase (T("FORCE_AMALGAMATOR_INCLUDE"))
  100. || ! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
  101. {
  102. if (! canFileBeReincluded (targetFile))
  103. alreadyIncludedFiles.add (targetFile.getFullPathName());
  104. dest.add (String::empty);
  105. dest.add (T("/********* Start of inlined file: ")
  106. + targetFile.getFileName()
  107. + T(" *********/"));
  108. if (! parseFile (rootFolder, newTargetFile,
  109. dest, targetFile, alreadyIncludedFiles, includesToIgnore,
  110. wildcards, false, stripUnnecessaryStuff))
  111. {
  112. return false;
  113. }
  114. dest.add (T("/********* End of inlined file: ")
  115. + targetFile.getFileName()
  116. + T(" *********/"));
  117. dest.add (String::empty);
  118. line = lineAfterInclude;
  119. }
  120. else
  121. {
  122. if (stripUnnecessaryStuff)
  123. line = String::empty;
  124. else
  125. line = T("/* ") + lineUpToEndOfInclude + T(" */") + lineAfterInclude;
  126. }
  127. }
  128. else
  129. {
  130. line = lineUpToEndOfInclude.upToFirstOccurrenceOf (T("\""), true, false)
  131. + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory())
  132. .replaceCharacter (T('\\'), T('/'))
  133. + T("\"")
  134. + lineAfterInclude;
  135. }
  136. }
  137. }
  138. dest.add (line.trimEnd());
  139. }
  140. return true;
  141. }
  142. //==============================================================================
  143. static bool munge (const File& templateFile, const File& targetFile, const String& wildcard,
  144. const bool stripUnnecessaryStuff, StringArray& alreadyIncludedFiles,
  145. const StringArray& includesToIgnore)
  146. {
  147. if (! templateFile.existsAsFile())
  148. {
  149. printf (" The template file doesn't exist!\n\n");
  150. return false;
  151. }
  152. StringArray lines, wildcards;
  153. wildcards.addTokens (wildcard, T(";,"), T("'\""));
  154. wildcards.trim();
  155. wildcards.removeEmptyStrings();
  156. if (! parseFile (targetFile.getParentDirectory(),
  157. targetFile,
  158. lines, templateFile,
  159. alreadyIncludedFiles,
  160. includesToIgnore,
  161. wildcards,
  162. true, stripUnnecessaryStuff))
  163. {
  164. return false;
  165. }
  166. //lines.trim();
  167. //lines.removeEmptyStrings();
  168. printf ("\nwriting: " + targetFile.getFullPathName() + "...\n\n");
  169. for (int i = 0; i < lines.size() - 2; ++i)
  170. {
  171. if (lines[i].isEmpty() && lines[i + 1].isEmpty())
  172. {
  173. lines.remove (i + 1);
  174. --i;
  175. }
  176. }
  177. MemoryBlock newData, oldData;
  178. const String newText (lines.joinIntoString (T("\n")) + T("\n"));
  179. newData.append ((const char*) newText, (int) strlen ((const char*) newText));
  180. targetFile.loadFileAsData (oldData);
  181. if (oldData == newData)
  182. {
  183. printf ("(No need to write - new file is identical)\n\n");
  184. return true;
  185. }
  186. if (! targetFile.replaceWithData (newData.getData(), newData.getSize()))
  187. {
  188. printf ("\n!! ERROR - couldn't write to the target file: " + targetFile.getFullPathName() + "\n\n");
  189. return false;
  190. }
  191. return true;
  192. }
  193. static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alreadyIncludedFiles)
  194. {
  195. StringArray lines;
  196. lines.addLines (hppTemplate.loadFileAsString());
  197. for (int i = 0; i < lines.size(); ++i)
  198. {
  199. String line (lines[i]);
  200. if (line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\"")))
  201. {
  202. const String filename (line.fromFirstOccurrenceOf (T("\""), false, false)
  203. .upToLastOccurrenceOf (T("\""), false, false));
  204. const File targetFile (hppTemplate.getSiblingFile (filename));
  205. if (! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
  206. {
  207. alreadyIncludedFiles.add (targetFile.getFullPathName());
  208. if (targetFile.getFileName().containsIgnoreCase (T("juce_")) && targetFile.exists())
  209. findAllFilesIncludedIn (targetFile, alreadyIncludedFiles);
  210. }
  211. }
  212. }
  213. }
  214. //==============================================================================
  215. static void mungeJuce (const File& juceFolder)
  216. {
  217. if (! juceFolder.isDirectory())
  218. {
  219. printf (" The folder supplied must be the root of your Juce directory!\n\n");
  220. return;
  221. }
  222. const File hppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.h")));
  223. const File cppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.cpp")));
  224. const File hppTarget (juceFolder.getChildFile (T("juce_amalgamated.h")));
  225. const File cppTarget (juceFolder.getChildFile (T("juce_amalgamated.cpp")));
  226. StringArray alreadyIncludedFiles, includesToIgnore;
  227. if (! munge (hppTemplate, hppTarget,
  228. "*.h", true, alreadyIncludedFiles, includesToIgnore))
  229. {
  230. return;
  231. }
  232. findAllFilesIncludedIn (hppTemplate, alreadyIncludedFiles);
  233. includesToIgnore.add (hppTarget.getFileName());
  234. munge (cppTemplate, cppTarget,
  235. "*.cpp;*.c;*.h;*.mm;*.m", true, alreadyIncludedFiles,
  236. includesToIgnore);
  237. }
  238. //==============================================================================
  239. int main (int argc, char* argv[])
  240. {
  241. // If you're running a command-line app, you need to initialise juce manually
  242. // before calling any Juce functionality..
  243. initialiseJuce_NonGUI();
  244. printf ("\n The C++ Amalgamator! Copyright 2008 by Julian Storer - www.rawmaterialsoftware.com\n\n");
  245. if (argc == 4)
  246. {
  247. const File templateFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
  248. const File targetFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[2]).unquoted()));
  249. const String wildcard (String (argv[3]).unquoted());
  250. StringArray alreadyIncludedFiles, includesToIgnore;
  251. munge (templateFile, targetFile, wildcard, false, alreadyIncludedFiles, includesToIgnore);
  252. }
  253. else if (argc == 2)
  254. {
  255. const File juceFolder (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
  256. mungeJuce (juceFolder);
  257. }
  258. else
  259. {
  260. printf (" Usage: amalgamator TemplateFile TargetFile \"FileToReplaceWildcard\"\n\n");
  261. printf (" amalgamator will run through a C++ file and replace any\n"
  262. " #include statements with the contents of the file they refer to.\n"
  263. " It'll only do this for files that are within the same parent\n"
  264. " directory as the target file, and will ignore include statements\n"
  265. " that use '<>' instead of quotes. It'll also only include a file once,\n"
  266. " ignoring any repeated instances of it.\n\n"
  267. " The wildcard lets you specify what kind of files will be replaced, so\n"
  268. " \"*.cpp;*.h\" would replace only includes that reference a .cpp or .h file.\n\n"
  269. " Or: just run 'amalgamator YourJuceDirectory' to rebuild the juce files."
  270. );
  271. }
  272. return 0;
  273. }