Merge: Unite app.nit metadata annotations common to Android and iOS
authorJean Privat <jean@pryen.org>
Fri, 3 Apr 2015 01:27:33 +0000 (08:27 +0700)
committerJean Privat <jean@pryen.org>
Fri, 3 Apr 2015 01:27:33 +0000 (08:27 +0700)
* Rename the annotation `java_package` to `app_namespace`.
* Move up `app_name`, `app_version` and `app_namespace` and their default values from the Android platform.
* Apply these annotations in iOS too.
* Use the annotations in Hello iOS.

Pull-Request: #1233
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

15 files changed:
examples/calculator/src/calculator_android.nit
examples/mnit_dino/src/dino_android.nit
examples/mnit_simple/src/complete_simple_android.nit
lib/android/README.md
lib/android/aware.nit
lib/android/examples/src/ui_test.nit
lib/app/app_base.nit
lib/ios/examples/hello_ios.nit
src/compiler/abstract_compiler.nit
src/model/mmodule.nit
src/platform/android.nit
src/platform/android_annotations.nit
src/platform/app_annotations.nit [new file with mode: 0644]
src/platform/ios.nit
src/platform/xcode_templates.nit

index ec7b374..1b1e686 100644 (file)
@@ -18,7 +18,7 @@
 module calculator_android is
        app_name "app.nit Calc."
        app_version(0, 1, git_revision)
-       java_package "org.nitlanguage.calculator"
+       app_namespace "org.nitlanguage.calculator"
 
        # Lock in portrait mode
        android_manifest_activity """android:screenOrientation="portrait""""
index aac6d47..be92d49 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 module dino_android is
-       java_package("org.nitlanguage.dino")
+       app_namespace "org.nitlanguage.dino"
 end
 
 import dino
index 8700ab2..f8cff4f 100644 (file)
@@ -15,8 +15,8 @@
 # limitations under the License.
 
 module complete_simple_android is
-       java_package("org.nitlanguage.test_all")
-       target_api_version(19)
+       app_namespace "org.nitlanguage.test_all"
+       target_api_version 19
 end
 
 import test_bundle
index 41f4830..fb11c65 100644 (file)
@@ -32,7 +32,7 @@ as the launcher name.
 
     Example: `app_name "My App"`
 
-* `java_package` specifies the package used by the generated Java
+* `app_namespace` specifies the package used by the generated Java
 classes and the APK file. Once the application is published, this
 value should not be changed. By default, the compiler will use
 the package `org.nitlanguage.{module_name}`.
index bb512d2..fec387d 100644 (file)
@@ -20,7 +20,6 @@
 # used to tag `ldflags` annotations.
 module aware is
        new_annotation android
-       new_annotation java_package
        new_annotation min_api_version
        new_annotation max_api_version
        new_annotation target_api_version
index f6c2a1b..bdb06c5 100644 (file)
@@ -16,9 +16,9 @@
 
 # Test for app.nit's UI services
 module ui_test is
-       app_name("app.nit UI test")
+       app_name "app.nit UI test"
        app_version(0, 1, git_revision)
-       java_package("org.nitlanguage.ui_test")
+       app_namespace "org.nitlanguage.ui_test"
        android_manifest_activity """android:theme="@android:style/Theme.Light""""
 end
 
index a528d57..f892033 100644 (file)
@@ -16,6 +16,7 @@
 
 module app_base is
        new_annotation app_name
+       new_annotation app_namespace
        new_annotation app_version
 end
 
index d11ffcb..3269906 100644 (file)
 # limitations under the License.
 
 # Simple iOS app with a single label
-module hello_ios
+module hello_ios is
+       app_name "Hello iOS"
+       app_namespace "nit.app.hello_ios"
+       app_version(0, 5, git_revision)
+end
 
 import ios
 
index 1a551d6..894a13d 100644 (file)
@@ -300,15 +300,8 @@ class MakefileToolchain
        # Get the default name of the executable to produce
        fun default_outname: String
        do
