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.

337 lines
13KB

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