Merge: Compilation to JavaScript using the Emscripten SDK
authorJean Privat <jean@pryen.org>
Tue, 17 Jun 2014 02:17:34 +0000 (22:17 -0400)
committerJean Privat <jean@pryen.org>
Tue, 17 Jun 2014 15:17:04 +0000 (11:17 -0400)
Published for review but still needs some doc and examples. Works with most small examples but not yet with the naive_interpreter (but it's not clear why).

Usage:

    apt-get emscripten
    nitg -m emscripten examples/hello_world.nit
    nodejs hello_wold.js

Pull-Request: #506
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

13 files changed:
examples/emscripten/README.md [new file with mode: 0644]
examples/emscripten/fibonacci/Makefile [new file with mode: 0644]
examples/emscripten/fibonacci/www/index.html [new file with mode: 0644]
examples/emscripten/hello_world/Makefile [new file with mode: 0644]
examples/emscripten/hello_world/www/index.html [new file with mode: 0644]
lib/emscripten.nit [new file with mode: 0644]
lib/standard/stream.nit
src/abstract_compiler.nit
src/emscripten_platform.nit [new file with mode: 0644]
src/nitg.nit
src/platform.nit
tests/niti.skip
tests/sav/emscripten.res [new file with mode: 0644]

diff --git a/examples/emscripten/README.md b/examples/emscripten/README.md
new file mode 100644 (file)
index 0000000..0d84b15
--- /dev/null
@@ -0,0 +1,9 @@
+# Hello world
+
+Minimal example using the standard Nit hello world program. The Nit code will be evaluated and executed once as soon as it is loaded. Standard output is redirected to the `console` element.
+
+# Fibonacci
+
+The Nit program is executed only when an input is supplied. The input is passed as the main arguments.
+
+This is not ideal, the whole Nit program is executed at each call.
diff --git a/examples/emscripten/fibonacci/Makefile b/examples/emscripten/fibonacci/Makefile
new file mode 100644 (file)
index 0000000..4ec01ea
--- /dev/null
@@ -0,0 +1,2 @@
+emscripten:
+       ../../../bin/nitg -o www/fibonacci.js ../../fibonacci.nit -m emscripten
diff --git a/examples/emscripten/fibonacci/www/index.html b/examples/emscripten/fibonacci/www/index.html
new file mode 100644 (file)
index 0000000..e0195f6
--- /dev/null
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<!-- This file is part of NIT ( http://www.nitlanguage.org )
+
+ Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+
+ 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. 
+-->
+<html>
+<head>
+       <title>Fibonacci</title>
+       <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
+       <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
+       <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+       <script>
+               // Redirect print to HTML
+               var Module = {
+                       'print': function(text) {
+                               $("#console").append(text + "<br>");
+                       },
+                       'noInitialRun': true,
+               };
+
+               $(document).ready(function() {
+                       // Report full loading
+                       $("#loading").text( "Loaded." );
+
+                       // Callback
+                       $("#in").keyup(function(){
+                               if(event.keyCode == 13){
+                                       fib();
+                               }
+                       });
+               });
+
+               function fib() {
+                       var input = $("#in").val();
+
+                       // Invoke the full Nit program
+               ret = Module['callMain']([input]);
+               }
+       </script>
+</head>
+<body>
+
+<div class="container">
+       <h1>Fibonacci calculator</h1>
+       <a href="https://github.com/privat/nit/blob/master/examples/fibonacci.nit">Nit source</a><br>
+       <a href="https://github.com/privat/nit/blob/master/examples/emscripten/fibonacci/www/index.html">HTML source</a>
+
+       <h2>Status</h2>
+       <p id="loading">Loading...</p>
+
+       <h2>Program Input</h2>
+       <input id="in" type="text" placeholder="12 (or any integer)"/><input type="button" value="Execute" onclick="fib();"/>
+
+       <h2>Program Output</h2>
+       <samp id="console"></samp>
+       
+       <script src="fibonacci.js"></script>
+</div>
+
+</body>
+</html>
diff --git a/examples/emscripten/hello_world/Makefile b/examples/emscripten/hello_world/Makefile
new file mode 100644 (file)
index 0000000..5fea224
--- /dev/null
@@ -0,0 +1,2 @@
+emscripten:
+       ../../../bin/nitg -o www/hello_world.js ../../hello_world.nit -m emscripten
diff --git a/examples/emscripten/hello_world/www/index.html b/examples/emscripten/hello_world/www/index.html
new file mode 100644 (file)
index 0000000..0820561
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- This file is part of NIT ( http://www.nitlanguage.org )
+
+ Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+
+ 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. 
+-->
+<html>
+<head>
+       <title></title>
+       <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
+       <script>
+               // Redirect print to HTML
+               var Module = {
+                       'print': function(text) {
+                               $("#console").append(text + "\n");
+                               $(document).title = text;
+                       }
+               };
+
+               // Report full loading
+               $(document).ready(function() {
+                       $("#loading").text( "Loaded and executed." );
+               });
+       </script>
+</head>
+<body>
+
+       <h1>Status</h1>
+       <p id="loading">Loading...</p>
+
+       <h1>Program output</h1>
+       <samp id="console"></samp>
+       
+       <script src="hello_world.js"></script>
+
+</body>
+</html>
diff --git a/lib/emscripten.nit b/lib/emscripten.nit
new file mode 100644 (file)
index 0000000..7fc5e76
--- /dev/null
@@ -0,0 +1,30 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+module emscripten is platform
+
+`{
+       #include <emscripten.h>
+`}
+
+redef class String
+       fun run_js do run_js_native(self.escape_to_js.to_cstring)
+       private fun run_js_native(script: NativeString) `{ emscripten_run_script(script); `}
+
+       fun escape_to_js: String do return self.replace('\n', "\\n")
+
+       fun alert do "alert('{self.escape_to_js}')".run_js
+end
index 504396a..79e601f 100644 (file)
@@ -20,6 +20,7 @@ in "C" `{
        #include <poll.h>
        #include <errno.h>
        #include <string.h>
+       #include <signal.h>
 `}
 
 # Abstract stream class