-               var mainmodule = compiler.mainmodule
-
-               # Search a non fictive module
-               var res = mainmodule.name
-               while mainmodule.is_fictive do
-                       mainmodule = mainmodule.in_importation.direct_greaters.first
-                       res = mainmodule.name
-               end
-               return res
+               var mainmodule = compiler.mainmodule.first_real_mmodule
+               return mainmodule.name
        end
 
        # Combine options and platform informations to get the final path of the outfile
index d88a0ba..ef548a8 100644 (file)
@@ -243,5 +243,15 @@ class MModule
        # Is `self` a unit test module used by `nitunit`?
        var is_test_suite: Bool = false is writable
 
+       # Get the first non `is_fictive` module greater than self
+       fun first_real_mmodule: MModule
+       do
+               var mmodule = self
+               while mmodule.is_fictive do
+                       mmodule = mmodule.in_importation.direct_greaters.first
+               end
+               return mmodule
+       end
+
        redef fun parent_concern do return mgroup
 end
index 2c9e706..404d780 100644 (file)
@@ -62,20 +62,18 @@ class AndroidToolchain
        redef fun write_files(compile_dir, cfiles)
        do
                var android_project_root = android_project_root.as(not null)
-               var project = toolcontext.modelbuilder.android_project_for(compiler.mainmodule)
-               var short_project_name = compiler.mainmodule.name.replace("-", "_")
+               var project = new AndroidProject(toolcontext.modelbuilder, compiler.mainmodule)
                var release = toolcontext.opt_release.value
 
                var app_name = project.name
-               if app_name == null then app_name = compiler.mainmodule.name
                if not release then app_name += " Debug"
 
-               var app_package = project.java_package
-               if app_package == null then app_package = "org.nitlanguage.{short_project_name}"
+               var short_project_name = project.short_name
+
+               var app_package = project.namespace
                if not release then app_package += "_debug"
 
                var app_version = project.version
-               if app_version == null then app_version = "1.0"
 
                var app_min_api = project.min_api
                if app_min_api == null then app_min_api = 10
index 717c6ae..347c4dc 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Annotations to gather metadata on Android projects. Get the metadata
-# by calling `ModelBuilder::android_project_for`.
+# Additionnal annotations to gather metadata on Android projects
 module android_annotations
 
-private import parser_util
-import modelize
-import literal
-import semantize
-private import annotation
+intrude import app_annotations
 
 # Metadata associated to an Android project
 class AndroidProject
-       # Name of the resulting application
-       var name: nullable String = null
-
-       # Java package used to identify the APK
-       var java_package: nullable String = null
-
-       # Version of the Android application and APK
-       var version: nullable String = null
-
-       # Numerical version code of the Android application and APK
-       var version_code: Int = 0
+       super AppProject
 
        # Custom lines to add to the AndroidManifest.xml in the <manifest> node
        var manifest_lines = new Array[String]
@@ -59,134 +44,48 @@ class AndroidProject
        # Activities to declare in the manifest
        var activities = new Array[String]
 
-       redef fun to_s do return """
-name: {{{name or else "null"}}}
-namespace: {{{java_package or else "null"}}}
-version: {{{version or else "null"}}}"""
-end
-
-redef class ModelBuilder
-       # Get the `AndroidProject` gathered from `mmodule` and its importations
-       fun android_project_for(mmodule: MModule): AndroidProject
+       init
        do
-               var project = new AndroidProject
-
-               var annot = lookup_annotation_on_modules("app_name", mmodule)
-               if annot != null then project.name = annot.arg_as_string(self)
-
-               annot =  lookup_annotation_on_modules("app_version", mmodule)
-               if annot != null then project.version =  annot.as_version(self)
-
-               annot = lookup_annotation_on_modules("java_package", mmodule)
-               if annot != null then project.java_package = annot.arg_as_string(self)
-
-               var annots = collect_annotations_on_modules("min_api_version", mmodule)
+               var annots = modelbuilder.collect_annotations_on_modules("min_api_version", mainmodule)
                if not annots.is_empty then
-                       var i = annots.pop.arg_as_int(self)
+                       var i = annots.pop.arg_as_int(modelbuilder)
                        if i == null then i = 0
-                       project.min_api = i
+                       min_api = i
                        for an in annots do
-                               i = an.arg_as_int(self)
+                               i = an.arg_as_int(modelbuilder)
                                if i == null then continue
