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.

341 lines
13KB

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