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.

1014 lines
47KB

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