-                               project.min_api = project.min_api.max(i)
+                               min_api = min_api.max(i)
                        end
                end
 
-               annots = collect_annotations_on_modules("max_api_version", mmodule)
+               annots = modelbuilder.collect_annotations_on_modules("max_api_version", mainmodule)
                if not annots.is_empty then
-                       var i = annots.pop.arg_as_int(self)
+                       var i = annots.pop.arg_as_int(modelbuilder)
                        if i == null then i = 0
-                       project.max_api = i
+                       max_api = i
                        for an in annots do
-                               i = an.arg_as_int(self)
+                               i = an.arg_as_int(modelbuilder)
                                if i == null then continue
-                               project.max_api = project.max_api.min(i)
+                               max_api = max_api.min(i)
                        end
                end
 
-               annot = lookup_annotation_on_modules("target_api_version", mmodule)
-               if annot != null then project.target_api = annot.arg_as_int(self) or else 0
+               var annot = modelbuilder.lookup_annotation_on_modules("target_api_version", mainmodule)
+               if annot != null then target_api = annot.arg_as_int(modelbuilder) or else 0
 
-               annots = collect_annotations_on_modules("android_manifest", mmodule)
-               for an in annots do project.manifest_lines.add an.arg_as_string(self) or else ""
+               annots = modelbuilder.collect_annotations_on_modules("android_manifest", mainmodule)
+               for an in annots do manifest_lines.add an.arg_as_string(modelbuilder) or else ""
 
-               annots = collect_annotations_on_modules("android_manifest_application", mmodule)
-               for an in annots do project.manifest_application_lines.add an.arg_as_string(self) or else ""
+               annots = modelbuilder.collect_annotations_on_modules("android_manifest_application", mainmodule)
+               for an in annots do manifest_application_lines.add an.arg_as_string(modelbuilder) or else ""
 
-               annots = collect_annotations_on_modules("android_manifest_activity", mmodule)
-               for an in annots do project.manifest_activity_attributes.add an.arg_as_string(self) or else ""
+               annots = modelbuilder.collect_annotations_on_modules("android_manifest_activity", mainmodule)
+               for an in annots do manifest_activity_attributes.add an.arg_as_string(modelbuilder) or else ""
 
-               annots = collect_annotations_on_modules("android_activity", mmodule)
+               annots = modelbuilder.collect_annotations_on_modules("android_activity", mainmodule)
                for an in annots do
-                       var activity = an.arg_as_string(self)
-                       if activity != null then project.activities.add activity
+                       var activity = an.arg_as_string(modelbuilder)
+                       if activity != null then activities.add activity
                end
-
-               # Get the date and time (down to the minute) as string
-               var local_time = new Tm.localtime
-               var local_time_s = local_time.strftime("%y%m%d%H%M")
-               project.version_code = local_time_s.to_i
-
-               toolcontext.check_errors
-
-               return project
-       end
-end
-
-redef class AAnnotation
-       # Returns a version string (example: "1.5.6b42a7c") from an annotation `version(1, 5, git_revision)`.
-       #
-       # The user can enter as many fields as needed. The call to `git_revision` will be replaced by the short
-       # revision number. If the working tree is dirty, it will append another field with "d" for dirty.
-       private fun as_version(modelbuilder: ModelBuilder): String
-       do
-               var version_fields = new Array[Object]
-
-               var args = n_args
-               if args.length < 1 then
-                       modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.")
-                       return ""
-               else
-                       for arg in args do
-                               var format_error = "Annotation error: \"{name}\" expects its arguments to be of type Int or a call to `git_revision`"
-
-                               var value
-                               value = arg.as_int
-                               if value != null then
-                                       version_fields.add value
-                                       continue
-                               end
-
-                               value = arg.as_string
-                               if value != null then
-                                       version_fields.add value
-                               end
-
-                               value = arg.as_id
-                               if value == "git_revision" then
-                                       # Get Git short revision
-                                       var proc = new ProcessReader("git", "rev-parse", "--short", "HEAD")
-                                       proc.wait
-                                       assert proc.status == 0
-                                       var lines = proc.read_all
-                                       var revision = lines.split("\n").first
-
-                                       # Is it dirty?
-                                       # If not, the return of `git diff --shortstat` is an empty line
-                                       proc = new ProcessReader("git", "diff-index", "--quiet", "HEAD")
-                                       proc.wait
-                                       var dirty = proc.status != 0
-                                       if dirty then revision += ".d"
-
-                                       version_fields.add revision
-                                       continue
-                               end
-
-                               modelbuilder.error(self, format_error)
-                               return ""
-                       end
-               end
-
-               return version_fields.join(".")
        end
 end
