Skip to content

Commit 27044d9

Browse files
committed
Bring compiling/running code parts from tracers repository
1 parent b539553 commit 27044d9

File tree

14 files changed

+141
-36
lines changed

14 files changed

+141
-36
lines changed

src/backend/common/config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
export {};
1+
const memoryLimit = 256; // in megabytes
2+
const timeLimit = 5000; // in milliseconds
3+
4+
export {
5+
memoryLimit,
6+
timeLimit,
7+
};

src/backend/common/util.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import fs from 'fs-extra';
55
import removeMarkdown from 'remove-markdown';
66

77
const execute = (command, cwd, { stdout = process.stdout, stderr = process.stderr } = {}) => new Promise((resolve, reject) => {
8-
if (!cwd) return reject(new Error('CWD Not Specified'));
98
const child = child_process.exec(command, { cwd }, (error, stdout, stderr) => {
109
if (error) return reject(error.code ? new Error(stderr) : error);
1110
resolve(stdout);

src/backend/controllers/auth.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ router.route('/response')
2929
router.route('/destroy')
3030
.get(destroy);
3131

32-
export default router;
32+
export default router;

src/backend/controllers/tracers.js

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,78 @@
11
import express from 'express';
22
import fs from 'fs-extra';
3+
import Promise from 'bluebird';
34
import uuid from 'uuid';
45
import path from 'path';
56
import { GitHubApi } from '/apis';
67
import { execute } from '/common/util';
78
import webhook from '/common/webhook';
9+
import { ImageBuilder, WorkerBuilder } from '/tracers';
10+
import { memoryLimit, timeLimit } from '/common/config';
811

912
const router = express.Router();
1013

11-
const repoPath = path.resolve(__dirname, '..', 'public', 'tracers');
12-
const getCodesPath = (...args) => path.resolve(__dirname, '..', 'public', 'codes', ...args);
13-
14-
const getJsWorker = (req, res, next) => {
15-
res.sendFile(path.resolve(repoPath, 'src', 'languages', 'js', 'tracers', 'build', 'tracers.js'));
16-
};
17-
1814
const trace = lang => (req, res, next) => {
1915
const { code } = req.body;
20-
const tempPath = getCodesPath(uuid.v4());
16+
const tempPath = path.resolve(__dirname, '..', 'public', 'codes', uuid.v4());
17+
const tracesPath = path.resolve(tempPath, 'traces.json');
2118
fs.outputFile(path.resolve(tempPath, `Main.${lang}`), code)
22-
.then(() => execute(`./bin/compile ${lang} ${tempPath}`, repoPath, { stdout: null, stderr: null }))
23-
.then(() => execute(`./bin/run ${lang} ${tempPath}`, repoPath, { stdout: null, stderr: null }))
24-
.then(() => res.sendFile(path.resolve(tempPath, 'traces.json')))
19+
.then(() => {
20+
const builder = builderMap[lang];
21+
const containerName = uuid.v4();
22+
let killed = false;
23+
const timer = setTimeout(() => {
24+
execute(`docker kill ${containerName}`).then(() => {
25+
killed = true;
26+
});
27+
}, timeLimit);
28+
return execute([
29+
'docker run --rm',
30+
`--name=${containerName}`,
31+
'-w=/usr/tracer',
32+
`-v=${tempPath}:/usr/tracer:rw`,
33+
`-m=${memoryLimit}m`,
34+
builder.imageName,
35+
].join(' ')).catch(error => {
36+
if (killed) throw new Error('Time Limit Exceeded');
37+
throw error;
38+
}).finally(() => clearTimeout(timer));
39+
})
40+
.then(() => fs.pathExists(tracesPath))
41+
.then(exists => {
42+
if (!exists) throw new Error('Traces Not Found');
43+
res.sendFile(tracesPath);
44+
})
2545
.catch(next)
2646
.finally(() => fs.remove(tempPath));
2747
};
2848

29-
const buildRelease = release => (
30-
fs.pathExistsSync(repoPath) ?
31-
execute(`git fetch && ! git diff-index --quiet ${release.target_commitish} -- ':!package-lock.json'`, repoPath) :
32-
execute(`git clone https://github.com/algorithm-visualizer/tracers.git ${repoPath}`, __dirname)
33-
).then(() => execute(`git reset --hard ${release.target_commitish} && npm install && npm run build && ./bin/build`, repoPath));
49+
const builderMap = {
50+
js: new WorkerBuilder(),
51+
cpp: new ImageBuilder('cpp'),
52+
java: new ImageBuilder('java'),
53+
};
3454

35-
GitHubApi.getLatestRelease('algorithm-visualizer', 'tracers').then(buildRelease).catch(console.error);
55+
Promise.map(Object.keys(builderMap), lang => {
56+
const builder = builderMap[lang];
57+
return GitHubApi.getLatestRelease('algorithm-visualizer', `tracers.${lang}`).then(builder.build);
58+
}).catch(console.error);
3659

37-
webhook.on('tracers', (event, data) => {
38-
switch (event) {
39-
case 'release':
40-
buildRelease(data.release).catch(console.error);
41-
break;
60+
webhook.on('release', (repo, data) => {
61+
const result = /^tracers\.(\w+)$/.exec(repo);
62+
if (result) {
63+
const [, lang] = result;
64+
const builder = builderMap[lang];
65+
builder.build(data.release).catch(console.error);
4266
}
4367
});
4468

45-
router.route('/js')
46-
.get(getJsWorker);
47-
48-
router.route('/java')
49-
.post(trace('java'));
50-
51-
router.route('/cpp')
52-
.post(trace('cpp'));
69+
Object.keys(builderMap).forEach(lang => {
70+
const builder = builderMap[lang];
71+
if (builder instanceof ImageBuilder) {
72+
router.post(`/${lang}`, trace(lang));
73+
} else if (builder instanceof WorkerBuilder) {
74+
router.get(`/${lang}`, (req, res) => res.sendFile(builder.workerPath));
75+
}
76+
});
5377

5478
export default router;

src/backend/tracers/ImageBuilder.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import path from 'path';
2+
import { execute } from '/common/util';
3+
4+
class ImageBuilder {
5+
constructor(lang) {
6+
this.lang = lang;
7+
this.directory = path.resolve(__dirname, lang);
8+
this.imageName = `tracer-${this.lang}`;
9+
10+
this.build = this.build.bind(this);
11+
}
12+
13+
build(release) {
14+
const { tag_name } = release;
15+
return execute(`docker build -t ${this.imageName} . --build-arg tag_name=${tag_name}`, this.directory);
16+
}
17+
}
18+
19+
export default ImageBuilder;

src/backend/tracers/WorkerBuilder.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import path from 'path';
2+
3+
class WorkerBuilder {
4+
constructor() {
5+
this.workerPath = path.resolve(__dirname, 'js', 'worker.js');
6+
7+
this.build = this.build.bind(this);
8+
}
9+
10+
build(release) {
11+
}
12+
}
13+
14+
export default WorkerBuilder;

src/backend/tracers/cpp/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM rikorose/gcc-cmake
2+
3+
ARG tag_name
4+
5+
RUN curl --create-dirs -o /usr/local/include/nlohmann/json.hpp -L "https://github.com/nlohmann/json/releases/download/v3.1.2/json.hpp" \
6+
&& curl --create-dirs -o /usr/tmp/algorithm-visualizer.tar.gz -L "https://github.com/algorithm-visualizer/tracers.cpp/archive/${tag_name}.tar.gz" \
7+
&& cd /usr/tmp \
8+
&& mkdir algorithm-visualizer \
9+
&& tar xvzf algorithm-visualizer.tar.gz -C algorithm-visualizer --strip-components=1 \
10+
&& cd /usr/tmp/algorithm-visualizer \
11+
&& mkdir build \
12+
&& cd build \
13+
&& cmake .. \
14+
&& make install
15+
16+
CMD g++ Main.cpp -o Main -O2 -std=c++11 \
17+
&& ./Main

src/backend/tracers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as ImageBuilder } from './ImageBuilder';
2+
export { default as WorkerBuilder } from './WorkerBuilder';

src/backend/tracers/java/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM openjdk:8
2+
3+
ARG tag_name
4+
5+
RUN curl --create-dirs -o /usr/local/lib/algorithm-visualizer.jar -L "https://github.com/algorithm-visualizer/tracers.java/releases/download/${tag_name}/algorithm-visualizer.jar"
6+
7+
CMD javac -cp /usr/local/lib/algorithm-visualizer.jar Main.java \
8+
&& java -cp /usr/local/lib/algorithm-visualizer.jar:. Main

src/backend/tracers/js/worker.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
importScripts('https://unpkg.com/algorithm-visualizer/dist/algorithm-visualizer.js');
2+
importScripts('https://unpkg.com/babel-standalone@6/babel.min.js');
3+
4+
const sandbox = code => {
5+
const require = name => ({ 'algorithm-visualizer': AlgorithmVisualizer }[name]); // fake require
6+
eval(code);
7+
};
8+
9+
onmessage = e => { // TODO: stop after the first delay() on the initial run
10+
const lines = e.data.split('\n').map((line, i) => line.replace(/(.+\. *delay *)(\( *\))/g, `$1(${i})`));
11+
const { code } = Babel.transform(lines.join('\n'), { presets: ['es2015'] });
12+
sandbox(code);
13+
postMessage(AlgorithmVisualizer.Tracer.traces);
14+
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy