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.

364 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class MakefileProjectExporter : public ProjectExporter
  18. {
  19. public:
  20. //==============================================================================
  21. static const char* getNameLinux() { return "Linux Makefile"; }
  22. static const char* getValueTreeTypeName() { return "LINUX_MAKE"; }
  23. static MakefileProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  24. {
  25. if (settings.hasType (getValueTreeTypeName()))
  26. return new MakefileProjectExporter (project, settings);
  27. return nullptr;
  28. }
  29. //==============================================================================
  30. MakefileProjectExporter (Project& p, const ValueTree& t) : ProjectExporter (p, t)
  31. {
  32. name = getNameLinux();
  33. if (getTargetLocationString().isEmpty())
  34. getTargetLocationValue() = getDefaultBuildsRootFolder() + "LinuxMakefile";
  35. initialiseDependencyPathValues();
  36. }
  37. //==============================================================================
  38. bool canLaunchProject() override { return false; }
  39. bool launchProject() override { return false; }
  40. bool usesMMFiles() const override { return false; }
  41. bool isLinuxMakefile() const override { return true; }
  42. bool isLinux() const override { return true; }
  43. bool canCopeWithDuplicateFiles() override { return false; }
  44. void createExporterProperties (PropertyListBuilder&) override
  45. {
  46. }
  47. //==============================================================================
  48. void create (const OwnedArray<LibraryModule>&) const override
  49. {
  50. Array<RelativePath> files;
  51. for (int i = 0; i < getAllGroups().size(); ++i)
  52. findAllFilesToCompile (getAllGroups().getReference(i), files);
  53. MemoryOutputStream mo;
  54. writeMakefile (mo, files);
  55. overwriteFileIfDifferentOrThrow (getTargetFolder().getChildFile ("Makefile"), mo);
  56. }
  57. protected:
  58. //==============================================================================
  59. class MakeBuildConfiguration : public BuildConfiguration
  60. {
  61. public:
  62. MakeBuildConfiguration (Project& p, const ValueTree& settings)
  63. : BuildConfiguration (p, settings)
  64. {
  65. setValueIfVoid (getLibrarySearchPathValue(), "/usr/X11R6/lib/");
  66. }
  67. Value getArchitectureType() { return getValue (Ids::linuxArchitecture); }
  68. var getArchitectureTypeVar() const { return config [Ids::linuxArchitecture]; }
  69. var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
  70. void createConfigProperties (PropertyListBuilder& props) override
  71. {
  72. addGCCOptimisationProperty (props);
  73. static const char* const archNames[] = { "(Default)", "<None>", "32-bit (-m32)", "64-bit (-m64)", "ARM v6", "ARM v7" };
  74. const var archFlags[] = { var(), var (String()), "-m32", "-m64", "-march=armv6", "-march=armv7" };
  75. props.add (new ChoicePropertyComponent (getArchitectureType(), "Architecture",
  76. StringArray (archNames, numElementsInArray (archNames)),
  77. Array<var> (archFlags, numElementsInArray (archFlags))));
  78. }
  79. };
  80. BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
  81. {
  82. return new MakeBuildConfiguration (project, tree);
  83. }
  84. private:
  85. //==============================================================================
  86. void findAllFilesToCompile (const Project::Item& projectItem, Array<RelativePath>& results) const
  87. {
  88. if (projectItem.isGroup())
  89. {
  90. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  91. findAllFilesToCompile (projectItem.getChild(i), results);
  92. }
  93. else
  94. {
  95. if (projectItem.shouldBeCompiled())
  96. results.add (RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder));
  97. }
  98. }
  99. void writeDefineFlags (OutputStream& out, const BuildConfiguration& config) const
  100. {
  101. StringPairArray defines;
  102. defines.set ("LINUX", "1");
  103. if (config.isDebug())
  104. {
  105. defines.set ("DEBUG", "1");
  106. defines.set ("_DEBUG", "1");
  107. }
  108. else
  109. {
  110. defines.set ("NDEBUG", "1");
  111. }
  112. out << createGCCPreprocessorFlags (mergePreprocessorDefs (defines, getAllPreprocessorDefs (config)));
  113. }
  114. void writeHeaderPathFlags (OutputStream& out, const BuildConfiguration& config) const
  115. {
  116. StringArray searchPaths (extraSearchPaths);
  117. searchPaths.addArray (config.getHeaderSearchPaths());
  118. searchPaths.insert (0, "/usr/include/freetype2");
  119. searchPaths.insert (0, "/usr/include");
  120. searchPaths = getCleanedStringArray (searchPaths);
  121. for (int i = 0; i < searchPaths.size(); ++i)
  122. out << " -I " << escapeSpaces (FileHelpers::unixStylePath (replacePreprocessorTokens (config, searchPaths[i])));
  123. }
  124. void writeCppFlags (OutputStream& out, const BuildConfiguration& config) const
  125. {
  126. out << " CPPFLAGS := $(DEPFLAGS)";
  127. writeDefineFlags (out, config);
  128. writeHeaderPathFlags (out, config);
  129. out << newLine;
  130. }
  131. void writeLinkerFlags (OutputStream& out, const BuildConfiguration& config) const
  132. {
  133. out << " LDFLAGS += $(TARGET_ARCH) -L$(BINDIR) -L$(LIBDIR)";
  134. {
  135. StringArray flags (makefileExtraLinkerFlags);
  136. if (makefileIsDLL)
  137. flags.add ("-shared");
  138. if (! config.isDebug())
  139. flags.add ("-fvisibility=hidden");
  140. if (flags.size() > 0)
  141. out << " " << getCleanedStringArray (flags).joinIntoString (" ");
  142. }
  143. out << config.getGCCLibraryPathFlags();
  144. for (int i = 0; i < linuxLibs.size(); ++i)
  145. out << " -l" << linuxLibs[i];
  146. if (getProject().isConfigFlagEnabled ("JUCE_USE_CURL"))
  147. out << " -lcurl";
  148. StringArray libraries;
  149. libraries.addTokens (getExternalLibrariesString(), ";", "\"'");
  150. libraries.removeEmptyStrings();
  151. if (libraries.size() != 0)
  152. out << " -l" << replacePreprocessorTokens (config, libraries.joinIntoString (" -l")).trim();
  153. out << " " << replacePreprocessorTokens (config, getExtraLinkerFlagsString()).trim()
  154. << newLine;
  155. }
  156. void writeConfig (OutputStream& out, const BuildConfiguration& config) const
  157. {
  158. const String buildDirName ("build");
  159. const String intermediatesDirName (buildDirName + "/intermediate/" + config.getName());
  160. String outputDir (buildDirName);
  161. if (config.getTargetBinaryRelativePathString().isNotEmpty())
  162. {
  163. RelativePath binaryPath (config.getTargetBinaryRelativePathString(), RelativePath::projectFolder);
  164. outputDir = binaryPath.rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle();
  165. }
  166. out << "ifeq ($(CONFIG)," << escapeSpaces (config.getName()) << ")" << newLine;
  167. out << " BINDIR := " << escapeSpaces (buildDirName) << newLine
  168. << " LIBDIR := " << escapeSpaces (buildDirName) << newLine
  169. << " OBJDIR := " << escapeSpaces (intermediatesDirName) << newLine
  170. << " OUTDIR := " << escapeSpaces (outputDir) << newLine
  171. << newLine
  172. << " ifeq ($(TARGET_ARCH),)" << newLine
  173. << " TARGET_ARCH := " << getArchFlags (config) << newLine
  174. << " endif" << newLine
  175. << newLine;
  176. writeCppFlags (out, config);
  177. out << " CFLAGS += $(CPPFLAGS) $(TARGET_ARCH)";
  178. if (config.isDebug())
  179. out << " -g -ggdb";
  180. if (makefileIsDLL)
  181. out << " -fPIC";
  182. out << " -O" << config.getGCCOptimisationFlag()
  183. << (" " + replacePreprocessorTokens (config, getExtraCompilerFlagsString())).trimEnd()
  184. << newLine;
  185. out << " CXXFLAGS += $(CFLAGS) -std=c++11" << newLine;
  186. writeLinkerFlags (out, config);
  187. out << newLine;
  188. String targetName (replacePreprocessorTokens (config, config.getTargetBinaryNameString()));
  189. if (projectType.isStaticLibrary() || projectType.isDynamicLibrary())
  190. targetName = getLibbedFilename (targetName);
  191. else
  192. targetName = targetName.upToLastOccurrenceOf (".", false, false) + makefileTargetSuffix;
  193. out << " TARGET := " << escapeSpaces (targetName) << newLine;
  194. if (projectType.isStaticLibrary())
  195. out << " BLDCMD = ar -rcs $(OUTDIR)/$(TARGET) $(OBJECTS)" << newLine;
  196. else
  197. out << " BLDCMD = $(CXX) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)" << newLine;
  198. out << " CLEANCMD = rm -rf $(OUTDIR)/$(TARGET) $(OBJDIR)" << newLine
  199. << "endif" << newLine
  200. << newLine;
  201. }
  202. void writeObjects (OutputStream& out, const Array<RelativePath>& files) const
  203. {
  204. out << "OBJECTS := \\" << newLine;
  205. for (int i = 0; i < files.size(); ++i)
  206. if (shouldFileBeCompiledByDefault (files.getReference(i)))
  207. out << " $(OBJDIR)/" << escapeSpaces (getObjectFileFor (files.getReference(i))) << " \\" << newLine;
  208. out << newLine;
  209. }
  210. void writeMakefile (OutputStream& out, const Array<RelativePath>& files) const
  211. {
  212. out << "# Automatically generated makefile, created by the Introjucer" << newLine
  213. << "# Don't edit this file! Your changes will be overwritten when you re-save the Introjucer project!" << newLine
  214. << newLine;
  215. out << "# (this disables dependency generation if multiple architectures are set)" << newLine
  216. << "DEPFLAGS := $(if $(word 2, $(TARGET_ARCH)), , -MMD)" << newLine
  217. << newLine;
  218. out << "ifndef CONFIG" << newLine
  219. << " CONFIG=" << escapeSpaces (getConfiguration(0)->getName()) << newLine
  220. << "endif" << newLine
  221. << newLine;
  222. for (ConstConfigIterator config (*this); config.next();)
  223. writeConfig (out, *config);
  224. writeObjects (out, files);
  225. out << ".PHONY: clean" << newLine
  226. << newLine;
  227. out << "$(OUTDIR)/$(TARGET): $(OBJECTS) $(RESOURCES)" << newLine
  228. << "\t@echo Linking " << projectName << newLine
  229. << "\t-@mkdir -p $(BINDIR)" << newLine
  230. << "\t-@mkdir -p $(LIBDIR)" << newLine
  231. << "\t-@mkdir -p $(OUTDIR)" << newLine
  232. << "\t@$(BLDCMD)" << newLine
  233. << newLine;
  234. out << "clean:" << newLine
  235. << "\t@echo Cleaning " << projectName << newLine
  236. << "\t@$(CLEANCMD)" << newLine
  237. << newLine;
  238. out << "strip:" << newLine
  239. << "\t@echo Stripping " << projectName << newLine
  240. << "\t-@strip --strip-unneeded $(OUTDIR)/$(TARGET)" << newLine
  241. << newLine;
  242. for (int i = 0; i < files.size(); ++i)
  243. {
  244. if (shouldFileBeCompiledByDefault (files.getReference(i)))
  245. {
  246. jassert (files.getReference(i).getRoot() == RelativePath::buildTargetFolder);
  247. out << "$(OBJDIR)/" << escapeSpaces (getObjectFileFor (files.getReference(i)))
  248. << ": " << escapeSpaces (files.getReference(i).toUnixStyle()) << newLine
  249. << "\t-@mkdir -p $(OBJDIR)" << newLine
  250. << "\t@echo \"Compiling " << files.getReference(i).getFileName() << "\"" << newLine
  251. << (files.getReference(i).hasFileExtension ("c;s;S") ? "\t@$(CC) $(CFLAGS) -o \"$@\" -c \"$<\""
  252. : "\t@$(CXX) $(CXXFLAGS) -o \"$@\" -c \"$<\"")
  253. << newLine << newLine;
  254. }
  255. }
  256. out << "-include $(OBJECTS:%.o=%.d)" << newLine;
  257. }
  258. String getArchFlags (const BuildConfiguration& config) const
  259. {
  260. if (const MakeBuildConfiguration* makeConfig = dynamic_cast<const MakeBuildConfiguration*> (&config))
  261. if (! makeConfig->getArchitectureTypeVar().isVoid())
  262. return makeConfig->getArchitectureTypeVar();
  263. return "-march=native";
  264. }
  265. String getObjectFileFor (const RelativePath& file) const
  266. {
  267. return file.getFileNameWithoutExtension()
  268. + "_" + String::toHexString (file.toUnixStyle().hashCode()) + ".o";
  269. }
  270. void initialiseDependencyPathValues()
  271. {
  272. vst2Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vstFolder),
  273. Ids::vst2Path,
  274. TargetOS::linux)));
  275. vst3Path.referTo (Value (new DependencyPathValueSource (getSetting (Ids::vst3Folder),
  276. Ids::vst3Path,
  277. TargetOS::linux)));
  278. }
  279. JUCE_DECLARE_NON_COPYABLE (MakefileProjectExporter)
  280. };