Repository navigation
Expand file tree
/
Copy pathsessPreparation.ts
More file actions
126 lines (124 loc) · 4.86 KB
/
Copy pathsessPreparation.ts
File metadata and controls
126 lines (124 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import * as fs from 'fs';
import * as path from 'path';
import { createHash } from 'crypto';
import { spawn, execFile } from 'child_process';
import { promisify } from 'util';
import { prepareStorage } from '../storage';
const run = promisify(execFile);
export async function installSessRuntime(
extensionPath: string,
root: string,
rPath: string,
log: (text: string) => void,
): Promise<{ library: string; resources: string }> {
prepareStorage(root);
const hash = createHash('sha256');
const bundledSess = 'dist/resources/sess';
const sources = [
`${bundledSess}/DESCRIPTION`,
`${bundledSess}/NAMESPACE`,
'R/interactive-worker.R',
'R/interactive-metrics.R',
'R/install_sess.R',
'R/sess-package-install.R',
'R/sess_source.R',
];
for (const directory of [`${bundledSess}/R`]) {
for (const name of fs.readdirSync(path.join(extensionPath, directory)).sort()) {
if (/\.(R|c|h)$/.test(name)) {
sources.push(`${directory}/${name}`);
}
}
}
for (const file of sources) {
hash.update(file).update(fs.readFileSync(path.join(extensionPath, file)));
}
const version = await run(rPath, [
'--vanilla',
'--slave',
'-e',
'cat(R.version$platform, R.version$major, R.version$minor)',
]);
hash.update(version.stdout);
const runtime = path.join(root, 'runtimes', hash.digest('hex').slice(0, 20));
const library = path.join(runtime, 'library');
const resources = path.join(runtime, 'R');
if (fs.existsSync(path.join(runtime, 'ready'))) {
return { library, resources };
}
const lock = `${runtime}.lock`;
// Keep lock ownership rules in sync with R/attach_sess.R: mkdir claims atomically,
// only the owner releases in finally, and timeout never clears a stale lock.
// Scope: TS lock spans resource staging through ready; the R lock spans consent, package setup, and load.
// Both ready markers mean setup succeeded: TS ready follows install, while R .ready stores the expected
// source revision after namespace load. R followers do not prompt again after contention.
const deadline = Date.now() + 120000;
for (;;) {
try {
fs.mkdirSync(lock, { mode: 0o700 });
break;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
throw error;
}
if (fs.existsSync(path.join(runtime, 'ready'))) {
return { library, resources };
}
if (Date.now() >= deadline) {
throw new Error(
`Runtime installation is locked. If its installer has exited, remove ${lock} and retry.`,
);
}
await new Promise((resolve) => setTimeout(resolve, 250));
}
}
try {
fs.mkdirSync(library, { recursive: true, mode: 0o700 });
fs.mkdirSync(resources, { recursive: true, mode: 0o700 });
const packagePath = path.join(runtime, 'sess');
fs.cpSync(path.join(extensionPath, bundledSess), packagePath, {
recursive: true,
filter: (source) => !/\.(o|so|dll)$/.test(source),
});
for (const file of [
'interactive-worker.R',
'interactive-metrics.R',
'install_sess.R',
'sess-package-install.R',
'sess_source.R',
]) {
fs.copyFileSync(path.join(extensionPath, 'R', file), path.join(resources, file));
}
await new Promise<void>((resolve, reject) => {
const child = spawn(
rPath,
['--vanilla', '--slave', `--file=${path.join(resources, 'install_sess.R')}`],
{
stdio: ['ignore', 'pipe', 'pipe'],
env: {
...process.env,
VSCODE_R_SESS_PKG_PATH: packagePath,
VSCODE_R_SESS_LIBRARY: library,
VSCODE_R_SESS_INTERACTIVE: '1',
},
},
);
child.stdout.on('data', (data: Buffer) => log(data.toString()));
child.stderr.on('data', (data: Buffer) => log(data.toString()));
child.on('error', reject);
child.on('exit', (code) =>
code === 0
? resolve()
: reject(
new Error(
'Could not install the private sess runtime from the bundled package. See R Interactive output for details.',
),
),
);
});
fs.writeFileSync(path.join(runtime, 'ready'), version.stdout, { mode: 0o600 });
return { library, resources };
} finally {
fs.rmSync(lock, { recursive: true, force: true });
}
}