From c24937aeb7ca619e74efd63fd7fa873b266e3377 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Thu, 30 Sep 2021 08:11:47 +1300 Subject: [PATCH 01/13] Add self to license --- license | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license b/license index 4f45aea..fd203f8 100644 --- a/license +++ b/license @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 Brian Seymour, EMKC Contributors +Copyright (c) 2018-2021 Brian Seymour, Thomas Hobson, EMKC Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 5d392effccd7da2584bfee4ef8cd6d565deff8a5 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Fri, 1 Oct 2021 20:28:54 +1300 Subject: [PATCH 02/13] api: maximum concurrent jobs and potential fix for gcc --- api/src/config.js | 7 +++++++ api/src/job.js | 38 +++++++++++++++++++++++++++++++------- docs/configuration.md | 9 +++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/api/src/config.js b/api/src/config.js index 84270aa..bbd7ae9 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -114,6 +114,13 @@ const options = [ 'https://github.com/engineer-man/piston/releases/download/pkgs/index', validators: [], }, + { + key: 'max_concurrent_jobs', + desc: 'Maximum number of concurrent jobs to run at one time', + default: 64, + parser: parse_int, + validators: [(x) => x > 0 || `${x} cannot be negative`] + } ]; logger.info(`Loading Configuration from environment`); diff --git a/api/src/job.js b/api/src/job.js index 683cda6..712dcd8 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -16,6 +16,19 @@ const job_states = { let uid = 0; let gid = 0; +let remainingJobSpaces = config.max_concurrent_jobs; +let jobQueue = []; + + +setInterval(()=>{ + // Every 10ms try resolve a new job, if there is an available slot + if(jobQueue.length > 0 && remainingJobSpaces > 0){ + jobQueue.shift()() + } +}, 10) + + + class Job { constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { this.uuid = uuidv4(); @@ -48,8 +61,15 @@ class Job { } async prime() { - logger.info(`Priming job uuid=${this.uuid}`); + if(remainingJobSpaces < 1){ + logger.info(`Awaiting job slot uuid=${this.uuid}`) + await new Promise((resolve)=>{ + jobQueue.push(resolve) + }) + } + logger.info(`Priming job uuid=${this.uuid}`); + remainingJobSpaces--; logger.debug('Writing files to job cache'); logger.debug(`Transfering ownership uid=${this.uid} gid=${this.gid}`); @@ -152,21 +172,23 @@ class Job { } }); - const exit_cleanup = () => { + const exit_cleanup = async () => { clear_timeout(kill_timeout); proc.stderr.destroy(); proc.stdout.destroy(); + + await this.cleanup_processes() }; - proc.on('exit', (code, signal) => { - exit_cleanup(); + proc.on('exit', async (code, signal) => { + await exit_cleanup(); resolve({stdout, stderr, code, signal, output }); }); - proc.on('error', err => { - exit_cleanup(); + proc.on('error', async err => { + await exit_cleanup(); reject({ error: err, stdout, stderr, output }); }); @@ -339,11 +361,13 @@ class Job { async cleanup() { logger.info(`Cleaning up job uuid=${this.uuid}`); - await this.cleanup_processes(); await this.cleanup_filesystem(); + + remainingJobSpaces++; } } + module.exports = { Job, }; diff --git a/docs/configuration.md b/docs/configuration.md index 1388e9d..16a5df0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -145,3 +145,12 @@ default: https://github.com/engineer-man/piston/releases/download/pkgs/index ``` URL for repository index, where packages will be downloaded from. + +## Maximum Concurrent Jobs + +```yaml +key: PISTON_MAX_CONCURRENT_JOBS +default: 64 +``` + +Maximum number of jobs to run concurrently. From 36d72383a53ebf752fb0ef28a22db422f668f987 Mon Sep 17 00:00:00 2001 From: Thomas Hobson Date: Sat, 2 Oct 2021 00:07:37 +1300 Subject: [PATCH 03/13] rework process janitor Old process janitor required starting a `ps` process. This was problematic, as `ps` requires another entry in the process table, which in some cases was impossible as it was exhausted. --- api/src/job.js | 74 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/api/src/job.js b/api/src/job.js index 712dcd8..ecc4ab3 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -146,26 +146,31 @@ class Job { const kill_timeout = set_timeout( - _ => proc.kill('SIGKILL'), + async _ => { + logger.info(`Timeout exceeded timeout=${timeout} uuid=${this.uuid}`) + process.kill(proc.pid, 'SIGKILL') + }, timeout ); - proc.stderr.on('data', data => { + proc.stderr.on('data', async data => { if(eventBus !== null) { eventBus.emit("stderr", data); } else if (stderr.length > config.output_max_size) { - proc.kill('SIGKILL'); + logger.info(`stderr length exceeded uuid=${this.uuid}`) + process.kill(proc.pid, 'SIGKILL') } else { stderr += data; output += data; } }); - proc.stdout.on('data', data => { + proc.stdout.on('data', async data => { if(eventBus !== null){ eventBus.emit("stdout", data); } else if (stdout.length > config.output_max_size) { - proc.kill('SIGKILL'); + logger.info(`stdout length exceeded uuid=${this.uuid}`) + process.kill(proc.pid, 'SIGKILL') } else { stdout += data; output += data; @@ -179,6 +184,7 @@ class Job { proc.stdout.destroy(); await this.cleanup_processes() + logger.debug(`Finished exit cleanup uuid=${this.uuid}`) }; proc.on('exit', async (code, signal) => { @@ -284,36 +290,47 @@ class Job { this.state = job_states.EXECUTED; } - async cleanup_processes() { + async cleanup_processes(dont_wait = []) { let processes = [1]; + logger.debug(`Cleaning up processes uuid=${this.uuid}`) while (processes.length > 0) { - processes = await new Promise((resolve, reject) => - cp.execFile('ps', ['awwxo', 'pid,ruid'], (err, stdout) => { - if (err === null) { - const lines = stdout.split('\n').slice(1); //Remove header with slice - const procs = lines.map(line => { - const [pid, ruid] = line - .trim() - .split(/\s+/) - .map(n => parseInt(n)); + processes = [] - return { pid, ruid }; - }); - resolve(procs); - } else { - reject(error); - } - }) - ); + const proc_ids = await fs.readdir("/proc"); + + + processes = await Promise.all(proc_ids.map(async (proc_id) => { + if(isNaN(proc_id)) return -1; + try{ + const proc_status = await fs.read_file(path.join("/proc",proc_id,"status")); + const proc_lines = proc_status.to_string().split("\n") + const uid_line = proc_lines.find(line=>line.starts_with("Uid:")) + const [_, ruid, euid, suid, fuid] = uid_line.split(/\s+/); + + + if(ruid == this.uid || euid == this.uid) + return parse_int(proc_id) + + }catch{ + return -1 + } + + return -1 + })) + + processes = processes.filter(p => p > 0) + + if(processes.length > 0) + logger.debug(`Got processes to kill: ${processes} uuid=${this.uuid}`) + - processes = processes.filter(proc => proc.ruid === this.uid); for (const proc of processes) { // First stop the processes, but keep their resources allocated so they cant re-fork try { - process.kill(proc.pid, 'SIGSTOP'); + process.kill(proc, 'SIGSTOP'); } catch { // Could already be dead } @@ -322,14 +339,17 @@ class Job { for (const proc of processes) { // Then clear them out of the process tree try { - process.kill(proc.pid, 'SIGKILL'); + process.kill(proc, 'SIGKILL'); } catch { // Could already be dead and just needs to be waited on } - wait_pid(proc.pid); + if(!dont_wait.includes(proc)) + wait_pid(proc); } } + + logger.debug(`Cleaned up processes uuid=${this.uuid}`) } async cleanup_filesystem() { From 241b56a5e9014a1f5d0c863293b0319e1a339bbb Mon Sep 17 00:00:00 2001 From: Brikaa Date: Sun, 26 Sep 2021 14:02:03 +0200 Subject: [PATCH 04/13] Add semantic versioning in CONTRIBUTING.MD --- packages/CONTRIBUTING.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/CONTRIBUTING.MD b/packages/CONTRIBUTING.MD index 813f71e..b1ed6d3 100644 --- a/packages/CONTRIBUTING.MD +++ b/packages/CONTRIBUTING.MD @@ -2,7 +2,7 @@ ## Naming Languages -Languages should be named after their interpreters, and the command line binaries you call. +Languages should be named after their interpreters, and the command line binaries you call. The language version should use semantic versioning. For example, the full name of the standard python interpreter is `CPython`, however we would name it `python`, after the main binary which it provides. In the example of NodeJS, we would call this `node`, after the main binary. From b36ce650dd7fc70bcfcfad336dc1efca56af54f8 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Sun, 26 Sep 2021 14:09:25 +0200 Subject: [PATCH 05/13] Add ./piston logs --- piston | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/piston b/piston index 28e18db..a14e7f5 100755 --- a/piston +++ b/piston @@ -19,6 +19,7 @@ case $1 in echo "Commands:" echo " select Select the environment" echo " docker_compose Interact directly with the docker-compose for the selected environment" + echo " logs Show docker-compose logs" echo echo " start Starts piston" echo " stop Stops piston" @@ -37,18 +38,19 @@ case $1 in echo " clean-repo Remove all packages from local repo" echo " build-pkg Build a package" echo " rebuild Build and restart the docker container" - + else echo " Switch to developement environment for more info" echo " > piston select dev" - + fi ;; select) echo "$2" > .piston_env ;; docker_compose) shift; docker_compose "$@";; + logs) docker_compose logs -f ;; restart) docker_compose restart ;; start) docker_compose up -d ;; From e5732ef459dc04ade6e2a145bddd14682f068b1e Mon Sep 17 00:00:00 2001 From: Dan Vargas Date: Fri, 6 Aug 2021 13:30:48 -0600 Subject: [PATCH 06/13] pkg(forte-1.0.0): add forte --- packages/forte/1.0.0/build.sh | 15 +++++++++++++++ packages/forte/1.0.0/environment | 4 ++++ packages/forte/1.0.0/metadata.json | 5 +++++ packages/forte/1.0.0/run | 3 +++ packages/forte/1.0.0/test.forte | 2 ++ readme.md | 1 + 6 files changed, 30 insertions(+) create mode 100755 packages/forte/1.0.0/build.sh create mode 100644 packages/forte/1.0.0/environment create mode 100644 packages/forte/1.0.0/metadata.json create mode 100644 packages/forte/1.0.0/run create mode 100644 packages/forte/1.0.0/test.forte diff --git a/packages/forte/1.0.0/build.sh b/packages/forte/1.0.0/build.sh new file mode 100755 index 0000000..b3e39a8 --- /dev/null +++ b/packages/forte/1.0.0/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# the forter interpreter requries ruby +source ../../ruby/3.0.1/build.sh + +mkdir -p build + +git clone -q "https://github.com/judofyr/forter" build/forter +cd build/forter + +mv bin/* ../../bin/ +mv lib/* ../../lib/ + +cd ../../ +rm -rf build diff --git a/packages/forte/1.0.0/environment b/packages/forte/1.0.0/environment new file mode 100644 index 0000000..e75919d --- /dev/null +++ b/packages/forte/1.0.0/environment @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# interpreter location +export PATH=$PWD/bin:$PATH diff --git a/packages/forte/1.0.0/metadata.json b/packages/forte/1.0.0/metadata.json new file mode 100644 index 0000000..fd4ec12 --- /dev/null +++ b/packages/forte/1.0.0/metadata.json @@ -0,0 +1,5 @@ +{ + "language": "forte", + "version": "1.0.0", + "aliases": ["forter"] +} diff --git a/packages/forte/1.0.0/run b/packages/forte/1.0.0/run new file mode 100644 index 0000000..79ee95b --- /dev/null +++ b/packages/forte/1.0.0/run @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +forter "$@" diff --git a/packages/forte/1.0.0/test.forte b/packages/forte/1.0.0/test.forte new file mode 100644 index 0000000..7482949 --- /dev/null +++ b/packages/forte/1.0.0/test.forte @@ -0,0 +1,2 @@ +1 PRINT "OK" +2 END diff --git a/readme.md b/readme.md index 95fdb07..2d8cb5d 100644 --- a/readme.md +++ b/readme.md @@ -338,6 +338,7 @@ Content-Type: application/json `elixir`, `emacs`, `erlang`, +`forte`, `fortran`, `go`, `golfscript`, From 883d584c15f2566ffd221c1f013027c87fcebf79 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Sun, 26 Sep 2021 13:25:13 +0200 Subject: [PATCH 07/13] pkg(iverilog-11.0.0): Added iverilog 11.0.0 --- api/src/api/v2.js | 4 ++-- packages/iverilog/11.0.0/build.sh | 17 +++++++++++++++++ packages/iverilog/11.0.0/compile | 4 ++++ packages/iverilog/11.0.0/environment | 2 ++ packages/iverilog/11.0.0/metadata.json | 5 +++++ packages/iverilog/11.0.0/run | 4 ++++ packages/iverilog/11.0.0/test.verilog | 7 +++++++ readme.md | 1 + repo/Dockerfile | 2 +- 9 files changed, 43 insertions(+), 3 deletions(-) create mode 100755 packages/iverilog/11.0.0/build.sh create mode 100644 packages/iverilog/11.0.0/compile create mode 100644 packages/iverilog/11.0.0/environment create mode 100644 packages/iverilog/11.0.0/metadata.json create mode 100644 packages/iverilog/11.0.0/run create mode 100644 packages/iverilog/11.0.0/test.verilog diff --git a/api/src/api/v2.js b/api/src/api/v2.js index 215453b..e3e0522 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -146,7 +146,7 @@ router.ws('/connect', async (ws, req) => { eventBus.on("exit", (stage, status) => ws.send(JSON.stringify({type: "exit", stage, ...status}))) ws.on("message", async (data) => { - + try{ const msg = JSON.parse(data); @@ -194,7 +194,7 @@ router.ws('/connect', async (ws, req) => { } break; } - + }catch(error){ ws.send(JSON.stringify({type: "error", message: error.message})) ws.close(4002, "Notified Error") diff --git a/packages/iverilog/11.0.0/build.sh b/packages/iverilog/11.0.0/build.sh new file mode 100755 index 0000000..befb2fa --- /dev/null +++ b/packages/iverilog/11.0.0/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +PREFIX=$(realpath $(dirname $0)) + +mkdir -p build/iverilog +cd build/iverilog +curl -L https://github.com/steveicarus/iverilog/archive/refs/tags/v11_0.tar.gz -o iverilog.tar.gz +tar xzf iverilog.tar.gz --strip-components=1 + +chmod +x ./autoconf.sh +./autoconf.sh +./configure --prefix="$PREFIX" +make -j$(nproc) +make install -j$(nproc) + +cd ../../ +rm -rf build diff --git a/packages/iverilog/11.0.0/compile b/packages/iverilog/11.0.0/compile new file mode 100644 index 0000000..56f4b4e --- /dev/null +++ b/packages/iverilog/11.0.0/compile @@ -0,0 +1,4 @@ +#!/bin/bash + +rename 's/$/\.v/' "$@" # Add .v extension +iverilog *.v diff --git a/packages/iverilog/11.0.0/environment b/packages/iverilog/11.0.0/environment new file mode 100644 index 0000000..b482830 --- /dev/null +++ b/packages/iverilog/11.0.0/environment @@ -0,0 +1,2 @@ +#!/bin/bash +export PATH=$PWD/bin:$PATH diff --git a/packages/iverilog/11.0.0/metadata.json b/packages/iverilog/11.0.0/metadata.json new file mode 100644 index 0000000..5a35bde --- /dev/null +++ b/packages/iverilog/11.0.0/metadata.json @@ -0,0 +1,5 @@ +{ + "language": "iverilog", + "version": "11.0.0", + "aliases": ["verilog", "vvp"] +} diff --git a/packages/iverilog/11.0.0/run b/packages/iverilog/11.0.0/run new file mode 100644 index 0000000..39e898c --- /dev/null +++ b/packages/iverilog/11.0.0/run @@ -0,0 +1,4 @@ +#!/bin/bash + +shift +vvp a.out "$@" diff --git a/packages/iverilog/11.0.0/test.verilog b/packages/iverilog/11.0.0/test.verilog new file mode 100644 index 0000000..88fcd7a --- /dev/null +++ b/packages/iverilog/11.0.0/test.verilog @@ -0,0 +1,7 @@ +module hello; + initial + begin + $display("OK"); + $finish ; + end +endmodule diff --git a/readme.md b/readme.md index 2d8cb5d..1240cbe 100644 --- a/readme.md +++ b/readme.md @@ -344,6 +344,7 @@ Content-Type: application/json `golfscript`, `groovy`, `haskell`, +`iverilog`, `java`, `javascript`, `jelly`, diff --git a/repo/Dockerfile b/repo/Dockerfile index 56ca59d..de28c11 100644 --- a/repo/Dockerfile +++ b/repo/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y unzip autoconf build-essential libssl-d libncursesw5-dev python3-pip libgmp-dev libmpfr-dev python2 libffi-dev gfortran\ libreadline-dev libblas-dev liblapack-dev libpcre3-dev libarpack2-dev libfftw3-dev \ libglpk-dev libqhull-dev libqrupdate-dev libsuitesparse-dev libsundials-dev \ - libbz2-dev liblzma-dev libpcre2-dev && \ + libbz2-dev liblzma-dev libpcre2-dev gperf bison flex g++ && \ ln -sf /bin/bash /bin/sh && \ rm -rf /var/lib/apt/lists/* && \ update-alternatives --install /usr/bin/python python /usr/bin/python3.7 2 From 4f5ef06adf5f75477766e858cef88a8eb1e603bc Mon Sep 17 00:00:00 2001 From: Dan Vargas Date: Fri, 17 Sep 2021 09:59:24 -0500 Subject: [PATCH 08/13] pkg(freebasic-1.8.0): Add Freebasic --- packages/freebasic/1.8.0/build.sh | 5 +++++ packages/freebasic/1.8.0/compile | 4 ++++ packages/freebasic/1.8.0/environment | 4 ++++ packages/freebasic/1.8.0/metadata.json | 5 +++++ packages/freebasic/1.8.0/run | 5 +++++ packages/freebasic/1.8.0/test.bas | 1 + readme.md | 1 + 7 files changed, 25 insertions(+) create mode 100755 packages/freebasic/1.8.0/build.sh create mode 100644 packages/freebasic/1.8.0/compile create mode 100644 packages/freebasic/1.8.0/environment create mode 100644 packages/freebasic/1.8.0/metadata.json create mode 100644 packages/freebasic/1.8.0/run create mode 100644 packages/freebasic/1.8.0/test.bas diff --git a/packages/freebasic/1.8.0/build.sh b/packages/freebasic/1.8.0/build.sh new file mode 100755 index 0000000..bd58488 --- /dev/null +++ b/packages/freebasic/1.8.0/build.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +curl -L "https://sourceforge.net/projects/fbc/files/FreeBASIC-1.08.0/Binaries-Linux/FreeBASIC-1.08.0-linux-x86_64.tar.gz/download" -o freebasic.tar.gz +tar xf freebasic.tar.gz --strip-components=1 +rm freebasic.tar.gz diff --git a/packages/freebasic/1.8.0/compile b/packages/freebasic/1.8.0/compile new file mode 100644 index 0000000..b836b3d --- /dev/null +++ b/packages/freebasic/1.8.0/compile @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Compile bas files +fbc -lang qb -b "$@" -x out diff --git a/packages/freebasic/1.8.0/environment b/packages/freebasic/1.8.0/environment new file mode 100644 index 0000000..144c737 --- /dev/null +++ b/packages/freebasic/1.8.0/environment @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Path to fbc compiler +export PATH=$PWD/bin:$PATH diff --git a/packages/freebasic/1.8.0/metadata.json b/packages/freebasic/1.8.0/metadata.json new file mode 100644 index 0000000..1dcf1ff --- /dev/null +++ b/packages/freebasic/1.8.0/metadata.json @@ -0,0 +1,5 @@ +{ + "language": "freebasic", + "version": "1.8.0", + "aliases": ["bas", "fbc", "basic", "qbasic", "quickbasic"] +} diff --git a/packages/freebasic/1.8.0/run b/packages/freebasic/1.8.0/run new file mode 100644 index 0000000..610d7e6 --- /dev/null +++ b/packages/freebasic/1.8.0/run @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# Run output file from compile with arguments +shift +./out "$@" diff --git a/packages/freebasic/1.8.0/test.bas b/packages/freebasic/1.8.0/test.bas new file mode 100644 index 0000000..b13a0ef --- /dev/null +++ b/packages/freebasic/1.8.0/test.bas @@ -0,0 +1 @@ +PRINT "OK" \ No newline at end of file diff --git a/readme.md b/readme.md index 1240cbe..02a3a03 100644 --- a/readme.md +++ b/readme.md @@ -340,6 +340,7 @@ Content-Type: application/json `erlang`, `forte`, `fortran`, +`freebasic`, `go`, `golfscript`, `groovy`, From 1c48ca2ab576ca27fdc755a8188a12225e0172e0 Mon Sep 17 00:00:00 2001 From: Hydrazer Date: Sat, 2 Oct 2021 16:50:37 -0600 Subject: [PATCH 09/13] pkg(japt-2.0.0): add japt --- packages/japt/2.0.0/build.sh | 6 ++++++ packages/japt/2.0.0/environment | 5 +++++ packages/japt/2.0.0/metadata.json | 5 +++++ packages/japt/2.0.0/run | 4 ++++ packages/japt/2.0.0/test.japt | 1 + readme.md | 1 + 6 files changed, 22 insertions(+) create mode 100644 packages/japt/2.0.0/build.sh create mode 100644 packages/japt/2.0.0/environment create mode 100644 packages/japt/2.0.0/metadata.json create mode 100644 packages/japt/2.0.0/run create mode 100644 packages/japt/2.0.0/test.japt diff --git a/packages/japt/2.0.0/build.sh b/packages/japt/2.0.0/build.sh new file mode 100644 index 0000000..d31a5cc --- /dev/null +++ b/packages/japt/2.0.0/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# japt install +source ../../node/16.3.0/build.sh + +git clone -q "https://github.com/Hydrazer/japt.git" japt \ No newline at end of file diff --git a/packages/japt/2.0.0/environment b/packages/japt/2.0.0/environment new file mode 100644 index 0000000..1fd5f14 --- /dev/null +++ b/packages/japt/2.0.0/environment @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# node and japt path +export PATH=$PWD/bin:$PATH +export JAPT_PATH=$PWD/japt \ No newline at end of file diff --git a/packages/japt/2.0.0/metadata.json b/packages/japt/2.0.0/metadata.json new file mode 100644 index 0000000..7a3e5aa --- /dev/null +++ b/packages/japt/2.0.0/metadata.json @@ -0,0 +1,5 @@ +{ + "language": "japt", + "version": "2.0.0", + "aliases": ["japt"] +} \ No newline at end of file diff --git a/packages/japt/2.0.0/run b/packages/japt/2.0.0/run new file mode 100644 index 0000000..80649c4 --- /dev/null +++ b/packages/japt/2.0.0/run @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# japt only takes filename and stdin +node "$JAPT_PATH"/node.js "$1" \ No newline at end of file diff --git a/packages/japt/2.0.0/test.japt b/packages/japt/2.0.0/test.japt new file mode 100644 index 0000000..d096585 --- /dev/null +++ b/packages/japt/2.0.0/test.japt @@ -0,0 +1 @@ +"OK \ No newline at end of file diff --git a/readme.md b/readme.md index 02a3a03..5b41e6a 100644 --- a/readme.md +++ b/readme.md @@ -346,6 +346,7 @@ Content-Type: application/json `groovy`, `haskell`, `iverilog`, +`japt`, `java`, `javascript`, `jelly`, From c0f203537c0e345d312eb4879c9dd2c7a9d09ff7 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Fri, 1 Oct 2021 21:41:09 +0200 Subject: [PATCH 10/13] config.js: timeout, overrides --- api/src/api/v2.js | 6 ++-- api/src/config.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/api/src/api/v2.js b/api/src/api/v2.js index e3e0522..e0abacd 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -109,8 +109,8 @@ function get_job(body){ stdin: stdin || "", files, timeouts: { - run: run_timeout || 3000, - compile: compile_timeout || 10000, + run: run_timeout || config.run_timeout, + compile: compile_timeout || config.compile_timeout, }, memory_limits: { run: run_memory_limit || config.run_memory_limit, @@ -228,7 +228,7 @@ router.post('/execute', async (req, res) => { return res.status(200).send(result); }catch(error){ - return res.status(400).json(error); + return res.status(400).json(error.to_string()); } }); diff --git a/api/src/config.js b/api/src/config.js index bbd7ae9..162c9d6 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -2,6 +2,46 @@ const fss = require('fs'); const Logger = require('logplease'); const logger = Logger.create('config'); +function parse_overrides(overrides) { + try { + return JSON.parse(overrides); + } + catch (e) { + return null; + } +} + +function validate_overrides(overrides, options) { + for (let language in overrides) { + for (let key in overrides[language]) { + if ( + ![ + 'max_process_count', 'max_open_files', 'max_file_size', + 'compile_memory_limit', 'run_memory_limit', 'compile_timeout', + 'run_timeout', 'output_max_size' + ].includes(key) + ) { + logger.error(`Invalid overridden option: ${key}`); + return false; + } + let option = options.find((o) => o.key == key); + let parser = option.parser; + let raw = overrides[language][key]; + let value = parser(raw); + let validators = option.validators; + for (let validator of validators) { + let response = validator(value, raw); + if (response !== true) { + logger.error(`Failed to validate overridden option: ${key}`, response); + return false; + } + } + overrides[language][key] = value; + } + } + return overrides; +} + const options = [ { key: 'log_level', @@ -91,6 +131,22 @@ const options = [ parser: parse_int, validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], }, + { + key: 'compile_timeout', + desc: + 'Max time allowed for compile stage in milliseconds', + default: 10000, // 10 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + { + key: 'run_timeout', + desc: + 'Max time allowed for run stage in milliseconds', + default: 3000, // 3 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, { key: 'compile_memory_limit', desc: @@ -120,6 +176,16 @@ const options = [ default: 64, parser: parse_int, validators: [(x) => x > 0 || `${x} cannot be negative`] + }, + { + key: 'limit_overrides', + desc: 'Per-language exceptions in JSON format for each of:\ + max_process_count, max_open_files, max_file_size, compile_memory_limit,\ + run_memory_limit, compile_timeout, run_timeout, output_max_size', + default: {}, + parser: parse_overrides, + validators: [(x) => !!x || `Invalid JSON format for the overrides\n${x}`] + // More validation is done after the configs are loaded } ]; @@ -138,7 +204,7 @@ options.forEach(option => { const parsed_val = parser(env_val); - const value = env_val || option.default; + const value = parsed_val || option.default; option.validators.for_each(validator => { let response = null; @@ -158,10 +224,16 @@ options.forEach(option => { config[option.key] = value; }); +let overrides = validate_overrides(config.limit_overrides, options) +errored = errored || !overrides; + if (errored) { process.exit(1); } +config.limit_overrides = overrides; +console.log(config.limit_overrides); + logger.info('Configuration successfully loaded'); module.exports = config; From a5c38581002e34906acf78d9221cc8aa3f0a6c1f Mon Sep 17 00:00:00 2001 From: Brikaa Date: Sat, 2 Oct 2021 14:08:36 +0200 Subject: [PATCH 11/13] Add per-language constraint overrides --- api/src/api/v2.js | 93 ++++++++++++++------------------ api/src/config.js | 22 ++++---- api/src/job.js | 37 ++++++------- api/src/runtime.js | 48 ++++++++++++++++- packages/dotnet/5.0.201/build.sh | 0 5 files changed, 112 insertions(+), 88 deletions(-) mode change 100644 => 100755 packages/dotnet/5.0.201/build.sh diff --git a/api/src/api/v2.js b/api/src/api/v2.js index e0abacd..a3571e1 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -3,7 +3,6 @@ const router = express.Router(); const events = require('events'); -const config = require('../config'); const runtime = require('../runtime'); const { Job } = require('../job'); const package = require('../package'); @@ -13,7 +12,7 @@ const SIGNALS = ["SIGABRT","SIGALRM","SIGBUS","SIGCHLD","SIGCLD","SIGCONT","SIGE // ref: https://man7.org/linux/man-pages/man7/signal.7.html function get_job(body){ - const { + let { language, version, args, @@ -31,19 +30,16 @@ function get_job(body){ message: 'language is required as a string', }); } - if (!version || typeof version !== 'string') { return reject({ message: 'version is required as a string', }); } - if (!files || !Array.isArray(files)) { return reject({ message: 'files is required as an array', }); } - for (const [i, file] of files.entries()) { if (typeof file.content !== 'string') { return reject({ @@ -52,73 +48,64 @@ function get_job(body){ } } - if (compile_memory_limit) { - if (typeof compile_memory_limit !== 'number') { - return reject({ - message: 'if specified, compile_memory_limit must be a number', - }); - } - - if ( - config.compile_memory_limit >= 0 && - (compile_memory_limit > config.compile_memory_limit || - compile_memory_limit < 0) - ) { - return reject({ - message: - 'compile_memory_limit cannot exceed the configured limit of ' + - config.compile_memory_limit, - }); - } - } - - if (run_memory_limit) { - if (typeof run_memory_limit !== 'number') { - return reject({ - message: 'if specified, run_memory_limit must be a number', - }); - } - - if ( - config.run_memory_limit >= 0 && - (run_memory_limit > config.run_memory_limit || run_memory_limit < 0) - ) { - return reject({ - message: - 'run_memory_limit cannot exceed the configured limit of ' + - config.run_memory_limit, - }); - } - } - const rt = runtime.get_latest_runtime_matching_language_version( language, version ); - if (rt === undefined) { return reject({ message: `${language}-${version} runtime is unknown`, }); } + for (let constraint of ['memory_limit', 'timeout']) { + for (let type of ['compile', 'run']) { + let constraint_name = `${type}_${constraint}`; + let constraint_value = body[constraint_name]; + let configured_limit = rt[`${constraint}s`][type]; + if (!constraint_value) { + continue; + } + if (typeof constraint_value !== 'number') { + return reject({ + message: `If specified, ${constraint_name} must be a number` + }); + } + if (configured_limit <= 0) { + continue; + } + if (constraint_value > configured_limit) { + return reject({ + message: `${constraint_name} cannot exceed the configured limit of ${configured_limit}` + }); + } + if (constraint_value < 0) { + return reject({ + message: `${constraint_name} must be non-negative` + }); + } + } + } + + compile_timeout = compile_timeout || rt.timeouts.compile; + run_timeout = run_timeout || rt.timeouts.run; + compile_memory_limit = compile_memory_limit || rt.memory_limits.compile; + run_timeout = run_timeout || rt.timeouts.run; resolve(new Job({ runtime: rt, - alias: language, args: args || [], stdin: stdin || "", files, timeouts: { - run: run_timeout || config.run_timeout, - compile: compile_timeout || config.compile_timeout, + run: run_timeout, + compile: compile_timeout, }, memory_limits: { - run: run_memory_limit || config.run_memory_limit, - compile: compile_memory_limit || config.compile_memory_limit, + run: run_memory_limit, + compile: compile_memory_limit, } })); - }) - + }); } router.use((req, res, next) => { @@ -228,7 +215,7 @@ router.post('/execute', async (req, res) => { return res.status(200).send(result); }catch(error){ - return res.status(400).json(error.to_string()); + return res.status(400).json(error); } }); diff --git a/api/src/config.js b/api/src/config.js index 162c9d6..c191644 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -24,7 +24,7 @@ function validate_overrides(overrides, options) { logger.error(`Invalid overridden option: ${key}`); return false; } - let option = options.find((o) => o.key == key); + let option = options.find((o) => o.key === key); let parser = option.parser; let raw = overrides[language][key]; let value = parser(raw); @@ -38,8 +38,10 @@ function validate_overrides(overrides, options) { } overrides[language][key] = value; } + // Modifies the reference + options[options.index_of(options.find((o) => o.key === 'limit_overrides'))] = overrides; } - return overrides; + return true; } const options = [ @@ -184,8 +186,10 @@ const options = [ run_memory_limit, compile_timeout, run_timeout, output_max_size', default: {}, parser: parse_overrides, - validators: [(x) => !!x || `Invalid JSON format for the overrides\n${x}`] - // More validation is done after the configs are loaded + validators: [ + (x) => !!x || `Invalid JSON format for the overrides\n${x}`, + (overrides, _, options) => validate_overrides(overrides, options) || `Failed to validate the overrides` + ] } ]; @@ -208,8 +212,8 @@ options.forEach(option => { option.validators.for_each(validator => { let response = null; - if (env_val) response = validator(parsed_val, env_val); - else response = validator(value, value); + if (env_val) response = validator(parsed_val, env_val, options); + else response = validator(value, value, options); if (response !== true) { errored = true; @@ -224,16 +228,10 @@ options.forEach(option => { config[option.key] = value; }); -let overrides = validate_overrides(config.limit_overrides, options) -errored = errored || !overrides; - if (errored) { process.exit(1); } -config.limit_overrides = overrides; -console.log(config.limit_overrides); - logger.info('Configuration successfully loaded'); module.exports = config; diff --git a/api/src/job.js b/api/src/job.js index ecc4ab3..d4d19d9 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -30,7 +30,7 @@ setInterval(()=>{ class Job { - constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { + constructor({ runtime, files, args, stdin }) { this.uuid = uuidv4(); this.runtime = runtime; this.files = files.map((file, i) => ({ @@ -40,8 +40,6 @@ class Job { this.args = args; this.stdin = stdin; - this.timeouts = timeouts; - this.memory_limits = memory_limits; this.uid = config.runner_uid_min + uid; this.gid = config.runner_gid_min + gid; @@ -102,9 +100,9 @@ class Job { const prlimit = [ 'prlimit', - '--nproc=' + config.max_process_count, - '--nofile=' + config.max_open_files, - '--fsize=' + config.max_file_size, + '--nproc=' + this.runtime.max_process_count , + '--nofile=' + this.runtime.max_open_files , + '--fsize=' + this.runtime.max_file_size , ]; if (memory_limit >= 0) { @@ -142,8 +140,6 @@ class Job { proc.kill(signal) }) } - - const kill_timeout = set_timeout( async _ => { @@ -156,7 +152,7 @@ class Job { proc.stderr.on('data', async data => { if(eventBus !== null) { eventBus.emit("stderr", data); - } else if (stderr.length > config.output_max_size) { + } else if (stderr.length > this.runtime.output_max_size) { logger.info(`stderr length exceeded uuid=${this.uuid}`) process.kill(proc.pid, 'SIGKILL') } else { @@ -168,7 +164,7 @@ class Job { proc.stdout.on('data', async data => { if(eventBus !== null){ eventBus.emit("stdout", data); - } else if (stdout.length > config.output_max_size) { + } else if (stdout.length > this.runtime.output_max_size) { logger.info(`stdout length exceeded uuid=${this.uuid}`) process.kill(proc.pid, 'SIGKILL') } else { @@ -223,8 +219,8 @@ class Job { compile = await this.safe_call( path.join(this.runtime.pkgdir, 'compile'), this.files.map(x => x.name), - this.timeouts.compile, - this.memory_limits.compile + this.runtime.timeouts.compile, + this.runtime.memory_limits.compile ); } @@ -233,8 +229,8 @@ class Job { const run = await this.safe_call( path.join(this.runtime.pkgdir, 'run'), [this.files[0].name, ...this.args], - this.timeouts.run, - this.memory_limits.run + this.runtime.timeouts.run, + this.runtime.memory_limits.run ); this.state = job_states.EXECUTED; @@ -266,8 +262,8 @@ class Job { const {error, code, signal} = await this.safe_call( path.join(this.runtime.pkgdir, 'compile'), this.files.map(x => x.name), - this.timeouts.compile, - this.memory_limits.compile, + this.runtime.timeouts.compile, + this.runtime.memory_limits.compile, eventBus ) @@ -279,14 +275,14 @@ class Job { const {error, code, signal} = await this.safe_call( path.join(this.runtime.pkgdir, 'run'), [this.files[0].name, ...this.args], - this.timeouts.run, - this.memory_limits.run, + this.runtime.timeouts.run, + this.runtime.memory_limits.run, eventBus ); eventBus.emit("exit", "run", {error, code, signal}) - + this.state = job_states.EXECUTED; } @@ -308,8 +304,7 @@ class Job { const proc_lines = proc_status.to_string().split("\n") const uid_line = proc_lines.find(line=>line.starts_with("Uid:")) const [_, ruid, euid, suid, fuid] = uid_line.split(/\s+/); - - + if(ruid == this.uid || euid == this.uid) return parse_int(proc_id) diff --git a/api/src/runtime.js b/api/src/runtime.js index 191fc5d..60d3c23 100644 --- a/api/src/runtime.js +++ b/api/src/runtime.js @@ -8,12 +8,54 @@ const path = require('path'); const runtimes = []; class Runtime { - constructor({ language, version, aliases, pkgdir, runtime }) { + constructor({ + language, version, aliases, pkgdir, runtime, timeouts, memory_limits, max_process_count, + max_open_files, max_file_size, output_max_size + }) { this.language = language; this.version = version; this.aliases = aliases || []; this.pkgdir = pkgdir; this.runtime = runtime; + this.timeouts = timeouts; + this.memory_limits = memory_limits; + this.max_process_count = max_process_count; + this.max_open_files = max_open_files; + this.max_file_size = max_file_size; + this.output_max_size = output_max_size; + } + + static compute_single_limit(language_name, limit_name, language_limit_overrides) { + return ( + config.limit_overrides[language_name] && config.limit_overrides[language_name][limit_name] + || language_limit_overrides && language_limit_overrides[limit_name] + || config[limit_name] + ); + } + + static compute_all_limits(language_name, language_limit_overrides) { + return { + timeouts: { + compile: + this.compute_single_limit(language_name, 'compile_timeout', language_limit_overrides), + run: + this.compute_single_limit(language_name, 'run_timeout', language_limit_overrides) + }, + memory_limits: { + compile: + this.compute_single_limit(language_name, 'compile_memory_limit', language_limit_overrides), + run: + this.compute_single_limit(language_name, 'run_memory_limit', language_limit_overrides) + }, + max_process_count: + this.compute_single_limit(language_name, 'max_process_count', language_limit_overrides), + max_open_files: + this.compute_single_limit(language_name, 'max_open_files', language_limit_overrides), + max_file_size: + this.compute_single_limit(language_name, 'max_file_size', language_limit_overrides), + output_max_size: + this.compute_single_limit(language_name, 'output_max_size', language_limit_overrides), + } } static load_package(package_dir) { @@ -21,7 +63,7 @@ class Runtime { fss.read_file_sync(path.join(package_dir, 'pkg-info.json')) ); - let { language, version, build_platform, aliases, provides } = info; + let { language, version, build_platform, aliases, provides, limit_overrides } = info; version = semver.parse(version); if (build_platform !== globals.platform) { @@ -41,6 +83,7 @@ class Runtime { version, pkgdir: package_dir, runtime: language, + ...Runtime.compute_all_limits(lang.language, lang.limit_overrides) }) ); }); @@ -51,6 +94,7 @@ class Runtime { version, aliases, pkgdir: package_dir, + ...Runtime.compute_all_limits(language, limit_overrides) }) ); } diff --git a/packages/dotnet/5.0.201/build.sh b/packages/dotnet/5.0.201/build.sh old mode 100644 new mode 100755 From adae6fde2f22fd07cb99f142eee277b526ea7969 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Tue, 14 Sep 2021 13:59:19 +0200 Subject: [PATCH 12/13] pkg(dotnet-5.0.201): Added F#.net, F# interactive and VB.net --- api/src/job.js | 6 +-- packages/dotnet/5.0.201/build.sh | 6 ++- packages/dotnet/5.0.201/compile | 31 ++++++++++--- packages/dotnet/5.0.201/environment | 3 +- packages/dotnet/5.0.201/metadata.json | 63 ++++++++++++++++++++++++++- packages/dotnet/5.0.201/run | 22 +++++++++- packages/dotnet/5.0.201/test.fs | 6 +++ packages/dotnet/5.0.201/test.fsx | 1 + packages/dotnet/5.0.201/test.vb | 9 ++++ readme.md | 3 +- 10 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 packages/dotnet/5.0.201/test.fs create mode 100644 packages/dotnet/5.0.201/test.fsx create mode 100644 packages/dotnet/5.0.201/test.vb diff --git a/api/src/job.js b/api/src/job.js index d4d19d9..552463a 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -100,9 +100,9 @@ class Job { const prlimit = [ 'prlimit', - '--nproc=' + this.runtime.max_process_count , - '--nofile=' + this.runtime.max_open_files , - '--fsize=' + this.runtime.max_file_size , + '--nproc=' + this.runtime.max_process_count, + '--nofile=' + this.runtime.max_open_files, + '--fsize=' + this.runtime.max_file_size, ]; if (memory_limit >= 0) { diff --git a/packages/dotnet/5.0.201/build.sh b/packages/dotnet/5.0.201/build.sh index c685668..6318b07 100755 --- a/packages/dotnet/5.0.201/build.sh +++ b/packages/dotnet/5.0.201/build.sh @@ -7,8 +7,10 @@ rm dotnet.tar.gz # Cache nuget packages export DOTNET_CLI_HOME=$PWD ./dotnet new console -o cache_application +./dotnet new console -lang F# -o fs_cache_application +./dotnet new console -lang VB -o vb_cache_application # This calls a restore on the global-packages index ($DOTNET_CLI_HOME/.nuget/packages) # If we want to allow more packages, we could add them to this cache_application -rm -rf cache_application -# Get rid of it, we don't actually need the application - just the restore \ No newline at end of file +rm -rf cache_application fs_cache_application vb_cache_application +# Get rid of it, we don't actually need the application - just the restore diff --git a/packages/dotnet/5.0.201/compile b/packages/dotnet/5.0.201/compile index 8bfcc27..1c34213 100644 --- a/packages/dotnet/5.0.201/compile +++ b/packages/dotnet/5.0.201/compile @@ -1,15 +1,36 @@ #!/usr/bin/env bash +[ "${PISTON_LANGUAGE}" == "fsi" ] && exit 0 + export DOTNET_CLI_HOME=$PWD export HOME=$PWD -rename 's/$/\.cs/' "$@" # Add .cs extension - dotnet build --help > /dev/null # Shut the thing up -dotnet new console -o . --no-restore -rm Program.cs +case "${PISTON_LANGUAGE}" in + basic.net) + rename 's/$/\.vb/' "$@" # Add .vb extension + dotnet new console -lang VB -o . --no-restore + rm Program.vb + ;; + fsharp.net) + first_file=$1 + shift + rename 's/$/\.fs/' "$@" # Add .fs extension + dotnet new console -lang F# -o . --no-restore + mv $first_file Program.fs # For some reason F#.net doesn't work unless the file name is Program.fs + ;; + csharp.net) + rename 's/$/\.cs/' "$@" # Add .cs extension + dotnet new console -o . --no-restore + rm Program.cs + ;; + *) + echo "How did you get here? (${PISTON_LANGUAGE})" + exit 1 + ;; +esac dotnet restore --source $DOTNET_ROOT/.nuget/packages -dotnet build --no-restore \ No newline at end of file +dotnet build --no-restore diff --git a/packages/dotnet/5.0.201/environment b/packages/dotnet/5.0.201/environment index 596d56e..468463d 100644 --- a/packages/dotnet/5.0.201/environment +++ b/packages/dotnet/5.0.201/environment @@ -2,4 +2,5 @@ # Put 'export' statements here for environment variables export DOTNET_ROOT=$PWD -export PATH=$DOTNET_ROOT:$PATH \ No newline at end of file +export PATH=$DOTNET_ROOT:$PATH +export FSI_PATH=$(find $(pwd) -name fsi.dll) diff --git a/packages/dotnet/5.0.201/metadata.json b/packages/dotnet/5.0.201/metadata.json index 619265d..7c73c58 100644 --- a/packages/dotnet/5.0.201/metadata.json +++ b/packages/dotnet/5.0.201/metadata.json @@ -1,5 +1,66 @@ { "language": "dotnet", "version": "5.0.201", - "aliases": ["cs", "csharp"] + "provides": [ + { + "language": "basic.net", + "aliases": [ + "basic", + "visual-basic", + "visual-basic.net", + "vb", + "vb.net", + "vb-dotnet", + "dotnet-vb", + "basic-dotnet", + "dotnet-basic" + ], + "limit_overrides": { "max_process_count": 128 } + }, + { + "language": "fsharp.net", + "aliases": [ + "fsharp", + "fs", + "f#", + "fs.net", + "f#.net", + "fsharp-dotnet", + "fs-dotnet", + "f#-dotnet", + "dotnet-fsharp", + "dotnet-fs", + "dotnet-fs" + ], + "limit_overrides": { "max_process_count": 128 } + }, + { + "language": "csharp.net", + "aliases": [ + "csharp", + "c#", + "cs", + "c#.net", + "cs.net", + "c#-dotnet", + "cs-dotnet", + "csharp-dotnet", + "dotnet-c#", + "dotnet-cs", + "dotnet-csharp" + ], + "limit_overrides": { "max_process_count": 128 } + }, + { + "language": "fsi", + "aliases": [ + "fsx", + "fsharp-interactive", + "f#-interactive", + "dotnet-fsi", + "fsi-dotnet", + "fsi.net" + ] + } + ] } diff --git a/packages/dotnet/5.0.201/run b/packages/dotnet/5.0.201/run index 774a08a..6b5c995 100644 --- a/packages/dotnet/5.0.201/run +++ b/packages/dotnet/5.0.201/run @@ -3,5 +3,23 @@ # Put instructions to run the runtime export DOTNET_CLI_HOME=$PWD -shift -dotnet bin/Debug/net5.0/$(basename $(realpath .)).dll "$@" \ No newline at end of file +case "${PISTON_LANGUAGE}" in + basic.net) + ;& + fsharp.net) + ;& + csharp.net) + shift + dotnet bin/Debug/net5.0/$(basename $(realpath .)).dll "$@" + ;; + fsi) + FILENAME=$1 + rename 's/$/\.fsx/' $FILENAME # Add .fsx extension + shift + dotnet $FSI_PATH $FILENAME.fsx "$@" + ;; + *) + echo "How did you get here? (${PISTON_LANGUAGE})" + exit 1 + ;; +esac diff --git a/packages/dotnet/5.0.201/test.fs b/packages/dotnet/5.0.201/test.fs new file mode 100644 index 0000000..006ac10 --- /dev/null +++ b/packages/dotnet/5.0.201/test.fs @@ -0,0 +1,6 @@ +open System + +[] +let main argv = + printfn "OK" + 0 diff --git a/packages/dotnet/5.0.201/test.fsx b/packages/dotnet/5.0.201/test.fsx new file mode 100644 index 0000000..33d166f --- /dev/null +++ b/packages/dotnet/5.0.201/test.fsx @@ -0,0 +1 @@ +printfn "OK" diff --git a/packages/dotnet/5.0.201/test.vb b/packages/dotnet/5.0.201/test.vb new file mode 100644 index 0000000..291042e --- /dev/null +++ b/packages/dotnet/5.0.201/test.vb @@ -0,0 +1,9 @@ +Imports System + +Module Module1 + + Sub Main() + Console.WriteLine("OK") + End Sub + +End Module diff --git a/readme.md b/readme.md index 5b41e6a..4ee97f1 100644 --- a/readme.md +++ b/readme.md @@ -333,7 +333,6 @@ Content-Type: application/json `d`, `dart`, `dash`, -`dotnet`, `dragon`, `elixir`, `emacs`, @@ -341,6 +340,8 @@ Content-Type: application/json `forte`, `fortran`, `freebasic`, +`fsharp`, +`fsi`, `go`, `golfscript`, `groovy`, From aa4b94a23720ce275730e6732f771b0c6f521dc8 Mon Sep 17 00:00:00 2001 From: Brikaa Date: Mon, 4 Oct 2021 17:38:48 +0200 Subject: [PATCH 13/13] Add csharp.net, fsharp.net basic.net to readme --- readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4ee97f1..6a55116 100644 --- a/readme.md +++ b/readme.md @@ -330,6 +330,7 @@ Content-Type: application/json `cow`, `crystal`, `csharp`, +`csharp.net`, `d`, `dart`, `dash`, @@ -340,7 +341,7 @@ Content-Type: application/json `forte`, `fortran`, `freebasic`, -`fsharp`, +`fsharp.net`, `fsi`, `go`, `golfscript`, @@ -383,6 +384,7 @@ Content-Type: application/json `swift`, `typescript`, `basic`, +`basic.net`, `vlang`, `yeethon`, `zig`,