Skip to content

Switch to entangled tangle #53

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

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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: 3 additions & 3 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
echo 'Check entangled files are up to date'

# Entangle Markdown to source code and store the output
LOG=$(docker run --rm --user $(id -u):$(id -g) -v ${PWD}:/data nlesc/pandoc-tangle:0.5.0 --preserve-tabs *.md 2>&1 > /dev/null)
# Parse which filenames have been written from output
FILES=$(echo $LOG | perl -ne 'print $1,"\n" if /^Writing \`(.*)\`./')
LOG=$(docker run --rm --user $(id -u):$(id -g) -v ${PWD}:/data nlesc/entangled -m tangle -a 2>&1 > /dev/null)
# Parse which filenames have been created or modified from output, ignoring deleted files (start with `- `)
FILES=$(echo $LOG | grep -v '^-' | cut -c 3-)
[ -z "$FILES" ] && exit 0
echo $FILES

Expand Down
19 changes: 10 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Before you submit a pull request, check that it meets these guidelines:

## Generating code from Markdown

The [Entangled - Pandoc filters](https://github.com/entangled/filters) Docker image can be used to generate source code files from the Markdown files.
The [Entangled](https://github.com/entangled/entangled) Docker image can be used to generate source code files from the Markdown files.

First, store your user id and group as environment variables:

Expand All @@ -125,8 +125,9 @@ export HOST_GID=$(id -g)

Then,

```{.awk #pandoc-tangle}
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/pandoc-tangle:0.5.0 --preserve-tabs *.md
```{.shell #entangled-tangle}
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled insert -s *.md
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled tangle -a
```

## Generate code from Markdown and vice versa
Expand Down Expand Up @@ -154,16 +155,16 @@ The rest of this section describes how the git hook works.

The pre-commit hook script runs entangle using Docker and adds newly written files to the current git commit.

```{.awk file=.githooks/pre-commit}
```{.shell file=.githooks/pre-commit}
#!/bin/sh
# this shell script is stored as .githooks/pre-commit

echo 'Check entangled files are up to date'

# Entangle Markdown to source code and store the output
LOG=$(docker run --rm --user $(id -u):$(id -g) -v ${PWD}:/data nlesc/pandoc-tangle:0.5.0 --preserve-tabs *.md 2>&1 > /dev/null)
# Parse which filenames have been written from output
FILES=$(echo $LOG | perl -ne 'print $1,"\n" if /^Writing \`(.*)\`./')
LOG=$(docker run --rm --user $(id -u):$(id -g) -v ${PWD}:/data nlesc/entangled -m tangle -a 2>&1 > /dev/null)
# Parse which filenames have been created or modified from output, ignoring deleted files (start with `- `)
FILES=$(echo $LOG | grep -v '^-' | cut -c 3-)
[ -z "$FILES" ] && exit 0
echo $FILES

Expand All @@ -174,13 +175,13 @@ echo $FILES | xargs git add

The hook must be made executable with

```{.awk #hook-permission}
```{.shell #hook-permission}
chmod +x .githooks/pre-commit
```

The git hook can be enabled with

```{.awk #init-git-hook}
```{.shell #init-git-hook}
git config --local core.hooksPath .githooks
```

Expand Down
7 changes: 5 additions & 2 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ All the commands in the [README.md](README.md) and [CONTRIBUTING.md](CONTRIBUTIN
HOST_UID := $(shell id -u)
HOST_GID := $(shell id -g)
# Prevent suicide by excluding Makefile
ENTANGLED := $(shell perl -ne 'print $$1,"\n" if /^```\{.*file=(.*)\}/' *.md | grep -v Makefile | sort -u)
ENTANGLED := $(shell docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled list)
COMPILED := cli/newtonraphson.exe openapi/newtonraphsonpy.*.so flask/newtonraphsonpy.*.so cgi/apache2/cgi-bin/newtonraphson webassembly/newtonraphsonwasm.js webassembly/newtonraphsonwasm.wasm react/newtonraphsonwasm.js react/newtonraphsonwasm.wasm

entangle: *.md
<<pandoc-tangle>>
<<entangled-tangle>>

$(ENTANGLED): entangle

Expand Down Expand Up @@ -174,6 +174,9 @@ init-git-hook:

check: entangle
git diff-index --quiet HEAD --
# TODO entangled always OK due to entangle target being run before. `make check` should not have entangle target called.
#check:
# docker run --rm --user ${UID} -v ${PWD}:/data nlesc/entangled -c tangle -a
```

For example the Python dependencies can be installed with
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
HOST_UID := $(shell id -u)
HOST_GID := $(shell id -g)
# Prevent suicide by excluding Makefile
ENTANGLED := $(shell perl -ne 'print $$1,"\n" if /^```\{.*file=(.*)\}/' *.md | grep -v Makefile | sort -u)
ENTANGLED := $(shell docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled list)
COMPILED := cli/newtonraphson.exe openapi/newtonraphsonpy.*.so flask/newtonraphsonpy.*.so cgi/apache2/cgi-bin/newtonraphson webassembly/newtonraphsonwasm.js webassembly/newtonraphsonwasm.wasm react/newtonraphsonwasm.js react/newtonraphsonwasm.wasm

entangle: *.md
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/pandoc-tangle:0.5.0 --preserve-tabs *.md
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled insert -s *.md
docker run --rm --user ${HOST_UID}:${HOST_GID} -v ${PWD}:/data nlesc/entangled tangle -a

$(ENTANGLED): entangle

Expand Down Expand Up @@ -178,4 +179,7 @@ init-git-hook:
git config --local core.hooksPath .githooks

check: entangle
git diff-index --quiet HEAD --
git diff-index --quiet HEAD --
# TODO entangled always OK due to entangle target being run before. `make check` should not have entangle target called.
#check:
# docker run --rm --user ${UID} -v ${PWD}:/data nlesc/entangled -c tangle -a
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ root is `-1`.

[![Plotted equation](images/equation.svg)](https://www.wolframalpha.com/input/?i=x%5E3+-+x%5E2+%2B+2)

```{.hpp file=cli/algebra.hpp}
```{.cpp file=cli/algebra.hpp}
// this C++ code snippet is store as cli/algebra.hpp

namespace algebra
Expand Down Expand Up @@ -145,13 +145,13 @@ int main()

The program can be compiled with

```{.awk #build-cli}
```{.shell #build-cli}
g++ cli/cli-newtonraphson.cpp -o cli/newtonraphson.exe
```

and it can be run with

```{.awk #test-cli}
```{.shell #test-cli}
./cli/newtonraphson.exe
```

Expand All @@ -170,7 +170,7 @@ The value of the root is : -1.000000
| :heart: Very few moving parts, just C++ and Apache web server | :no_entry: Complicated Apache web server configuration |
| :heart: Proven technology | :no_entry: Not suitable for long initialization or calculations |

The classic way to run programs when accessing a url is to use the Common Gateway Interface (CGI). In the
The classic way to run programs when accessing a url is to use the Common Gateway Interface (CGI). In the
[Apache httpd web server](https://httpd.apache.org/docs/2.4/howto/cgi.html) you can configure a directory as a
`ScriptAlias`, when visiting a file inside that directory the file will be executed. The executable can read the
request body from the `stdin` and the response must be printed to the `stdout`. A response should consist of a
Expand Down Expand Up @@ -222,13 +222,13 @@ And lastly, return a JSON document with the result

This can be compiled with

```{.awk #build-cgi}
```{.shell #build-cgi}
g++ -Icgi/deps/ -Icli/ cgi/cgi-newtonraphson.cpp -o cgi/apache2/cgi-bin/newtonraphson
```

The CGI script can be tested from the command line

```{.awk #test-cgi}
```{.shell #test-cgi}
echo '{"guess":-20, "epsilon":0.001}' | cgi/apache2/cgi-bin/newtonraphson
```

Expand Down Expand Up @@ -314,7 +314,7 @@ is a header-only library.

To use ``pybind11``, it must installed with ``pip``

```{.awk #pip-pybind11}
```{.shell #pip-pybind11}
pip install pybind11
```

Expand Down Expand Up @@ -347,7 +347,7 @@ PYBIND11_MODULE(newtonraphsonpy, m) {

Compile with

```{.awk #build-py}
```{.shell #build-py}
g++ -O3 -Wall -shared -std=c++14 -fPIC -Icli/ `python3 -m pybind11 --includes` \
openapi/py-newtonraphson.cpp -o openapi/newtonraphsonpy`python3-config --extension-suffix`
```
Expand All @@ -366,7 +366,7 @@ print ("{0:.6f}".format(root))

The Python example can be run with

```{.awk #test-py}
```{.shell #test-py}
python openapi/example.py
```

Expand Down Expand Up @@ -563,7 +563,7 @@ The Python standard library ships with a [HTTP server](https://docs.python.org/3

The Flask Python library can be installed with

```{.awk #pip-flask}
```{.shell #pip-flask}
pip install flask
```

Expand Down Expand Up @@ -638,7 +638,7 @@ app.run(port=5001)

And running it with

```{.awk #run-webapp}
```{.shell #run-webapp}
python flask/webapp.py
```

Expand Down Expand Up @@ -669,13 +669,13 @@ Our Celery powered web application will have 3 pages:
Celery needs a broker for a queue and result storage. We'll use [redis](https://redis.io/) in a Docker container as
Celery broker, because it's simple to setup. Redis can be started with the following command

```{.awk #start-redis}
```{.shell #start-redis}
docker run --rm -d -p 6379:6379 --name some-redis redis
```

To use Celery we must install the redis flavored version with

```{.awk #pip-celery}
```{.shell #pip-celery}
pip install celery[redis]
```

Expand Down Expand Up @@ -768,13 +768,13 @@ if __name__ == '__main__':

Start the web application like before with

```{.awk #run-celery-webapp}
```{.shell #run-celery-webapp}
python flask/webapp-celery.py
```

Tasks will be run by the Celery worker. The worker can be started with

```{.awk #run-celery-worker}
```{.shell #run-celery-worker}
PYTHONPATH=flask celery worker -A tasks
```

Expand Down Expand Up @@ -1165,7 +1165,7 @@ curl --request POST \

### Long running task with worker threads

The web service we made in the prevous chapter will block any other requests while the algorithm solving is running. This is due to the inner workings of Node.js. Node.js uses a single threaded event loop, so while an event is being handled Node.js is busy. Node.js uses callbacks and promises to handle long IO tasks efficiently.
The web service we made in the prevous chapter will block any other requests while the algorithm solving is running. This is due to the inner workings of Node.js. Node.js uses a single threaded event loop, so while an event is being handled Node.js is busy. Node.js uses callbacks and promises to handle long IO tasks efficiently.

To use the CPU in parallel Node.js has [worker threads](https://nodejs.org/dist/latest-v12.x/docs/api/worker_threads.html). We don't want to start a new thread each time a request is recieved to perform the calculation, we want to use a pool of waiting threads. So each request will be computed by a thread from the pool.
Node.js gives use the low level primitives to create a thread. A thread pool implementation is explained in the [Node.js documentation](https://nodejs.org/docs/latest-v12.x/api/async_hooks.html#async_hooks_using_asyncresource_for_a_worker_thread_pool), we could copy it here or use an existing package.
Expand Down Expand Up @@ -1367,12 +1367,12 @@ document.getElementById('answer').innerHTML = root.toFixed(2);
The web browser can only load the `newtonraphsonwasm.js` file when hosted by a web server. Python ships with a built-in
web server, we will use it to host all files of the repository on port 8000.

```{.awk #host-files}
```{.shell #host-files}
python3 -m http.server 8000
```

Visit [http://localhost:8000/webassembly/example.html](http://localhost:8000/webassembly/example.html) to see the result
of the calculation. Embedded below is the example hosted on
of the calculation. Embedded below is the example hosted on
[GitHub pages](https://nlesc-jcer.github.io/cpp2wasm/webassembly/example.html)

[https://nlesc-jcer.github.io/cpp2wasm/webassembly/example.html](https://nlesc-jcer.github.io/cpp2wasm/webassembly/example.html ':include :type=iframe width=100% height=60px').
Expand Down Expand Up @@ -1583,7 +1583,7 @@ offline.
The web application in our example should have a form with `epsilon` and `guess` input fields, as well as a ``submit`` button.
The form in JSX can be written in the following way:

```{.jsx #react-form}
```{.jsxinline #react-form}
{ /* this JavaScript snippet is later referred to as <<react-form>> */ }
<form onSubmit={handleSubmit}>
<label>
Expand Down Expand Up @@ -1842,7 +1842,7 @@ function handleChange(event) {

The form can be rendered with

```{.jsx #jsonschema-form}
```{.jsxinline #jsonschema-form}
{ /* this JavaScript snippet is later referred to as <<jsonschema-form>> */}
<Form
schema={schema}
Expand Down
9 changes: 9 additions & 0 deletions entangled.dhall
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
let entangled = https://raw.githubusercontent.com/entangled/entangled/v1.0.1/data/config-schema.dhall
sha256:9fd18824499379eee53b974ca7570b3bc064fda546348d9b31841afab3b053a7

let jsxinlineComment = entangled.Comment.Block { start = "{ /*", end = "*/ }" }

let languages = entangled.languages #
[ { name = "jsx", identifiers = ["jsx"], comment = entangled.comments.cppStyle }
,{ name = "jsxinline", identifiers = ["jsxinline"], comment = jsxinlineComment }
,{ name = "Bash", identifiers = ["bash", "sh", "shell"], comment = entangled.comments.hash }
]

in { entangled = entangled.Config :: { database = Some ".entangled/db.sqlite"
, watchList = ["README.md", "INSTALL.md", "CONTRIBUTING.md", "TESTING.md"] : List Text
, languages = languages
}
}