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.

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