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.

874 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class AndroidStudioProjectExporter : public AndroidProjectExporterBase
  18. {
  19. public:
  20. //==============================================================================
  21. bool usesMMFiles() const override { return false; }
  22. bool canCopeWithDuplicateFiles() override { return false; }
  23. bool supportsUserDefinedConfigurations() const override { return false; }
  24. bool isAndroidStudio() const override { return true; }
  25. bool isAndroidAnt() const override { return false; }
  26. static const char* getName() { return "Android Studio"; }
  27. static const char* getValueTreeTypeName() { return "ANDROIDSTUDIO"; }
  28. static AndroidStudioProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  29. {
  30. if (settings.hasType (getValueTreeTypeName()))
  31. return new AndroidStudioProjectExporter (project, settings);
  32. return nullptr;
  33. }
  34. //==============================================================================
  35. Value getGradleVersionValue() { return getSetting (Ids::gradleVersion); }
  36. String getGradleVersionString() const { return settings [Ids::gradleVersion]; }
  37. Value getGradleWrapperVersionValue() { return getSetting (Ids::gradleWrapperVersion); }
  38. String getGradleWrapperVersionString() const { return settings [Ids::gradleWrapperVersion]; }
  39. Value getGradleToolchainValue() { return getSetting (Ids::gradleToolchain); }
  40. String getGradleToolchainString() const { return settings [Ids::gradleToolchain]; }
  41. Value getGradleToolchainVersionValue() { return getSetting (Ids::gradleToolchainVersion); }
  42. String getGradleToolchainVersionString() const { return settings [Ids::gradleToolchainVersion]; }
  43. //==============================================================================
  44. AndroidStudioProjectExporter (Project& p, const ValueTree& t)
  45. : AndroidProjectExporterBase (p, t),
  46. androidStudioExecutable (findAndroidStudioExecutable())
  47. {
  48. name = getName();
  49. setEmptyPropertiesToDefaultValues();
  50. }
  51. //==============================================================================
  52. void setEmptyPropertiesToDefaultValues()
  53. {
  54. if (getTargetLocationString().isEmpty())
  55. getTargetLocationValue() = getDefaultBuildsRootFolder() + "AndroidStudio";
  56. if (getGradleVersionString().isEmpty())
  57. getGradleVersionValue() = "2.10";
  58. if (getGradleWrapperVersionString().isEmpty())
  59. getGradleWrapperVersionValue() = "0.7.0-alpha4";
  60. if (getGradleToolchainString().isEmpty())
  61. getGradleToolchainValue() = "clang";
  62. if (getGradleToolchainVersionString().isEmpty())
  63. getGradleToolchainVersionValue() = getGradleToolchainValue() == "clang" ? "3.6" : "4.9";
  64. if (getBuildToolsVersionString().isEmpty())
  65. getBuildToolsVersionValue() = "23.0.1";
  66. }
  67. //==============================================================================
  68. void createToolchainExporterProperties (PropertyListBuilder& props) override
  69. {
  70. props.add (new TextPropertyComponent (getGradleVersionValue(), "gradle version", 32, false),
  71. "The version of gradle that Android Studio should use to build this app");
  72. props.add (new TextPropertyComponent (getGradleWrapperVersionValue(), "gradle-experimental wrapper version", 32, false),
  73. "The version of the gradle-experimental wrapper that Android Studio should use to build this app");
  74. static const char* toolchains[] = { "clang", "gcc", nullptr };
  75. props.add (new ChoicePropertyComponent (getGradleToolchainValue(), "NDK Toolchain", StringArray (toolchains), Array<var> (toolchains)),
  76. "The toolchain that gradle should invoke for NDK compilation (variable model.android.ndk.tooclhain in app/build.gradle)");
  77. props.add (new TextPropertyComponent (getGradleToolchainVersionValue(), "NDK Toolchain version", 32, false),
  78. "The version number of the toolchainthat gradle should invoke for NDK compilation (variable model.android.ndk.tooclhainVersion in app/build.gradle)");
  79. }
  80. void createLibraryModuleExporterProperties (PropertyListBuilder&) override
  81. {
  82. // gradle cannot do native library modules as far as we know...
  83. }
  84. //==============================================================================
  85. bool canLaunchProject() override
  86. {
  87. return androidStudioExecutable.exists();
  88. }
  89. bool launchProject() override
  90. {
  91. if (! androidStudioExecutable.exists())
  92. {
  93. jassertfalse;
  94. return false;
  95. }
  96. const File targetFolder (getTargetFolder());
  97. // we have to surround the path with extra quotes, otherwise Android Studio
  98. // will choke if there are any space characters in the path.
  99. return androidStudioExecutable.startAsProcess ("\"" + targetFolder.getFullPathName() + "\"");
  100. }
  101. Value getNDKPlatformVersionValue() { return getSetting (Ids::androidNdkPlatformVersion); }
  102. String getNDKPlatformVersionString() const { return settings [Ids::androidNdkPlatformVersion]; }
  103. Value getBuildToolsVersionValue() { return getSetting (Ids::buildToolsVersion); }
  104. String getBuildToolsVersionString() const { return settings [Ids::buildToolsVersion]; }
  105. //==============================================================================
  106. void create (const OwnedArray<LibraryModule>& modules) const override
  107. {
  108. const File targetFolder (getTargetFolder());
  109. removeOldFiles (targetFolder);
  110. {
  111. const String package (getActivityClassPackage());
  112. const String path (package.replaceCharacter ('.', File::separator));
  113. const File javaTarget (targetFolder.getChildFile ("app/src/main/java").getChildFile (path));
  114. copyActivityJavaFiles (modules, javaTarget, package);
  115. }
  116. writeFile (targetFolder, "settings.gradle", getSettingsGradleFileContent());
  117. writeFile (targetFolder, "build.gradle", getProjectBuildGradleFileContent());
  118. writeFile (targetFolder, "app/build.gradle", getAppBuildGradleFileContent());
  119. writeFile (targetFolder, "local.properties", getLocalPropertiesFileContent());
  120. writeFile (targetFolder, "gradle/wrapper/gradle-wrapper.properties", getGradleWrapperPropertiesFileContent());
  121. writeAndroidManifest (targetFolder);
  122. writeStringsXML (targetFolder);
  123. writeAppIcons (targetFolder);
  124. createSourceSymlinks (targetFolder);
  125. }
  126. void removeOldFiles (const File& targetFolder) const
  127. {
  128. targetFolder.getChildFile ("app/src").deleteRecursively();
  129. targetFolder.getChildFile ("app/build").deleteRecursively();
  130. targetFolder.getChildFile ("app/build.gradle").deleteFile();
  131. targetFolder.getChildFile ("gradle").deleteRecursively();
  132. targetFolder.getChildFile ("local.properties").deleteFile();
  133. targetFolder.getChildFile ("settings.gradle").deleteFile();
  134. }
  135. void writeFile (const File& gradleProjectFolder, const String& filePath, const String& fileContent) const
  136. {
  137. MemoryOutputStream outStream;
  138. outStream << fileContent;
  139. overwriteFileIfDifferentOrThrow (gradleProjectFolder.getChildFile (filePath), outStream);
  140. }
  141. //==============================================================================
  142. static File findAndroidStudioExecutable()
  143. {
  144. #if JUCE_WINDOWS
  145. const File defaultInstallation ("C:\\Program Files\\Android\\Android Studio\\bin");
  146. if (defaultInstallation.exists())
  147. {
  148. {
  149. const File studio64 = defaultInstallation.getChildFile ("studio64.exe");
  150. if (studio64.existsAsFile())
  151. return studio64;
  152. }
  153. {
  154. const File studio = defaultInstallation.getChildFile ("studio.exe");
  155. if (studio.existsAsFile())
  156. return studio;
  157. }
  158. }
  159. #elif JUCE_MAC
  160. const File defaultInstallation ("/Applications/Android Studio.app");
  161. if (defaultInstallation.exists())
  162. return defaultInstallation;
  163. #endif
  164. return File::nonexistent;
  165. }
  166. protected:
  167. //==============================================================================
  168. class AndroidStudioBuildConfiguration : public BuildConfiguration
  169. {
  170. public:
  171. AndroidStudioBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
  172. : BuildConfiguration (p, settings, e)
  173. {
  174. if (getArchitectures().isEmpty())
  175. {
  176. if (isDebug())
  177. getArchitecturesValue() = "armeabi x86";
  178. else
  179. getArchitecturesValue() = "armeabi armeabi-v7a x86";
  180. }
  181. }
  182. Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); }
  183. String getArchitectures() const { return config [Ids::androidArchitectures]; }
  184. var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
  185. void createConfigProperties (PropertyListBuilder& props) override
  186. {
  187. addGCCOptimisationProperty (props);
  188. props.add (new TextPropertyComponent (getArchitecturesValue(), "Architectures", 256, false),
  189. "A list of the ARM architectures to build (for a fat binary).");
  190. }
  191. };
  192. BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
  193. {
  194. return new AndroidStudioBuildConfiguration (project, v, *this);
  195. }
  196. private:
  197. static void createSymlinkAndParentFolders (const File& originalFile, const File& linkFile)
  198. {
  199. {
  200. const File linkFileParentDirectory (linkFile.getParentDirectory());
  201. // this will recursively creative the parent directories for the file.
  202. // without this, the symlink would fail because it doesn't automatically create
  203. // the folders if they don't exist
  204. if (! linkFileParentDirectory.createDirectory())
  205. throw SaveError (String ("Could not create directory ") + linkFileParentDirectory.getFullPathName());
  206. }
  207. if (! originalFile.createSymbolicLink (linkFile, true))
  208. throw SaveError (String ("Failed to create symlink from ")
  209. + linkFile.getFullPathName() + " to "
  210. + originalFile.getFullPathName() + "!");
  211. }
  212. void makeSymlinksForGroup (const Project::Item& group, const File& targetFolder) const
  213. {
  214. if (! group.isGroup())
  215. {
  216. throw SaveError ("makeSymlinksForGroup was called with something other than a group!");
  217. }
  218. for (int i = 0; i < group.getNumChildren(); ++i)
  219. {
  220. const Project::Item& projectItem = group.getChild (i);
  221. if (projectItem.isGroup())
  222. {
  223. makeSymlinksForGroup (projectItem, targetFolder.getChildFile (projectItem.getName()));
  224. }
  225. else if (projectItem.shouldBeAddedToTargetProject()) // must be a file then
  226. {
  227. const File originalFile (projectItem.getFile());
  228. const File targetFile (targetFolder.getChildFile (originalFile.getFileName()));
  229. createSymlinkAndParentFolders (originalFile, targetFile);
  230. }
  231. }
  232. }
  233. void createSourceSymlinks (const File& folder) const
  234. {
  235. const File targetFolder (folder.getChildFile ("app/src/main/jni"));
  236. // here we make symlinks to only to files included in the groups inside the project
  237. // this is because Android Studio does not have a concept of groups and just uses
  238. // the file system layout to determine what's to be compiled
  239. {
  240. const Array<Project::Item>& groups = getAllGroups();
  241. for (int i = 0; i < groups.size(); ++i)
  242. {
  243. const Project::Item projectItem (groups.getReference (i));
  244. const String projectItemName (projectItem.getName());
  245. if (projectItem.isGroup())
  246. makeSymlinksForGroup (projectItem, projectItemName == "Juce Modules" ? targetFolder.getChildFile ("JuceModules") : targetFolder);
  247. }
  248. }
  249. }
  250. void writeAppIcons (const File& folder) const
  251. {
  252. writeIcons (folder.getChildFile ("app/src/main/res/"));
  253. }
  254. static String sanitisePath (String path)
  255. {
  256. return expandHomeFolderToken (path).replace ("\\", "\\\\");
  257. }
  258. static String expandHomeFolderToken (const String& path)
  259. {
  260. String homeFolder = File::getSpecialLocation (File::userHomeDirectory).getFullPathName();
  261. return path.replace ("${user.home}", homeFolder)
  262. .replace ("~", homeFolder);
  263. }
  264. //==============================================================================
  265. struct GradleElement
  266. {
  267. virtual ~GradleElement() {}
  268. String toString() const { return toStringIndented (0); }
  269. virtual String toStringIndented (int indentLevel) const = 0;
  270. static String indent (int indentLevel) { return String::repeatedString (" ", indentLevel); }
  271. };
  272. //==============================================================================
  273. struct GradleStatement : public GradleElement
  274. {
  275. GradleStatement (const String& s) : statement (s) {}
  276. String toStringIndented (int indentLevel) const override { return indent (indentLevel) + statement; }
  277. String statement;
  278. };
  279. //==============================================================================
  280. struct GradleCppFlag : public GradleStatement
  281. {
  282. GradleCppFlag (const String& flag)
  283. : GradleStatement ("cppFlags.add(" + flag.quoted() + ")") {}
  284. };
  285. struct GradlePreprocessorDefine : public GradleStatement
  286. {
  287. GradlePreprocessorDefine (const String& define, const String& value)
  288. : GradleStatement ("cppFlags.add(\"-D" + define + "=" + value + "\")") {}
  289. };
  290. struct GradleHeaderIncludePath : public GradleStatement
  291. {
  292. GradleHeaderIncludePath (const String& path)
  293. : GradleStatement ("cppFlags.add(\"-I" + sanitisePath (path) + "\".toString())") {}
  294. };
  295. struct GradleLibrarySearchPath : public GradleStatement
  296. {
  297. GradleLibrarySearchPath (const String& path)
  298. : GradleStatement ("cppFlags.add(\"-L" + sanitisePath (path) + "\".toString())") {}
  299. };
  300. struct GradleLinkerFlag : public GradleStatement
  301. {
  302. GradleLinkerFlag (const String& flag)
  303. : GradleStatement ("ldFlags.add(" + flag.quoted() + "\")") {}
  304. };
  305. struct GradleLinkLibrary : public GradleStatement
  306. {
  307. GradleLinkLibrary (const String& lib)
  308. : GradleStatement ("ldLibs.add(" + lib.quoted() + ")") {}
  309. };
  310. //==============================================================================
  311. struct GradleValue : public GradleElement
  312. {
  313. template <typename ValueType>
  314. GradleValue (const String& k, const ValueType& v)
  315. : key (k), value (v) {}
  316. GradleValue (const String& k, bool boolValue)
  317. : key (k), value (boolValue ? "true" : "false") {}
  318. String toStringIndented (int indentLevel) const override
  319. {
  320. return indent (indentLevel) + key + " = " + value;
  321. }
  322. protected:
  323. String key, value;
  324. };
  325. struct GradleString : public GradleValue
  326. {
  327. GradleString (const String& k, const String& str)
  328. : GradleValue (k, str.quoted())
  329. {
  330. if (str.containsAnyOf ("${\"\'"))
  331. value += ".toString()";
  332. }
  333. };
  334. struct GradleFilePath : public GradleValue
  335. {
  336. GradleFilePath (const String& k, const String& path)
  337. : GradleValue (k, "new File(\"" + sanitisePath (path) + "\")") {}
  338. };
  339. //==============================================================================
  340. struct GradleObject : public GradleElement
  341. {
  342. GradleObject (const String& nm) : name (nm) {}
  343. #if JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES
  344. template <typename GradleType, typename... Args>
  345. void add (Args... args)
  346. {
  347. children.add (new GradleType (args...));
  348. // Note: can't use std::forward because it doesn't compile for OS X 10.8
  349. }
  350. #else // Remove this workaround once we drop VS2012 support!
  351. template <typename GradleType, typename Arg1>
  352. void add (Arg1 arg1)
  353. {
  354. children.add (new GradleType (arg1));
  355. }
  356. template <typename GradleType, typename Arg1, typename Arg2>
  357. void add (Arg1 arg1, Arg2 arg2)
  358. {
  359. children.add (new GradleType (arg1, arg2));
  360. }
  361. #endif
  362. void addChildObject (GradleObject* objectToAdd) noexcept
  363. {
  364. children.add (objectToAdd);
  365. }
  366. String toStringIndented (int indentLevel) const override
  367. {
  368. String result;
  369. result << indent (indentLevel) << name << " {" << newLine;
  370. for (const auto& child : children)
  371. result << child->toStringIndented (indentLevel + 1) << newLine;
  372. result << indent (indentLevel) << "}";
  373. if (indentLevel == 0)
  374. result << newLine;
  375. return result;
  376. }
  377. private:
  378. String name;
  379. OwnedArray<GradleElement> children;
  380. };
  381. //==============================================================================
  382. String getSettingsGradleFileContent() const
  383. {
  384. return "include ':app'";
  385. }
  386. String getProjectBuildGradleFileContent() const
  387. {
  388. String projectBuildGradle;
  389. projectBuildGradle << getGradleBuildScript();
  390. projectBuildGradle << getGradleAllProjects();
  391. return projectBuildGradle;
  392. }
  393. //==============================================================================
  394. String getGradleBuildScript() const
  395. {
  396. GradleObject buildScript ("buildscript");
  397. buildScript.addChildObject (getGradleRepositories());
  398. buildScript.addChildObject (getGradleDependencies());
  399. return buildScript.toString();
  400. }
  401. GradleObject* getGradleRepositories() const
  402. {
  403. auto repositories = new GradleObject ("repositories");
  404. repositories->add<GradleStatement> ("jcenter()");
  405. return repositories;
  406. }
  407. GradleObject* getGradleDependencies() const
  408. {
  409. auto dependencies = new GradleObject ("dependencies");
  410. dependencies->add<GradleStatement> ("classpath 'com.android.tools.build:gradle-experimental:"
  411. + getGradleWrapperVersionString() + "'");
  412. return dependencies;
  413. }
  414. String getGradleAllProjects() const
  415. {
  416. GradleObject allProjects ("allprojects");
  417. allProjects.addChildObject (getGradleRepositories());
  418. return allProjects.toString();
  419. }
  420. //==============================================================================
  421. String getAppBuildGradleFileContent() const
  422. {
  423. String appBuildGradle;
  424. appBuildGradle << "apply plugin: 'com.android.model.application'" << newLine;
  425. appBuildGradle << getAndroidModel();
  426. appBuildGradle << getAppDependencies();
  427. return appBuildGradle;
  428. }
  429. String getAndroidModel() const
  430. {
  431. GradleObject model ("model");
  432. model.addChildObject (getAndroidObject());
  433. model.addChildObject (getAndroidNdkSettings());
  434. model.addChildObject (getAndroidSources());
  435. model.addChildObject (getAndroidBuildConfigs());
  436. model.addChildObject (getAndroidSigningConfigs());
  437. model.addChildObject (getAndroidProductFlavours());
  438. return model.toString();
  439. }
  440. String getAppDependencies() const
  441. {
  442. GradleObject dependencies ("dependencies");
  443. dependencies.add<GradleStatement> ("compile \"com.android.support:support-v4:+\"");
  444. return dependencies.toString();
  445. }
  446. //==============================================================================
  447. GradleObject* getAndroidObject() const
  448. {
  449. auto android = new GradleObject ("android");
  450. android->add<GradleValue> ("compileSdkVersion", getMinimumSDKVersionString().getIntValue());
  451. android->add<GradleString> ("buildToolsVersion", getBuildToolsVersionString());
  452. android->addChildObject (getAndroidDefaultConfig());
  453. return android;
  454. }
  455. GradleObject* getAndroidDefaultConfig() const
  456. {
  457. const String bundleIdentifier = project.getBundleIdentifier().toString().toLowerCase();
  458. const int minSdkVersion = getMinimumSDKVersionString().getIntValue();
  459. auto defaultConfig = new GradleObject ("defaultConfig.with");
  460. defaultConfig->add<GradleString> ("applicationId", bundleIdentifier);
  461. defaultConfig->add<GradleValue> ("minSdkVersion.apiLevel", minSdkVersion);
  462. defaultConfig->add<GradleValue> ("targetSdkVersion.apiLevel", minSdkVersion);
  463. return defaultConfig;
  464. }
  465. GradleObject* getAndroidNdkSettings() const
  466. {
  467. const String toolchain = getGradleToolchainString();
  468. const String toolchainVersion = getGradleToolchainVersionString();
  469. const bool isClang = (toolchain == "clang");
  470. auto ndkSettings = new GradleObject ("android.ndk");
  471. ndkSettings->add<GradleString> ("moduleName", "juce_jni");
  472. ndkSettings->add<GradleString> ("toolchain", toolchain);
  473. ndkSettings->add<GradleValue> ("toolchainVersion", toolchainVersion);
  474. ndkSettings->add<GradleString> ("stl", isClang ? "c++_static" : "gnustl_static");
  475. ndkSettings->addChildObject (getNdkJuceExtraProperties());
  476. addAllNdkCompilerSettings (ndkSettings);
  477. return ndkSettings;
  478. }
  479. GradleObject* getNdkJuceExtraProperties() const
  480. {
  481. auto ext = new GradleObject ("ext");
  482. ext->add<GradleString> ("juceRootDir", "${project.rootDir}/../../../../");
  483. ext->add<GradleString> ("juceModuleDir", "${juceRootDir}/modules");
  484. return ext;
  485. }
  486. void addAllNdkCompilerSettings (GradleObject* ndk) const
  487. {
  488. addNdkCppFlags (ndk);
  489. addNdkPreprocessorDefines (ndk);
  490. addNdkHeaderIncludePaths (ndk);
  491. addNdkLinkerFlags (ndk);
  492. addNdkLibraries (ndk);
  493. }
  494. void addNdkCppFlags (GradleObject* ndk) const
  495. {
  496. const char* alwaysUsedFlags[] = { "-fsigned-char", "-fexceptions", "-frtti", "-std=c++11", nullptr };
  497. StringArray cppFlags (alwaysUsedFlags);
  498. cppFlags.mergeArray (StringArray::fromTokens (getExtraCompilerFlagsString(), " ", ""));
  499. for (int i = 0; i < cppFlags.size(); ++i)
  500. ndk->add<GradleCppFlag> (cppFlags[i]);
  501. }
  502. void addNdkPreprocessorDefines (GradleObject* ndk) const
  503. {
  504. const auto& defines = getAllPreprocessorDefs();
  505. for (int i = 0; i < defines.size(); ++i)
  506. ndk->add<GradlePreprocessorDefine> ( defines.getAllKeys()[i], defines.getAllValues()[i]);
  507. }
  508. void addNdkHeaderIncludePaths (GradleObject* ndk) const
  509. {
  510. const char* basicJucePaths[] = { "${project.rootDir}/app", "${ext.juceRootDir}", "${ext.juceModuleDir}", nullptr };
  511. StringArray includePaths (basicJucePaths);
  512. auto cppFiles = getAllIncludedCppFiles();
  513. for (const auto& cppFile : cppFiles)
  514. includePaths.addIfNotAlreadyThere (getIncludePathForFile (cppFile));
  515. for (const auto& path : includePaths)
  516. ndk->add<GradleHeaderIncludePath> (path);
  517. }
  518. Array<RelativePath> getAllIncludedCppFiles() const
  519. {
  520. Array<RelativePath> cppFiles;
  521. const auto& groups = getAllGroups();
  522. for (int i = 0; i < groups.size(); ++i)
  523. findAllProjectItemsWithPredicate (groups.getReference (i), cppFiles, ShouldBeAddedToProjectPredicate());
  524. return cppFiles;
  525. }
  526. String getIncludePathForFile (const RelativePath& file) const
  527. {
  528. return sanitisePath (project.getProjectFolder().getFullPathName() + "/"
  529. + file.rebased (getTargetFolder(),
  530. project.getProjectFolder(),
  531. RelativePath::projectFolder)
  532. .toUnixStyle()
  533. .upToLastOccurrenceOf ("/", false, false));
  534. }
  535. void addNdkLinkerFlags (GradleObject* ndk) const
  536. {
  537. const auto linkerFlags = StringArray::fromTokens (getExtraLinkerFlagsString(), " ", "");
  538. for (const auto& flag : linkerFlags)
  539. ndk->add<GradleLinkerFlag> (flag);
  540. }
  541. void addNdkLibraries (GradleObject* ndk) const
  542. {
  543. const char* requiredAndroidLibs[] = { "android", "EGL", "GLESv2", "log", nullptr };
  544. StringArray libs (requiredAndroidLibs);
  545. libs.addArray (StringArray::fromTokens(getExternalLibrariesString(), ";", ""));
  546. for (const auto& lib : libs)
  547. ndk->add<GradleLinkLibrary> (lib);
  548. }
  549. GradleObject* getAndroidSources() const
  550. {
  551. auto source = new GradleObject ("source"); // app source folder
  552. source->add<GradleStatement> ("exclude \"**/JuceModules/\"");
  553. auto jni = new GradleObject ("jni"); // all C++ sources for app
  554. jni->addChildObject (source);
  555. auto main = new GradleObject ("main"); // all sources for app
  556. main->addChildObject (jni);
  557. auto sources = new GradleObject ("android.sources"); // all sources
  558. sources->addChildObject (main);
  559. return sources;
  560. }
  561. GradleObject* getAndroidBuildConfigs() const
  562. {
  563. auto buildConfigs = new GradleObject ("android.buildTypes");
  564. for (ConstConfigIterator config (*this); config.next();)
  565. buildConfigs->addChildObject (getBuildConfig (*config));
  566. return buildConfigs;
  567. }
  568. GradleObject* getBuildConfig (const BuildConfiguration& config) const
  569. {
  570. const String configName (config.getName());
  571. // Note: at the moment, Android Studio only supports a "debug" and a "release"
  572. // build config, but no custom build configs like Projucer's other exporters do.
  573. if (configName != "Debug" && configName != "Release")
  574. throw SaveError ("Build configurations other than Debug and Release are not yet support for Android Studio");
  575. auto gradleConfig = new GradleObject (configName.toLowerCase());
  576. if (! config.isDebug())
  577. gradleConfig->add<GradleValue> ("signingConfig", "$(\"android.signingConfigs.releaseConfig\")");
  578. addConfigNdkSettings (gradleConfig, config);
  579. return gradleConfig;
  580. }
  581. void addConfigNdkSettings (GradleObject* buildConfig, const BuildConfiguration& config) const
  582. {
  583. auto ndkSettings = new GradleObject ("ndk.with");
  584. if (config.isDebug())
  585. {
  586. ndkSettings->add<GradleValue> ("debuggable", true);
  587. ndkSettings->add<GradleCppFlag> ("-g");
  588. ndkSettings->add<GradlePreprocessorDefine> ("DEBUG", "1");
  589. ndkSettings->add<GradlePreprocessorDefine> ("_DEBUG", "1");
  590. }
  591. else
  592. {
  593. ndkSettings->add<GradlePreprocessorDefine> ("NDEBUG", "1");
  594. }
  595. ndkSettings->add<GradleCppFlag> ("-O" + config.getGCCOptimisationFlag());
  596. for (const auto& path : config.getHeaderSearchPaths())
  597. ndkSettings->add<GradleHeaderIncludePath> (path);
  598. for (const auto& path : config.getLibrarySearchPaths())
  599. ndkSettings->add<GradleLibrarySearchPath> (path);
  600. ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID", "1");
  601. ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_API_VERSION", getMinimumSDKVersionString());
  602. ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_'));
  603. ndkSettings->add<GradlePreprocessorDefine> ("JUCE_ANDROID_ACTIVITY_CLASSPATH","\\\"" + getActivityClassPath().replaceCharacter('.', '/') + "\\\"");
  604. const auto defines = config.getAllPreprocessorDefs();
  605. for (int i = 0; i < defines.size(); ++i)
  606. ndkSettings->add<GradlePreprocessorDefine> (defines.getAllKeys()[i], defines.getAllValues()[i]);
  607. buildConfig->addChildObject (ndkSettings);
  608. }
  609. GradleObject* getAndroidSigningConfigs() const
  610. {
  611. auto releaseConfig = new GradleObject ("create(\"releaseConfig\")");
  612. releaseConfig->add<GradleFilePath> ("storeFile", getKeyStoreString());
  613. releaseConfig->add<GradleString> ("storePassword", getKeyStorePassString());
  614. releaseConfig->add<GradleString> ("keyAlias", getKeyAliasString());
  615. releaseConfig->add<GradleString> ("keyPassword", getKeyAliasPassString());
  616. releaseConfig->add<GradleString> ("storeType", "jks");
  617. auto signingConfigs = new GradleObject ("android.signingConfigs");
  618. signingConfigs->addChildObject (releaseConfig);
  619. // Note: no need to add a debugConfig, Android Studio will use debug.keystore by default
  620. return signingConfigs;
  621. }
  622. GradleObject* getAndroidProductFlavours() const
  623. {
  624. auto flavours = new GradleObject ("android.productFlavors");
  625. StringArray architectures (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (true), " ", ""));
  626. architectures.mergeArray (StringArray::fromTokens (getABIs<AndroidStudioBuildConfiguration> (false), " ", ""));
  627. if (architectures.size() == 0)
  628. throw SaveError ("Can't build for no architectures!");
  629. for (int i = 0; i < architectures.size(); ++i)
  630. {
  631. String arch (architectures[i].trim());
  632. if ((arch).isEmpty())
  633. continue;
  634. flavours->addChildObject (getGradleProductFlavourForArch (arch));
  635. }
  636. return flavours;
  637. }
  638. GradleObject* getGradleProductFlavourForArch (const String& arch) const
  639. {
  640. auto flavour = new GradleObject ("create(\"" + arch + "\")");
  641. flavour->add<GradleStatement> ("ndk.abiFilters.add(\"" + arch + "\")");
  642. return flavour;
  643. }
  644. //==============================================================================
  645. String getLocalPropertiesFileContent() const
  646. {
  647. String props;
  648. props << "ndk.dir=" << sanitisePath (getNDKPathString()) << newLine
  649. << "sdk.dir=" << sanitisePath (getSDKPathString()) << newLine;
  650. return props;
  651. }
  652. String getGradleWrapperPropertiesFileContent() const
  653. {
  654. String props;
  655. props << "distributionUrl=https\\://services.gradle.org/distributions/gradle-"
  656. << getGradleVersionString() << "-all.zip";
  657. return props;
  658. }
  659. //==============================================================================
  660. void writeStringsXML (const File& folder) const
  661. {
  662. XmlElement strings ("resources");
  663. XmlElement* resourceName = strings.createNewChildElement ("string");
  664. resourceName->setAttribute ("name", "app_name");
  665. resourceName->addTextElement (projectName);
  666. writeXmlOrThrow (strings, folder.getChildFile ("app/src/main/res/values/string.xml"), "utf-8", 100, true);
  667. }
  668. //==============================================================================
  669. void writeAndroidManifest (const File& folder) const
  670. {
  671. ScopedPointer<XmlElement> manifest (createManifestXML());
  672. writeXmlOrThrow (*manifest, folder.getChildFile ("app/src/main/AndroidManifest.xml"), "utf-8", 100, true);
  673. }
  674. //==============================================================================
  675. struct ShouldBeAddedToProjectPredicate
  676. {
  677. bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeAddedToTargetProject(); }
  678. };
  679. //==============================================================================
  680. const File androidStudioExecutable;
  681. JUCE_DECLARE_NON_COPYABLE (AndroidStudioProjectExporter)
  682. };