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.

854 lines
32KB

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