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.

468 lines
20KB

  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 AndroidAntProjectExporter : public AndroidProjectExporterBase
  18. {
  19. public:
  20. //==============================================================================
  21. bool canLaunchProject() override { return false; }
  22. bool launchProject() override { return false; }
  23. bool isAndroid() const override { return true; }
  24. bool usesMMFiles() const override { return false; }
  25. bool canCopeWithDuplicateFiles() override { return false; }
  26. bool supportsUserDefinedConfigurations() const override { return true; }
  27. bool isAndroidStudio() const override { return false; }
  28. bool isAndroidAnt() const override { return true; }
  29. bool supportsVST() const override { return false; }
  30. bool supportsVST3() const override { return false; }
  31. bool supportsAAX() const override { return false; }
  32. bool supportsRTAS() const override { return false; }
  33. bool supportsStandalone() const override { return false; }
  34. //==============================================================================
  35. static const char* getName() { return "Android Ant Project"; }
  36. static const char* getValueTreeTypeName() { return "ANDROID"; }
  37. //==============================================================================
  38. Value getNDKToolchainVersionValue() { return getSetting (Ids::toolset); }
  39. String getNDKToolchainVersionString() const { return settings [Ids::toolset]; }
  40. Value getStaticLibrariesValue() { return getSetting (Ids::androidStaticLibraries); }
  41. String getStaticLibrariesString() const { return settings [Ids::androidStaticLibraries]; }
  42. Value getSharedLibrariesValue() { return getSetting (Ids::androidSharedLibraries); }
  43. String getSharedLibrariesString() const { return settings [Ids::androidSharedLibraries]; }
  44. //==============================================================================
  45. static AndroidAntProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  46. {
  47. if (settings.hasType (getValueTreeTypeName()))
  48. return new AndroidAntProjectExporter (project, settings);
  49. return nullptr;
  50. }
  51. //==============================================================================
  52. AndroidAntProjectExporter (Project& p, const ValueTree& t)
  53. : AndroidProjectExporterBase (p, t)
  54. {
  55. name = getName();
  56. if (getTargetLocationString().isEmpty())
  57. getTargetLocationValue() = getDefaultBuildsRootFolder() + "Android";
  58. }
  59. //==============================================================================
  60. void createToolchainExporterProperties (PropertyListBuilder& props) override
  61. {
  62. props.add (new TextPropertyComponent (getNDKToolchainVersionValue(), "NDK Toolchain version", 32, false),
  63. "The variable NDK_TOOLCHAIN_VERSION in Application.mk - leave blank for a default value");
  64. }
  65. void createLibraryModuleExporterProperties (PropertyListBuilder& props) override
  66. {
  67. props.add (new TextPropertyComponent (getStaticLibrariesValue(), "Import static library modules", 8192, true),
  68. "Comma or whitespace delimited list of static libraries (.a) defined in NDK_MODULE_PATH.");
  69. props.add (new TextPropertyComponent (getSharedLibrariesValue(), "Import shared library modules", 8192, true),
  70. "Comma or whitespace delimited list of shared libraries (.so) defined in NDK_MODULE_PATH.");
  71. }
  72. //==============================================================================
  73. void create (const OwnedArray<LibraryModule>& modules) const override
  74. {
  75. AndroidProjectExporterBase::create (modules);
  76. const File target (getTargetFolder());
  77. const File jniFolder (target.getChildFile ("jni"));
  78. createDirectoryOrThrow (jniFolder);
  79. createDirectoryOrThrow (target.getChildFile ("res").getChildFile ("values"));
  80. createDirectoryOrThrow (target.getChildFile ("libs"));
  81. createDirectoryOrThrow (target.getChildFile ("bin"));
  82. {
  83. ScopedPointer<XmlElement> manifest (createManifestXML());
  84. writeXmlOrThrow (*manifest, target.getChildFile ("AndroidManifest.xml"), "utf-8", 100, true);
  85. }
  86. writeApplicationMk (jniFolder.getChildFile ("Application.mk"));
  87. writeAndroidMk (jniFolder.getChildFile ("Android.mk"));
  88. {
  89. ScopedPointer<XmlElement> antBuildXml (createAntBuildXML());
  90. writeXmlOrThrow (*antBuildXml, target.getChildFile ("build.xml"), "UTF-8", 100);
  91. }
  92. writeProjectPropertiesFile (target.getChildFile ("project.properties"));
  93. writeLocalPropertiesFile (target.getChildFile ("local.properties"));
  94. writeStringsFile (target.getChildFile ("res/values/strings.xml"));
  95. writeIcons (target.getChildFile ("res"));
  96. }
  97. //==============================================================================
  98. class AndroidBuildConfiguration : public BuildConfiguration
  99. {
  100. public:
  101. AndroidBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
  102. : BuildConfiguration (p, settings, e)
  103. {
  104. if (getArchitectures().isEmpty())
  105. {
  106. if (isDebug())
  107. getArchitecturesValue() = "armeabi x86";
  108. else
  109. getArchitecturesValue() = "armeabi armeabi-v7a x86";
  110. }
  111. }
  112. Value getArchitecturesValue() { return getValue (Ids::androidArchitectures); }
  113. String getArchitectures() const { return config [Ids::androidArchitectures]; }
  114. var getDefaultOptimisationLevel() const override { return var ((int) (isDebug() ? gccO0 : gccO3)); }
  115. void createConfigProperties (PropertyListBuilder& props) override
  116. {
  117. addGCCOptimisationProperty (props);
  118. props.add (new TextPropertyComponent (getArchitecturesValue(), "Architectures", 256, false),
  119. "A list of the ARM architectures to build (for a fat binary).");
  120. }
  121. };
  122. BuildConfiguration::Ptr createBuildConfig (const ValueTree& v) const override
  123. {
  124. return new AndroidBuildConfiguration (project, v, *this);
  125. }
  126. private:
  127. //==============================================================================
  128. String getToolchainVersion() const
  129. {
  130. String v (getNDKToolchainVersionString());
  131. return v.isNotEmpty() ? v : "4.9";
  132. }
  133. //==============================================================================
  134. String getCppFlags() const
  135. {
  136. String flags ("-fsigned-char -fexceptions -frtti");
  137. if (! getNDKToolchainVersionString().startsWithIgnoreCase ("clang"))
  138. flags << " -Wno-psabi";
  139. return flags;
  140. }
  141. String getAppPlatform() const
  142. {
  143. int ndkVersion = androidMinimumSDK.get().getIntValue();
  144. if (ndkVersion == 9)
  145. ndkVersion = 10; // (doesn't seem to be a version '9')
  146. return "android-" + String (ndkVersion);
  147. }
  148. void writeApplicationMk (const File& file) const
  149. {
  150. MemoryOutputStream mo;
  151. mo << "# Automatically generated makefile, created by the Projucer" << newLine
  152. << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine
  153. << newLine
  154. << "APP_STL := gnustl_static" << newLine
  155. << "APP_CPPFLAGS += " << getCppFlags() << newLine
  156. << "APP_PLATFORM := " << getAppPlatform() << newLine
  157. << "NDK_TOOLCHAIN_VERSION := " << getToolchainVersion() << newLine
  158. << newLine
  159. << "ifeq ($(NDK_DEBUG),1)" << newLine
  160. << " APP_ABI := " << getABIs<AndroidBuildConfiguration> (true) << newLine
  161. << "else" << newLine
  162. << " APP_ABI := " << getABIs<AndroidBuildConfiguration> (false) << newLine
  163. << "endif" << newLine;
  164. overwriteFileIfDifferentOrThrow (file, mo);
  165. }
  166. struct ShouldFileBeCompiledPredicate
  167. {
  168. bool operator() (const Project::Item& projectItem) const { return projectItem.shouldBeCompiled(); }
  169. };
  170. void writeAndroidMk (const File& file) const
  171. {
  172. Array<RelativePath> files;
  173. for (int i = 0; i < getAllGroups().size(); ++i)
  174. findAllProjectItemsWithPredicate (getAllGroups().getReference(i), files, ShouldFileBeCompiledPredicate());
  175. MemoryOutputStream mo;
  176. writeAndroidMk (mo, files);
  177. overwriteFileIfDifferentOrThrow (file, mo);
  178. }
  179. void writeAndroidMkVariableList (OutputStream& out, const String& variableName, const String& settingsValue) const
  180. {
  181. const StringArray separatedItems (getCommaOrWhitespaceSeparatedItems (settingsValue));
  182. if (separatedItems.size() > 0)
  183. out << newLine << variableName << " := " << separatedItems.joinIntoString (" ") << newLine;
  184. }
  185. void writeAndroidMk (OutputStream& out, const Array<RelativePath>& files) const
  186. {
  187. out << "# Automatically generated makefile, created by the Projucer" << newLine
  188. << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine
  189. << newLine
  190. << "LOCAL_PATH := $(call my-dir)" << newLine
  191. << newLine
  192. << "include $(CLEAR_VARS)" << newLine
  193. << newLine
  194. << "ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)" << newLine
  195. << " LOCAL_ARM_MODE := arm" << newLine
  196. << "endif" << newLine
  197. << newLine
  198. << "LOCAL_MODULE := juce_jni" << newLine
  199. << "LOCAL_SRC_FILES := \\" << newLine;
  200. for (int i = 0; i < files.size(); ++i)
  201. out << " " << (files.getReference(i).isAbsolute() ? "" : "../")
  202. << escapeSpaces (files.getReference(i).toUnixStyle()) << "\\" << newLine;
  203. writeAndroidMkVariableList (out, "LOCAL_STATIC_LIBRARIES", getStaticLibrariesString());
  204. writeAndroidMkVariableList (out, "LOCAL_SHARED_LIBRARIES", getSharedLibrariesString());
  205. out << newLine
  206. << "ifeq ($(NDK_DEBUG),1)" << newLine;
  207. writeConfigSettings (out, true);
  208. out << "else" << newLine;
  209. writeConfigSettings (out, false);
  210. out << "endif" << newLine
  211. << newLine
  212. << "include $(BUILD_SHARED_LIBRARY)" << newLine;
  213. StringArray importModules (getCommaOrWhitespaceSeparatedItems (getStaticLibrariesString()));
  214. importModules.addArray (getCommaOrWhitespaceSeparatedItems (getSharedLibrariesString()));
  215. for (int i = 0; i < importModules.size(); ++i)
  216. out << "$(call import-module," << importModules[i] << ")" << newLine;
  217. }
  218. void writeConfigSettings (OutputStream& out, bool forDebug) const
  219. {
  220. for (ConstConfigIterator config (*this); config.next();)
  221. {
  222. if (config->isDebug() == forDebug)
  223. {
  224. const AndroidBuildConfiguration& androidConfig = dynamic_cast<const AndroidBuildConfiguration&> (*config);
  225. String cppFlags;
  226. cppFlags << createCPPFlags (androidConfig)
  227. << (" " + replacePreprocessorTokens (androidConfig, getExtraCompilerFlagsString()).trim()).trimEnd()
  228. << newLine
  229. << getLDLIBS (androidConfig).trimEnd()
  230. << newLine;
  231. out << " LOCAL_CPPFLAGS += " << cppFlags;
  232. out << " LOCAL_CFLAGS += " << cppFlags;
  233. break;
  234. }
  235. }
  236. }
  237. String getLDLIBS (const AndroidBuildConfiguration& config) const
  238. {
  239. return " LOCAL_LDLIBS :=" + config.getGCCLibraryPathFlags()
  240. + " -llog -lGLESv2 -landroid -lEGL" + getExternalLibraryFlags (config)
  241. + " " + replacePreprocessorTokens (config, getExtraLinkerFlagsString());
  242. }
  243. String createIncludePathFlags (const BuildConfiguration& config) const
  244. {
  245. String flags;
  246. StringArray searchPaths (extraSearchPaths);
  247. searchPaths.addArray (config.getHeaderSearchPaths());
  248. searchPaths = getCleanedStringArray (searchPaths);
  249. for (int i = 0; i < searchPaths.size(); ++i)
  250. flags << " -I " << FileHelpers::unixStylePath (replacePreprocessorTokens (config, searchPaths[i])).quoted();
  251. return flags;
  252. }
  253. String createCPPFlags (const BuildConfiguration& config) const
  254. {
  255. StringPairArray defines;
  256. defines.set ("JUCE_ANDROID", "1");
  257. defines.set ("JUCE_ANDROID_API_VERSION", androidMinimumSDK.get());
  258. defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_'));
  259. defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\\\"" + getJNIActivityClassName() + "\\\"");
  260. String flags ("-fsigned-char -fexceptions -frtti");
  261. if (config.isDebug())
  262. {
  263. flags << " -g";
  264. defines.set ("DEBUG", "1");
  265. defines.set ("_DEBUG", "1");
  266. }
  267. else
  268. {
  269. defines.set ("NDEBUG", "1");
  270. }
  271. flags << createIncludePathFlags (config)
  272. << " -O" << config.getGCCOptimisationFlag();
  273. flags << " -std=gnu++11";
  274. defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config));
  275. return flags + createGCCPreprocessorFlags (defines);
  276. }
  277. //==============================================================================
  278. XmlElement* createAntBuildXML() const
  279. {
  280. XmlElement* proj = new XmlElement ("project");
  281. proj->setAttribute ("name", projectName);
  282. proj->setAttribute ("default", "debug");
  283. proj->createNewChildElement ("loadproperties")->setAttribute ("srcFile", "local.properties");
  284. proj->createNewChildElement ("loadproperties")->setAttribute ("srcFile", "project.properties");
  285. {
  286. XmlElement* target = proj->createNewChildElement ("target");
  287. target->setAttribute ("name", "clean");
  288. target->setAttribute ("depends", "android_rules.clean");
  289. target->createNewChildElement ("delete")->setAttribute ("dir", "libs");
  290. target->createNewChildElement ("delete")->setAttribute ("dir", "obj");
  291. XmlElement* executable = target->createNewChildElement ("exec");
  292. executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
  293. executable->setAttribute ("dir", "${basedir}");
  294. executable->setAttribute ("failonerror", "true");
  295. executable->createNewChildElement ("arg")->setAttribute ("value", "clean");
  296. }
  297. {
  298. XmlElement* target = proj->createNewChildElement ("target");
  299. target->setAttribute ("name", "-pre-build");
  300. addDebugConditionClause (target, "makefileConfig", "Debug", "Release");
  301. addDebugConditionClause (target, "ndkDebugValue", "NDK_DEBUG=1", "NDK_DEBUG=0");
  302. String debugABIs, releaseABIs;
  303. for (ConstConfigIterator config (*this); config.next();)
  304. {
  305. const AndroidBuildConfiguration& androidConfig = dynamic_cast<const AndroidBuildConfiguration&> (*config);
  306. if (config->isDebug())
  307. debugABIs = androidConfig.getArchitectures();
  308. else
  309. releaseABIs = androidConfig.getArchitectures();
  310. }
  311. addDebugConditionClause (target, "app_abis", debugABIs, releaseABIs);
  312. XmlElement* executable = target->createNewChildElement ("exec");
  313. executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
  314. executable->setAttribute ("dir", "${basedir}");
  315. executable->setAttribute ("failonerror", "true");
  316. executable->createNewChildElement ("arg")->setAttribute ("value", "--jobs=4");
  317. executable->createNewChildElement ("arg")->setAttribute ("value", "CONFIG=${makefileConfig}");
  318. executable->createNewChildElement ("arg")->setAttribute ("value", "${ndkDebugValue}");
  319. executable->createNewChildElement ("arg")->setAttribute ("value", "APP_ABI=${app_abis}");
  320. target->createNewChildElement ("delete")->setAttribute ("file", "${out.final.file}");
  321. target->createNewChildElement ("delete")->setAttribute ("file", "${out.packaged.file}");
  322. }
  323. proj->createNewChildElement ("import")->setAttribute ("file", "${sdk.dir}/tools/ant/build.xml");
  324. return proj;
  325. }
  326. void addDebugConditionClause (XmlElement* target, const String& property,
  327. const String& debugValue, const String& releaseValue) const
  328. {
  329. XmlElement* condition = target->createNewChildElement ("condition");
  330. condition->setAttribute ("property", property);
  331. condition->setAttribute ("value", debugValue);
  332. condition->setAttribute ("else", releaseValue);
  333. XmlElement* equals = condition->createNewChildElement ("equals");
  334. equals->setAttribute ("arg1", "${ant.project.invoked-targets}");
  335. equals->setAttribute ("arg2", "debug");
  336. }
  337. void writeProjectPropertiesFile (const File& file) const
  338. {
  339. MemoryOutputStream mo;
  340. mo << "# This file is used to override default values used by the Ant build system." << newLine
  341. << "# It is automatically generated - DO NOT EDIT IT or your changes will be lost!." << newLine
  342. << newLine
  343. << "target=" << getAppPlatform() << newLine
  344. << newLine;
  345. overwriteFileIfDifferentOrThrow (file, mo);
  346. }
  347. void writeLocalPropertiesFile (const File& file) const
  348. {
  349. MemoryOutputStream mo;
  350. mo << "# This file is used to override default values used by the Ant build system." << newLine
  351. << "# It is automatically generated by the Projucer - DO NOT EDIT IT or your changes will be lost!." << newLine
  352. << newLine
  353. << "sdk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), sdkPath.toString())) << newLine
  354. << "ndk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), ndkPath.toString())) << newLine
  355. << "key.store=" << androidKeyStore.get() << newLine
  356. << "key.alias=" << androidKeyAlias.get() << newLine
  357. << "key.store.password=" << androidKeyStorePass.get() << newLine
  358. << "key.alias.password=" << androidKeyAliasPass.get() << newLine
  359. << newLine;
  360. overwriteFileIfDifferentOrThrow (file, mo);
  361. }
  362. void writeStringsFile (const File& file) const
  363. {
  364. XmlElement strings ("resources");
  365. XmlElement* resourceName = strings.createNewChildElement ("string");
  366. resourceName->setAttribute ("name", "app_name");
  367. resourceName->addTextElement (projectName);
  368. writeXmlOrThrow (strings, file, "utf-8", 100);
  369. }
  370. //==============================================================================
  371. JUCE_DECLARE_NON_COPYABLE (AndroidAntProjectExporter)
  372. };