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.

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