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.

352 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. namespace build_tools
  21. {
  22. void overwriteFileIfDifferentOrThrow (const File& file, const MemoryOutputStream& newData)
  23. {
  24. if (! overwriteFileWithNewDataIfDifferent (file, newData))
  25. throw SaveError (file);
  26. }
  27. void overwriteFileIfDifferentOrThrow (const File& file, const String& newData)
  28. {
  29. if (! overwriteFileWithNewDataIfDifferent (file, newData))
  30. throw SaveError (file);
  31. }
  32. String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString)
  33. {
  34. for (int i = 0; i < definitions.size(); ++i)
  35. {
  36. const String key (definitions.getAllKeys()[i]);
  37. const String value (definitions.getAllValues()[i]);
  38. sourceString = sourceString.replace ("${" + key + "}", value);
  39. }
  40. return sourceString;
  41. }
  42. String getXcodePackageType (ProjectType::Target::Type type)
  43. {
  44. switch (type)
  45. {
  46. case ProjectType::Target::Type::GUIApp:
  47. case ProjectType::Target::Type::StandalonePlugIn:
  48. return "APPL";
  49. case ProjectType::Target::Type::VSTPlugIn:
  50. case ProjectType::Target::Type::VST3PlugIn:
  51. case ProjectType::Target::Type::AudioUnitPlugIn:
  52. case ProjectType::Target::Type::UnityPlugIn:
  53. return "BNDL";
  54. case ProjectType::Target::Type::AudioUnitv3PlugIn:
  55. return "XPC!";
  56. case ProjectType::Target::Type::AAXPlugIn:
  57. case ProjectType::Target::Type::RTASPlugIn:
  58. return "TDMw";
  59. case ProjectType::Target::Type::ConsoleApp:
  60. case ProjectType::Target::Type::StaticLibrary:
  61. case ProjectType::Target::Type::DynamicLibrary:
  62. case ProjectType::Target::Type::SharedCodeTarget:
  63. case ProjectType::Target::Type::AggregateTarget:
  64. case ProjectType::Target::Type::unspecified:
  65. default:
  66. return {};
  67. }
  68. }
  69. String getXcodeBundleSignature (ProjectType::Target::Type type)
  70. {
  71. switch (type)
  72. {
  73. case ProjectType::Target::Type::GUIApp:
  74. case ProjectType::Target::Type::VSTPlugIn:
  75. case ProjectType::Target::Type::VST3PlugIn:
  76. case ProjectType::Target::Type::AudioUnitPlugIn:
  77. case ProjectType::Target::Type::StandalonePlugIn:
  78. case ProjectType::Target::Type::AudioUnitv3PlugIn:
  79. case ProjectType::Target::Type::UnityPlugIn:
  80. return "????";
  81. case ProjectType::Target::Type::AAXPlugIn:
  82. case ProjectType::Target::Type::RTASPlugIn:
  83. return "PTul";
  84. case ProjectType::Target::Type::ConsoleApp:
  85. case ProjectType::Target::Type::StaticLibrary:
  86. case ProjectType::Target::Type::DynamicLibrary:
  87. case ProjectType::Target::Type::SharedCodeTarget:
  88. case ProjectType::Target::Type::AggregateTarget:
  89. case ProjectType::Target::Type::unspecified:
  90. default:
  91. return {};
  92. }
  93. }
  94. static unsigned int calculateHash (const String& s, const unsigned int hashMultiplier)
  95. {
  96. auto t = s.toUTF8();
  97. unsigned int hash = 0;
  98. while (*t != 0)
  99. hash = hashMultiplier * hash + (unsigned int) *t++;
  100. return hash;
  101. }
  102. static unsigned int findBestHashMultiplier (const StringArray& strings)
  103. {
  104. unsigned int v = 31;
  105. for (;;)
  106. {
  107. SortedSet<unsigned int> hashes;
  108. bool collision = false;
  109. for (int i = strings.size(); --i >= 0;)
  110. {
  111. auto hash = calculateHash (strings[i], v);
  112. if (hashes.contains (hash))
  113. {
  114. collision = true;
  115. break;
  116. }
  117. hashes.add (hash);
  118. }
  119. if (! collision)
  120. break;
  121. v += 2;
  122. }
  123. return v;
  124. }
  125. String makeValidIdentifier (String s, bool makeCamelCase, bool removeColons, bool allowTemplates, bool allowAsterisks)
  126. {
  127. if (s.isEmpty())
  128. return "unknown";
  129. if (removeColons)
  130. s = s.replaceCharacters (".,;:/@", "______");
  131. else
  132. s = s.replaceCharacters (".,;/@", "_____");
  133. for (int i = s.length(); --i > 0;)
  134. if (CharacterFunctions::isLetter (s[i])
  135. && CharacterFunctions::isLetter (s[i - 1])
  136. && CharacterFunctions::isUpperCase (s[i])
  137. && ! CharacterFunctions::isUpperCase (s[i - 1]))
  138. s = s.substring (0, i) + " " + s.substring (i);
  139. String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
  140. if (allowTemplates)
  141. allowedChars += "<>";
  142. if (! removeColons)
  143. allowedChars += ":";
  144. if (allowAsterisks)
  145. allowedChars += "*";
  146. StringArray words;
  147. words.addTokens (s.retainCharacters (allowedChars), false);
  148. words.trim();
  149. auto n = words[0];
  150. if (makeCamelCase)
  151. n = n.toLowerCase();
  152. for (int i = 1; i < words.size(); ++i)
  153. {
  154. if (makeCamelCase && words[i].length() > 1)
  155. n << words[i].substring (0, 1).toUpperCase()
  156. << words[i].substring (1).toLowerCase();
  157. else
  158. n << words[i];
  159. }
  160. if (CharacterFunctions::isDigit (n[0]))
  161. n = "_" + n;
  162. if (isReservedKeyword (n))
  163. n << '_';
  164. return n;
  165. }
  166. String makeBinaryDataIdentifierName (const File& file)
  167. {
  168. return makeValidIdentifier (file.getFileName()
  169. .replaceCharacters (" .", "__")
  170. .retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"),
  171. false, true, false);
  172. }
  173. void writeDataAsCppLiteral (const MemoryBlock& mb, OutputStream& out,
  174. bool breakAtNewLines, bool allowStringBreaks)
  175. {
  176. const int maxCharsOnLine = 250;
  177. auto data = (const unsigned char*) mb.getData();
  178. int charsOnLine = 0;
  179. bool canUseStringLiteral = mb.getSize() < 32768; // MS compilers can't handle big string literals..
  180. if (canUseStringLiteral)
  181. {
  182. unsigned int numEscaped = 0;
  183. for (size_t i = 0; i < mb.getSize(); ++i)
  184. {
  185. auto num = (unsigned int) data[i];
  186. if (! ((num >= 32 && num < 127) || num == '\t' || num == '\r' || num == '\n'))
  187. {
  188. if (++numEscaped > mb.getSize() / 4)
  189. {
  190. canUseStringLiteral = false;
  191. break;
  192. }
  193. }
  194. }
  195. }
  196. if (! canUseStringLiteral)
  197. {
  198. out << "{ ";
  199. for (size_t i = 0; i < mb.getSize(); ++i)
  200. {
  201. auto num = (int) (unsigned int) data[i];
  202. out << num << ',';
  203. charsOnLine += 2;
  204. if (num >= 10)
  205. {
  206. ++charsOnLine;
  207. if (num >= 100)
  208. ++charsOnLine;
  209. }
  210. if (charsOnLine >= maxCharsOnLine)
  211. {
  212. charsOnLine = 0;
  213. out << newLine;
  214. }
  215. }
  216. out << "0,0 };";
  217. }
  218. else
  219. {
  220. out << "\"";
  221. writeEscapeChars (out, (const char*) data, (int) mb.getSize(),
  222. maxCharsOnLine, breakAtNewLines, false, allowStringBreaks);
  223. out << "\";";
  224. }
  225. }
  226. void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
  227. const StringArray& strings, const StringArray& codeToExecute, const int indentLevel)
  228. {
  229. jassert (strings.size() == codeToExecute.size());
  230. auto indent = String::repeatedString (" ", indentLevel);
  231. auto hashMultiplier = findBestHashMultiplier (strings);
  232. out << indent << "unsigned int hash = 0;" << newLine
  233. << newLine
  234. << indent << "if (" << utf8PointerVariable << " != nullptr)" << newLine
  235. << indent << " while (*" << utf8PointerVariable << " != 0)" << newLine
  236. << indent << " hash = " << (int) hashMultiplier << " * hash + (unsigned int) *" << utf8PointerVariable << "++;" << newLine
  237. << newLine
  238. << indent << "switch (hash)" << newLine
  239. << indent << "{" << newLine;
  240. for (int i = 0; i < strings.size(); ++i)
  241. {
  242. out << indent << " case 0x" << hexString8Digits ((int) calculateHash (strings[i], hashMultiplier))
  243. << ": " << codeToExecute[i] << newLine;
  244. }
  245. out << indent << " default: break;" << newLine
  246. << indent << "}" << newLine << newLine;
  247. }
  248. String unixStylePath (const String& path) { return path.replaceCharacter ('\\', '/'); }
  249. String windowsStylePath (const String& path) { return path.replaceCharacter ('/', '\\'); }
  250. String currentOSStylePath (const String& path)
  251. {
  252. #if JUCE_WINDOWS
  253. return windowsStylePath (path);
  254. #else
  255. return unixStylePath (path);
  256. #endif
  257. }
  258. bool isAbsolutePath (const String& path)
  259. {
  260. return File::isAbsolutePath (path)
  261. || path.startsWithChar ('/') // (needed because File::isAbsolutePath will ignore forward-slashes on Windows)
  262. || path.startsWithChar ('$')
  263. || path.startsWithChar ('~')
  264. || (CharacterFunctions::isLetter (path[0]) && path[1] == ':')
  265. || path.startsWithIgnoreCase ("smb:");
  266. }
  267. String getRelativePathFrom (const File& file, const File& sourceFolder)
  268. {
  269. #if ! JUCE_WINDOWS
  270. // On a non-windows machine, we can't know if a drive-letter path may be relative or not.
  271. if (CharacterFunctions::isLetter (file.getFullPathName()[0]) && file.getFullPathName()[1] == ':')
  272. return file.getFullPathName();
  273. #endif
  274. return file.getRelativePathFrom (sourceFolder);
  275. }
  276. void writeStreamToFile (const File& file, const std::function<void (MemoryOutputStream&)>& writer)
  277. {
  278. MemoryOutputStream mo;
  279. writer (mo);
  280. overwriteFileIfDifferentOrThrow (file, mo);
  281. }
  282. }
  283. }