nitg: Added PNaCl support for Nit
authorDjomanix <johan.kayser@viacesi.fr>
Thu, 24 Apr 2014 20:28:14 +0000 (16:28 -0400)
committerDjomanix <johan.kayser@viacesi.fr>
Fri, 2 May 2014 14:43:25 +0000 (10:43 -0400)
Signed-off-by: Djomanix <johan.kayser@viacesi.fr>

lib/pnacl.nit [new file with mode: 0644]
src/abstract_compiler.nit
src/nitg.nit
src/pnacl_platform.nit [new file with mode: 0644]

diff --git a/lib/pnacl.nit b/lib/pnacl.nit
new file mode 100644 (file)
index 0000000..c7fb2ab
--- /dev/null
@@ -0,0 +1,464 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Johan Kayser <kayser.johan@gmail.com>
+#
+# 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.
+#
+# Targets the PNaCl platform
+#
+# To use this module and compile for PNaCl, you must install the
+# NaCl SDK (This file is based on Pepper 33).
+# If NACL_SDK_ROOT is not set in your PATH, you have to work in
+# 'nacl_sdk/pepper_your_pepper_version/getting_started/your_project_folder'.
+#
+# Provides PNaCl support for Nit
+module pnacl is platform
+`{
+       #include <unistd.h>
+       #include <stddef.h>
+       #include <string.h>
+       #include <stdlib.h>
+       #include "ppapi/c/pp_errors.h"
+       #include "ppapi/c/ppp.h"
+       #include "ppapi/c/ppp_instance.h"
+       #include "ppapi/c/pp_bool.h"
+       #include "ppapi/c/ppb_var.h"
+       #include "ppapi/c/ppb_messaging.h"
+       #include "ppapi/c/ppp_messaging.h"
+       #include "ppapi/c/ppb_var_dictionary.h"
+       #include "ppapi/c/ppb_var_array.h"
+
+       extern int nit_main(int, char**);
+
+       const PPB_Messaging* g_varMessagingInterface;
+       const PPB_Var* g_varInterface;
+       const PPB_VarDictionary* g_varDictionaryInterface;
+       const PPB_VarArray* g_varArrayInterface;
+
+       PP_Instance g_instance;
+       PnaclApp app;
+
+       /* Posts a string message to JS. */
+       void PostMessage(char* message) {
+           /* Create PP_Var containing the message body. */
+           struct PP_Var varString = g_varInterface->VarFromUtf8(message, strlen(message));
+
+           /* Post message to the JavaScript layer. */
+           g_varMessagingInterface->PostMessage(g_instance, varString);
+       }
+
+       /* Posts a Dictionary (JS like object) to JS. */
+       void PostDictionary(struct PP_Var dictionary) {
+           g_varMessagingInterface->PostMessage(g_instance, dictionary);
+       }
+
+       /* Posts a Variable (aka PepperVar) to JS.
+          Should only be used for testing, conventional conversation is made
+          with Strings or Dictionaries. */
+       void PostVar(struct PP_Var v) {
+           g_varMessagingInterface->PostMessage(g_instance, v);
+       }
+
+       static PP_Bool Instance_DidCreate(PP_Instance instance, uint32_t argc, const char* argn[], const char* argv[]) {
+           g_instance = instance;
+           nit_main(0, NULL);
+           return PP_TRUE;
+       }
+
+       static void Instance_DidDestroy(PP_Instance instance) {
+               // TODO
+       }
+
+       static void Instance_DidChangeView(PP_Instance pp_instance, PP_Resource view) {
+               // TODO
+       }
+
+       static void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
+               // TODO
+       }
+
+       static PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance, PP_Resource pp_url_loader) {
+               // TODO
+               return PP_FALSE;
+       }
+
+       /* char* to PP_Var. */
+       static struct PP_Var CStrToVar(const char* str) {
+         if (g_varInterface != NULL) {
+           return g_varInterface->VarFromUtf8(str, strlen(str));
+         }
+         return PP_MakeUndefined();
+       }
+
+       /* Called when JS sends something, is set to accept Strings or Dictionaries,
+          returns an error if received object is not a String or Dictionary. */
+       void Messaging_HandleMessage(PP_Instance instance, struct PP_Var varMessage) {
+           if(varMessage.type == PP_VARTYPE_DICTIONARY) {
+               PnaclApp_handle_dictionary(app, &varMessage);
+           }
+           else if(varMessage.type == PP_VARTYPE_STRING) {
+               uint32_t len;
+               char* message = (char*)g_varInterface->VarToUtf8(varMessage, &len);
+               PnaclApp_handle_message(app, NativeString_to_s_with_length(message, len));
+           }
+           else {
+               struct PP_Var errorMessage = CStrToVar("TypeError : only accepts JS objects or Strings");
+               g_varMessagingInterface->PostMessage(g_instance, errorMessage);
+           }
+       }
+
+       /* Entry point */
+       PP_EXPORT int32_t PPP_InitializeModule(PP_Module module_id, PPB_GetInterface get_browser_interface) {
+           /* Initializing global pointers */
+           g_varMessagingInterface = (const PPB_Messaging*) get_browser_interface(PPB_MESSAGING_INTERFACE);
+           g_varInterface = (const PPB_Var*) get_browser_interface(PPB_VAR_INTERFACE);
+           g_varDictionaryInterface = (const PPB_VarDictionary*) get_browser_interface(PPB_VAR_DICTIONARY_INTERFACE);
+           g_varArrayInterface = (const PPB_VarArray*) get_browser_interface(PPB_VAR_ARRAY_INTERFACE);
+           return PP_OK;
+       }
+
+       PP_EXPORT void PPP_ShutdownModule() {
+               // TODO
+       }
+
+       PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
+           if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
+           {
+               static PPP_Instance instance_interface = {
+                   &Instance_DidCreate,
+                   &Instance_DidDestroy,
+                   &Instance_DidChangeView,
+                   &Instance_DidChangeFocus,
+                   &Instance_HandleDocumentLoad
+               };
+               return &instance_interface;
+           }
+           else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
+               static PPP_Messaging messaging_interface = {
+                       &Messaging_HandleMessage
+               };
+               return &messaging_interface;
+           }
+           return NULL;
+       }
+`}
+
+# Nit class representing a Pepper C API PP_Var typed as a Dictionary.
+extern class PepperDictionary `{ struct PP_Var* `}
+
+       new `{
+               struct PP_Var* recv = malloc( sizeof( struct PP_Var ) );
+               *recv = g_varDictionaryInterface->Create();
+               return recv;
+       `}
+
+       # Get fonction using PepperVars.
+       #
+       # Returns the value that is associated with 'key'.
+       # If 'key' is not a String typed PepperVar, or doesn't exist in the Dictionary, an undefined PepperVar is returned.
+       fun native_get(key: PepperVar): PepperVar `{
+               struct PP_Var* value = malloc( sizeof ( struct PP_Var ) );
+               *value = g_varDictionaryInterface->Get(*recv, *key);
+               return value;
+       `}
+
+       # Returns the value associated with 'key'.
+       #
+       # 'key' must be a String.
+       # If 'key' is not a String or doesn't exist in the Dictionary, 'null' is returned.
+       fun [](key: nullable Pepperable): nullable Pepperable
+       do
+               var native_key = key.to_pepper
+               var native_value = native_get(native_key)
+               return native_value.to_nit
+       end
+       
+       # Set function using PepperVars. 
+       #
+       # Sets the value associated with the specified key.
+       # 'key' must be a String typed PepperVar.
+       # If 'key' hasn't existed in the Dictionary, it is added and associated with 'value'.
+       # Otherwise, the previous value is replaced with 'value'.
+       # Returns a Boolean indicating whether the operation succeeds.
+       fun native_set(key: PepperVar, value: PepperVar): Bool `{
+               PP_Bool b;
+               b = g_varDictionaryInterface->Set(*recv, *key, *value);
+               return b;
+       `}
+
+       # Sets the value associated with the specified key.
+       #
+       # 'key' must be a String.
+       # If 'key' hasn't existed in the Dictionary, it is added and associated with 'value'.
+       # Otherwise, the previous value is replaced with 'value'.
+       # Returns a Boolean indicating whether the operation succeeds.
+       fun []=(key: nullable Pepperable, value: nullable Pepperable): Bool
+       do
+               var native_key = key.to_pepper
+               var native_value = value.to_pepper
+               return native_set(native_key, native_value)
+       end
+
+       # Deletes the specified key and its associated value, if the key exists.
+       #
+       # Takes a String typed PepperVar.
+       fun native_delete(key: PepperVar) `{
+               g_varDictionaryInterface->Delete(*recv, *key);
+       `}
+       
+       # Deletes the specified key and its associated value, if the key exists.
+       #
+       # Takes a String.
+       fun delete(key: String) 
+       do
+               var native_key = key.to_pepper
+               native_delete native_key
+       end
+
+       # Checks whether a key exists.
+       #
+       # Takes a String typed PepperVar.
+       fun native_has_key(key: PepperVar): Bool `{
+               PP_Bool b;
+               b = g_varDictionaryInterface->HasKey(*recv, *key);
+               return b;
+       `}
+
+       # Checks whether a key exists.
+        #
+       # Takes a String.
+       fun has_key(key: String): Bool
+       do
+               var native_key = key.to_pepper
+               return native_has_key(native_key)
+       end
+
+       # Gets all the keys in a dictionary.
+       #
+       # Returns a PepperArray which contains all the keys of the Dictionary. The elements are string vars.
+       fun get_keys: PepperArray `{
+               struct PP_Var* array = malloc( sizeof( struct PP_Var ) );
+               *array = g_varDictionaryInterface->GetKeys(*recv);
+               return array;
+       `}
+
+       # Use this function to copy a dictionary.
+       fun copy: PepperDictionary `{
+               struct PP_Var* varDictionary = malloc( sizeof( struct PP_Var ) );
+               *varDictionary = g_varDictionaryInterface->Create();
+               *varDictionary = *recv;
+               return varDictionary;
+       `}
+end
+
+# Nit class representing a Pepper C API PP_Var typed as an Array.
+extern class PepperArray `{ struct PP_Var* `}
+
+       new `{
+               struct PP_Var* recv = malloc( sizeof( struct PP_Var ) );
+               *recv = g_varArrayInterface->Create();
+               return recv;
+       `}
+
+       # Returns the element at the specified position as a PepperVar.
+       #
+       # If 'index' is larger than or equal to the array length, an undefined PepperVar is returned.
+       fun native_get(index: Int): PepperVar `{
+               struct PP_Var* value = malloc( sizeof( struct PP_Var ) );
+               *value = g_varArrayInterface->Get(*recv, index);
+               return value;
+       `}
+
+       # Returns the element at the specified position.
+       #
+       # If 'index' is larger than or equal to the array length, 'null' is returned.
+       fun [](index: Int): nullable Pepperable
+       do
+               var native_value = native_get(index)
+               return native_value.to_nit
+       end
+
+       # Returns an int containing the length of the PepperArray.
+       fun length: Int `{
+               int length = g_varArrayInterface->GetLength(*recv);
+               return length;
+       `}
+
+       # Takes a PepperVar for the 'value' param.
+       #
+       # Sets the value of an element in the array at indicated index.
+       # If 'index' is larger than or equal to the array length, the length is updated to be 'index' + 1.
+       # Any position in the array that hasn't been set before is set to undefined, i.e., PepperVar of C type PP_VARTYPE_UNDEFINED.
+       # Returns a Boolean indicating whether the operation succeeds.
+       fun native_set(index: Int, value: PepperVar): Bool `{
+               PP_Bool b;
+               b = g_varArrayInterface->Set(*recv, index, *value);
+               return b;
+       `}
+
+       # Sets the value of an element in the array at indicated index.
+       #
+       # If 'index' is larger than or equal to the array length, the length is updated to be 'index' + 1.
+       # Any position in the array that hasn't been set before is set to undefined, i.e., PepperVar of C type PP_VARTYPE_UNDEFINED.
+       # Returns a Boolean indicating whether the operation succeeds.
+       fun []=(index: Int, value: nullable Pepperable): Bool
+       do
+               var native_value = value.to_pepper
+               return native_set(index, native_value)
+       end
+
+       # Sets the array length.
+       #
+       # If 'length' is smaller than its current value, the array is truncated to the new length.
+       # Any elements that no longer fit are removed and the references to them will be released.
+       # If 'length' is larger than its current value, undefined PepperVars are appended to increase the array to the specified length.
+       # Returns a Boolean indicating whether the operation succeeds.
+       fun length=(length: Int): Bool `{
+               PP_Bool b;
+               b = g_varArrayInterface->SetLength(*recv, length);
+               return b;
+       `}
+end
+
+# Nit class representing a Pepper C API PP_Var.
+extern class PepperVar `{ struct PP_Var* `}
+
+       new `{
+               return malloc( sizeof( struct PP_Var ) );
+       `}
+
+       # Converts PepperVar to standard types.
+       #
+       # Actually supports bools, ints, floats, strings. To be used with 'isa'.
+       fun to_nit: nullable Pepperable
+       do
+               if isa_null then return null
+               if isa_bool then return as_bool
+               if isa_int then return as_int
+               if isa_float then return as_float
+               if isa_string then return as_string
+               if is_undefined then return null
+
+               return null
+       end
+
+       private fun isa_null: Bool `{ return recv->type == PP_VARTYPE_NULL; `}
+       private fun isa_bool: Bool `{ return recv->type == PP_VARTYPE_BOOL; `}
+       private fun isa_int: Bool `{ return recv->type == PP_VARTYPE_INT32; `}
+       private fun isa_float: Bool `{ return recv->type == PP_VARTYPE_DOUBLE; `}
+       private fun isa_string: Bool `{ return recv->type == PP_VARTYPE_STRING; `}
+       private fun is_undefined: Bool `{ return recv->type == PP_VARTYPE_UNDEFINED; `}
+
+       private fun as_bool: Bool `{ return recv->value.as_bool; `}
+       private fun as_int: Int `{ return recv->value.as_int; `}
+       private fun as_float: Float `{ return recv->value.as_double; `}
+       private fun as_string: String import NativeString.to_s_with_length `{
+               uint32_t len;
+               char* str = (char*)g_varInterface->VarToUtf8(*recv, &len);
+               return NativeString_to_s_with_length(str, len);
+       `}
+end
+
+# Provides a method to convert in PepperVars.
+interface Pepperable
+       fun to_pepper: PepperVar is abstract
+end
+
+redef class Int
+       super Pepperable
+
+       # Converts a Int into a PepperVar with Int type.
+       redef fun to_pepper `{
+               struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
+               *var = PP_MakeInt32(recv);
+               return var;
+       `}
+end
+
+redef class Float
+       super Pepperable
+
+       # Converts a Float into a PepperVar with Float type.
+       redef fun to_pepper `{
+               struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
+               *var = PP_MakeDouble(recv);
+               return var;
+       `}
+end
+
+redef class Bool
+       super Pepperable
+
+       # Converts a Bool into a PepperVar with Bool type.
+       redef fun to_pepper `{
+               struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
+               *var = PP_MakeBool(recv);
+               return var;
+       `}
+end
+
+redef class String
+       super Pepperable
+
+       # Converts a String into a PepperVar with String type.
+       redef fun to_pepper: PepperVar import String.to_cstring, String.length `{
+               char *str = String_to_cstring(recv);
+               struct PP_Var* var = malloc( sizeof( struct PP_Var ) );
+               *var = g_varInterface->VarFromUtf8(str, String_length(recv));
+               return var;
+       `}
+end
+
+# Class that provides the tools to interact with PNaCl.
+class PnaclApp
+
+       # Sets everything up to work, need to be called at first.
+       fun initialize import PnaclApp.handle_message, PnaclApp.handle_dictionary, NativeString.to_s_with_length `{
+               app = recv;
+       `}
+
+       # Posts a message to JS.
+       fun post_message(message: String) import String.to_cstring `{
+               char* str = String_to_cstring(message);
+               PostMessage(str);
+       `}
+
+       # Posts a dictionary to JS.
+       fun post_dictionary(dictionary: PepperDictionary) `{
+               PostDictionary(*dictionary);
+       `}
+
+       # Posts a PepperVar to JS.
+       #
+       # Should be used for testing, not recommanded for conventional conversation.
+       private fun post_var(v: PepperVar) `{
+               PostVar(*v);
+       `}
+
+       # Is called when a message is received from JS.
+       #
+       # Is set to be redefined in your application to handle like you want.
+       fun handle_message(message: String)
+       do
+               # To be Implemented by user.
+       end
+
+       # Is called when a Dictionary is received from JS.
+       #
+       # Is set to be redefined in your application to handle like you want.
+       # The dictionary is freed after this method returns.
+       fun handle_dictionary(dictionary: PepperDictionary)
+       do
+               # To be Implemented by user.
+       end
+end
index d03c1ea..906d484 100644 (file)
@@ -30,6 +30,8 @@ redef class ToolContext
        var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
        # --no-cc
        var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
