Merge: Misc for lib
authorJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 00:44:48 +0000 (07:44 +0700)
committerJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 00:44:48 +0000 (07:44 +0700)
Litttle features and warning-fixes for the libs

Pull-Request: #1228
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

17 files changed:
lib/ios/app.nit [new file with mode: 0644]
lib/ios/examples/hello_ios.nit [new file with mode: 0644]
lib/ios/ios.nit [new file with mode: 0644]
lib/ios/platform.nit [new file with mode: 0644]
lib/standard/exec_nit.c
lib/standard/string.nit
src/compiler/abstract_compiler.nit
src/compiler/compiler.nit
src/compiler/separate_compiler.nit
src/platform/android.nit
src/platform/emscripten.nit
src/platform/ios.nit [new file with mode: 0644]
src/platform/pnacl.nit
src/platform/xcode_templates.nit [new file with mode: 0644]
tests/Linux.skip
tests/sav/test_parser_args1.res
tests/test_platform_ios.nit [new file with mode: 0644]

diff --git a/lib/ios/app.nit b/lib/ios/app.nit
new file mode 100644 (file)
index 0000000..0e337bc
--- /dev/null
@@ -0,0 +1,191 @@
+# 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.
+
+# Basic structure for Nit apps on iOS
+module app
+
+import platform
+import ::app
+
+in "ObjC Header" `{
+       #import <UIKit/UIKit.h>
+
+       // Our interface to the iOS system
+       @interface AppDelegate: UIResponder <UIApplicationDelegate>
+
+               // The main window
+               @property (strong, nonatomic) UIWindow *window;
+       @end
+`}
+
+in "ObjC" `{
+
+       // Global reference to the App from app.nit
+       App app_nit_ios_app;
+
+       // Our own C argc and argv
+       int app_nit_ios_argc;
+       char **app_nit_ios_argv;
+
+       @implementation AppDelegate
+
+       - (BOOL)application:(UIApplication *)application
+               willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+               // Set aside `application` to be used from Nit
+               App_ui_application__assign(app_nit_ios_app, application);
+               App_app_delegate__assign(app_nit_ios_app, self);
+
+               // Complete the callback
+               return App_will_finish_launching_with_options(app_nit_ios_app);
+       }
+
+       - (BOOL)application:(UIApplication *)application
+               didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+               return App_did_finish_launching_with_options(app_nit_ios_app);
+       }
+
+       - (void)applicationWillResignActive:(UIApplication *)application {
+               App_will_resign_active(app_nit_ios_app);
+       }
+
+       - (void)applicationDidEnterBackground:(UIApplication *)application {
+               App_did_enter_background(app_nit_ios_app);
+       }
+
+       - (void)applicationWillEnterForeground:(UIApplication *)application {
+               App_will_enter_foreground(app_nit_ios_app);
+       }
+
+       - (void)applicationDidBecomeActive:(UIApplication *)application {
+               App_did_become_active(app_nit_ios_app);
+       }
+
+       - (void)applicationWillTerminate:(UIApplication *)application {
+               App_will_terminate(app_nit_ios_app);
+       }
+
+       @end
+`}
+
+# Application interface to the iOS system
+extern class AppDelegate in "ObjC" `{ AppDelegate * `}
+end
+
+# Graphical application to which events are sent
+extern class UIApplication in "ObjC" `{ UIApplication * `}
+end
+
+redef class App
+
+       # Main graphical application
+       var ui_application: UIApplication
+
+       # Application interface to the iOS system
+       var app_delegate: AppDelegate
+
+       # Copy back to C the command line arguments
+       #
+       # Nit extracts the first arguments from the `args` sequence,
+       # so we need to add it back. That's why Nit's `args` is smaller than in C.
+       private fun register_args(program_name: NativeString, argc: Int,
+       argv: Sequence[String]) import Sequence[String].[], String.to_cstring in "ObjC" `{
+               app_nit_ios_argc = argc+1;
+
+               // TODO copy or pin the strings when needed
+               app_nit_ios_argv = malloc(argc * sizeof(char*));
+               app_nit_ios_argv[0] = program_name;
+               for (int i = 0; i < argc; i ++) {
+                       String arg = Sequence_of_String__index(argv, i);
+                       app_nit_ios_argv[i+1] = String_to_cstring(arg);
+               }
+       `}
+
+       # Register `self` globally in C so it can be retrieved from iOS callbacks
+       private fun register_globally in "ObjC" `{
+               App_incr_ref(recv);
+               app_nit_ios_app = recv;
+       `}
+
+       # Entry point to the iOS framework
+       private fun ui_application_main: Bool import did_finish_launching_with_options,
+       will_finish_launching_with_options,
+       will_resign_active, did_enter_background, will_enter_foreground,
+       did_become_active, will_terminate, ui_application=, app_delegate= in "ObjC" `{
+
+               @autoreleasepool {
+                       return UIApplicationMain(app_nit_ios_argc, app_nit_ios_argv,
+                               nil, NSStringFromClass([AppDelegate class]));
+               }
+       `}
+
+       # The application is about to launch
+       #
+       # Redef this method to set the very first custom code to be executed.
+       fun will_finish_launching_with_options: Bool do return true
+
+       # The application just launched but is not yet displayed to the user
+       #
+       # Redef this method to customize the behavior.
+       fun did_finish_launching_with_options: Bool do return true
+
+       # The application is about to move from active to inactive state
+       #
+       # This can occur for certain types of temporary interruptions
+       # (such as an incoming phone call or SMS message) or when the
+       # user quits the application and it begins the transition to
+       # the background state.
+       #
+       # Redef this method to pause ongoing tasks, disable timers, and
+       # throttle down OpenGL ES frame rates. Games should use this
+       # method to pause.
+       fun will_resign_active do end
+
+       # The application just left foreground it can be suspended at any time
+       #
+       # Redef this method to release shared resources, save user data,
+       # invalidate timers, and store application state to restore your
+       # application to its current state in case it is terminated later.
+       #
+       # If your application supports background execution, this method
+       # is called instead of `will_terminate` when the user quits.
+       fun did_enter_background do end
+
+       # The application will enter the foreground
+       #
+       # Called as part of the transition from the background to the
+       # inactive state.
+       #
+       # Redef to und changes made on entering the background.
+       fun will_enter_foreground do end
+
+       # The application just became active
+       #
+       # Redef to restart any tasks that were paused (or not yet started) while
+       # the application was inactive. If the application was previously
+       # in the background, optionally refresh the user interface.
+       fun did_become_active do end
+
+       # The application is about to terminate (not suspended)
+       #
+       # Redef to save data if appropriate.
+       fun will_terminate do end
+end
+
+app.register_args(program_name.to_cstring, args.length, args)
+app.register_globally
+
+var ret = app.ui_application_main
+exit if ret then 0 else 1
diff --git a/lib/ios/examples/hello_ios.nit b/lib/ios/examples/hello_ios.nit
new file mode 100644 (file)
index 0000000..d11ffcb
--- /dev/null
@@ -0,0 +1,50 @@
+# 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.
+
+# Simple iOS app with a single label
+module hello_ios
+
+import ios
+
+redef class App
+       redef fun did_finish_launching_with_options
+       do
+               return app_delegate.hello_world
+       end
+end
+
+redef class AppDelegate
+
+       # Print and show "Hello World!"
+       private fun hello_world: Bool in "ObjC" `{
+
+               // Print to the console
+               NSLog(@"Hello World!");
+
+               // Display "Hello world!" on the screen
+               recv.window = [[UIWindow alloc] initWithFrame:
+               [[UIScreen mainScreen] bounds]];
+               recv.window.backgroundColor = [UIColor whiteColor];
+
+               UILabel *label = [[UILabel alloc] init];
+               label.text = @"Hello World!";
+               label.center = CGPointMake(100, 100);
+               [label sizeToFit];
+
+               [recv.window addSubview: label];
+               [recv.window makeKeyAndVisible];
+
+               return YES;
+       `}
+end
diff --git a/lib/ios/ios.nit b/lib/ios/ios.nit
new file mode 100644 (file)
index 0000000..9d5079d
--- /dev/null
@@ -0,0 +1,19 @@
+# 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.
+
+# iOS services for Nit app on iOS
+module ios
+
+import platform
+import app
diff --git a/lib/ios/platform.nit b/lib/ios/platform.nit
new file mode 100644 (file)
index 0000000..47d1536
--- /dev/null
@@ -0,0 +1,16 @@
+# 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.
+
+# Triggers compilation for the iOS platform
+module platform is platform "ios"
index 5b5ffbb..93f5f9b 100644 (file)
@@ -149,4 +149,5 @@ int string_NativeString_NativeString_system_0(const char *cmd) {
                // cmd exited on SIGINT: in my opinion the user wants the main to be discontinued
                kill(getpid(), SIGINT);
        }
