Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[emscripten] build with emscripten #16

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ __pycache__/
/experiments/issue*/data/
/misc/.tox/
/misc/autodoc/downward-xmlrpc.secret
*.cc.o
/src/bin
/src/CMakeFiles
/src/Makefile
/src/search/Makefile
/src/search/CMakeFiles
8 changes: 7 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ include(FastDownwardMacros)

fast_downward_default_to_release_build()
project(fast-downward)
fast_downward_report_bitwidth()
if (EMSCRIPTEN)
MESSAGE("Configuring emscripten build.")
else()
MESSAGE("Configuring native build.")
fast_downward_report_bitwidth()
endif()

# Due to a bug in cmake, configuration types are only set up correctly on the second cmake run.
# This means that cmake has to be called twice for multi-config generators like Visual Studio.
fast_downward_set_configuration_types()
Expand Down
4 changes: 3 additions & 1 deletion src/cmake_modules/FastDownwardMacros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ macro(fast_downward_set_compiler_flags)
endmacro()

macro(fast_downward_set_linker_flags)
if(UNIX)
if (EMSCRIPTEN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 --bind -s INVOKE_RUN=0 -s EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']")
elseif(UNIX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -g")
endif()
endmacro()
Expand Down
33 changes: 33 additions & 0 deletions src/javascript/search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Planning in the browser
- Tested with emscripten version 2.0.8

## Building the JS+WASM files:

- make sure the emscripten sdk is installed and executables (mainly em++ and emcmake) are on the PATH.

- cmake needs to be installed

- you can use the `./configure_cmake.sh` file to configure cmake for an emscripten or native build using `./configure_cmake.sh emscripten` or `./configure_cmake.sh native` respectively.

- use above mentioned to switch to emscripten build type, this executes `cmake` with custom parameters: `CMAKE_CXX_COMPILER=em++` and `EMSCRIPTEN=1`.

- build the WebAssembly and Javascript files using `make` in the `/src` directory, output files can be found in `src/bin`.

- as the `CMAKE_CXX_COMPILER` stays to be `em++`, you need to change it back in case you want to build the native version. The configuration script assums `g++` to be your default native compiler.

## Using the JS+WASM files:

- instead of using STDIN to pass the translated `output.sas` file to the program, we added the option to use the `--input` argument, which takes a file handle and treats it as input stream

- thus, before calling the search, but after the Module was loaded, we need to save the translated output file on the browsers virtual filesystem

- There are multiple ways to do this, an example however can be found under `usage_example.js` loaded at the end of `index.html`. The example assumes `output.sas`, `downward.js` as well as `downward.wasm` files in the working directory

- the `runFastDownward()` function executes the example. Make sure to have added the necessary files in beforehand.

## Debug

- you can use the included function `Module.getExceptionMessage(pointer)` to get a more detailed exception message in case you need it for debugging:
in the `downward.js` file, you need to replace the original exception logging by the result of `Module.getExceptionMessage(pointer)`. I.e. replace `err('exception thrown: ' + toLog);` by
`err('exception thrown: ' + Module.getExceptionMessage(toLog));`
`
31 changes: 31 additions & 0 deletions src/javascript/search/configure_cmake.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh
cd "$(dirname "$0")/../../"
if [ $# -eq 0 ]; then
echo "No arguments provided"
echo "Run either with "
echo "./configure_cmake.sh emscripten"
echo "or "
echo "./configure_cmake.sh native"
exit 1
fi

if [ "$1" = "emscripten" ];
then
echo 'setting cmake settings for building with emscripten'
# in case of switch we need to execute twice, as cmake notices changed variables and executes itself again but fails as EMSCRIPTEN variable is not passed
emcmake cmake -DEMSCRIPTEN=True -DCMAKE_CXX_COMPILER=$(which em++) .
emcmake cmake -DEMSCRIPTEN=True -DCMAKE_CXX_COMPILER=$(which em++) .
elif [ "$1" = "native" ];
then
echo "setting cmake settings for native build"
# in case of switch we need to execute twice, as cmake notices changed variables and executes itself again but fails as EMSCRIPTEN variable is not passed
cmake -DEMSCRIPTEN=False -DCMAKE_CXX_COMPILER=$(which g++) .
cmake -DEMSCRIPTEN=False -DCMAKE_CXX_COMPILER=$(which g++) .
else
echo "argument not recognized"
echo "Run either with "
echo "./configure_cmake.sh emscripten"
echo "or "
echo "./configure_cmake.sh native"
exit 1
fi
13 changes: 13 additions & 0 deletions src/javascript/search/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

<title>Planning in the browser</title>
</head>
<body>
<h1>Planning in the browser</h1>
</body>

<script type="text/javascript" src='usage_example.js'></script>
</html>
39 changes: 39 additions & 0 deletions src/javascript/search/usage_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FILE_WASM='downward.wasm'
FILE_JAVASCRIPT='downward.js'
PATH_TO_INPUT='output.sas'

// Load the javascript file containing the Downward module
downwardscript = document.createElement('script');
downwardscript.src = FILE_JAVASCRIPT;
downwardscript.onload = function() {
// Fetch input data via XHR and save it on the browsers virtual filesystem once it is loaded
console.log("Fetching data from " + PATH_TO_INPUT);
var inputURL = PATH_TO_INPUT;
var inputXHR = new XMLHttpRequest();
inputXHR.open('GET', inputURL, true);
inputXHR.responseType = 'text';
inputXHR.onload = function() {
if (inputXHR.status === 200 || inputXHR.status === 0) {
let data = new TextEncoder().encode(inputXHR.response);
let stream = FS.open('output.sas', 'w+');
FS.write(stream, data, 0, data.length, 0);
FS.close(stream);
console.log('wrote to' + PATH_TO_INPUT);
}
}
inputXHR.send();
}
document.body.appendChild(downwardscript);

// function to start the actual program
function runFastDownward() {
// define parameters and split them to a list
let parameter_string = "--search astar(lmcut()) --input output.sas"
let parameter_list = parameter_string.split(" ")

Module.callMain(parameter_list);

// results are saved on the virtual filesystem in the browser
let result = new TextDecoder().decode(FS.readFile('sas_plan'));
console.log(result);
}
19 changes: 19 additions & 0 deletions src/javascript/translator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Translator in the browser
We offer to build the translator as python wheel.
This is done by executing the `setup.py` script.
**Pyodide** can then be used to load the python wheel in the browser.

## How to execute the translator using pyodide
The following steps are exemplarily presented in `usage_example.html` and `usage_example.js`
- you need to serve the JS file through a server; python can be used for that: `python -m http.server`
- in your javascript file, load the current pyodide version
- load pyodide's *micropip* package
- load the *.pddl* files into two javascript strings
- use micropip to install the translator-wheel created by `setup.py` into the python environment
- store the pddl strings to the browser's virtual filesystem using python
- import and execute the translator in python:
```Python
from translator.translate import run
run(["domain.pddl", "problem.pddl", "--sas-file", "output.sas"])
```
- load the result from the python environment to the javascript environment for further use
34 changes: 34 additions & 0 deletions src/javascript/translator/exampleFiles/domain.pddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
(define (domain gripper-strips)
(:predicates (room ?r)
(ball ?b)
(gripper ?g)
(at-robby ?r)
(at ?b ?r)
(free ?g)
(carry ?o ?g))

(:action move
:parameters (?from ?to)
:precondition (and (room ?from) (room ?to) (at-robby ?from))
:effect (and (at-robby ?to)
(not (at-robby ?from))))



(:action pick
:parameters (?obj ?room ?gripper)
:precondition (and (ball ?obj) (room ?room) (gripper ?gripper)
(at ?obj ?room) (at-robby ?room) (free ?gripper))
:effect (and (carry ?obj ?gripper)
(not (at ?obj ?room))
(not (free ?gripper))))


(:action drop
:parameters (?obj ?room ?gripper)
:precondition (and (ball ?obj) (room ?room) (gripper ?gripper)
(carry ?obj ?gripper) (at-robby ?room))
:effect (and (at ?obj ?room)
(free ?gripper)
(not (carry ?obj ?gripper)))))

22 changes: 22 additions & 0 deletions src/javascript/translator/exampleFiles/problem.pddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(define (problem strips-gripper-x-1)
(:domain gripper-strips)
(:objects rooma roomb ball4 ball3 ball2 ball1 left right)
(:init (room rooma)
(room roomb)
(ball ball4)
(ball ball3)
(ball ball2)
(ball ball1)
(at-robby rooma)
(free left)
(free right)
(at ball4 rooma)
(at ball3 rooma)
(at ball2 rooma)
(at ball1 rooma)
(gripper left)
(gripper right))
(:goal (and (at ball4 roomb)
(at ball3 roomb)
(at ball2 roomb)
(at ball1 roomb))))
35 changes: 35 additions & 0 deletions src/javascript/translator/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /usr/bin/python3
import os
import sys

sys.argv.append("bdist_wheel")

home = os.getcwd()

# go to translator root folder
os.chdir("../../translate")

# setup for bdist_wheel
from setuptools import setup, find_packages
setup(
name = 'translator',
version='1.0',
# Use one of the below approach to define package and/or module names:

#if there are only handful of modules placed in root directory, and no packages/directories exist then can use below syntax
# packages=[''], #have to import modules directly in code after installing this wheel, like import mod2 (respective file name in this case is mod2.py) - no direct use of distribution name while importing

#can list down each package names - no need to keep __init__.py under packages / directories
packages=['translator', 'translator/pddl', 'translator/pddl_parser'], #importing is like: from package1 import mod2, or import package1.mod2 as m2

# this approach automatically finds out all directories (packages) - those must contain a file named __init__.py (can be empty)
# packages=find_packages(), #include/exclude arguments take * as wildcard, . for any sub-package names
)

# move files to home directory and remove unnecessary files
import shutil
shutil.rmtree('build/')
shutil.rmtree('translator.egg-info/')
for file in os.listdir('dist'):
shutil.move('dist/' + file, os.path.join(home, file))
shutil.rmtree('dist/')
43 changes: 43 additions & 0 deletions src/javascript/translator/usage_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Translator</title>
<script src="https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js" type="text/javascript"></script>
<script src="usage_example.js" type="text/javascript" defer></script>
</head>
<body>
<h1>Translator in the browser</h1>
Usage example
<br>
<hr>

<style>
.contentHolder {
display: flex;
}
.contentHolder > div {
padding: 0vh 1vw;
}
.result {
padding: 0vh 1vw;
}
</style>
<div class=contentHolder>
<div class="domain">
<h5>Domain:</h5>
<code>...</code>
</div>
<div class="problem">
<h5>Problem:</h5>
<code>...</code>
</div>
</div>
<hr>

<div class=result>
<code></code>
</div>
</body>
</html>

Loading