Merge branch 'dump_rta'
[nit.git] / src / android_platform.nit
1 # This file is part of NIT ( http://www.nitlanguage.org )t
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Compile program for the Android platform
18 module android_platform
19
20 import platform
21 import abstract_compiler
22
23 redef class ToolContext
24 redef fun platform_from_name(name)
25 do
26 if name == "android" then return new AndroidPlatform
27 return super
28 end
29
30 fun exec_and_check(args: Array[String])
31 do
32 var prog = args.first
33 args.remove_at 0
34
35 # Is the wanted program available?
36 var proc_which = new IProcess.from_a("which", [prog])
37 proc_which.wait
38 var res = proc_which.status
39 if res != 0 then
40 print "Android project error: executable \"{prog}\" not found"
41 exit 1
42 end
43
44 # Execute the wanted program
45 var proc = new Process.from_a(prog, args)
46 proc.wait
47 res = proc.status
48 if res != 0 then
49 print "Android project error: execution of \"{prog} {args.join(" ")}\" failed"
50 exit 1
51 end
52 end
53 end
54
55 class AndroidPlatform
56 super Platform
57
58 redef fun toolchain(toolcontext) do return new AndroidToolchain(toolcontext)
59 end
60
61 class AndroidToolchain
62 super MakefileToolchain
63
64 var android_project_root: String
65
66 redef fun compile_dir
67 do
68 var normal_compile_dir = super
69 android_project_root = normal_compile_dir
70 return "{normal_compile_dir}/jni/nit_compile/"
71 end
72
73 redef fun write_files(compiler, compile_dir, cfiles)
74 do
75 var app_name = compiler.mainmodule.name
76 var app_package = "org.nitlanguage.{app_name}"
77 var app_version = "0.1"
78
79 var args = ["android", "-s", "create", "project", "--name", app_name,
80 "--target", "android-10", "--path", android_project_root,
81 "--package", app_package, "--activity", app_name]
82 toolcontext.exec_and_check(args)
83
84 # create compile_dir
85 var dir = "{android_project_root}/jni/"
86 if not dir.file_exists then dir.mkdir
87
88 dir = compile_dir
89 if not dir.file_exists then dir.mkdir
90
91 # compile normal C files
92 super(compiler, compile_dir, cfiles)
93
94 # Gather extra C files generated elsewhere than in super
95 for f in compiler.extern_bodies do
96 if f isa ExternCFile then cfiles.add(f.filename.basename(""))
97 end
98
99 ## Generate delagating makefile
100 dir = "{android_project_root}/jni/"
101 var file = new OFStream.open("{dir}/Android.mk")
102 file.write """
103 include $(call all-subdir-makefiles)
104 """
105 file.close
106
107 ### generate makefile into "{compile_dir}/Android.mk"
108 dir = compile_dir
109 file = new OFStream.open("{dir}/Android.mk")
110 file.write """
111 LOCAL_PATH := $(call my-dir)
112 include $(CLEAR_VARS)
113
114 LOCAL_CFLAGS := -D ANDROID
115 LOCAL_MODULE := main
116 LOCAL_SRC_FILES := \\
117 {{{cfiles.join(" \\\n")}}}
118 LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -lz
119 LOCAL_STATIC_LIBRARIES := android_native_app_glue png
120
121 include $(BUILD_SHARED_LIBRARY)
122
123 $(call import-module,android/native_app_glue)
124 """
125 file.close
126
127 ### generate AndroidManifest.xml
128 dir = android_project_root
129 file = new OFStream.open("{dir}/AndroidManifest.xml")
130 file.write """<?xml version="1.0" encoding="utf-8"?>
131 <!-- BEGIN_INCLUDE(manifest) -->
132 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
133 package="{{{app_package}}}"
134 android:versionCode="1"
135 android:versionName="{{{app_version}}}"
136 android:debuggable="true">
137
138 <!-- This is the platform API where NativeActivity was introduced. -->
139 <uses-sdk android:minSdkVersion="9" />
140
141 <!-- This .apk has no Java code itself, so set hasCode to false. -->
142 <application android:label="@string/app_name" android:hasCode="false" android:debuggable="true">
143
144 <!-- Our activity is the built-in NativeActivity framework class.
145 This will take care of integrating with our NDK code. -->
146 <activity android:name="android.app.NativeActivity"
147 android:label="@string/app_name"
148 android:configChanges="orientation|keyboardHidden"
149 android:screenOrientation="portrait">
150 <!-- Tell NativeActivity the name of or .so -->
151 <meta-data android:name=\"{{{app_package}}}\"
152 android:value=\"{{{app_name}}}\" />
153 <intent-filter>
154 <action android:name="android.intent.action.MAIN" />
155 <category android:name="android.intent.category.LAUNCHER" />
156 </intent-filter>
157 </activity>
158 </application>
159
160 </manifest>
161 <!-- END_INCLUDE(manifest) -->
162 """
163 file.close
164
165 ### generate res/values/strings.xml
166 dir = "{android_project_root}/res/"
167 if not dir.file_exists then dir.mkdir
168 dir = "{dir}/values/"
169 if not dir.file_exists then dir.mkdir
170 file = new OFStream.open("{dir}/strings.xml")
171 file.write """<?xml version="1.0" encoding="utf-8"?>
172 <resources>
173 <string name="app_name">{{{app_name}}}</string>
174 </resources>"""
175 file.close
176
177 ### Link to png sources
178 # libpng is not available on Android NDK
179 # FIXME make obtionnal when we have alternatives to mnit
180 var nit_dir = "NIT_DIR".environ
181 var share_dir
182 if not nit_dir.is_empty then
183 share_dir = "{nit_dir}/share/"
184 else
185 share_dir = "{sys.program_name.dirname}/../share/"
186 end
187 if not share_dir.file_exists then
188 print "Android project error: Nit share directory not found, please use the environment variable NIT_DIR"
189 exit 1
190 end
191 share_dir = share_dir.realpath
192 var target_png_dir = "{android_project_root}/jni/png"
193 if not target_png_dir.file_exists then
194 toolcontext.exec_and_check(["ln", "-s", "{share_dir}/png/", target_png_dir])
195 end
196
197 ### Link to assets (for mnit and others)
198 # This will be accessed from `android_project_root`
199 var mainmodule_dir = compiler.mainmodule.location.file.filename.dirname
200 var assets_dir = "{mainmodule_dir}/../assets"
201 if not assets_dir.file_exists then assets_dir = "{mainmodule_dir}/assets"
202 if assets_dir.file_exists then
203 assets_dir = assets_dir.realpath
204 var target_assets_dir = "{android_project_root}/assets"
205 if not target_assets_dir.file_exists then
206 toolcontext.exec_and_check(["ln", "-s", assets_dir, target_assets_dir])
207 end
208 end
209 end
210
211 redef fun write_makefile(compiler, compile_dir, cfiles)
212 do
213 # Do nothing, already done in `write_files`
214 end
215
216 redef fun compile_c_code(compiler, compile_dir)
217 do
218 # Compile C code (and thus Nit)
219 toolcontext.exec_and_check(["ndk-build", "-s", "-j", "4", "-C", android_project_root])
220
221 # Generate the apk
222 toolcontext.exec_and_check(["ant", "-q", "debug", "-f", android_project_root+"/build.xml"])
223
224 # Move the apk to the target
225 var outname = toolcontext.opt_output.value
226 if outname == null then outname = "{compiler.mainmodule.name}.apk"
227 toolcontext.exec_and_check(["mv", "{android_project_root}/bin/{compiler.mainmodule.name}-debug.apk", outname])
228 end
229 end