+       return status;
 }
index 4892f95..e974d91 100644 (file)
@@ -531,6 +531,14 @@ abstract class Text
        #
        #     assert "abAB12<>&".escape_to_c         == "abAB12<>&"
        #     assert "\n\"'\\".escape_to_c         == "\\n\\\"\\'\\\\"
+       #
+       # Most non-printable characters (bellow ASCII 32) are escaped to an octal form `\nnn`.
+       # Three digits are always used to avoid following digits to be interpreted as an element
+       # of the octal sequence.
+       #
+       #     assert "{0.ascii}{1.ascii}{8.ascii}{31.ascii}{32.ascii}".escape_to_c == "\\000\\001\\010\\037 "
+       #
+       # The exceptions are the common `\t` and `\n`.
        fun escape_to_c: String
        do
                var b = new FlatBuffer
@@ -538,8 +546,10 @@ abstract class Text
                        var c = chars[i]
                        if c == '\n' then
                                b.append("\\n")
+                       else if c == '\t' then
+                               b.append("\\t")
                        else if c == '\0' then
-                               b.append("\\0")
+                               b.append("\\000")
                        else if c == '"' then
                                b.append("\\\"")
                        else if c == '\'' then
@@ -547,7 +557,17 @@ abstract class Text
                        else if c == '\\' then
                                b.append("\\\\")
                        else if c.ascii < 32 then