+       # --no-main
+       var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main")
        # --cc-paths
        var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
@@ -62,7 +64,7 @@ redef class ToolContext
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics)
                self.option_context.add_option(self.opt_stacktrace)
@@ -612,7 +614,11 @@ abstract class AbstractCompiler
                v.add_decl("exit(signo);")
                v.add_decl("\}")
 
-               v.add_decl("int main(int argc, char** argv) \{")
+               if modelbuilder.toolcontext.opt_no_main.value then
+                       v.add_decl("int nit_main(int argc, char** argv) \{")
+               else
+                       v.add_decl("int main(int argc, char** argv) \{")
+               end
 
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
index 345fb43..7feafb2 100644 (file)
@@ -26,6 +26,7 @@ import separate_erasure_compiler
 import separate_compiler
 import android_platform
 import compiler_ffi
+import pnacl_platform
 
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
diff --git a/src/pnacl_platform.nit b/src/pnacl_platform.nit
new file mode 100644 (file)
index 0000000..d6291da
--- /dev/null
@@ -0,0 +1,268 @@
+# This file is part of NIT ( http://www.nitlanguage.org )
+#
+# Copyright 2014 Johan Kayser <kayser.johan@gmail.com>
+#
+# 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 program for the PNaCl platform
+module pnacl_platform
+
+import platform
+import abstract_compiler
+
+redef class ToolContext
+       redef fun platform_from_name(name)
+       do
+               if name == "pnacl" then return new PnaclPlatform
+               return super
+       end
+end
+
+class PnaclPlatform
+       super Platform
+
+       redef fun toolchain(toolcontext) do return new PnaclToolchain(toolcontext)
+end
+
+class PnaclToolchain
+       super MakefileToolchain
+
+       var pnacl_project_root: String
+
+       redef fun compile_dir
+       do
+               var normal_compile_dir = super
+               pnacl_project_root = "{normal_compile_dir}/pnacl/"
+               return "{normal_compile_dir}/pnacl/src/"
+       end
+
+       redef fun write_files(compiler, compile_dir, cfiles)
+       do
+               var app_name = compiler.mainmodule.name
+
+               # create compile_dir
+               var dir = pnacl_project_root
+               if not dir.file_exists then dir.mkdir
+
+               dir = compile_dir
+               if not dir.file_exists then dir.mkdir
+
+               # compile normal C files
+               super(compiler, compile_dir, cfiles)
+
+               # Gather extra C files generated elsewhere than in super
+               for f in compiler.extern_bodies do
+                       if f isa ExternCFile then cfiles.add(f.filename.basename(""))
+               end
+
+               # Outname
+               var outname = toolcontext.opt_output.value
+               if outname == null then outname = "{compiler.mainmodule.name}"
+
+               ## Generate makefile
+               dir = pnacl_project_root
+               if not dir.file_exists then dir.mkdir
+               var file = "{dir}/Makefile"
+               """
+# This file was generated by Nit, any modification will be lost.
+
+# Get pepper directory for toolchain and includes.
+#
+# If NACL_SDK_ROOT is not set, then assume it can be found five directories up.
+#
+THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
+NACL_SDK_ROOT ?= $(abspath $(dir $(THIS_MAKEFILE))../../../..)
+
+# Project Build flags
+WARNINGS := -Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
+CXXFLAGS := -pthread -std=gnu++98 $(WARNINGS)
+
+#
+# Compute tool paths
+#
+GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
+OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
+OSNAME := $(shell $(GETOS))
+RM := $(OSHELPERS) rm
+
+PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
+PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang
+PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
+CXXFLAGS := -I$(NACL_SDK_ROOT)/include
+LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi
+
+#
+# Disable DOS PATH warning when using Cygwin based tools Windows
+#
+CYGWIN ?= nodosfilewarning
+export CYGWIN
+
+
+# Declare the ALL target first, to make the 'all' target the default build
+all: ../../{{{outname}}}/{{{app_name}}}.pexe
+
+clean:
+       $(RM) {{{app_name}}}.pexe
+
+{{{app_name}}}.pexe: src/{{{cfiles.join(" src/")}}}
+       $(PNACL_CXX) -o $@ $^ -g -O0 $(CXXFLAGS) $(LDFLAGS) # For Debug
+       # $(PNACL_CXX) -o $@ $^ -O3 $(CXXFLAGS) $(LDFLAGS) # For Release
+
+../../{{{outname}}}/{{{app_name}}}.pexe: {{{app_name}}}.pexe
+       $(PNACL_FINALIZE) -o $@ $<
+
+#
+# Makefile target to run the SDK's simple HTTP server and serve this example.
+#
+HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py
+
+.PHONY: serve
+serve: all
+       $(HTTPD_PY) -C $(CURDIR)
+               """.write_to_file(file)
+
+               ### generate the minimal index.html
+               if not outname.file_exists then outname.mkdir
+               file = "{outname}/index.html"
+               if not file.file_exists then
+               """
+<!DOCTYPE html>
+<html>
+  <!--
+  This file was generated by Nit, any modification will be lost.
+  -->
+<head>
+       <title>{{{app_name}}}</title>
+       <script src="js/pnacl_js.js"></script>
+</head>
+<body onload="pageDidLoad()">
+       <h1>PNaCl : Minimal HTML for {{{app_name}}}</h1>
+       <p>
+  <!--
+  Load the published pexe.
+  Note: Since this module does not use any real-estate in the browser, its
+  width and height are set to 0.
+
+  Note: The <embed> element is wrapped inside a <div>, which has both a 'load'
+  and a 'message' event listener attached.  This wrapping method is used
+  instead of attaching the event listeners directly to the <embed> element to
+  ensure that the listeners are active before the NaCl module 'load' event
+  fires.  This also allows you to use PPB_Messaging.PostMessage() (in C) or
+  pp::Instance.PostMessage() (in C++) from within the initialization code in
+  your module.
+  -->
+               <div id="listener">
+                       <script type="text/javascript">
+                               var listener = document.getElementById('listener');
+                               listener.addEventListener('load', moduleDidLoad, true);
+                               listener.addEventListener('message', handleMessage, true);
+                       </script>
+
+                       <embed id="{{{app_name}}}"
+                               width=0 height=0
+                               src="{{{app_name}}}.nmf"
+                               type="application/x-pnacl" />
+               </div>
+               </p>
+               <h2>Status <code id="statusField">NO-STATUS</code></h2>
+</body>
+</html>
+               """.write_to_file(file)
+               end
+
+               ### generate pnacl_js.js in a folder named 'js'
+               dir = "{outname}/js/"
+               if not dir.file_exists then dir.mkdir
+               file = "{dir}/pnacl_js.js"
+               if not file.file_exists then
+               """
+// This file was generated by Nit, any modification will be lost.
+
+{{{app_name}}}Module = null;  // Global application object.
+statusText = 'NO-STATUS';
+
+// Indicate load success.
+function moduleDidLoad() {
+       {{{app_name}}}Module = document.getElementById('{{{app_name}}}');
+       updateStatus('SUCCESS');
+       // Send a message to the Native Client module like that
+       //{{{app_name}}}Module.postMessage('Hello World');
+}
+
+// The 'message' event handler.  This handler is fired when the NaCl module
+// posts a message to the browser by calling PPB_Messaging.PostMessage()
+// (in C) or pp::Instance.PostMessage() (in C++).  This implementation
+// simply displays the content of the message in an alert panel.
+function handleMessage(message_event) {
+       console.log(message_event.data);
+}
+
+// If the page loads before the Native Client module loads, then set the
+// status message indicating that the module is still loading.  Otherwise,
+// do not change the status message.
+function pageDidLoad() {
+       if ({{{app_name}}}Module == null) {
+               updateStatus('LOADING...');
+       } else {
+               // It's possible that the Native Client module onload event fired
+               // before the page's onload event.  In this case, the status message
+               // will reflect 'SUCCESS', but won't be displayed.  This call will
+               // display the current message.
+               updateStatus();
+       }
+}
+
+// Set the global status message.  If the element with id 'statusField'
+// exists, then set its HTML to the status message as well.
+// opt_message The message test.  If this is null or undefined, then
+// attempt to set the element with id 'statusField' to the value of
+// |statusText|.
+function updateStatus(opt_message) {
+       if (opt_message)
+               statusText = opt_message;
+       var statusField = document.getElementById('statusField');
+       if (statusField) {
+               statusField.innerHTML = statusText;
+       }
+}
+               """.write_to_file(file)
+               end
+
+               ### generate the manifest file : app_name.nmf
+               # used to point the HTML to the Native Client module
+               # and optionally provide additional commands to the PNaCl translator in Chrome
+               file = "{outname}/{app_name}.nmf"
+               """
+{
+       "program": {
+               "portable": {
+                       "pnacl-translate": {
+                               "url": "{{{app_name}}}.pexe"
+                       }
+               }
+       }
+}
+               """.write_to_file(file)
+       end
+
+       redef fun write_makefile(compiler, compile_dir, cfiles)
+       do
+               # Do nothing, already done in `write_files`
+       end
+
+       redef fun compile_c_code(compiler, compile_dir)
+       do
+               # Generate the pexe
+               toolcontext.exec_and_check(["make", "-C", pnacl_project_root], "PNaCl project error")
+       end
+end