android: adds support for the android platform
[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/"
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 # compile normal C files
89 super(compiler, compile_dir, cfiles)
90
91 # Gather extra C files generated elsewhere than in super
92 for f in compiler.extern_bodies do
93 if f isa ExternCFile then cfiles.add(f.filename.basename(""))
94 end
95
96 ### generate makefile into "{compile_dir}/Android.mk"
97 if not dir.file_exists then dir.mkdir
98 var file = new OFStream.open("{dir}/Android.mk")
99 file.write """
100 LOCAL_PATH := $(call my-dir)
101 include $(CLEAR_VARS)
102
103 LOCAL_CFLAGS := -D ANDROID
104 LOCAL_MODULE := main
105 LOCAL_SRC_FILES := \\
106 {{{cfiles.join(" \\\n")}}}
107 LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -lz
108 LOCAL_STATIC_LIBRARIES := android_native_app_glue
109
110 include $(BUILD_SHARED_LIBRARY)
111
112 $(call import-module,android/native_app_glue)
113 """
114 file.close
115
116 ### generate AndroidManifest.xml
117 dir = android_project_root
118 file = new OFStream.open("{dir}/AndroidManifest.xml")
119 file.write """<?xml version="1.0" encoding="utf-8"?>
120 <!-- BEGIN_INCLUDE(manifest) -->
121 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
122 package="{{{app_package}}}"
123 android:versionCode="1"
124 android:versionName="{{{app_version}}}"
125 android:debuggable="true">
126
127 <!-- This is the platform API where NativeActivity was introduced. -->
128 <uses-sdk android:minSdkVersion="9" />
129
130 <!-- This .apk has no Java code itself, so set hasCode to false. -->
131 <application android:label="@string/app_name" android:hasCode="false" android:debuggable="true">
132
133 <!-- Our activity is the built-in NativeActivity framework class.
134 This will take care of integrating with our NDK code. -->
135 <activity android:name="android.app.NativeActivity"
136 android:label="@string/app_name"
137 android:configChanges="orientation|keyboardHidden">
138 <!-- Tell NativeActivity the name of or .so -->
139 <meta-data android:name=\"{{{app_package}}}\"
140 android:value=\"{{{app_name}}}\" />
141 <intent-filter>
142 <action android:name="android.intent.action.MAIN" />
143 <category android:name="android.intent.category.LAUNCHER" />
144 </intent-filter>
145 </activity>
146 </application>
147
148 </manifest>
149 <!-- END_INCLUDE(manifest) -->
150 """
151 file.close
152
153 ### generate res/values/strings.xml
154 dir = "{android_project_root}/res/"
155 if not dir.file_exists then dir.mkdir
156 dir = "{dir}/values/"
157 if not dir.file_exists then dir.mkdir
158 file = new OFStream.open("{dir}/strings.xml")
159 file.write """<?xml version="1.0" encoding="utf-8"?>
160 <resources>
161 <string name="app_name">{{{app_name}}}</string>
162 </resources>"""
163 file.close
164 end
165
166 redef fun write_makefile(compiler, compile_dir, cfiles)
167 do
168 # Do nothing, already done in `write_files`
169 end
170
171 redef fun compile_c_code(compiler, compile_dir)
172 do
173 # Compile C code (and thus Nit)
174 toolcontext.exec_and_check(["ndk-build", "-s", "-j", "4", "-C", android_project_root])
175
176 # Generate the apk
177 toolcontext.exec_and_check(["ant", "-q", "debug", "-f", android_project_root+"/build.xml"])
178 end
179 end