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.

978 lines
46KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  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 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "jucer_ProjectExport_CodeBlocks.h"
  20. #include "jucer_ProjectExport_Make.h"
  21. #include "jucer_ProjectExport_Xcode.h"
  22. class CLionProjectExporter : public ProjectExporter
  23. {
  24. protected:
  25. //==============================================================================
  26. class CLionBuildConfiguration : public BuildConfiguration
  27. {
  28. public:
  29. CLionBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
  30. : BuildConfiguration (p, settings, e)
  31. {
  32. }
  33. void createConfigProperties (PropertyListBuilder&) override {}
  34. var getDefaultOptimisationLevel() const override { return {}; }
  35. String getModuleLibraryArchName() const override { return {}; }
  36. };
  37. BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
  38. {
  39. return new CLionBuildConfiguration (project, tree, *this);
  40. }
  41. public:
  42. //==============================================================================
  43. static const char* getName() { return "CLion (beta)"; }
  44. static const char* getValueTreeTypeName() { return "CLION"; }
  45. static CLionProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  46. {
  47. if (settings.hasType (getValueTreeTypeName()))
  48. return new CLionProjectExporter (project, settings);
  49. return nullptr;
  50. }
  51. static bool isExporterSupported (const ProjectExporter& exporter)
  52. {
  53. return exporter.isMakefile()
  54. || (exporter.isXcode() && ! exporter.isiOS())
  55. || (exporter.isCodeBlocks() && exporter.isWindows());
  56. }
  57. //==============================================================================
  58. CLionProjectExporter (Project& p, const ValueTree& t) : ProjectExporter (p, t)
  59. {
  60. name = getName();
  61. if (getTargetLocationString().isEmpty())
  62. getTargetLocationValue() = getDefaultBuildsRootFolder() + "CLion";
  63. }
  64. //==============================================================================
  65. bool canLaunchProject() override { return false; }
  66. bool launchProject() override { return false; }
  67. bool usesMMFiles() const override { return false; }
  68. bool canCopeWithDuplicateFiles() override { return false; }
  69. bool supportsUserDefinedConfigurations() const override { return false; }
  70. bool isXcode() const override { return false; }
  71. bool isVisualStudio() const override { return false; }
  72. bool isCodeBlocks() const override { return false; }
  73. bool isMakefile() const override { return false; }
  74. bool isAndroidStudio() const override { return false; }
  75. bool isCLion() const override { return true; }
  76. bool isAndroid() const override { return false; }
  77. bool isWindows() const override { return false; }
  78. bool isLinux() const override { return false; }
  79. bool isOSX() const override { return false; }
  80. bool isiOS() const override { return false; }
  81. bool supportsTargetType (ProjectType::Target::Type) const override { return true; }
  82. void addPlatformSpecificSettingsForProjectType (const ProjectType&) override {}
  83. void initialiseDependencyPathValues() override {}
  84. String getDescription() override
  85. {
  86. String description;
  87. description << "The " << getName() << " exporter produces a single CMakeLists.txt file with "
  88. << "multiple platform dependant sections, where the configuration for each section "
  89. << "is inherited from other exporters added to this project." << newLine
  90. << newLine
  91. << "The exporters which provide the CLion configuration for the corresponding platform are:" << newLine
  92. << newLine;
  93. for (auto& exporterName : getExporterNames())
  94. {
  95. ScopedPointer<ProjectExporter> exporter = createNewExporter (getProject(), exporterName);
  96. if (isExporterSupported (*exporter))
  97. description << exporter->getName() << newLine;
  98. }
  99. description << newLine
  100. << "Add these exporters to the project to enable CLion builds." << newLine
  101. << newLine
  102. << "Not all features of all the exporters are currently supported. Notable omissions are AUv3 "
  103. << "plug-ins, embedding resources and fat binaries on MacOS, and adding application icons. On "
  104. << "Windows CLion requires a GCC-based compiler like MinGW.";
  105. return description;
  106. }
  107. void createExporterProperties (PropertyListBuilder& properties) override
  108. {
  109. for (Project::ExporterIterator exporter (getProject()); exporter.next();)
  110. if (isExporterSupported (*exporter))
  111. properties.add (new BooleanPropertyComponent (getExporterEnabledValue (*exporter),
  112. "Import settings from exporter",
  113. exporter->getName()),
  114. "If this is enabled then settings from the corresponding exporter will "
  115. "be used in the generated CMakeLists.txt");
  116. }
  117. void createDefaultConfigs() override {}
  118. void create (const OwnedArray<LibraryModule>&) const override
  119. {
  120. MemoryOutputStream out;
  121. out << "# Automatically generated CMakeLists, created by the Projucer" << newLine
  122. << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine
  123. << newLine;
  124. out << "cmake_minimum_required (VERSION 3.4.1)" << newLine
  125. << newLine;
  126. out << "if (NOT CMAKE_BUILD_TYPE)" << newLine
  127. << " set (CMAKE_BUILD_TYPE \"Debug\" CACHE STRING \"Choose the type of build.\" FORCE)" << newLine
  128. << "endif (NOT CMAKE_BUILD_TYPE)" << newLine
  129. << newLine;
  130. // We'll append to this later.
  131. overwriteFileIfDifferentOrThrow (getTargetFolder().getChildFile ("CMakeLists.txt"), out);
  132. }
  133. void writeCMakeListsExporterSection (ProjectExporter* exporter) const
  134. {
  135. if (! (isExporterSupported (*exporter) && isExporterEnabled (*exporter)))
  136. return;
  137. MemoryBlock existingContent;
  138. getTargetFolder().getChildFile ("CMakeLists.txt").loadFileAsData (existingContent);
  139. MemoryOutputStream out (existingContent, true);
  140. out << "###############################################################################" << newLine
  141. << "# " << exporter->getName() << newLine
  142. << "###############################################################################" << newLine
  143. << newLine;
  144. if (auto* makefileExporter = dynamic_cast<MakefileProjectExporter*> (exporter))
  145. {
  146. out << "if (UNIX AND NOT APPLE)" << newLine << newLine;
  147. writeCMakeListsMakefileSection (out, *makefileExporter);
  148. }
  149. else if (auto* xcodeExporter = dynamic_cast<XcodeProjectExporter*> (exporter))
  150. {
  151. out << "if (APPLE)" << newLine << newLine;
  152. writeCMakeListsXcodeSection (out, *xcodeExporter);
  153. }
  154. else if (auto* codeBlocksExporter = dynamic_cast<CodeBlocksProjectExporter*> (exporter))
  155. {
  156. out << "if (WIN32)" << newLine << newLine;
  157. writeCMakeListsCodeBlocksSection (out, *codeBlocksExporter);
  158. }
  159. out << "endif()" << newLine << newLine;
  160. overwriteFileIfDifferentOrThrow (getTargetFolder().getChildFile ("CMakeLists.txt"), out);
  161. }
  162. private:
  163. //==============================================================================
  164. Identifier getExporterEnabledId (const ProjectExporter& exporter) const
  165. {
  166. jassert (isExporterSupported (exporter));
  167. if (exporter.isMakefile()) return Ids::clionMakefileEnabled;
  168. else if (exporter.isXcode()) return Ids::clionXcodeEnabled;
  169. else if (exporter.isCodeBlocks()) return Ids::clionCodeBlocksEnabled;
  170. jassertfalse;
  171. return {};
  172. }
  173. bool isExporterEnabled (const ProjectExporter& exporter) const
  174. {
  175. auto setting = settings[getExporterEnabledId (exporter)];
  176. return setting.isVoid() || setting;
  177. }
  178. Value getExporterEnabledValue (const ProjectExporter& exporter)
  179. {
  180. auto enabledID = getExporterEnabledId (exporter);
  181. getSetting (enabledID) = isExporterEnabled (exporter);
  182. return getSetting (enabledID);
  183. }
  184. //==============================================================================
  185. static String setCMakeVariable (const String& variableName, const String& value)
  186. {
  187. return String ("set (") + variableName + " \"" + value + "\")";
  188. }
  189. static String addToCMakeVariable (const String& variableName, const String& value)
  190. {
  191. return setCMakeVariable (variableName, "${" + variableName + "} " + value);
  192. }
  193. static String getTargetVarName (ProjectType::Target& target)
  194. {
  195. return String (target.getName()).toUpperCase().replaceCharacter (L' ', L'_');
  196. }
  197. template <class Target, class Exporter>
  198. void getFileInfoList (Target& target, Exporter& exporter, const Project::Item& projectItem, std::vector<std::pair<String, bool>>& fileInfoList) const
  199. {
  200. const auto targetType = (getProject().getProjectType().isAudioPlugin() ? target.type : Target::Type::SharedCodeTarget);
  201. if (projectItem.isGroup())
  202. {
  203. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  204. getFileInfoList (target, exporter, projectItem.getChild(i), fileInfoList);
  205. }
  206. else if (projectItem.shouldBeAddedToTargetProject() && getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType )
  207. {
  208. const auto path = RelativePath (projectItem.getFile(), exporter.getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle();
  209. fileInfoList.push_back ({ path, projectItem.shouldBeCompiled() });
  210. }
  211. }
  212. template <class Exporter>
  213. void writeCMakeTargets (OutputStream& out, Exporter& exporter) const
  214. {
  215. for (auto* target : exporter.targets)
  216. {
  217. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::macOSAppex
  218. || target->type == ProjectType::Target::Type::AggregateTarget
  219. || target->type == ProjectType::Target::Type::AudioUnitv3PlugIn)
  220. continue;
  221. String functionName;
  222. StringArray properties;
  223. switch (target->getTargetFileType())
  224. {
  225. case ProjectType::Target::TargetFileType::executable:
  226. functionName = "add_executable";
  227. break;
  228. case ProjectType::Target::TargetFileType::staticLibrary:
  229. case ProjectType::Target::TargetFileType::sharedLibraryOrDLL:
  230. case ProjectType::Target::TargetFileType::pluginBundle:
  231. functionName = "add_library";
  232. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::staticLibrary)
  233. properties.add ("STATIC");
  234. else if (target->getTargetFileType() == ProjectType::Target::TargetFileType::sharedLibraryOrDLL)
  235. properties.add ("SHARED");
  236. else
  237. properties.add ("MODULE");
  238. break;
  239. default:
  240. continue;
  241. }
  242. out << functionName << " (" << getTargetVarName (*target);
  243. if (! properties.isEmpty())
  244. out << " " << properties.joinIntoString (" ");
  245. out << newLine;
  246. std::vector<std::pair<String, bool>> fileInfoList;
  247. for (auto& group : exporter.getAllGroups())
  248. getFileInfoList (*target, exporter, group, fileInfoList);
  249. for (auto& fileInfo : fileInfoList)
  250. out << " " << fileInfo.first.quoted() << newLine;
  251. out << ")" << newLine << newLine;
  252. for (auto& fileInfo : fileInfoList)
  253. if (! fileInfo.second)
  254. out << "set_source_files_properties (" << fileInfo.first.quoted() << " PROPERTIES HEADER_FILE_ONLY TRUE)" << newLine;
  255. out << newLine;
  256. }
  257. }
  258. //==============================================================================
  259. void writeCMakeListsMakefileSection (OutputStream& out, MakefileProjectExporter& exporter) const
  260. {
  261. out << "project (" << getProject().getTitle() << " C CXX)" << newLine
  262. << newLine;
  263. out << "find_package (PkgConfig REQUIRED)" << newLine;
  264. StringArray cmakePkgconfigPackages;
  265. for (auto& package : exporter.getPackages())
  266. {
  267. cmakePkgconfigPackages.add (package.toUpperCase());
  268. out << "pkg_search_module (" << cmakePkgconfigPackages.strings.getLast() << " REQUIRED " << package << ")" << newLine;
  269. }
  270. out << newLine;
  271. writeCMakeTargets (out, exporter);
  272. for (auto* target : exporter.targets)
  273. {
  274. if (target->type == ProjectType::Target::Type::AggregateTarget)
  275. continue;
  276. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle)
  277. out << "set_target_properties (" << getTargetVarName (*target) << " PROPERTIES PREFIX \"\")" << newLine;
  278. out << "set_target_properties (" << getTargetVarName (*target) << " PROPERTIES SUFFIX \"" << target->getTargetFileSuffix() << "\")" << newLine
  279. << newLine;
  280. }
  281. for (ProjectExporter::ConstConfigIterator c (exporter); c.next();)
  282. {
  283. auto& config = dynamic_cast<const MakefileProjectExporter::MakeBuildConfiguration&> (*c);
  284. out << "#------------------------------------------------------------------------------" << newLine
  285. << "# Config: " << config.getName() << newLine
  286. << "#------------------------------------------------------------------------------" << newLine
  287. << newLine;
  288. const auto buildTypeCondition = String ("CMAKE_BUILD_TYPE STREQUAL " + config.getName());
  289. out << "if (" << buildTypeCondition << ")" << newLine
  290. << newLine;
  291. out << "include_directories (" << newLine;
  292. for (auto& path : exporter.getHeaderSearchPaths (config))
  293. out << " " << path.quoted() << newLine;
  294. for (auto& package : cmakePkgconfigPackages)
  295. out << " ${" << package << "_INCLUDE_DIRS}" << newLine;
  296. out << ")" << newLine << newLine;
  297. StringArray cmakeFoundLibraries;
  298. for (auto& library : exporter.getLibraryNames (config))
  299. {
  300. const String cmakeLibraryID (library.toUpperCase());
  301. cmakeFoundLibraries.add (String ("${") + cmakeLibraryID + "}");
  302. out << "find_library (" << cmakeLibraryID << " " << library << newLine;
  303. for (auto& path : exporter.getLibrarySearchPaths (config))
  304. out << " " << path.quoted() << newLine;
  305. out << ")" << newLine
  306. << newLine;
  307. }
  308. for (auto* target : exporter.targets)
  309. {
  310. if (target->type == ProjectType::Target::Type::AggregateTarget)
  311. continue;
  312. const auto targetVarName = getTargetVarName (*target);
  313. out << "set_target_properties (" << targetVarName << " PROPERTIES" << newLine
  314. << " OUTPUT_NAME " << config.getTargetBinaryNameString().quoted() << newLine
  315. << ")" << newLine << newLine;
  316. auto defines = exporter.getDefines (config);
  317. defines.addArray (target->getDefines (config));
  318. out << "target_compile_definitions (" << targetVarName << " PRIVATE" << newLine;
  319. for (auto& key : defines.getAllKeys())
  320. out << " " << key << "=" << defines[key] << newLine;
  321. out << ")" << newLine << newLine;
  322. auto targetFlags = target->getCompilerFlags();
  323. if (! targetFlags.isEmpty())
  324. {
  325. out << "target_compile_options (" << targetVarName << " PRIVATE" << newLine;
  326. for (auto& flag : targetFlags)
  327. out << " " << flag << newLine;
  328. out << ")" << newLine << newLine;
  329. }
  330. out << "target_link_libraries (" << targetVarName << " PRIVATE" << newLine;
  331. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  332. || target->type == ProjectType::Target::Type::StandalonePlugIn)
  333. out << " SHARED_CODE" << newLine;
  334. out << " " << exporter.getArchFlags (config) << newLine;
  335. for (auto& flag : target->getLinkerFlags())
  336. out << " " << flag << newLine;
  337. for (auto& flag : exporter.getLinkerFlags (config))
  338. out << " " << flag << newLine;
  339. for (auto& lib : cmakeFoundLibraries)
  340. out << " " << lib << newLine;
  341. for (auto& package : cmakePkgconfigPackages)
  342. out << " ${" << package << "_LIBRARIES}" << newLine;
  343. out << ")" << newLine << newLine;
  344. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  345. || target->type == ProjectType::Target::Type::StandalonePlugIn)
  346. out << "add_dependencies (" << targetVarName << " " << "SHARED_CODE)" << newLine << newLine;
  347. }
  348. StringArray cFlags;
  349. cFlags.add (exporter.getArchFlags (config));
  350. cFlags.addArray (exporter.getCPreprocessorFlags (config));
  351. cFlags.addArray (exporter.getCFlags (config));
  352. out << addToCMakeVariable ("CMAKE_C_FLAGS", cFlags.joinIntoString (" ")) << newLine
  353. << addToCMakeVariable ("CMAKE_CXX_FLAGS", "${CMAKE_C_FLAGS} " + exporter.getCXXFlags().joinIntoString (" ")) << newLine
  354. << newLine;
  355. out << "endif (" << buildTypeCondition << ")" << newLine
  356. << newLine;
  357. }
  358. }
  359. //==============================================================================
  360. void writeCMakeListsCodeBlocksSection (OutputStream& out, CodeBlocksProjectExporter& exporter) const
  361. {
  362. out << "project (" << getProject().getTitle() << " C CXX)" << newLine
  363. << newLine;
  364. writeCMakeTargets (out, exporter);
  365. for (auto* target : exporter.targets)
  366. {
  367. if (target->type == ProjectType::Target::Type::AggregateTarget)
  368. continue;
  369. out << "set_target_properties (" << getTargetVarName (*target) << " PROPERTIES PREFIX \"\")" << newLine
  370. << "set_target_properties (" << getTargetVarName (*target) << " PROPERTIES SUFFIX " << target->getTargetSuffix().quoted() << ")" << newLine
  371. << newLine;
  372. }
  373. for (ProjectExporter::ConstConfigIterator c (exporter); c.next();)
  374. {
  375. auto& config = dynamic_cast<const CodeBlocksProjectExporter::CodeBlocksBuildConfiguration&> (*c);
  376. out << "#------------------------------------------------------------------------------" << newLine
  377. << "# Config: " << config.getName() << newLine
  378. << "#------------------------------------------------------------------------------" << newLine
  379. << newLine;
  380. const auto buildTypeCondition = String ("CMAKE_BUILD_TYPE STREQUAL " + config.getName());
  381. out << "if (" << buildTypeCondition << ")" << newLine
  382. << newLine;
  383. out << "include_directories (" << newLine;
  384. for (auto& path : exporter.getIncludePaths (config))
  385. out << " " << path.quoted() << newLine;
  386. out << ")" << newLine << newLine;
  387. for (auto* target : exporter.targets)
  388. {
  389. if (target->type == ProjectType::Target::Type::AggregateTarget)
  390. continue;
  391. const auto targetVarName = getTargetVarName (*target);
  392. out << "set_target_properties (" << targetVarName << " PROPERTIES" << newLine
  393. << " OUTPUT_NAME " << config.getTargetBinaryNameString().quoted() << newLine
  394. << ")" << newLine << newLine;
  395. out << "target_compile_definitions (" << targetVarName << " PRIVATE" << newLine;
  396. for (auto& def : exporter.getDefines (config, *target))
  397. out << " " << def << newLine;
  398. out << ")" << newLine << newLine;
  399. out << "target_compile_options (" << targetVarName << " PRIVATE" << newLine;
  400. for (auto& option : exporter.getCompilerFlags (config, *target))
  401. out << " " << option.quoted() << newLine;
  402. out << ")" << newLine << newLine;
  403. out << "target_link_libraries (" << targetVarName << " PRIVATE" << newLine;
  404. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  405. || target->type == ProjectType::Target::Type::StandalonePlugIn)
  406. out << " SHARED_CODE" << newLine
  407. << " -L." << newLine;
  408. for (auto& flag : exporter.getLinkerFlags (config, *target))
  409. out << " " << flag << newLine;
  410. for (auto& flag : exporter.getProjectLinkerLibs())
  411. out << " -l" << flag << newLine;
  412. for (auto& lib : exporter.mingwLibs)
  413. out << " -l" << lib << newLine;
  414. out << ")" << newLine << newLine;
  415. }
  416. out << addToCMakeVariable ("CMAKE_CXX_FLAGS", exporter.getProjectCompilerOptions().joinIntoString (" ")) << newLine
  417. << addToCMakeVariable ("CMAKE_C_FLAGS", "${CMAKE_CXX_FLAGS}") << newLine
  418. << newLine;
  419. out << "endif (" << buildTypeCondition << ")" << newLine
  420. << newLine;
  421. }
  422. }
  423. //==============================================================================
  424. void writeCMakeListsXcodeSection (OutputStream& out, XcodeProjectExporter& exporter) const
  425. {
  426. // We need to dind out the SDK root before defining the project. Unfortunately this is
  427. // set per-target in the Xcode project, but we want it per-configuration.
  428. for (ProjectExporter::ConstConfigIterator c (exporter); c.next();)
  429. {
  430. auto& config = dynamic_cast<const XcodeProjectExporter::XcodeBuildConfiguration&> (*c);
  431. for (auto* target : exporter.targets)
  432. {
  433. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::macOSAppex
  434. || target->type == ProjectType::Target::Type::AggregateTarget
  435. || target->type == ProjectType::Target::Type::AudioUnitv3PlugIn)
  436. continue;
  437. const auto targetAttributes = target->getTargetSettings (config);
  438. const auto targetAttributeKeys = targetAttributes.getAllKeys();
  439. if (targetAttributes.getAllKeys().contains ("SDKROOT"))
  440. {
  441. out << "if (CMAKE_BUILD_TYPE STREQUAL " + config.getName() << ")" << newLine
  442. << " set (CMAKE_OSX_SYSROOT " << targetAttributes["SDKROOT"] << ")" << newLine
  443. << "endif()" << newLine << newLine;
  444. break;
  445. }
  446. }
  447. }
  448. out << "project (" << getProject().getTitle() << " C CXX)" << newLine << newLine;
  449. writeCMakeTargets (out, exporter);
  450. for (auto* target : exporter.targets)
  451. {
  452. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::macOSAppex
  453. || target->type == ProjectType::Target::Type::AggregateTarget
  454. || target->type == ProjectType::Target::Type::AudioUnitv3PlugIn)
  455. continue;
  456. if (target->type == ProjectType::Target::Type::AudioUnitPlugIn)
  457. out << "find_program (RC_COMPILER Rez NO_DEFAULT_PATHS PATHS /Applications/Xcode.app/Contents/Developer/usr/bin)" << newLine
  458. << "if (NOT RC_COMPILER)" << newLine
  459. << " message (WARNING \"failed to find Rez; older resource-based AU plug-ins may not work correctly\")" << newLine
  460. << "endif (NOT RC_COMPILER)" << newLine << newLine;
  461. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::staticLibrary
  462. || target->getTargetFileType() == ProjectType::Target::TargetFileType::sharedLibraryOrDLL)
  463. out << "set_target_properties (" << getTargetVarName (*target) << " PROPERTIES SUFFIX \"" << target->xcodeBundleExtension << "\")" << newLine
  464. << newLine;
  465. }
  466. for (ProjectExporter::ConstConfigIterator c (exporter); c.next();)
  467. {
  468. auto& config = dynamic_cast<const XcodeProjectExporter::XcodeBuildConfiguration&> (*c);
  469. out << "#------------------------------------------------------------------------------" << newLine
  470. << "# Config: " << config.getName() << newLine
  471. << "#------------------------------------------------------------------------------" << newLine
  472. << newLine;
  473. const auto buildTypeCondition = String ("CMAKE_BUILD_TYPE STREQUAL " + config.getName());
  474. out << "if (" << buildTypeCondition << ")" << newLine
  475. << newLine;
  476. const auto configSettings = exporter.getProjectSettings (config);
  477. auto configSettingsKeys = configSettings.getAllKeys();
  478. auto binaryName = config.getTargetBinaryNameString();
  479. if (configSettingsKeys.contains ("PRODUCT_NAME"))
  480. binaryName = configSettings["PRODUCT_NAME"].unquoted();
  481. for (auto* target : exporter.targets)
  482. {
  483. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::macOSAppex
  484. || target->type == ProjectType::Target::Type::AggregateTarget
  485. || target->type == ProjectType::Target::Type::AudioUnitv3PlugIn)
  486. continue;
  487. const auto targetVarName = getTargetVarName (*target);
  488. auto targetAttributes = target->getTargetSettings (config);
  489. auto targetAttributeKeys = targetAttributes.getAllKeys();
  490. StringArray libSearchPaths;
  491. if (targetAttributeKeys.contains ("LIBRARY_SEARCH_PATHS"))
  492. {
  493. auto paths = targetAttributes["LIBRARY_SEARCH_PATHS"].trim().substring (1).dropLastCharacters (1);
  494. paths = paths.replace ("\"$(inherited)\"", {});
  495. paths = paths.replace ("$(HOME)", "$ENV{HOME}");
  496. libSearchPaths.addTokens (paths, ",\"\t\\", {});
  497. libSearchPaths.removeEmptyStrings();
  498. targetAttributeKeys.removeString ("LIBRARY_SEARCH_PATHS");
  499. }
  500. StringArray headerSearchPaths;
  501. if (targetAttributeKeys.contains ("HEADER_SEARCH_PATHS"))
  502. {
  503. auto paths = targetAttributes["HEADER_SEARCH_PATHS"].trim().substring (1).dropLastCharacters (1);
  504. paths = paths.replace ("\"$(inherited)\"", {});
  505. paths = paths.replace ("$(HOME)", "$ENV{HOME}");
  506. headerSearchPaths.addTokens (paths, ",\"\t\\", {});
  507. headerSearchPaths.removeEmptyStrings();
  508. targetAttributeKeys.removeString ("HEADER_SEARCH_PATHS");
  509. }
  510. out << "target_include_directories (" << targetVarName << " PRIVATE" << newLine;
  511. for (auto& path : headerSearchPaths)
  512. out << " " << path.quoted() << newLine;
  513. out << ")" << newLine << newLine;
  514. StringArray defines;
  515. if (targetAttributeKeys.contains ("GCC_PREPROCESSOR_DEFINITIONS"))
  516. {
  517. defines.addTokens (targetAttributes["GCC_PREPROCESSOR_DEFINITIONS"], "() ,\t", {});
  518. defines.removeEmptyStrings();
  519. targetAttributeKeys.removeString ("GCC_PREPROCESSOR_DEFINITIONS");
  520. }
  521. out << "target_compile_definitions (" << targetVarName << " PRIVATE" << newLine;
  522. for (auto& def : defines)
  523. out << " " << def << newLine;
  524. out << ")" << newLine << newLine;
  525. StringArray cppFlags;
  526. // Fat binaries are not supported.
  527. if (targetAttributeKeys.contains ("ARCHS"))
  528. {
  529. auto value = targetAttributes["ARCHS"].unquoted();
  530. if (value.contains ("NATIVE_ARCH_ACTUAL"))
  531. cppFlags.add ("-march=native");
  532. else if (value.contains ("ARCHS_STANDARD_32_BIT"))
  533. cppFlags.add ("-arch x86");
  534. else if (value.contains ("ARCHS_STANDARD_32_64_BIT")
  535. || value.contains ("ARCHS_STANDARD_64_BIT"))
  536. cppFlags.add ("-arch x86_64");
  537. targetAttributeKeys.removeString ("ARCHS");
  538. }
  539. if (targetAttributeKeys.contains ("MACOSX_DEPLOYMENT_TARGET"))
  540. {
  541. cppFlags.add ("-mmacosx-version-min=" + targetAttributes["MACOSX_DEPLOYMENT_TARGET"]);
  542. targetAttributeKeys.removeString ("MACOSX_DEPLOYMENT_TARGET");
  543. }
  544. if (targetAttributeKeys.contains ("OTHER_CPLUSPLUSFLAGS"))
  545. {
  546. cppFlags.add (targetAttributes["OTHER_CPLUSPLUSFLAGS"].unquoted());
  547. targetAttributeKeys.removeString ("OTHER_CPLUSPLUSFLAGS");
  548. }
  549. if (targetAttributeKeys.contains ("GCC_OPTIMIZATION_LEVEL"))
  550. {
  551. cppFlags.add ("-O" + targetAttributes["GCC_OPTIMIZATION_LEVEL"]);
  552. targetAttributeKeys.removeString ("GCC_OPTIMIZATION_LEVEL");
  553. }
  554. if (targetAttributeKeys.contains ("LLVM_LTO"))
  555. {
  556. cppFlags.add ("-flto");
  557. targetAttributeKeys.removeString ("LLVM_LTO");
  558. }
  559. if (targetAttributeKeys.contains ("GCC_FAST_MATH"))
  560. {
  561. cppFlags.add ("-ffast-math");
  562. targetAttributeKeys.removeString ("GCC_FAST_MATH");
  563. }
  564. if (targetAttributeKeys.contains ("CLANG_CXX_LANGUAGE_STANDARD"))
  565. {
  566. cppFlags.add ("-std=" + targetAttributes["CLANG_CXX_LANGUAGE_STANDARD"].unquoted());
  567. targetAttributeKeys.removeString ("CLANG_CXX_LANGUAGE_STANDARD");
  568. }
  569. if (targetAttributeKeys.contains ("CLANG_CXX_LIBRARY"))
  570. {
  571. cppFlags.add ("-stdlib=" + targetAttributes["CLANG_CXX_LIBRARY"].unquoted());
  572. targetAttributeKeys.removeString ("CLANG_CXX_LIBRARY");
  573. }
  574. out << "target_compile_options (" << targetVarName << " PRIVATE" << newLine;
  575. for (auto& flag : cppFlags)
  576. out << " " << flag << newLine;
  577. out << ")" << newLine << newLine;
  578. StringArray linkerFlags;
  579. if (targetAttributeKeys.contains ("OTHER_LDFLAGS"))
  580. {
  581. // CMake adds its own SHARED_CODE library linking flags
  582. auto flagsWithReplacedSpaces = targetAttributes["OTHER_LDFLAGS"].unquoted().replace ("\\\\ ", "^^%%^^");
  583. linkerFlags.addTokens (flagsWithReplacedSpaces, true);
  584. linkerFlags.removeString ("-bundle");
  585. linkerFlags.removeString ("-l" + binaryName.replace (" ", "^^%%^^"));
  586. for (auto& flag : linkerFlags)
  587. flag = flag.replace ("^^%%^^", " ");
  588. targetAttributeKeys.removeString ("OTHER_LDFLAGS");
  589. }
  590. if (target->type == ProjectType::Target::Type::AudioUnitPlugIn)
  591. {
  592. String rezFlags;
  593. if (targetAttributeKeys.contains ("OTHER_REZFLAGS"))
  594. {
  595. rezFlags = targetAttributes["OTHER_REZFLAGS"];
  596. targetAttributeKeys.removeString ("OTHER_REZFLAGS");
  597. }
  598. for (auto& item : exporter.getAllGroups())
  599. {
  600. if (item.getName() == "Juce Library Code")
  601. {
  602. String resSourcesVar (targetVarName + "_REZ_SOURCES");
  603. String resOutputVar (targetVarName + "_REZ_OUTPUT");
  604. out << "if (RC_COMPILER)" << newLine
  605. << " set (" << resSourcesVar << newLine
  606. << " \"" << item.determineGroupFolder().getFullPathName() + "/include_juce_audio_plugin_client_AU.r\"" << newLine
  607. << " )" << newLine
  608. << " set (" << resOutputVar << " \"${CMAKE_CURRENT_BINARY_DIR}/" << binaryName << ".rsrc\")" << newLine
  609. << " target_sources (" << targetVarName << " PRIVATE" << newLine
  610. << " ${" << resSourcesVar << "}" << newLine
  611. << " ${" << resOutputVar << "}" << newLine
  612. << " )" << newLine
  613. << " execute_process (COMMAND" << newLine
  614. << " ${RC_COMPILER}" << newLine
  615. << " " << rezFlags.unquoted().removeCharacters ("\\") << newLine;
  616. for (auto& path : headerSearchPaths)
  617. out << " -I \"${PROJECT_SOURCE_DIR}/" << path.unquoted() << "\"" << newLine;
  618. out << " ${" << resSourcesVar << "}" << newLine
  619. << " -o ${" << resOutputVar << "}" << newLine
  620. << " )" << newLine
  621. << " set_source_files_properties (${" << resOutputVar << "} PROPERTIES" << newLine
  622. << " GENERATED TRUE" << newLine
  623. << " MACOSX_PACKAGE_LOCATION Resources" << newLine
  624. << " )" << newLine
  625. << "endif (RC_COMPILER)" << newLine << newLine;
  626. break;
  627. }
  628. }
  629. }
  630. if (targetAttributeKeys.contains ("INFOPLIST_FILE"))
  631. {
  632. auto plistFile = exporter.getTargetFolder().getChildFile (targetAttributes["INFOPLIST_FILE"]);
  633. XmlDocument infoPlistData (plistFile);
  634. ScopedPointer<XmlElement> plist = infoPlistData.getDocumentElement();
  635. if (plist != nullptr)
  636. {
  637. if (auto* dict = plist->getChildByName ("dict"))
  638. {
  639. if (auto* entry = dict->getChildByName ("key"))
  640. {
  641. while (entry != nullptr)
  642. {
  643. if (entry->getAllSubText() == "CFBundleExecutable")
  644. {
  645. if (auto* bundleName = entry->getNextElementWithTagName ("string"))
  646. {
  647. bundleName->deleteAllTextElements();
  648. bundleName->addTextElement (binaryName);
  649. }
  650. }
  651. entry = entry->getNextElementWithTagName ("key");
  652. }
  653. }
  654. }
  655. File updatedPlist (getTargetFolder().getChildFile (config.getName() + "-" + plistFile.getFileName()));
  656. plist->writeToFile (updatedPlist, "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
  657. targetAttributes.set ("INFOPLIST_FILE", updatedPlist.getFullPathName().quoted());
  658. }
  659. else
  660. {
  661. targetAttributeKeys.removeString ("INFOPLIST_FILE");
  662. }
  663. }
  664. targetAttributeKeys.sort (false);
  665. out << "set_target_properties (" << targetVarName << " PROPERTIES" << newLine
  666. << " OUTPUT_NAME " << binaryName.quoted() << newLine;
  667. for (auto& key : targetAttributeKeys)
  668. out << " XCODE_ATTRIBUTE_" << key << " " << targetAttributes[key] << newLine;
  669. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::executable
  670. || target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle)
  671. {
  672. out << " MACOSX_BUNDLE_INFO_PLIST " << targetAttributes.getValue ("INFOPLIST_FILE", "\"\"") << newLine
  673. << " XCODE_ATTRIBUTE_PRODUCT_NAME " << binaryName.quoted() << newLine;
  674. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::executable)
  675. {
  676. out << " MACOSX_BUNDLE TRUE" << newLine;
  677. }
  678. else
  679. {
  680. out << " BUNDLE TRUE" << newLine
  681. << " BUNDLE_EXTENSION " << targetAttributes.getValue ("WRAPPER_EXTENSION", "\"\"") << newLine
  682. << " XCODE_ATTRIBUTE_MACH_O_TYPE \"mh_bundle\"" << newLine;
  683. }
  684. }
  685. out << ")" << newLine << newLine;
  686. out << "target_link_libraries (" << targetVarName << " PRIVATE" << newLine;
  687. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  688. || target->type == ProjectType::Target::Type::StandalonePlugIn)
  689. out << " SHARED_CODE" << newLine;
  690. for (auto& path : libSearchPaths)
  691. out << " \"-L" << path << "\"" << newLine;
  692. for (auto& flag : linkerFlags)
  693. out << " " << flag.quoted() << newLine;
  694. for (auto& framework : target->frameworkNames)
  695. out << " \"-framework " << framework << "\"" << newLine;
  696. out << ")" << newLine << newLine;
  697. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  698. || target->type == ProjectType::Target::Type::StandalonePlugIn)
  699. {
  700. if (target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle
  701. && targetAttributeKeys.contains("INSTALL_PATH"))
  702. {
  703. const auto installPath = targetAttributes["INSTALL_PATH"].unquoted();
  704. const auto productFilename = binaryName + (targetAttributeKeys.contains ("WRAPPER_EXTENSION") ? "." + targetAttributes["WRAPPER_EXTENSION"] : String());
  705. auto productPath = (installPath + productFilename).quoted();
  706. out << "add_custom_command (TARGET " << targetVarName << " POST_BUILD" << newLine
  707. << " COMMAND cmake -E remove_directory " << productPath << newLine
  708. << " COMMAND cmake -E copy_directory \"${CMAKE_BINARY_DIR}/" << productFilename << "\" " << productPath << newLine
  709. << " COMMENT \"Copying \\\"" << productFilename << "\\\" to \\\"" << installPath.unquoted() << "\\\"\"" << newLine
  710. << ")" << newLine << newLine;
  711. }
  712. }
  713. }
  714. std::map<String, String> basicWarnings
  715. {
  716. { "CLANG_WARN_BOOL_CONVERSION", "bool-conversion" },
  717. { "CLANG_WARN_COMMA", "comma" },
  718. { "CLANG_WARN_CONSTANT_CONVERSION", "constant-conversion" },
  719. { "CLANG_WARN_EMPTY_BODY", "empty-body" },
  720. { "CLANG_WARN_ENUM_CONVERSION", "enum-conversion" },
  721. { "CLANG_WARN_INFINITE_RECURSION", "infinite-recursion" },
  722. { "CLANG_WARN_INT_CONVERSION", "int-conversion" },
  723. { "CLANG_WARN_RANGE_LOOP_ANALYSIS", "range-loop-analysis" },
  724. { "CLANG_WARN_STRICT_PROTOTYPES", "strict-prototypes" },
  725. { "GCC_WARN_CHECK_SWITCH_STATEMENTS", "switch" },
  726. { "GCC_WARN_UNUSED_VARIABLE", "unused-variable" },
  727. { "GCC_WARN_MISSING_PARENTHESES", "parentheses" },
  728. { "GCC_WARN_NON_VIRTUAL_DESTRUCTOR", "non-virtual-dtor" },
  729. { "GCC_WARN_64_TO_32_BIT_CONVERSION", "shorten-64-to-32" },
  730. { "GCC_WARN_UNDECLARED_SELECTOR", "undeclared-selector" },
  731. { "GCC_WARN_UNUSED_FUNCTION", "unused-function" }
  732. };
  733. StringArray compilerFlags;
  734. for (auto& key : configSettingsKeys)
  735. {
  736. auto basicWarning = basicWarnings.find (key);
  737. if (basicWarning != basicWarnings.end())
  738. {
  739. compilerFlags.add (configSettings[key] == "YES" ? "-W" + basicWarning->second : "-Wno-" + basicWarning->second);
  740. }
  741. else if (key == "CLANG_WARN_SUSPICIOUS_MOVE" && configSettings[key] == "YES") compilerFlags.add ("-Wmove");
  742. else if (key == "CLANG_WARN_UNREACHABLE_CODE" && configSettings[key] == "YES") compilerFlags.add ("-Wunreachable-code");
  743. else if (key == "CLANG_WARN__DUPLICATE_METHOD_MATCH" && configSettings[key] == "YES") compilerFlags.add ("-Wduplicate-method-match");
  744. else if (key == "GCC_INLINES_ARE_PRIVATE_EXTERN" && configSettings[key] == "YES") compilerFlags.add ("-fvisibility-inlines-hidden");
  745. else if (key == "GCC_NO_COMMON_BLOCKS" && configSettings[key] == "YES") compilerFlags.add ("-fno-common");
  746. else if (key == "GCC_WARN_ABOUT_RETURN_TYPE" && configSettings[key] != "YES") compilerFlags.add (configSettings[key] == "YES_ERROR" ? "-Werror=return-type" : "-Wno-return-type");
  747. else if (key == "GCC_WARN_TYPECHECK_CALLS_TO_PRINTF" && configSettings[key] != "YES") compilerFlags.add ("-Wno-format");
  748. else if (key == "GCC_WARN_UNINITIALIZED_AUTOS")
  749. {
  750. if (configSettings[key] == "YES") compilerFlags.add ("-Wuninitialized");
  751. else if (configSettings[key] == "YES_AGGRESSIVE") compilerFlags.add ("--Wconditional-uninitialized");
  752. else compilerFlags.add (")-Wno-uninitialized");
  753. }
  754. else if (key == "WARNING_CFLAGS") compilerFlags.add (configSettings[key]);
  755. }
  756. out << addToCMakeVariable ("CMAKE_CXX_FLAGS", compilerFlags.joinIntoString (" ")) << newLine
  757. << addToCMakeVariable ("CMAKE_C_FLAGS", "${CMAKE_CXX_FLAGS}") << newLine
  758. << newLine;
  759. out << "endif (" << buildTypeCondition << ")" << newLine
  760. << newLine;
  761. }
  762. }
  763. //==============================================================================
  764. JUCE_DECLARE_NON_COPYABLE (CLionProjectExporter)
  765. };