Add remaining files
[juce-lv2.git] / juce / source / extras / amalgamator / Source / Main.cpp
blobdafa8e16f93fbb6e647079886945e91aee8b767f
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-9 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../JuceLibraryCode/JuceHeader.h"
29 //==============================================================================
30 static bool matchesWildcard (const String& filename, const StringArray& wildcards)
32 for (int i = wildcards.size(); --i >= 0;)
33 if (filename.matchesWildcard (wildcards[i], true))
34 return true;
36 return false;
39 static bool canFileBeReincluded (const File& f)
41 String content (f.loadFileAsString());
43 for (;;)
45 content = content.trimStart();
47 if (content.startsWith ("//"))
48 content = content.fromFirstOccurrenceOf ("\n", false, false);
49 else if (content.startsWith ("/*"))
50 content = content.fromFirstOccurrenceOf ("*/", false, false);
51 else
52 break;
55 StringArray lines;
56 lines.addLines (content);
57 lines.trim();
58 lines.removeEmptyStrings();
60 const String l1 (lines[0].removeCharacters (" \t").trim());
61 const String l2 (lines[1].removeCharacters (" \t").trim());
63 if (l1.replace ("#ifndef", "#define") == l2)
64 return false;
66 return true;
69 static int64 calculateStreamHashCode (InputStream& in)
71 int64 t = 0;
73 const int bufferSize = 4096;
74 HeapBlock <uint8> buffer;
75 buffer.malloc (bufferSize);
77 for (;;)
79 const int num = in.read (buffer, bufferSize);
81 if (num <= 0)
82 break;
84 for (int i = 0; i < num; ++i)
85 t = t * 65599 + buffer[i];
88 return t;
91 static int64 calculateFileHashCode (const File& file)
93 ScopedPointer <FileInputStream> stream (file.createInputStream());
94 return stream != 0 ? calculateStreamHashCode (*stream) : 0;
98 //==============================================================================
99 static bool parseFile (const File& rootFolder,
100 const File& newTargetFile,
101 OutputStream& dest,
102 const File& file,
103 StringArray& alreadyIncludedFiles,
104 const StringArray& includesToIgnore,
105 const StringArray& wildcards,
106 bool isOuterFile,
107 bool stripCommentBlocks)
109 if (! file.exists())
111 std::cout << "!! ERROR - file doesn't exist!";
112 return false;
115 StringArray lines;
116 lines.addLines (file.loadFileAsString());
118 if (lines.size() == 0)
120 std::cout << "!! ERROR - input file was empty: " << file.getFullPathName();
121 return false;
124 bool lastLineWasBlank = true;
126 for (int i = 0; i < lines.size(); ++i)
128 String line (lines[i]);
129 String trimmed (line.trimStart());
131 if ((! isOuterFile) && trimmed.startsWith ("//================================================================"))
132 line = String::empty;
134 if (trimmed.startsWithChar ('#')
135 && trimmed.removeCharacters (" \t").startsWithIgnoreCase ("#include\""))
137 const int endOfInclude = line.indexOfChar (line.indexOfChar ('\"') + 1, '\"') + 1;
138 const String lineUpToEndOfInclude (line.substring (0, endOfInclude));
139 const String lineAfterInclude (line.substring (endOfInclude));
141 const String filename (line.fromFirstOccurrenceOf ("\"", false, false)
142 .upToLastOccurrenceOf ("\"", false, false));
143 const File targetFile (file.getSiblingFile (filename));
145 if (targetFile.exists() && targetFile.isAChildOf (rootFolder))
147 if (matchesWildcard (filename.replaceCharacter ('\\', '/'), wildcards)
148 && ! includesToIgnore.contains (targetFile.getFileName()))
150 if (line.containsIgnoreCase ("FORCE_AMALGAMATOR_INCLUDE")
151 || ! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
153 if (! canFileBeReincluded (targetFile))
154 alreadyIncludedFiles.add (targetFile.getFullPathName());
156 dest << newLine << "/*** Start of inlined file: " << targetFile.getFileName() << " ***/" << newLine;
158 if (! parseFile (rootFolder, newTargetFile,
159 dest, targetFile, alreadyIncludedFiles, includesToIgnore,
160 wildcards, false, stripCommentBlocks))
162 return false;
165 dest << "/*** End of inlined file: " << targetFile.getFileName() << " ***/" << newLine << newLine;
167 line = lineAfterInclude;
169 else
171 line = String::empty;
174 else
176 line = lineUpToEndOfInclude.upToFirstOccurrenceOf ("\"", true, false)
177 + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory())
178 .replaceCharacter ('\\', '/')
179 + "\""
180 + lineAfterInclude;
185 if ((stripCommentBlocks || i == 0) && trimmed.startsWith ("/*") && (i > 10 || ! isOuterFile))
187 int originalI = i;
188 String originalLine = line;
190 for (;;)
192 int end = line.indexOf ("*/");
194 if (end >= 0)
196 line = line.substring (end + 2);
198 // If our comment appeared just before an assertion, leave it in, as it
199 // might be useful..
200 if (lines [i + 1].contains ("assert")
201 || lines [i + 2].contains ("assert"))
203 i = originalI;
204 line = originalLine;
207 break;
210 line = lines [++i];
212 if (i >= lines.size())
213 break;
216 line = line.trimEnd();
217 if (line.isEmpty())
218 continue;
221 line = line.trimEnd();
224 // Turn initial spaces into tabs..
225 int numIntialSpaces = 0;
226 int len = line.length();
227 while (numIntialSpaces < len && line [numIntialSpaces] == ' ')
228 ++numIntialSpaces;
230 if (numIntialSpaces > 0)
232 int tabSize = 4;
233 int numTabs = numIntialSpaces / tabSize;
234 line = String::repeatedString ("\t", numTabs) + line.substring (numTabs * tabSize);
237 if (! line.containsChar ('"'))
239 // turn large areas of spaces into tabs - this will mess up alignment a bit, but
240 // it's only the amalgamated file, so doesn't matter...
241 line = line.replace (" ", "\t", false);
242 line = line.replace (" ", "\t", false);
246 if (line.isNotEmpty() || ! lastLineWasBlank)
247 dest << line << newLine;
249 lastLineWasBlank = line.isEmpty();
252 return true;
255 //==============================================================================
256 static bool munge (const File& templateFile, const File& targetFile, const String& wildcard,
257 StringArray& alreadyIncludedFiles, const StringArray& includesToIgnore)
259 if (! templateFile.existsAsFile())
261 std::cout << " The template file doesn't exist!\n\n";
262 return false;
265 StringArray wildcards;
266 wildcards.addTokens (wildcard, ";,", "'\"");
267 wildcards.trim();
268 wildcards.removeEmptyStrings();
270 std::cout << "Building: " << targetFile.getFullPathName() << "...\n";
272 TemporaryFile temp (targetFile);
273 ScopedPointer <FileOutputStream> out (temp.getFile().createOutputStream (1024 * 128));
275 if (out == 0)
277 std::cout << "\n!! ERROR - couldn't write to the target file: "
278 << temp.getFile().getFullPathName() << "\n\n";
279 return false;
282 out->setNewLineString ("\n");
284 if (! parseFile (targetFile.getParentDirectory(),
285 targetFile,
286 *out, templateFile,
287 alreadyIncludedFiles,
288 includesToIgnore,
289 wildcards,
290 true, false))
292 return false;
295 out = 0;
297 if (calculateFileHashCode (targetFile) == calculateFileHashCode (temp.getFile()))
299 std::cout << " -- No need to write - new file is identical\n";
300 return true;
303 if (! temp.overwriteTargetFileWithTemporary())
305 std::cout << "\n!! ERROR - couldn't write to the target file: "
306 << targetFile.getFullPathName() << "\n\n";
307 return false;
310 return true;
313 static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alreadyIncludedFiles)
315 StringArray lines;
316 lines.addLines (hppTemplate.loadFileAsString());
318 for (int i = 0; i < lines.size(); ++i)
320 String line (lines[i]);
322 if (line.removeCharacters (" \t").startsWithIgnoreCase ("#include\""))
324 const String filename (line.fromFirstOccurrenceOf ("\"", false, false)
325 .upToLastOccurrenceOf ("\"", false, false));
326 const File targetFile (hppTemplate.getSiblingFile (filename));
328 if (! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
330 alreadyIncludedFiles.add (targetFile.getFullPathName());
332 if (targetFile.getFileName().containsIgnoreCase ("juce_") && targetFile.exists())
333 findAllFilesIncludedIn (targetFile, alreadyIncludedFiles);
339 //==============================================================================
340 static void mungeJuce (const File& juceFolder)
342 if (! juceFolder.isDirectory())
344 std::cout << " The folder supplied must be the root of your Juce directory!\n\n";
345 return;
348 const File hppTemplate (juceFolder.getChildFile ("amalgamation/juce_amalgamated_template.h"));
349 const File cppTemplate (juceFolder.getChildFile ("amalgamation/juce_amalgamated_template.cpp"));
351 const File hppTarget (juceFolder.getChildFile ("juce_amalgamated.h"));
352 const File cppTarget (juceFolder.getChildFile ("juce_amalgamated.cpp"));
354 StringArray alreadyIncludedFiles, includesToIgnore;
356 if (! munge (hppTemplate, hppTarget, "*.h", alreadyIncludedFiles, includesToIgnore))
358 return;
361 findAllFilesIncludedIn (hppTemplate, alreadyIncludedFiles);
362 includesToIgnore.add (hppTarget.getFileName());
364 munge (cppTemplate, cppTarget, "*.cpp;*.c;*.h;*.mm;*.m", alreadyIncludedFiles, includesToIgnore);
367 //==============================================================================
368 int main (int argc, char* argv[])
370 std::cout << "\n*** The C++ Amalgamator! Written for Juce - www.rawmaterialsoftware.com\n";
372 if (argc == 4)
374 const File templateFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
375 const File targetFile (File::getCurrentWorkingDirectory().getChildFile (String (argv[2]).unquoted()));
376 const String wildcard (String (argv[3]).unquoted());
377 StringArray alreadyIncludedFiles, includesToIgnore;
379 munge (templateFile, targetFile, wildcard, alreadyIncludedFiles, includesToIgnore);
381 else if (argc == 2)
383 const File juceFolder (File::getCurrentWorkingDirectory().getChildFile (String (argv[1]).unquoted()));
384 mungeJuce (juceFolder);
386 else
388 std::cout << " Usage: amalgamator TemplateFile TargetFile \"FileToReplaceWildcard\"\n\n"
389 " amalgamator will run through a C++ file and replace any\n"
390 " #include statements with the contents of the file they refer to.\n"
391 " It'll only do this for files that are within the same parent\n"
392 " directory as the target file, and will ignore include statements\n"
393 " that use '<>' instead of quotes. It'll also only include a file once,\n"
394 " ignoring any repeated instances of it.\n\n"
395 " The wildcard lets you specify what kind of files will be replaced, so\n"
396 " \"*.cpp;*.h\" would replace only includes that reference a .cpp or .h file.\n\n"
397 " Or: just run 'amalgamator YourJuceDirectory' to rebuild the juce files.";
400 std::cout << "\n";
401 return 0;