-                               b.append("\\{c.ascii.to_base(8, false)}")
+                               b.add('\\')
+                               var oct = c.ascii.to_base(8, false)
+                               # Force 3 octal digits since it is the
+                               # maximum allowed in the C specification
+                               if oct.length == 1 then
+                                       b.add('0')
+                                       b.add('0')
+                               else if oct.length == 2 then
+                                       b.add('0')
+                               end
+                               b.append(oct)
                        else
                                b.add(c)
                        end
index 658f0e7..311d71b 100644 (file)
@@ -121,20 +121,30 @@ redef class ModelBuilder
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
-               var toolchain = platform.toolchain(toolcontext)
+               var toolchain = platform.toolchain(toolcontext, compiler)
                compile_dir = toolchain.compile_dir
-               toolchain.write_and_make compiler
+               toolchain.write_and_make
        end
 end
 
 redef class Platform
        # The specific tool-chain associated to the platform
-       fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
+       fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
+       do
+               return new MakefileToolchain(toolcontext, compiler)
+       end
 end
 
+# Build toolchain for a specific target program, varies per `Platform`
 class Toolchain
+
+       # Toolcontext
        var toolcontext: ToolContext
 
+       # Compiler of the target program
+       var compiler: AbstractCompiler
+
+       # Directory where to generate all C files
        fun compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
@@ -142,13 +152,15 @@ class Toolchain
                return compile_dir
        end
 
-       fun write_and_make(compiler: AbstractCompiler) is abstract
+       # Write all C files and compile them
+       fun write_and_make is abstract
 end
 
+# Default toolchain using a Makefile
 class MakefileToolchain
        super Toolchain
 
-       redef fun write_and_make(compiler)
+       redef fun write_and_make
        do
                var compile_dir = compile_dir
 
@@ -161,11 +173,11 @@ class MakefileToolchain
                compile_dir.mkdir
 
                var cfiles = new Array[String]
-               write_files(compiler, compile_dir, cfiles)
+               write_files(compile_dir, cfiles)
 
                # Generate the Makefile
 
-               write_makefile(compiler, compile_dir, cfiles)
+               write_makefile(compile_dir, cfiles)
 
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
@@ -177,13 +189,14 @@ class MakefileToolchain
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
 
-               compile_c_code(compiler, compile_dir)
+               compile_c_code(compile_dir)
 
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
 
-       fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write all source files to the `compile_dir`
+       fun write_files(compile_dir: String, cfiles: Array[String])
        do
                var platform = compiler.target_platform
                if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
@@ -280,10 +293,14 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
-       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
+       # Get the name of the Makefile to use
+       fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
 
-       fun default_outname(mainmodule: MModule): String
+       # 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
@@ -298,13 +315,14 @@ class MakefileToolchain
        do
                var res = self.toolcontext.opt_output.value
                if res != null then return res
-               res = default_outname(mainmodule)
+               res = default_outname
                var dir = self.toolcontext.opt_dir.value
                if dir != null then return dir.join_path(res)
                return res
        end
 
