From: Jean Privat Date: Fri, 3 Apr 2015 01:27:33 +0000 (+0700) Subject: Merge: Unite app.nit metadata annotations common to Android and iOS X-Git-Tag: v0.7.4~42 X-Git-Url: http://nitlanguage.org?hp=3926f1a682afcab3d38ee4f73aa65f58c912d1a1 Merge: Unite app.nit metadata annotations common to Android and iOS * 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 Reviewed-by: Alexandre Terrasa --- diff --git a/examples/calculator/src/calculator_android.nit b/examples/calculator/src/calculator_android.nit index ec7b374..1b1e686 100644 --- a/examples/calculator/src/calculator_android.nit +++ b/examples/calculator/src/calculator_android.nit @@ -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"""" diff --git a/examples/mnit_dino/src/dino_android.nit b/examples/mnit_dino/src/dino_android.nit index aac6d47..be92d49 100644 --- a/examples/mnit_dino/src/dino_android.nit +++ b/examples/mnit_dino/src/dino_android.nit @@ -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 diff --git a/examples/mnit_simple/src/complete_simple_android.nit b/examples/mnit_simple/src/complete_simple_android.nit index 8700ab2..f8cff4f 100644 --- a/examples/mnit_simple/src/complete_simple_android.nit +++ b/examples/mnit_simple/src/complete_simple_android.nit @@ -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 diff --git a/lib/android/README.md b/lib/android/README.md index 41f4830..fb11c65 100644 --- a/lib/android/README.md +++ b/lib/android/README.md @@ -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}`. diff --git a/lib/android/aware.nit b/lib/android/aware.nit index bb512d2..fec387d 100644 --- a/lib/android/aware.nit +++ b/lib/android/aware.nit @@ -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 diff --git a/lib/android/examples/src/ui_test.nit b/lib/android/examples/src/ui_test.nit index f6c2a1b..bdb06c5 100644 --- a/lib/android/examples/src/ui_test.nit +++ b/lib/android/examples/src/ui_test.nit @@ -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 diff --git a/lib/app/app_base.nit b/lib/app/app_base.nit index a528d57..f892033 100644 --- a/lib/app/app_base.nit +++ b/lib/app/app_base.nit @@ -16,6 +16,7 @@ module app_base is new_annotation app_name + new_annotation app_namespace new_annotation app_version end diff --git a/lib/ios/examples/hello_ios.nit b/lib/ios/examples/hello_ios.nit index d11ffcb..3269906 100644 --- a/lib/ios/examples/hello_ios.nit +++ b/lib/ios/examples/hello_ios.nit @@ -13,7 +13,11 @@ # 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 diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 1a551d6..894a13d 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -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 diff --git a/src/model/mmodule.nit b/src/model/mmodule.nit index d88a0ba..ef548a8 100644 --- a/src/model/mmodule.nit +++ b/src/model/mmodule.nit @@ -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 diff --git a/src/platform/android.nit b/src/platform/android.nit index 2c9e706..404d780 100644 --- a/src/platform/android.nit +++ b/src/platform/android.nit @@ -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 diff --git a/src/platform/android_annotations.nit b/src/platform/android_annotations.nit index 717c6ae..347c4dc 100644 --- a/src/platform/android_annotations.nit +++ b/src/platform/android_annotations.nit @@ -14,29 +14,14 @@ # 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 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 index 0000000..ab0b067 --- /dev/null +++ b/src/platform/app_annotations.nit @@ -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 diff --git a/src/platform/ios.nit b/src/platform/ios.nit index 94d0c5f..f81b5d2 100644 --- a/src/platform/ios.nit +++ b/src/platform/ios.nit @@ -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) diff --git a/src/platform/xcode_templates.nit b/src/platform/xcode_templates.nit index de9d500..e4ebbdf 100644 --- a/src/platform/xcode_templates.nit +++ b/src/platform/xcode_templates.nit @@ -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 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - {{{package_name}}}.$(PRODUCT_NAME:rfc1034identifier) + {{{bundle_identifier}}} CFBundleInfoDictionaryVersion 6.0 CFBundleName - $(PRODUCT_NAME) + {{{product_name}}} CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + {{{short_version}}} CFBundleSignature \\?\\?\\?\\? CFBundleVersion - 1 + {{{bundle_version}}} LSRequiresIPhoneOS UIRequiredDeviceCapabilities