From 899a15b725fb2577d2003ecbfd9dac306d651fbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Nieper-Wi=C3=9Fkirchen?= <marc@nieper-wisskirchen.de>
Date: Mon, 22 Jun 2015 20:37:52 +0200
Subject: [PATCH] Integrate emscripten build process in Makefile

Move Emscripten dependencies into separate directory
---
 .gitignore                 |   6 ++
 Makefile                   |  16 +++++
 js/exported_functions.json |   5 ++
 js/index.html              | 116 +++++++++++++++++++++++++++++++++++++
 js/post.js                 |   2 +
 js/pre.js                  |   6 ++
 6 files changed, 151 insertions(+)
 create mode 100644 js/exported_functions.json
 create mode 100644 js/index.html
 create mode 100644 js/post.js
 create mode 100644 js/pre.js

diff --git a/.gitignore b/.gitignore
index 13594214..3009cebd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 # Object files
 *.o
+*.bc
 *.ko
 *.obj
 *.elf
@@ -36,6 +37,7 @@ lib/.*.meta
 
 # Generated files
 chibi-scheme
+chibi-scheme-emscripten
 chibi-scheme.pc
 include/chibi/install.h
 lib/chibi/emscripten.c
@@ -48,6 +50,7 @@ lib/chibi/system.c
 lib/chibi/time.c
 *.tgz
 *.html
+!index.html
 
 examples/snow-fort
 examples/synthcode
@@ -58,3 +61,6 @@ tmp
 /lib/chibi/crypto/crypto.c
 /chibi-scheme-ulimit
 /clibs.c
+
+js/chibi.*
+
diff --git a/Makefile b/Makefile
index dfee9663..7e87009a 100644
--- a/Makefile
+++ b/Makefile
@@ -82,6 +82,20 @@ endif
 
 all: chibi-scheme$(EXE) all-libs chibi-scheme.pc $(META_FILES)
 
+js: js/chibi.js
+
+js/chibi.js: chibi-scheme-emscripten chibi-scheme-static.bc js/pre.js js/post.js js/exported_functions.json
+	emcc -O2 chibi-scheme-static.bc -o $@ -s MODULARIZE=1 -s EXPORT_NAME=\"Chibi\" -s EXPORTED_FUNCTIONS=@js/exported_functions.json `find  lib -type f \( -name "*.scm" -or -name "*.sld" \) -printf " --preload-file %p"` --pre-js js/pre.js --post-js js/post.js
+
+chibi-scheme-static.bc:
+	emmake $(MAKE) PLATFORM=emscripten CHIBI_DEPENDENCIES= CHIBI=./chibi-scheme-emscripten PREFIX= CFLAGS=-O2 SEXP_USE_DL=0 EXE=.bc SO=.bc CPPFLAGS="-DSEXP_USE_STRICT_TOPLEVEL_BINDINGS=1 -DSEXP_USE_ALIGNED_BYTECODE=1 -DSEXP_USE_STATIC_LIBS=1" clibs.c chibi-scheme-static.bc
+
+chibi-scheme-emscripten: VERSION
+	$(MAKE) clean
+	$(MAKE) chibi-scheme-static PLATFORM=emscripten SEXP_USE_DL=0
+	mv chibi-scheme-static$(EXE) chibi-scheme-emscripten
+	$(MAKE) clean
+
 include/chibi/install.h: Makefile
 	echo '#define sexp_so_extension "'$(SO)'"' > $@
 	echo '#define sexp_default_module_path "'$(MODDIR):$(BINMODDIR)'"' >> $@
@@ -242,6 +256,8 @@ clean: clean-libs
 cleaner: clean
 	-$(RM) chibi-scheme$(EXE) chibi-scheme-static$(EXE) chibi-scheme-ulimit$(EXE) \
 	    libchibi-scheme$(SO)* *.a *.pc include/chibi/install.h lib/.*.meta \
