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.

377 lines
15KB

  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. writeJNIMakefile (jniFolder.getChildFile ("Android.mk"));
  88. {
  89. ScopedPointer<XmlElement> antBuildXml (createAntBuildXML());
  90. writeXmlOrThrow (*antBuildXml, target.getChildFile ("build.xml"), "UTF-8", 100);
  91. }
  92. writeBuildPropertiesFile (target.getChildFile ("build.properties"));
  93. writeDefaultPropertiesFile (target.getChildFile ("default.properties"));
  94. writeLocalPropertiesFile (target.getChildFile ("local.properties"));
  95. writeIcon (target.getChildFile ("res/drawable-hdpi/icon.png"), 72);
  96. writeIcon (target.getChildFile ("res/drawable-mdpi/icon.png"), 48);
  97. writeIcon (target.getChildFile ("res/drawable-ldpi/icon.png"), 36);
  98. writeStringsFile (target.getChildFile ("res/values/strings.xml"));
  99. }
  100. private:
  101. //==============================================================================
  102. XmlElement* createManifestXML()
  103. {
  104. XmlElement* manifest = new XmlElement ("manifest");
  105. manifest->setAttribute ("xmlns:android", "http://schemas.android.com/apk/res/android");
  106. manifest->setAttribute ("android:versionCode", "1");
  107. manifest->setAttribute ("android:versionName", "1.0");
  108. manifest->setAttribute ("package", "com.juce");
  109. XmlElement* screens = manifest->createNewChildElement ("supports-screens");
  110. screens->setAttribute ("android:smallScreens", "true");
  111. screens->setAttribute ("android:normalScreens", "true");
  112. screens->setAttribute ("android:largeScreens", "true");
  113. screens->setAttribute ("android:xlargeScreens", "true");
  114. screens->setAttribute ("android:anyDensity", "true");
  115. XmlElement* app = manifest->createNewChildElement ("application");
  116. app->setAttribute ("android:label", "@string/app_name");
  117. app->setAttribute ("android:icon", "@drawable/icon");
  118. XmlElement* act = app->createNewChildElement ("activity");
  119. act->setAttribute ("android:name", "JuceAppActivity");
  120. act->setAttribute ("android:label", "@string/app_name");
  121. XmlElement* intent = act->createNewChildElement ("intent-filter");
  122. intent->createNewChildElement ("action")->setAttribute ("android:name", "android.intent.action.MAIN");
  123. intent->createNewChildElement ("category")->setAttribute ("android:name", "android.intent.category.LAUNCHER");
  124. return manifest;
  125. }
  126. //==============================================================================
  127. void findAllFilesToCompile (const Project::Item& projectItem, Array<RelativePath>& results)
  128. {
  129. if (projectItem.isGroup())
  130. {
  131. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  132. findAllFilesToCompile (projectItem.getChild(i), results);
  133. }
  134. else
  135. {
  136. if (projectItem.shouldBeCompiled())
  137. results.add (RelativePath (projectItem.getFile(), getTargetFolder(), RelativePath::buildTargetFolder));
  138. }
  139. }
  140. void writeJNIMakefile (const File& file)
  141. {
  142. Array<RelativePath> files;
  143. findAllFilesToCompile (project.getMainGroup(), files);
  144. for (int i = 0; i < juceWrapperFiles.size(); ++i)
  145. if (shouldFileBeCompiledByDefault (juceWrapperFiles.getReference(i)))
  146. files.add (juceWrapperFiles.getReference(i));
  147. MemoryOutputStream mo;
  148. writeJNIMakefile (mo, files);
  149. overwriteFileIfDifferentOrThrow (file, mo);
  150. }
  151. void writeJNIMakefile (OutputStream& out, const Array<RelativePath>& files)
  152. {
  153. out << "# Automatically generated makefile, created by the Jucer" << newLine
  154. << "# Don't edit this file! Your changes will be overwritten when you re-save the Jucer project!" << newLine
  155. << newLine
  156. << "LOCAL_PATH := $(call my-dir)" << newLine
  157. << newLine
  158. << "include $(CLEAR_VARS)" << newLine
  159. << newLine
  160. << "LOCAL_CPP_EXTENSION := cpp" << newLine
  161. << "LOCAL_MODULE := juce_jni" << newLine
  162. << "LOCAL_SRC_FILES := \\" << newLine;
  163. for (int i = 0; i < files.size(); ++i)
  164. out << " ../" << escapeSpaces (files.getReference(i).toUnixStyle()) << "\\" << newLine;
  165. String cFlags ("-fsigned-char");
  166. out << newLine
  167. << "ifeq ($(CONFIG),Debug)" << newLine
  168. << " LOCAL_CFLAGS += -g " << cFlags << createPreprocessorDefs (true) << newLine
  169. << "else" << newLine
  170. << " LOCAL_CFLAGS += " << cFlags << createPreprocessorDefs (false) << newLine
  171. << "endif" << newLine
  172. << newLine
  173. << "include $(BUILD_SHARED_LIBRARY)" << newLine;
  174. }
  175. const String createPreprocessorDefs (bool forDebug)
  176. {
  177. StringPairArray defines;
  178. defines.set ("JUCE_ANDROID", "1");
  179. if (forDebug)
  180. {
  181. defines.set ("DEBUG", "1");
  182. defines.set ("_DEBUG", "1");
  183. }
  184. else
  185. {
  186. defines.set ("NDEBUG", "1");
  187. }
  188. for (int i = 0; i < project.getNumConfigurations(); ++i)
  189. {
  190. Project::BuildConfiguration config (project.getConfiguration(i));
  191. if (config.isDebug() == forDebug)
  192. {
  193. defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config));
  194. break;
  195. }
  196. }
  197. return createGCCPreprocessorFlags (defines);
  198. }
  199. //==============================================================================
  200. XmlElement* createAntBuildXML()
  201. {
  202. XmlElement* proj = new XmlElement ("project");
  203. proj->setAttribute ("name", project.getProjectName().toString());
  204. proj->setAttribute ("default", "help");
  205. proj->createNewChildElement ("property")->setAttribute ("file", "local.properties");
  206. proj->createNewChildElement ("property")->setAttribute ("file", "build.properties");
  207. proj->createNewChildElement ("property")->setAttribute ("file", "default.properties");
  208. XmlElement* path = proj->createNewChildElement ("path");
  209. path->setAttribute ("id", "android.antlibs");
  210. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/anttasks.jar");
  211. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/sdklib.jar");
  212. path->createNewChildElement ("pathelement")->setAttribute ("path", "${sdk.dir}/tools/lib/androidprefs.jar");
  213. XmlElement* taskdef = proj->createNewChildElement ("taskdef");
  214. taskdef->setAttribute ("name", "setup");
  215. taskdef->setAttribute ("classname", "com.android.ant.SetupTask");
  216. taskdef->setAttribute ("classpathref", "android.antlibs");
  217. addNDKBuildStep (proj, "clean", "clean");
  218. //addLinkStep (proj, "${basedir}/" + rebaseFromProjectFolderToBuildTarget (RelativePath()).toUnixStyle() + "/", "jni/app");
  219. addLinkStep (proj, "${basedir}/" + getJucePathFromTargetFolder().toUnixStyle() + "/src/native/android/java/", "src/com/juce");
  220. addNDKBuildStep (proj, "debug", "CONFIG=Debug");
  221. addNDKBuildStep (proj, "release", "CONFIG=Release");
  222. proj->createNewChildElement ("setup");
  223. return proj;
  224. }
  225. static void addNDKBuildStep (XmlElement* project, const String& type, const String& arg)
  226. {
  227. XmlElement* target = project->createNewChildElement ("target");
  228. target->setAttribute ("name", type);
  229. XmlElement* executable = target->createNewChildElement ("exec");
  230. executable->setAttribute ("executable", "${ndk.dir}/ndk-build");
  231. executable->setAttribute ("dir", "${basedir}");
  232. executable->setAttribute ("failonerror", "true");
  233. executable->createNewChildElement ("arg")->setAttribute ("value", arg);
  234. }
  235. static void addLinkStep (XmlElement* project, const String& from, const String& to)
  236. {
  237. XmlElement* executable = project->createNewChildElement ("exec");
  238. executable->setAttribute ("executable", "ln");
  239. executable->setAttribute ("dir", "${basedir}");
  240. executable->setAttribute ("failonerror", "false");
  241. executable->createNewChildElement ("arg")->setAttribute ("value", "-s");
  242. executable->createNewChildElement ("arg")->setAttribute ("value", from);
  243. executable->createNewChildElement ("arg")->setAttribute ("value", to);
  244. }
  245. void writeBuildPropertiesFile (const File& file)
  246. {
  247. MemoryOutputStream mo;
  248. mo << "# This file is used to override default values used by the Ant build system." << newLine;
  249. overwriteFileIfDifferentOrThrow (file, mo);
  250. }
  251. void writeDefaultPropertiesFile (const File& file)
  252. {
  253. MemoryOutputStream mo;
  254. mo << "# This file is used to override default values used by the Ant build system." << newLine
  255. << "# It is automatically generated - DO NOT EDIT IT or your changes will be lost!." << newLine
  256. << newLine
  257. << "target=android-9"
  258. << newLine;
  259. overwriteFileIfDifferentOrThrow (file, mo);
  260. }
  261. void writeLocalPropertiesFile (const File& file)
  262. {
  263. MemoryOutputStream mo;
  264. mo << "# This file is used to override default values used by the Ant build system." << newLine
  265. << "# It is automatically generated by the Jucer - DO NOT EDIT IT or your changes will be lost!." << newLine
  266. << newLine
  267. << "sdk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getSDKPath().toString())) << newLine
  268. << "ndk.dir=" << escapeSpaces (replacePreprocessorDefs (getAllPreprocessorDefs(), getNDKPath().toString())) << newLine
  269. << newLine;
  270. overwriteFileIfDifferentOrThrow (file, mo);
  271. }
  272. void writeIcon (const File& file, int size)
  273. {
  274. Image im (project.getBestIconForSize (size, false));
  275. if (im.isValid())
  276. {
  277. PNGImageFormat png;
  278. MemoryOutputStream mo;
  279. if (! png.writeImageToStream (im, mo))
  280. throw SaveError ("Can't generate Android icon file");
  281. overwriteFileIfDifferentOrThrow (file, mo);
  282. }
  283. }
  284. void writeStringsFile (const File& file)
  285. {
  286. XmlElement strings ("resources");
  287. XmlElement* name = strings.createNewChildElement ("string");
  288. name->setAttribute ("name", "app_name");
  289. name->addTextElement (project.getProjectName().toString());
  290. writeXmlOrThrow (strings, file, "utf-8", 100);
  291. }
  292. //==============================================================================
  293. JUCE_DECLARE_NON_COPYABLE (AndroidProjectExporter);
  294. };
  295. #endif // __JUCER_PROJECTEXPORT_ANDROID_JUCEHEADER__