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.

396 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__
  19. #define __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__
  20. #include "jucer_ProjectExporter.h"
  21. //==============================================================================
  22. class AndroidProjectExporter : public ProjectExporter
  23. {
  24. public:
  25. //==============================================================================
  26. static const char* getNameAndroid() { return "Android Project"; }
  27. static const char* getValueTreeTypeName() { return "ANDROID"; }
  28. static AndroidProjectExporter* createForSettings (Project& project, const ValueTree& settings)
  29. {
  30. if (settings.hasType (getValueTreeTypeName()))
  31. return new AndroidProjectExporter (project, settings);
  32. return 0;
  33. }
  34. //==============================================================================
  35. AndroidProjectExporter (Project& project_, const ValueTree& settings_)
  36. : ProjectExporter (project_, settings_)
  37. {
  38. name = getNameAndroid();
  39. if (getTargetLocation().toString().isEmpty())
  40. getTargetLocation() = getDefaultBuildsRootFolder() + "Android";
  41. if (getSDKPath().toString().isEmpty())
  42. getSDKPath() = "${user.home}/SDKs/android-sdk-mac_86";
  43. if (getNDKPath().toString().isEmpty())
  44. getNDKPath() = "${user.home}/SDKs/android-ndk-r4-crystax";
  45. }
  46. //==============================================================================
  47. bool isDefaultFormatForCurrentOS()
  48. {
  49. #if JUCE_ANDROID
  50. return true;
  51. #else
  52. return false;
  53. #endif
  54. }
  55. bool isPossibleForCurrentProject() { return project.isGUIApplication(); }
  56. bool usesMMFiles() const { return false; }
  57. void launchProject()
  58. {
  59. }
  60. void createPropertyEditors (Array <PropertyComponent*>& props)
  61. {
  62. ProjectExporter::createPropertyEditors (props);
  63. props.add (new TextPropertyComponent (getSDKPath(), "Android SDK Path", 1024, false));
  64. props.getLast()->setTooltip ("The path to the Android SDK folder on the target build machine");
  65. props.add (new TextPropertyComponent (getNDKPath(), "Android NDK Path", 1024, false));
  66. props.getLast()->setTooltip ("The path to the Android NDK folder on the target build machine");
  67. }
  68. Value getSDKPath() const { return getSetting (Ids::androidSDKPath); }
  69. Value getNDKPath() const { return getSetting (Ids::androidNDKPath); }
  70. //==============================================================================
  71. void create()
  72. {
  73. const File target (getTargetFolder());
  74. const File jniFolder (target.getChildFile ("jni"));
  75. createDirectoryOrThrow (target.getChildFile ("src/com"));
  76. createDirectoryOrThrow (jniFolder);
  77. createDirectoryOrThrow (target.getChildFile ("res/drawable-hdpi"));
  78. createDirectoryOrThrow (target.getChildFile ("res/drawable-mdpi"));
  79. createDirectoryOrThrow (target.getChildFile ("res/drawable-ldpi"));
  80. createDirectoryOrThrow (target.getChildFile ("res/values"));
  81. createDirectoryOrThrow (target.getChildFile ("libs"));
  82. createDirectoryOrThrow (target.getChildFile ("bin"));
  83. {
  84. ScopedPointer<XmlElement> manifest (createManifestXML());
  85. writeXmlOrThrow (*manifest, target.getChildFile ("AndroidManifest.xml"), "utf-8", 100);
  86. }
  87. writeApplicationMk (jniFolder.getChildFile ("Application.mk"));
  88. writeAndroidMk (jniFolder.getChildFile ("Android.mk"));
  89. {
  90. ScopedPointer<XmlElement> antBuildXml (createAntBuildXML());
  91. writeXmlOrThrow (*antBuildXml, target.getChildFile ("build.xml"), "UTF-8", 100);
  92. }
  93. writeBuildPropertiesFile (target.getChildFile ("build.properties"));
  94. writeDefaultPropertiesFile (target.getChildFile ("default.properties"));
  95. writeLocalPropertiesFile (target.getChildFile ("local.properties"));
  96. writeIcon (target.getChildFile ("res/drawable-hdpi/icon.png"), 72);
  97. writeIcon (target.getChildFile ("res/drawable-mdpi/icon.png"), 48);
  98. writeIcon (target.getChildFile ("res/drawable-ldpi/icon.png"), 36);
  99. writeStringsFile (target.getChildFile ("res/values/strings.xml"));
  100. }
  101. private:
  102. //==============================================================================
  103. XmlElement* createManifestXML()
  104. {
  105. XmlElement* manifest = new XmlElement ("manifest");
  106. manifest->setAttribute ("xmlns:android", "http://schemas.android.com/apk/res/android");
  107. manifest->setAttribute ("android:versionCode", "1");
  108. manifest->setAttribute ("android:versionName", "1.0");
  109. manifest->setAttribute ("package", "com.juce");
  110. XmlElement* screens = manifest->createNewChildElement ("supports-screens");
  111. screens->setAttribute ("android:smallScreens", "true");
  112. screens->setAttribute ("android:normalScreens", "true");
  113. screens->setAttribute ("android:largeScreens", "true");
  114. screens->setAttribute ("android:xlargeScreens", "true");
  115. screens->setAttribute ("android:anyDensity", "true");
  116. XmlElement* app = manifest->createNewChildElement ("application");
  117. app->setAttribute ("android:label", "@string/app_name");
  118. app->setAttribute ("android:icon", "@drawable/icon");
  119. XmlElement* act = app->createNewChildElement ("activity");
  120. act->setAttribute ("android:name", "JuceAppActivity");
  121. act->setAttribute ("android:label", "@string/app_name");
  122. XmlElement* intent = act->createNewChildElement ("intent-filter");
  123. intent->createNewChildElement ("action")->setAttribute ("android:name", "android.intent.action.MAIN");
  124. intent->createNewChildElement ("category")->setAttribute ("android:name", "android.intent.category.LAUNCHER");
  125. return manifest;
  126. }
  127. //==============================================================================
  128. void findAllFilesToCompile (const Project::Item& projectItem, Array<RelativePath>& results)
  129. {
  130. if (projectItem.isGroup())
  131. {
  132. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  133. findAllFilesToCompile (projectItem.getChild(i), results);
  134. }
  135. else
  136. {
  137. if (projectItem.shouldBeCompiled())
  138. results.add (RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder));
  139. }
  140. }
  141. void writeApplicationMk (const File& file)
  142. {
  143. MemoryOutputStream mo;
  144. mo << "# Automatically generated makefile, created by the Jucer" << newLine
  145. << "# Don't edit this file! Your changes will be overwritten when you re-save the Jucer project!" << newLine
  146. << newLine
  147. << "APP_STL := stlport_static" << newLine
  148. << "APP_CPPFLAGS += -fsigned-char -fexceptions -frtti" << newLine;
  149. overwriteFileIfDifferentOrThrow (file, mo);
  150. }
  151. void writeAndroidMk (const File& file)
  152. {
  153. Array<RelativePath> files;
  154. findAllFilesToCompile (project.getMainGroup(), files);
  155. for (int i = 0; i < juceWrapperFiles.size(); ++i)
  156. if (shouldFileBeCompiledByDefault (juceWrapperFiles.getReference(i)))
  157. files.add (juceWrapperFiles.getReference(i));
  158. MemoryOutputStream mo;
  159. writeAndroidMk (mo, files);
  160. overwriteFileIfDifferentOrThrow (file, mo);
  161. }
  162. void writeAndroidMk (OutputStream& out, const Array<RelativePath>& files)
  163. {
  164. out << "# Automatically generated makefile, created by the Jucer" << newLine
  165. << "# Don't edit this file! Your changes will be overwritten when you re-save the Jucer project!" << newLine
  166. << newLine
  167. << "LOCAL_PATH := $(call my-dir)" << newLine
  168. << newLine
  169. << "include $(CLEAR_VARS)" << newLine
  170. << newLine
  171. << "LOCAL_CPP_EXTENSION := cpp" << newLine
  172. << "LOCAL_MODULE := juce_jni" << newLine
  173. << "LOCAL_SRC_FILES := \\" << newLine;
  174. for (int i = 0; i < files.size(); ++i)
  175. out << " ../" << escapeSpaces (files.getReference(i).toUnixStyle()) << "\\" << newLine;
  176. out << newLine
  177. << "ifeq ($(CONFIG),Debug)" << newLine
  178. << " LOCAL_CPPFLAGS += " << createCPPFlags (true) << newLine
  179. << "else" << newLine
  180. << " LOCAL_CPPFLAGS += " << createCPPFlags (false) << newLine
  181. << "endif" << newLine
  182. << newLine
  183. << "include $(BUILD_SHARED_LIBRARY)" << newLine;
  184. }
  185. const String createCPPFlags (bool forDebug)
  186. {
  187. String flags ("-fsigned-char -fexceptions -frtti");
  188. if (forDebug)
  189. flags << " -g";
  190. StringPairArray defines;
  191. defines.set ("JUCE_ANDROID", "1");
  192. if (forDebug)
  193. {
  194. defines.set ("DEBUG", "1");
  195. defines.set ("_DEBUG", "1");
  196. }
  197. else
  198. {
  199. defines.set ("NDEBUG", "1");
  200. }
  201. for (int i = 0; i < project.getNumConfigurations(); ++i)
  202. {
  203. Project::BuildConfiguration config (project.getConfiguration(i));
  204. if (config.isDebug() == forDebug)
  205. {
  206. flags << " -O" << config.getGCCOptimisationFlag();
  207. defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config));
  208. break;
  209. }
  210. }
  211. return flags + createGCCPreprocessorFlags (defines);
  212. }
  213. //==============================================================================
  214. XmlElement* createAntBuildXML()
  215. {
  216. XmlElement* proj = new XmlElement ("project");
  217. proj->setAttribute ("name", project.getProjectName().toString());
  218. proj->setAttribute ("default", "help");
  219. proj->createNewChildElement ("property")->setAttribute ("file", "local.properties");
  220. proj->createNewChildElement ("property")->setAttribute ("file", "build.properties");
  221. proj->createNewChildElement ("property")->setAttribute ("file", "default.properties");
  222. XmlElement* path = proj->createNewChildElement ("path");
  223. path->setAttribute ("id", "android.antlibs");
  224. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/anttasks.jar");
  225. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/sdklib.jar");
  226. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/androidprefs.jar");
  227. XmlElement* taskdef = proj->createNewChildElement ("taskdef");
  228. taskdef->setAttribute ("name", "setup");
  229. taskdef->setAttribute ("classname", "com.android.ant.SetupTask");
  230. taskdef->setAttribute ("classpathref", "android.antlibs");
  231. addNDKBuildStep (proj, "clean", "clean");
  232. //addLinkStep (proj, "${basedir}/" + rebaseFromProjectFolderToBuildTarget (RelativePath()).toUnixStyle() + "/", "jni/app");
  233. addLinkStep (proj, "${basedir}/" + getJucePathFromTargetFolder().toUnixStyle() + "/src/native/android/java/", "src/com/juce");
  234. addNDKBuildStep (proj, "debug", "CONFIG=Debug");
  235. addNDKBuildStep (proj, "release", "CONFIG=Release");
  236. proj->createNewChildElement ("setup");
  237. return proj;
  238. }
  239. static void addNDKBuildStep (XmlElement* project, const String& type, const String& arg)
  240. {
  241. XmlElement* target = project->createNewChildElement ("target");
  242. target->setAttribute ("name", type);
  243. XmlElement* executable = target->createNewChildElement ("exec");
  244. executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
  245. executable->setAttribute ("dir", "${basedir}");
  246. executable->setAttribute ("failonerror", "true");
  247. executable->createNewChildElement ("arg")->setAttribute ("value", arg);
  248. }
  249. static void addLinkStep (XmlElement* project, const String& from, const String& to)
  250. {
  251. XmlElement* executable = project->createNewChildElement ("exec");
  252. executable->setAttribute ("executable", "ln");
  253. executable->setAttribute ("dir", "${basedir}");
  254. executable->setAttribute ("failonerror", "false");
  255. executable->createNewChildElement ("arg")->setAttribute ("value", "-s");
  256. executable->createNewChildElement ("arg")->setAttribute ("value", from);
  257. executable->createNewChildElement ("arg")->setAttribute ("value", to);
  258. }
  259. void writeBuildPropertiesFile (const File& file)
  260. {
  261. MemoryOutputStream mo;
  262. mo << "# This file is used to override default values used by the Ant build system." << newLine;
  263. overwriteFileIfDifferentOrThrow (file, mo);
  264. }
  265. void writeDefaultPropertiesFile (const File& file)
  266. {
  267. MemoryOutputStream mo;
  268. mo << "# This file is used to override default values used by the Ant build system." << newLine
  269. << "# It is automatically generated - DO NOT EDIT IT or your changes will be lost!." << newLine
  270. << newLine
  271. << "target=android-9"
  272. << newLine;
  273. overwriteFileIfDifferentOrThrow (file, mo);
  274. }
  275. void writeLocalPropertiesFile (const File& file)
  276. {
  277. MemoryOutputStream mo;
  278. mo << "# This file is used to override default values used by the Ant build system." << newLine
  279. << "# It is automatically generated by the Jucer - DO NOT EDIT IT or your changes will be lost!." << newLine
  280. << newLine
  281. << "sdk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getSDKPath().toString())) << newLine
  282. << "ndk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getNDKPath().toString())) << newLine
  283. << newLine;
  284. overwriteFileIfDifferentOrThrow (file, mo);
  285. }
  286. void writeIcon (const File& file, int size)
  287. {
  288. Image im (project.getBestIconForSize (size, false));
  289. if (im.isValid())
  290. {
  291. PNGImageFormat png;
  292. MemoryOutputStream mo;
  293. if (! png.writeImageToStream (im, mo))
  294. throw SaveError ("Can't generate Android icon file");
  295. overwriteFileIfDifferentOrThrow (file, mo);
  296. }
  297. }
  298. void writeStringsFile (const File& file)
  299. {
  300. XmlElement strings ("resources");
  301. XmlElement* name = strings.createNewChildElement ("string");
  302. name->setAttribute ("name", "app_name");
  303. name->addTextElement (project.getProjectName().toString());
  304. writeXmlOrThrow (strings, file, "utf-8", 100);
  305. }
  306. //==============================================================================
  307. JUCE_DECLARE_NON_COPYABLE (AndroidProjectExporter);
  308. };
  309. #endif // __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__