-       fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write the Makefile
+       fun write_makefile(compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
                var platform = compiler.target_platform
@@ -319,7 +337,7 @@ class MakefileToolchain
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
-               var makename = makefile_name(mainmodule)
+               var makename = makefile_name
                var makepath = "{compile_dir}/{makename}"
                var makefile = new FileWriter.open(makepath)
 
@@ -444,9 +462,10 @@ endif
                makepath.file_copy_to "{compile_dir}/Makefile"
        end
 
-       fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
+       # The C code is generated, compile it to an executable
+       fun compile_c_code(compile_dir: String)
        do
-               var makename = makefile_name(compiler.mainmodule)
+               var makename = makefile_name
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
@@ -589,6 +608,8 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               self.header.add_decl("#include <sys/types.h>\n")
+               self.header.add_decl("#include <unistd.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
index ea60c0a..c013295 100644 (file)
@@ -22,3 +22,4 @@ import compiler_ffi
 import platform::android
 import platform::pnacl
 import platform::emscripten
+import platform::ios
index ecfcc8e..f590c1a 100644 (file)
@@ -2266,7 +2266,7 @@ class SeparateRuntimeFunction
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2283,7 +2283,7 @@ class SeparateRuntimeFunction
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
index e2db002..2c9e706 100644 (file)
@@ -42,7 +42,7 @@ class AndroidPlatform
 
        redef fun supports_linker_script do return false
 
-       redef fun toolchain(toolcontext) do return new AndroidToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new AndroidToolchain(toolcontext, compiler)
 end
 
 class AndroidToolchain
@@ -57,9 +57,9 @@ class AndroidToolchain
                return "{android_project_root}/jni/nit_compile/"
        end
 
-       redef fun default_outname(mainmodule) do return "{mainmodule.name}.apk"
+       redef fun default_outname do return "{super}.apk"
 
-       redef fun write_files(compiler, compile_dir, cfiles)
+       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)
@@ -107,7 +107,7 @@ class AndroidToolchain
                if not dir.file_exists then dir.mkdir
 
                # compile normal C files
-               super(compiler, compile_dir, cfiles)
+               super
 
                # Gather extra C files generated elsewhere than in super
                for f in compiler.extern_bodies do
@@ -311,12 +311,12 @@ $(call import-module,android/native_app_glue)
                end
        end
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                # Do nothing, already done in `write_files`
        end
 
-       redef fun compile_c_code(compiler, compile_dir)
+       redef fun compile_c_code(compile_dir)
        do
                var android_project_root = android_project_root.as(not null)
                var short_project_name = compiler.mainmodule.name.replace("-", "_")
index 3e9199a..90e361e 100644 (file)
@@ -35,17 +35,17 @@ class EmscriptenPlatform
        redef fun supports_libunwind do return false
        redef fun supports_libgc do return false
        redef fun supports_linker_script do return false
-       redef fun toolchain(toolcontext) do return new EnscriptenToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new EnscriptenToolchain(toolcontext, compiler)
 end
 
 class EnscriptenToolchain
        super MakefileToolchain
 
-       redef fun makefile_name(mainmodule) do return "{mainmodule.name}.js.mk"
+       redef fun makefile_name do return "{super}.js.mk"
 
-       redef fun default_outname(mainmodule) do return "{super}.js"
+       redef fun default_outname do return "{super}.js"
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                super
 
diff --git a/src/platform/ios.nit b/src/platform/ios.nit
new file mode 100644 (file)
index 0000000..94d0c5f
--- /dev/null
@@ -0,0 +1,122 @@
+# 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.
+
+# Compile programs for the iOS platform
+module ios
+
+import platform
+import compiler::abstract_compiler
+import xcode_templates
+private import annotation
+
+redef class ToolContext
+       redef fun platform_from_name(name)
+       do
+               if name == "ios" then return new IOSPlatform
+               return super
+       end
+end
+
+private class IOSPlatform
+       super Platform
+
+       redef fun supports_libunwind do return false
+       redef fun supports_libgc do return false
+       redef fun toolchain(toolcontext, compiler) do return new IOSToolchain(toolcontext, compiler)
+end
+
+private class IOSToolchain
+       super MakefileToolchain
+
+       # Root of the iOS project, usually `.nit_compile/ios/`
+       var ios_project_root: String is noinit
+
+       redef fun default_outname do return "{super}.app"
+
+       # 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
+
+       # Compile C files in `ios_project_root/project_name`
+       redef fun compile_dir
+       do
+               ios_project_root = super/"ios"
+               return ios_project_root/project_name
+       end
+
+       redef fun write_files(compile_dir, cfiles)
+       do
+               # Clear the project directory before writing anything
+               if ios_project_root.file_exists then ios_project_root.rmdir
+               compile_dir.mkdir
+
+               super
+       end
+
+       redef fun write_makefile(compile_dir, cfiles)
+       do
+               var project_name = project_name
+
+               # Create an XCode project directory
+               var dir = ios_project_root/project_name+".xcodeproj"
+               if not dir.file_exists then dir.mkdir
+
+               # Create a PBX project file
+               var pbx = new PbxprojectTemplate(project_name)
+
+               ## Register all source files
+               for file in cfiles do pbx.add_file new PbxFile(file)
+               for file in compiler.extern_bodies do
+                       pbx.add_file new PbxFile(file.filename.basename(""))
+               end
+
+               ## TODO Register asset files
+
+               pbx.write_to_file dir/"project.pbxproj"
+
+               # 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
+               plist.write_to_file compile_dir/"Info.plist"
+       end
+
+       redef fun compile_c_code(compile_dir)
+       do
+               var project_name = project_name
+               var release = toolcontext.opt_release.value
+               var outfile = outfile(compiler.mainmodule)
+
+               # Compile with `xcodebuild`
+               #
+               # TODO support more than the iPhone and the simulator.
+               var args = ["sh", "-c", "cd {ios_project_root}; " +
+                       "xcodebuild -target '{project_name}' " +
+                       "-destination 'platform=iOS Simulator,name=iPhone' " +
+                       "-configuration {if release then "Release" else "Debug"} " +
+                       "-sdk iphonesimulator build"]
+               toolcontext.exec_and_check(args, "iOS project error")
+
+               # Move compiled app to destination
+               if outfile.file_exists then outfile.rmdir
+               args = ["mv", "{ios_project_root}/build/Debug-iphonesimulator/{project_name}.app", outfile]
+               toolcontext.exec_and_check(args, "iOS project error")
+       end
+end
index d7103db..8136187 100644 (file)
@@ -37,13 +37,13 @@ class PnaclPlatform
 
        redef fun no_main do return true
 
-       redef fun toolchain(toolcontext) do return new PnaclToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new PnaclToolchain(toolcontext, compiler)
 end
 
 class PnaclToolchain
        super MakefileToolchain
 
-       redef fun write_files(compiler, compile_dir, cfiles)
+       redef fun write_files(compile_dir, cfiles)
        do
                var app_name = compiler.mainmodule.name
 
@@ -52,7 +52,7 @@ class PnaclToolchain
                if not dir.file_exists then dir.mkdir
 
                # compile normal C files
-               super(compiler, compile_dir, cfiles)
+               super
 
                # Gather extra C files generated elsewhere than in super
                for f in compiler.extern_bodies do
@@ -238,12 +238,12 @@ function updateStatus(opt_message) {
                """.write_to_file(file)
        end
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                # Do nothing, already done in `write_files`
        end
 
-       redef fun compile_c_code(compiler, compile_dir)
+       redef fun compile_c_code(compile_dir)
        do
                # Generate the pexe
                toolcontext.exec_and_check(["make", "-C", compile_dir, "-j", "4"], "PNaCl project error")
diff --git a/src/platform/xcode_templates.nit b/src/platform/xcode_templates.nit
new file mode 100644 (file)
index 0000000..de9d500
--- /dev/null
@@ -0,0 +1,681 @@
+# 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.
+
+# Templates and other services to create XCode projects
+module xcode_templates
+
+import template
+
+import platform
+import compiler::abstract_compiler
+
+redef class Sys
+       # Map to identify the PBX file type for a given file extension
+       private var pbx_file_types: Map[String, String] is lazy do
+               var map = new HashMap[String, String]
+
+               # Source code
+               map["m"] =                      "sourcecode.c.objc"
+               map["c"] =                      "sourcecode.c.c"
+               map["h"] =                      "sourcecode.c.h"
+               map["cpp"] =            "sourcecode.cpp.cpp"
+               map["hpp"] =            "sourcecode.cpp.h"
+               map["vsh"] =            "sourcecode.glsl"
+               map["fsh"] =            "sourcecode.glsl"
+
+               # Images
+               map["png"] =            "image.png"
+               map["gif"] =            "image.gif"
+               map["jpg"] =            "image.jpeg"
+               map["jpeg"] =           "image.jpeg"
+               map["pdf"] =            "image.pdf"
+               map["ico"] =            "image.ico"
+
+               # Others
+               map["app"] =            "wrapper.application"
+               map["plist"] =          "text.plist.xml"
+               map["storyboard"] =     "file.storyboard"
+               map["xib"] =            "file.xib"
+               map["xcassets"] =       "folder.assetcatalog"
+               map["xctest"] =         "wrapper.cfbundle"
+
+               return map
+       end
+
+       # Generator of PBX UUIDs quique to an execution of the compiler
+       private var pbx_uuid_generator = new PbxUUIDGenerator is lazy
+end
+
+# Generator of PBX UUIDs
+#
+# PBX UUID are composed of 24 hex characters.
+# They only need to be unique within the same project.
+#
+# This implementation simply counts upward from 0.
+class PbxUUIDGenerator
+       private var seed = 0
+
+       # Generate a new UUID
+       fun next_uuid: String
+       do
+               seed += 1
+
+               var hex_val = seed.to_hex.to_upper
+               return "0"*(24-hex_val.length) + hex_val
+       end
+end
+
+# Reference to a file for the PBX format of a project file
+#
+# TODO create subclasses for different file types, this is currently for
+# compilable source files only.
+class PbxFile
+
+       # Path to `self`
+       var path: String
+
+       # UUID for build elements
+       private var build_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
+
+       # File reference UUID
+       private var ref_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
+
+       # Documentation to add besides this file in the template
+       private fun doc: String do return path
+
+       # PBX file type for `self`
+       fun file_type: String
+       do
+               var map = sys.pbx_file_types
+               var ext = path.file_extension
+               if ext != null and map.keys.has(ext) then return map[ext]
+               return "unknown"
+       end
+
+       # PBX description of this file
+       private fun description: Writable do return """
+               {{{ref_uuid}}} /* {{{doc}}} */ = {
+                       isa = PBXFileReference;
+                       fileEncoding = 4;
+                       lastKnownFileType = {{{file_type}}};
+                       path = {{{path}}};
+                       sourceTree = "<group>";
+                       };
+"""
+
+       private fun add_to_project(project: PbxprojectTemplate)
+       do
+               project.source_files.add self
+               project.files.add self
+       end
+end
+
+# Template for a PBX project file, usually a `project.pbcproj`
+#
+# This file list all information required to build an XCode project.
+# It would usually be written and read by XCode.
+# From the command line, xcodebuild can read this file but not write it.
+#
+# Information in the file (simplified list):
+#
+# * Compilable source files
+# * Asset files
+# * Build configurations (Release and debug modes, cflags, etc.)
+# * List of files composing the project
+class PbxprojectTemplate
+       super Template
+
+       # Name of the project
+       var name: String
+
+       # All body/implementation source files to be compiled
+       private var source_files = new Array[PbxFile]
+
+       # All asset files added to the app package
+       private var asset_files = new Array[PbxFile]
+
+       # All files used by this project
+       private var files = new Array[PbxFile]
+
+       # Add `file` to this project
+       fun add_file(file: PbxFile) do file.add_to_project(self)
+
+       redef fun rendering
+       do
+               add """
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 46;
+       objects = {
+
+/* Begin PBXBuildFile section */
+"""
+
+               # List build files (compilable sources and assets) with their reference UUID
+               for array in [source_files, asset_files] do for file in array do add """
+               {{{file.build_uuid}}} /* {{{file.doc}}} */ = {
+                       isa = PBXBuildFile;
+                       fileRef = {{{file.ref_uuid}}} /* {{{file.doc}}} */;
+                       };
+"""
+
+               add """
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               AF9F83EA1A5F0D21004B62C0 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = AF9F83C41A5F0D21004B62C0 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = AF9F83CB1A5F0D21004B62C0;
+                       remoteInfo = {{{name}}};
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+       /* Static generated files */
+               AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */ = {
+                       isa = PBXFileReference;
+                       explicitFileType = wrapper.application;
+                       includeInIndex = 0;
+                       path = {{{name}}}.app;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+                       };
+               AF9F83D01A5F0D21004B62C0 /* Info.plist */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = text.plist.xml;
+                       path = Info.plist;
+                       sourceTree = "<group>";
+                       };
+               AF9F83DE1A5F0D21004B62C0 /* Base */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = file.storyboard;
+                       name = Base;
+                       path = Base.lproj/Main.storyboard;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E01A5F0D21004B62C0 /* Images.xcassets */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = folder.assetcatalog;
+                       path = Images.xcassets;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E31A5F0D21004B62C0 /* Base */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = file.xib;
+                       name = Base;
+                       path = Base.lproj/LaunchScreen.xib;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */ = {
+                       isa = PBXFileReference;
+                       explicitFileType = wrapper.cfbundle;
+                       includeInIndex = 0;
+                       path = {{{name}}}Tests.xctest;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+                       };
+               AF9F83EE1A5F0D21004B62C0 /* Info.plist */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = text.plist.xml;
+                       path = Info.plist;
+                       sourceTree = "<group>";
+                       };
+               AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.objc;
+                       path = {{{name}}}Tests.m;
+                       sourceTree = "<group>";
+                       };
+
+       /* Changing generated files */
+"""
+               # Describe all known files
+               for file in files do add file.description
+
+               add """
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               AF9F83C91A5F0D21004B62C0 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E61A5F0D21004B62C0 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               AF9F83C31A5F0D21004B62C0 = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */,
+                               AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */,
+                               AF9F83CD1A5F0D21004B62C0 /* Products */,
+                       );
+                       sourceTree = "<group>";
+               };
+               AF9F83CD1A5F0D21004B62C0 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */,
+                               AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */ = {
+                       isa = PBXGroup;
+                       children = (
+"""
+                       # Reference all known files
+                       for file in files do add """
+                               {{{file.ref_uuid}}} /* {{{file.doc}}} */,
+"""
+
+               add """
+                       );
+                       path = {{{name}}};
+                       sourceTree = "<group>";
+               };
+               AF9F83CF1A5F0D21004B62C0 /* Supporting Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83D01A5F0D21004B62C0 /* Info.plist */,
+                               AF9F83D11A5F0D21004B62C0 /* main.m */,
+                       );
+                       name = "Supporting Files";
+                       sourceTree = "<group>";
+               };
+               AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */,
+                               AF9F83ED1A5F0D21004B62C0 /* Supporting Files */,
+                       );
+                       path = {{{name}}}Tests;
+                       sourceTree = "<group>";
+               };
+               AF9F83ED1A5F0D21004B62C0 /* Supporting Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83EE1A5F0D21004B62C0 /* Info.plist */,
+                       );
+                       name = "Supporting Files";
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */;
+                       buildPhases = (
+                               AF9F83C81A5F0D21004B62C0 /* Sources */,
+                               AF9F83C91A5F0D21004B62C0 /* Frameworks */,
+                               AF9F83CA1A5F0D21004B62C0 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = {{{name}}};
+                       productName = {{{name}}};
+                       productReference = AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */;
+                       productType = "com.apple.product-type.application";
+               };
+               AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */;
+                       buildPhases = (
+                               AF9F83E51A5F0D21004B62C0 /* Sources */,
+                               AF9F83E61A5F0D21004B62C0 /* Frameworks */,
+                               AF9F83E71A5F0D21004B62C0 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */,
+                       );
+                       name = {{{name}}}Tests;
+                       productName = {{{name}}}Tests;
+                       productReference = AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               AF9F83C41A5F0D21004B62C0 /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastUpgradeCheck = 0610;
+                               TargetAttributes = {
+                                       AF9F83CB1A5F0D21004B62C0 = {
+                                               CreatedOnToolsVersion = 6.1.1;
+                                       };
+                                       AF9F83E81A5F0D21004B62C0 = {
+                                               CreatedOnToolsVersion = 6.1.1;
+                                               TestTargetID = AF9F83CB1A5F0D21004B62C0;
+                                       };
+                               };
+                       };
+                       buildConfigurationList = AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */;
+                       compatibilityVersion = "Xcode 3.2";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                               Base,
+                       );
+                       mainGroup = AF9F83C31A5F0D21004B62C0;
+                       productRefGroup = AF9F83CD1A5F0D21004B62C0 /* Products */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */,
+                               AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               AF9F83CA1A5F0D21004B62C0 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+"""
+               # Reference all asset files by their build UUID
+               for file in asset_files do add """
+                               {{{file.build_uuid}}} /* {{{file.doc}}} */,
+"""
+
+               add """
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E71A5F0D21004B62C0 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               AF9F83C81A5F0D21004B62C0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+"""
+               # Reference all compilable source files by their build UUID
+               for file in source_files do add """
+                               {{{file.build_uuid}}} /* {{{file.doc}}} */,
+"""
+               add """
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E51A5F0D21004B62C0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               AF9F83F01A5F0D21004B62C0 /* {{{name}}}Tests.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */;
+                       targetProxy = AF9F83EA1A5F0D21004B62C0 /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+               AF9F83DD1A5F0D21004B62C0 /* Main.storyboard */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               AF9F83DE1A5F0D21004B62C0 /* Base */,
+                       );
+                       name = Main.storyboard;
+                       sourceTree = "<group>";
+               };
+               AF9F83E21A5F0D21004B62C0 /* LaunchScreen.xib */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               AF9F83E31A5F0D21004B62C0 /* Base */,
+                       );
+                       name = LaunchScreen.xib;
+                       sourceTree = "<group>";
+               };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+               AF9F83F11A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               COPY_PHASE_STRIP = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               SDKROOT = iphoneos;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               AF9F83F21A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               COPY_PHASE_STRIP = YES;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               SDKROOT = iphoneos;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                       };
+                       name = Release;
+               };
+               AF9F83F41A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               INFOPLIST_FILE = {{{name}}}/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               AF9F83F51A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               INFOPLIST_FILE = {{{name}}}/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
+               AF9F83F71A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(SDKROOT)/Developer/Library/Frameworks",
+                                       "$(inherited)",
+                               );
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               INFOPLIST_FILE = {{{name}}}Tests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}";
+                       };
+                       name = Debug;
+               };
+               AF9F83F81A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(SDKROOT)/Developer/Library/Frameworks",
+                                       "$(inherited)",
+                               );
+                               INFOPLIST_FILE = {{{name}}}Tests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F11A5F0D21004B62C0 /* Debug */,
+                               AF9F83F21A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F41A5F0D21004B62C0 /* Debug */,
+                               AF9F83F51A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+               };
+               AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F71A5F0D21004B62C0 /* Debug */,
+                               AF9F83F81A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = AF9F83C41A5F0D21004B62C0 /* Project object */;
+}
+"""
+       end
+end
+
+# Template for a property list used by XCode for iOS projects
+class PlistTemplate
+       super Template
+
+       # Package of the app
+       var package_name: String
+
+       redef fun rendering
+       do
+               add """
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>{{{package_name}}}.$(PRODUCT_NAME:rfc1034identifier)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleSignature</key>
+       <string>\\?\\?\\?\\?</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+       <key>LSRequiresIPhoneOS</key>
+       <true/>
+       <key>UIRequiredDeviceCapabilities</key>
+       <array>
+               <string>armv7</string>
+       </array>
+       <key>UISupportedInterfaceOrientations</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>UISupportedInterfaceOrientations~ipad</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+</dict>
+</plist>
+"""
+       end
+end
index 609105e..8a90f1d 100644 (file)
@@ -1,3 +1,5 @@
 cocoa_extern_types
 cocoa_message_box
 hello_cocoa
+hello_ios
+test_platform_ios
index 36503ed..3e3247e 100644 (file)
@@ -475,7 +475,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:71,7--36
                   TOpar "(" ../src/test_parser.nit:71,7
                   AStringExpr ../src/test_parser.nit:71,8--35
-                    TString "\"  -n\11do not print anything\"" ../src/test_parser.nit:71,8--35
+                    TString "\"  -n\tdo not print anything\"" ../src/test_parser.nit:71,8--35
                   TCpar ")" ../src/test_parser.nit:71,36
               ACallExpr ../src/test_parser.nit:72,2--25
                 AImplicitSelfExpr ../src/test_parser.nit:72,2
@@ -483,7 +483,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:72,7--25
                   TOpar "(" ../src/test_parser.nit:72,7
                   AStringExpr ../src/test_parser.nit:72,8--24
-                    TString "\"  -l\11only lexer\"" ../src/test_parser.nit:72,8--24
+                    TString "\"  -l\tonly lexer\"" ../src/test_parser.nit:72,8--24
                   TCpar ")" ../src/test_parser.nit:72,25
               ACallExpr ../src/test_parser.nit:73,2--41
                 AImplicitSelfExpr ../src/test_parser.nit:73,2
@@ -491,7 +491,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:73,7--41
                   TOpar "(" ../src/test_parser.nit:73,7
                   AStringExpr ../src/test_parser.nit:73,8--40
-                    TString "\"  -p\11lexer and parser (default)\"" ../src/test_parser.nit:73,8--40
+                    TString "\"  -p\tlexer and parser (default)\"" ../src/test_parser.nit:73,8--40
                   TCpar ")" ../src/test_parser.nit:73,41
               ACallExpr ../src/test_parser.nit:74,2--68
                 AImplicitSelfExpr ../src/test_parser.nit:74,2
@@ -499,7 +499,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:74,7--68
                   TOpar "(" ../src/test_parser.nit:74,7
                   AStringExpr ../src/test_parser.nit:74,8--67
-                    TString "\"  -e\11instead on files, each argument is a content to parse\"" ../src/test_parser.nit:74,8--67
+                    TString "\"  -e\tinstead on files, each argument is a content to parse\"" ../src/test_parser.nit:74,8--67
                   TCpar ")" ../src/test_parser.nit:74,68
               ACallExpr ../src/test_parser.nit:75,2--51
                 AImplicitSelfExpr ../src/test_parser.nit:75,2
@@ -507,7 +507,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:75,7--51
                   TOpar "(" ../src/test_parser.nit:75,7
                   AStringExpr ../src/test_parser.nit:75,8--50
-                    TString "\"  -i\11tree to parse are read interactively\"" ../src/test_parser.nit:75,8--50
+                    TString "\"  -i\ttree to parse are read interactively\"" ../src/test_parser.nit:75,8--50
                   TCpar ")" ../src/test_parser.nit:75,51
               ACallExpr ../src/test_parser.nit:76,2--30
                 AImplicitSelfExpr ../src/test_parser.nit:76,2
@@ -515,7 +515,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:76,7--30
                   TOpar "(" ../src/test_parser.nit:76,7
                   AStringExpr ../src/test_parser.nit:76,8--29
-                    TString "\"  -h\11print this help\"" ../src/test_parser.nit:76,8--29
+                    TString "\"  -h\tprint this help\"" ../src/test_parser.nit:76,8--29
                   TCpar ")" ../src/test_parser.nit:76,30
             AIfExpr ../src/test_parser.nit:77,6--146,3
               TKwif "if" ../src/test_parser.nit:77,6--7
diff --git a/tests/test_platform_ios.nit b/tests/test_platform_ios.nit
new file mode 100644 (file)
index 0000000..b49f379
--- /dev/null
@@ -0,0 +1,15 @@
+# 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.
+
+import ios