diff --git a/src/platform/app_annotations.nit b/src/platform/app_annotations.nit
new file mode 100644 (file)
index 0000000..ab0b067
--- /dev/null
@@ -0,0 +1,132 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Annotations to gather metadata on `app.nit` projects
+module app_annotations
+
+private import parser_util
+import modelize
+import literal
+import semantize
+private import annotation
+
+# Metadata associated to an `app.nit` project
+class AppProject
+       # Pretty name of the resulting application
+       var name: String = mainmodule.first_real_mmodule.name is lazy
+
+       # Short project name used in `namespace` and configuration files
+       var short_name: String = mainmodule.name.replace("-", "_") is lazy
+
+       # Namespace/package used to identify the application
+       var namespace = "org.nitlanguage.{short_name}" is lazy
+
+       # Version of the application
+       var version = "0.1"
+
+       # Numerical version code of the application
+       var version_code: Int is lazy do
+
+               # Get the date and time (down to the minute) as string
+               var local_time = new Tm.localtime
+               var local_time_s = local_time.strftime("%y%m%d%H%M")
+               return local_time_s.to_i
+       end
+
+       private var modelbuilder: ModelBuilder
+       private var mainmodule: MModule
+
+       init
+       do
+               var annot = modelbuilder.lookup_annotation_on_modules("app_name", mainmodule)
+               if annot != null then
+                       var val = annot.arg_as_string(modelbuilder)
+                       if val != null then name = val
+               end
+
+               annot = modelbuilder.lookup_annotation_on_modules("app_version", mainmodule)
+               if annot != null then version = annot.as_version(modelbuilder)
+
+               annot = modelbuilder.lookup_annotation_on_modules("app_namespace", mainmodule)
+               if annot != null then
+                       var val = annot.arg_as_string(modelbuilder)
+                       if val != null then namespace = val
+               end
+
+               modelbuilder.toolcontext.check_errors
+       end
+
+       redef fun to_s do return """
+name: {{{name or else "null"}}}
+namespace: {{{namespace or else "null"}}}
+version: {{{version or else "null"}}}"""
+end
+
+redef class AAnnotation
+       # Returns a version string (example: "1.5.6b42a7c") from an annotation `version(1, 5, git_revision)`.
+       #
+       # The user can enter as many fields as needed. The call to `git_revision` will be replaced by the short
+       # revision number. If the working tree is dirty, it will append another field with "d" for dirty.
+       private fun as_version(modelbuilder: ModelBuilder): String
+       do
+               var version_fields = new Array[Object]
+
+               var args = n_args
+               if args.length < 1 then
+                       modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.")
+                       return ""
+               else
+                       for arg in args do
+                               var format_error = "Annotation error: \"{name}\" expects its arguments to be of type Int or a call to `git_revision`"
+
+                               var value
+                               value = arg.as_int
+                               if value != null then
+                                       version_fields.add value
+                                       continue
+                               end
+
+                               value = arg.as_string
+                               if value != null then
+                                       version_fields.add value
+                               end
+
+                               value = arg.as_id
+                               if value == "git_revision" then
+                                       # Get Git short revision
+                                       var proc = new ProcessReader("git", "rev-parse", "--short", "HEAD")
+                                       proc.wait
+                                       assert proc.status == 0
+                                       var lines = proc.read_all
+                                       var revision = lines.split("\n").first
+
+                                       # Is it dirty?
+                                       # If not, the return of `git diff --shortstat` is an empty line
+                                       proc = new ProcessReader("git", "diff-index", "--quiet", "HEAD")
+                                       proc.wait
+                                       var dirty = proc.status != 0
+                                       if dirty then revision += ".d"
+
+                                       version_fields.add revision
+                                       continue
+                               end
+
+                               modelbuilder.error(self, format_error)
+                               return ""
+                       end
+               end
+
+               return version_fields.join(".")
+       end
+end
index 94d0c5f..f81b5d2 100644 (file)
@@ -18,7 +18,7 @@ module ios
 import platform
 import compiler::abstract_compiler
 import xcode_templates
