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.

342 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.size() > 0 && 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 (line.containsIgnoreCase (T("FORCE_AMALGAMATOR_INCLUDE"))
  105. || ! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
  106. {
  107. if (! canFileBeReincluded (targetFile))
  108. alreadyIncludedFiles.add (targetFile.getFullPathName());
  109. dest.add (String::empty);
  110. dest.add (T("/********* Start of inlined file: ")
  111. + targetFile.getFileName()
  112. + T(" *********/"));
  113. if (! parseFile (rootFolder, newTargetFile,
  114. dest, targetFile, alreadyIncludedFiles, includesToIgnore,
  115. wildcards, false, stripUnnecessaryStuff))
  116. {
  117. return false;
  118. }
  119. dest.add (T("/********* End of inlined file: ")
  120. + targetFile.getFileName()
  121. + T(" *********/"));
  122. dest.add (String::empty);
  123. line = lineAfterInclude;
  124. }
  125. else
  126. {
  127. if (stripUnnecessaryStuff)
  128. line = String::empty;
  129. else
  130. line = T("/* ") + lineUpToEndOfInclude + T(" */") + lineAfterInclude;
  131. }
  132. }
  133. else
  134. {
  135. line = lineUpToEndOfInclude.upToFirstOccurrenceOf (T("\""), true, false)
  136. + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory())
  137. .replaceCharacter (T('\\'), T('/'))
  138. + T("\"")
  139. + lineAfterInclude;
  140. }
  141. }
  142. }
  143. dest.add (line.trimEnd());
  144. }
  145. return true;
  146. }
  147. //==============================================================================
  148. static bool munge (const File& templateFile, const File& targetFile, const String& wildcard,
  149. const bool stripUnnecessaryStuff, StringArray& alreadyIncludedFiles,
  150. const StringArray& includesToIgnore)
  151. {
  152. if (! templateFile.existsAsFile())
  153. {
  154. printf (" The template file doesn't exist!\n\n");
  155. return false;
  156. }
  157. StringArray lines, wildcards;
  158. wildcards.addTokens (wildcard, T(";,"), T("'\""));
  159. wildcards.trim();
  160. wildcards.removeEmptyStrings();
  161. if (! parseFile (targetFile.getParentDirectory(),
  162. targetFile,
  163. lines, templateFile,
  164. alreadyIncludedFiles,
  165. includesToIgnore,
  166. wildcards,
  167. true, stripUnnecessaryStuff))
  168. {
  169. return false;
  170. }
  171. //lines.trim();
  172. //lines.removeEmptyStrings();
  173. printf ("\nwriting: " + targetFile.getFullPathName() + "...\n\n");
  174. for (int i = 0; i < lines.size() - 2; ++i)
  175. {
  176. if (lines[i].isEmpty() && lines[i + 1].isEmpty())
  177. {
  178. lines.remove (i + 1);
  179. --i;
  180. }
  181. }
  182. MemoryBlock newData, oldData;
  183. const String newText (lines.joinIntoString (T("\n")) + T("\n"));
  184. newData.append ((const char*) newText, (int) strlen ((const char*) newText));
  185. targetFile.loadFileAsData (oldData);
  186. if (oldData == newData)
  187. {
  188. printf ("(No need to write - new file is identical)\n\n");
  189. return true;
  190. }
  191. if (! targetFile.replaceWithData (newData.getData(), newData.getSize()))
  192. {
  193. printf ("\n!! ERROR - couldn't write to the target file: " + targetFile.getFullPathName() + "\n\n");
  194. return false;
  195. }
  196. return true;
  197. }
  198. static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alreadyIncludedFiles)
  199. {
  200. StringArray lines;
  201. lines.addLines (hppTemplate.loadFileAsString());
  202. for (int i = 0; i < lines.size(); ++i)
  203. {
  204. String line (lines[i]);
  205. if (line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\"")))
  206. {
  207. const String filename (line.fromFirstOccurrenceOf (T("\""), false, false)
  208. .upToLastOccurrenceOf (T("\""), false, false));
  209. const File targetFile (hppTemplate.getSiblingFile (filename));
  210. if (! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
  211. {
  212. alreadyIncludedFiles.add (targetFile.getFullPathName());
  213. if (targetFile.getFileName().containsIgnoreCase (T("juce_")) && targetFile.exists())
  214. findAllFilesIncludedIn (targetFile, alreadyIncludedFiles);
  215. }
  216. }
  217. }
  218. }
  219. //==============================================================================
  220. static void mungeJuce (const File& juceFolder)
  221. {
  222. if (! juceFolder.isDirectory())
  223. {
  224. printf (" The folder supplied must be the root of your Juce directory!\n\n");
  225. return;
  226. }
  227. const File hppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.h")));
  228. const File cppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.cpp")));
  229. const File hppTarget (juceFolder.getChildFile (T("juce_amalgamated.h")));
  230. const File cppTarget (juceFolder.getChildFile (T("juce_amalgamated.cpp")));
  231. StringArray alreadyIncludedFiles, includesToIgnore;
  232. if (! munge (hppTemplate, hppTarget,
  233. "*.h", true, alreadyIncludedFiles, includesToIgnore))
  234. {
  235. return;
  236. }
  237. findAllFilesIncludedIn (hppTemplate, alreadyIncludedFiles);
  238. includesToIgnore.add (hppTarget.getFileName());
  239. munge (cppTemplate, cppTarget,
  240. "*.cpp;*.c;*.h;*.mm;*.m", true, alreadyIncludedFiles,
  241. includesToIgnore);
  242. }
  243. //==============================================================================
  244. int main (int argc, char* argv[])
  245. {
  246. // If you're running a command-line app, you need to initialise juce manually
  247. // before calling any Juce functionality..
  248. initialiseJuce_NonGUI();
  249. printf ("\n The C++ Amalgamator! Copyright 2008 by Julian Storer - www.rawmaterialsoftware.com\n\n");
  250. if (argc == 4)
  251. {
  252. const File templateFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
  253. const File targetFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[2]).unquoted()));
  254. const String wildcard (String (argv[3]).unquoted());
  255. StringArray alreadyIncludedFiles, includesToIgnore;
  256. munge (templateFile, targetFile, wildcard, false, alreadyIncludedFiles, includesToIgnore);
  257. }
  258. else if (argc == 2)
  259. {
  260. const File juceFolder (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
  261. mungeJuce (juceFolder);
  262. }
  263. else
  264. {
  265. printf (" Usage: amalgamator TemplateFile TargetFile \"FileToReplaceWildcard\"\n\n");
  266. printf (" amalgamator will run through a C++ file and replace any\n"
  267. " #include statements with the contents of the file they refer to.\n"
  268. " It'll only do this for files that are within the same parent\n"
  269. " directory as the target file, and will ignore include statements\n"
  270. " that use '<>' instead of quotes. It'll also only include a file once,\n"
  271. " ignoring any repeated instances of it.\n\n"
  272. " The wildcard lets you specify what kind of files will be replaced, so\n"
  273. " \"*.cpp;*.h\" would replace only includes that reference a .cpp or .h file.\n\n"
  274. " Or: just run 'amalgamator YourJuceDirectory' to rebuild the juce files."
  275. );
  276. }
  277. return 0;
  278. }