+	    chibi-scheme-emscripten \
+	    js/chibi.* \
 	    $(shell $(FIND) lib -name \*.o)
 
 dist-clean: dist-clean-libs cleaner
diff --git a/js/exported_functions.json b/js/exported_functions.json
new file mode 100644
index 00000000..da719553
--- /dev/null
+++ b/js/exported_functions.json
@@ -0,0 +1,5 @@
+[
+  '_main',
+  '_sexp_resume'
+]
+
diff --git a/js/index.html b/js/index.html
new file mode 100644
index 00000000..be273b9c
--- /dev/null
+++ b/js/index.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Chibi-Scheme</title>
+    <style>
+      body {
+        font-family: sans-serif;
+        height: 100vh;
+        margin: 0;
+        padding: 0;
+        display: flex;
+        flex-direction: column;
+      }
+      main {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+      }
+      #program {
+        flex: 1 1 0;
+        padding: 0.5em;
+      }
+      #start {
+        font-size: inherit;
+        padding: 0.5em;
+      }
+      #output {
+        font-family: monospace;
+        padding: 0.5em;
+        white-space: pre;
+        background-color: #000;
+        color: #fff;
+        overflow: auto;
+        flex: 1 1 0;
+      }
+    </style>
+  </head>
+  <body>
+    <main>
+      <textarea id="program" spellcheck="false">;
+; This is Chibi-Scheme compiled with Emscripten to run in the browser.
+;
+
+(import (scheme base))
+(write-string "Hello, world!\n")
+
+;
+; You can also run arbitrary JavaScript code from scheme and yield control back and forth between Scheme and the browser
+;
+
+(import (chibi emscripten)) ; exports: eval-script!, integer-eval-script, string-eval-script, wait-on-event!
+
+(write-string (number->string (integer-eval-script "6 * 7")))
+(newline)
+
+(eval-script! "window.addEventListener('click', function () {
+  Module['resume'](); // give control back to the Scheme process
+})")
+
+(let loop ()
+  (wait-on-event!) ; yields control back to the browser
+  (write-string "You have clicked me!\n")
+  (loop))
+
+(write-string "Control never reaches this point\n")
+</textarea>
+      <button type="button" id="start" disabled>Start Program</button>
+      <div id="output"></div>
+    </main>
+    <script src="chibi.js"></script>
+    <script>
+      function start(program, args, onOutput, onError) {
+        var firstError = true;
+        Chibi({
+          print: onOutput,
+          printErr: function (text) {
+            if (firstError) {
+              firstError = false;
+              return;
+            }
+            if (onError !== undefined) {
+              onError(text);
+            } else {
+              onOutput(text);
+            }
+          },
+          program: program,
+          arguments: args
+        });
+      }
+    </script>
+    <script>
+      (function () {
+        var programField = document.querySelector('#program');
+        var startButton = document.querySelector('#start');
+        var program = sessionStorage.getItem('program');
+        if (program) {
+          programField.value = program;
+        }
+        programField.addEventListener('input', function() {
+          sessionStorage.setItem('program', programField.value);
+        });
+        startButton.addEventListener('click', function() {
+          var program = programField.value;
+          startButton.disabled = true;
+          start(program, [],
+              function(text) {
+                output.textContent = output.textContent + text + '\n'
+              });
+        });
+        startButton.disabled = false;
+      })();
+    </script>
+  </body>
+</html>
diff --git a/js/post.js b/js/post.js
new file mode 100644
index 00000000..40e37566
--- /dev/null
+++ b/js/post.js
@@ -0,0 +1,2 @@
+Module['resume'] = Module.cwrap('sexp_resume', 'void', []);
+
diff --git a/js/pre.js b/js/pre.js
new file mode 100644
index 00000000..8bb97bf5
--- /dev/null
+++ b/js/pre.js
@@ -0,0 +1,6 @@
+Module['preRun'].push(function () {
+  FS.writeFile('program.scm', Module['program']);
+});
+Module['arguments'] = Module['arguments'] || [];
+Module['arguments'].unshift('program.scm');
+