-private import annotation
+import app_annotations
 
 redef class ToolContext
        redef fun platform_from_name(name)
@@ -42,24 +42,16 @@ private class IOSToolchain
        # Root of the iOS project, usually `.nit_compile/ios/`
        var ios_project_root: String is noinit
 
-       redef fun default_outname do return "{super}.app"
+       # `app.nit` project for the current compilation target
+       var app_project = new AppProject(compiler.modelbuilder, compiler.mainmodule) is lazy
 
-       # Name of the current project of `compiler`
-       fun project_name: String
-       do
-               var project_name = null
-               # TODO unite the app_name annotation from Android with iOS
-               var annot = compiler.modelbuilder.lookup_annotation_on_modules("app_name", compiler.mainmodule)
-               if annot != null then project_name = annot.arg_as_string(compiler.modelbuilder)
-               if project_name == null then project_name = compiler.mainmodule.name
-               return project_name
-       end
+       redef fun default_outname do return "{super}.app"
 
-       # Compile C files in `ios_project_root/project_name`
+       # Compile C files in `ios_project_root/app_project.name`
        redef fun compile_dir
        do
                ios_project_root = super/"ios"
-               return ios_project_root/project_name
+               return ios_project_root/app_project.short_name
        end
 
        redef fun write_files(compile_dir, cfiles)
@@ -73,7 +65,7 @@ private class IOSToolchain
 
        redef fun write_makefile(compile_dir, cfiles)
        do
-               var project_name = project_name
+               var project_name = app_project.short_name
 
                # Create an XCode project directory
                var dir = ios_project_root/project_name+".xcodeproj"
@@ -94,13 +86,14 @@ private class IOSToolchain
 
                # Create the plist in the same directory as the generated C code
                if not compile_dir.file_exists then compile_dir.mkdir
-               var plist = new PlistTemplate("org.nitlanguage") # TODO customize using an annotation
+               var plist = new PlistTemplate(app_project.name, app_project.namespace,
+                       app_project.version, app_project.version_code.to_s)
                plist.write_to_file compile_dir/"Info.plist"
        end
 
        redef fun compile_c_code(compile_dir)
        do
-               var project_name = project_name
+               var project_name = app_project.short_name
                var release = toolcontext.opt_release.value
                var outfile = outfile(compiler.mainmodule)
 
index de9d500..e4ebbdf 100644 (file)
@@ -627,8 +627,17 @@ end
 class PlistTemplate
        super Template
 
-       # Package of the app
-       var package_name: String
+       # Value of CFBundleName, pretty name of the application
+       var product_name: String
+
+       # Value of CFBundleIdentifier, namespace of the app
+       var bundle_identifier: String
+
+       # Value of CFBundleShortVersionString, human readable version
+       var short_version: String
+
+       # Value of CFBundleVersion, often a revision number
+       var bundle_version: String
 
        redef fun rendering
        do
@@ -642,19 +651,19 @@ class PlistTemplate
        <key>CFBundleExecutable</key>
        <string>$(EXECUTABLE_NAME)</string>
        <key>CFBundleIdentifier</key>
-       <string>{{{package_name}}}.$(PRODUCT_NAME:rfc1034identifier)</string>
+       <string>{{{bundle_identifier}}}</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
-       <string>$(PRODUCT_NAME)</string>
+       <string>{{{product_name}}}</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
-       <string>1.0</string>
+       <string>{{{short_version}}}</string>
        <key>CFBundleSignature</key>
        <string>\\?\\?\\?\\?</string>
        <key>CFBundleVersion</key>
-       <string>1</string>
+       <string>{{{bundle_version}}}</string>
        <key>LSRequiresIPhoneOS</key>
        <true/>
        <key>UIRequiredDeviceCapabilities</key>