484c0a6f797270e7140ed6aa30bdfd8664aaabe9
[nit.git] / src / platform / android_annotations.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
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 # Annotations to gather metadata on Android projects. Get the metadata
18 # by calling `ModelBuilder::android_project_for`.
19 module android_annotations
20
21 private import parser_util
22 import modelize
23 import literal
24 import semantize
25 private import annotation
26
27 # Metadata associated to an Android project
28 class AndroidProject
29 # Name of the resulting application
30 var name: nullable String = null
31
32 # Java package used to identify the APK
33 var java_package: nullable String = null
34
35 # Version of the Android application and APK
36 var version: nullable String = null
37
38 # Numerical version code of the Android application and APK
39 var version_code: Int = 0
40
41 # Custom lines to add to the AndroidManifest.xml in the <manifest> node
42 var manifest_lines = new Array[String]
43
44 # Custom lines to add to the AndroidManifest.xml in the <application> node
45 var manifest_application_lines = new Array[String]
46
47 # Custom lines to add to AndroidManifest.xml as attributes inside the <activity> node
48 var manifest_activity_attributes = new Array[String]
49
50 # Minimum API level required for the application to run
51 var min_api: nullable Int = null
52
53 # Build target API level
54 var target_api: nullable Int = null
55
56 # Maximum API level on which the application will be allowed to run
57 var max_api: nullable Int = null
58
59 redef fun to_s do return """
60 name: {{{name or else "null"}}}
61 namespace: {{{java_package or else "null"}}}
62 version: {{{version or else "null"}}}"""
63 end
64
65 redef class ModelBuilder
66 # Get the `AndroidProject` gathered from `mmodule` and its importations
67 fun android_project_for(mmodule: MModule): AndroidProject
68 do
69 var project = new AndroidProject
70
71 var annot = lookup_annotation_on_modules("app_name", mmodule)
72 if annot != null then project.name = annot.arg_as_string(self)
73
74 annot = lookup_annotation_on_modules("app_version", mmodule)
75 if annot != null then project.version = annot.as_version(self)
76
77 annot = lookup_annotation_on_modules("java_package", mmodule)
78 if annot != null then project.java_package = annot.arg_as_string(self)
79
80 var annots = collect_annotations_on_modules("min_api_version", mmodule)
81 if not annots.is_empty then
82 var i = annots.pop.arg_as_int(self)
83 if i == null then i = 0
84 project.min_api = i
85 for an in annots do
86 i = an.arg_as_int(self)
87 if i == null then continue
88 project.min_api = project.min_api.max(i)
89 end
90 end
91
92 annots = collect_annotations_on_modules("max_api_version", mmodule)
93 if not annots.is_empty then
94 var i = annots.pop.arg_as_int(self)
95 if i == null then i = 0
96 project.max_api = i
97 for an in annots do
98 i = an.arg_as_int(self)
99 if i == null then continue
100 project.max_api = project.max_api.min(i)
101 end
102 end
103
104 annot = lookup_annotation_on_modules("target_api_version", mmodule)
105 if annot != null then project.target_api = annot.arg_as_int(self) or else 0
106
107 annots = collect_annotations_on_modules("android_manifest", mmodule)
108 for an in annots do project.manifest_lines.add an.arg_as_string(self) or else ""
109
110 annots = collect_annotations_on_modules("android_manifest_application", mmodule)
111 for an in annots do project.manifest_application_lines.add an.arg_as_string(self) or else ""
112
113 annots = collect_annotations_on_modules("android_manifest_activity", mmodule)
114 for an in annots do project.manifest_activity_attributes.add an.arg_as_string(self) or else ""
115
116 # Get the date and time (down to the minute) as string
117 var local_time = new Tm.localtime
118 var local_time_s = local_time.strftime("%y%m%d%H%M")
119 project.version_code = local_time_s.to_i
120
121 toolcontext.check_errors
122
123 return project
124 end
125 end
126
127 redef class AAnnotation
128 # Returns a version string (example: "1.5.6b42a7c") from an annotation `version(1, 5, git_revision)`.
129 #
130 # The user can enter as many fields as needed. The call to `git_revision` will be replaced by the short
131 # revision number. If the working tree is dirty, it will append another field with "d" for dirty.
132 private fun as_version(modelbuilder: ModelBuilder): String
133 do
134 var version_fields = new Array[Object]
135
136 var args = n_args
137 if args.length < 1 then
138 modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.")
139 return ""
140 else
141 for arg in args do
142 var format_error = "Annotation error: \"{name}\" expects its arguments to be of type Int or a call to `git_revision`"
143
144 var value
145 value = arg.as_int
146 if value != null then
147 version_fields.add value
148 continue
149 end
150
151 value = arg.as_string
152 if value != null then
153 version_fields.add value
154 end
155
156 value = arg.as_id
157 if value == "git_revision" then
158 # Get Git short revision
159 var proc = new ProcessReader("git", "rev-parse", "--short", "HEAD")
160 proc.wait
161 assert proc.status == 0
162 var lines = proc.read_all
163 var revision = lines.split("\n").first
164
165 # Is it dirty?
166 # If not, the return of `git diff --shortstat` is an empty line
167 proc = new ProcessReader("git", "diff-index", "--quiet", "HEAD")
168 proc.wait
169 var dirty = proc.status != 0
170 if dirty then revision += ".d"
171
172 version_fields.add revision
173 continue
174 end
175
176 modelbuilder.error(self, format_error)
177 return ""
178 end
179 end
180
181 return version_fields.join(".")
182 end
183 end