index 0093e60..c2785ed 100644 (file)
@@ -202,8 +202,12 @@ class MakefileToolchain
        do
                if self.toolcontext.opt_stacktrace.value == "nitstack" then compiler.build_c_to_nit_bindings
 
+               var platform = compiler.mainmodule.target_platform
+               var cc_opt_with_libgc = "-DWITH_LIBGC"
+               if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
+
                # Add gc_choser.h to aditionnal bodies
-               var gc_chooser = new ExternCFile("gc_chooser.c", "-DWITH_LIBGC")
+               var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
                compiler.extern_bodies.add(gc_chooser)
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.c"
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
@@ -288,18 +292,19 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
+       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
+
+       fun default_outname(mainmodule: MModule): String do return mainmodule.name
+
        fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
 
-               var outname = self.toolcontext.opt_output.value
-               if outname == null then
-                       outname = "{mainmodule.name}"
-               end
+               var outname = self.toolcontext.opt_output.value or else default_outname(mainmodule)
 
                var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
                var outpath = orig_dir.join_path(outname).simplify_path
-               var makename = "{mainmodule.name}.mk"
+               var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
 
@@ -367,7 +372,7 @@ class MakefileToolchain
 
        fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
        do
-               var makename = "{compiler.mainmodule.name}.mk" # FIXME duplicated from write_makefile
+               var makename = makefile_name(compiler.mainmodule)
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
diff --git a/src/emscripten_platform.nit b/src/emscripten_platform.nit
new file mode 100644 (file)
index 0000000..4c53b36
--- /dev/null
@@ -0,0 +1,56 @@
+# This file is part of NIT ( http://www.nitlanguage.org )
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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 to JavaScript using the Emscripten SDK
+module emscripten_platform
+
+import platform
+import abstract_compiler
+
+redef class ToolContext
+       redef fun platform_from_name(name)
+       do
+               if name == "emscripten" then return new EmscriptenPlatform
+               return super
+       end
+end
+
+class EmscriptenPlatform
+       super Platform
+
+       redef fun supports_libunwind do return false
+       redef fun supports_libgc do return false
+       redef fun toolchain(toolcontext) do return new EnscriptenToolchain(toolcontext)
+end
+
+class EnscriptenToolchain
+       super MakefileToolchain
+
+       redef fun makefile_name(mainmodule) do return "{mainmodule.name}.js.mk"
+
+       redef fun default_outname(mainmodule) do return "{super}.js"
+
+       redef fun write_makefile(compiler, compile_dir, cfiles)
+       do
+               super
+
+               var emcc_make_flags = "CC=emcc CFLAGS='-g -Wno-unused-value -Wno-switch -Qunused-arguments'"
+
+               var make_flags = self.toolcontext.opt_make_flags.value or else ""
+               make_flags += emcc_make_flags
+               self.toolcontext.opt_make_flags.value = make_flags
+       end
+end
index 0714632..66cd891 100644 (file)
@@ -27,6 +27,7 @@ import separate_compiler
 import android_platform
 import compiler_ffi
 import pnacl_platform
+import emscripten_platform
 
 redef class ToolContext
        redef fun process_options(args)
index 17257f5..3c5367f 100644 (file)
@@ -113,6 +113,8 @@ end
 abstract class Platform
        fun supports_libunwind: Bool do return true
 
+       fun supports_libgc: Bool do return true
+
        # Does this platform declare its own main function? If so, we won't generate one in Nit.
        fun no_main: Bool do return false
 end
index f9489e3..7c2762c 100644 (file)
@@ -19,3 +19,4 @@ test_android_platform
 android
 nitcc_parser_gen
 mnit
+emscripten
diff --git a/tests/sav/emscripten.res b/tests/sav/emscripten.res
new file mode 100644 (file)
index 0000000..174d681
--- /dev/null
@@ -0,0 +1 @@
+Not executable (platform?)