cpp.js 0.6.1 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,130 @@
1
+ /* eslint-disable import/no-unresolved */
2
+ /* eslint-disable import/first */
3
+ /* eslint-disable no-restricted-syntax */
4
+ /* eslint-disable no-undef */
5
+
6
+ import Module from 'cpp.js/module';
7
+ import systemConfig from 'cpp.js/systemConfig';
8
+
9
+ let cppJsPromise;
10
+
11
+ function isObject(item) {
12
+ return (item && typeof item === 'object' && !Array.isArray(item));
13
+ }
14
+
15
+ function mergeDeep(target, ...sources) {
16
+ if (!sources.length) return target;
17
+ const source = sources.shift();
18
+
19
+ if (isObject(target) && isObject(source)) {
20
+ for (const key in source) {
21
+ if (isObject(source[key])) {
22
+ if (!target[key]) Object.assign(target, { [key]: {} });
23
+ mergeDeep(target[key], source[key]);
24
+ } else {
25
+ Object.assign(target, { [key]: source[key] });
26
+ }
27
+ }
28
+ }
29
+
30
+ return mergeDeep(target, ...sources);
31
+ }
32
+
33
+ function initCppJs(userConfig = {}) {
34
+ if (cppJsPromise) return cppJsPromise;
35
+
36
+ const config = mergeDeep(systemConfig, userConfig);
37
+ Object.entries(config.env).forEach(([key, value]) => config.env[key] = value.replace('_CPPJS_DATA_PATH_', `${__dirname}/data`));
38
+
39
+ cppJsPromise = new Promise((resolve, reject) => {
40
+ const m = {
41
+ print(text) {
42
+ if (config.logHandler) {
43
+ config.logHandler(text, 'stdout');
44
+ } else {
45
+ console.debug(`wasm stdout: ${text}`);
46
+ }
47
+ },
48
+ printErr(text) {
49
+ if (config.errorHandler) {
50
+ config.errorHandler(text, 'stderr');
51
+ } else {
52
+ console.error(`wasm stderr: ${text}`);
53
+ }
54
+ },
55
+ locateFile(fileName) {
56
+ let path = fileName;
57
+ if (config.paths && config.paths.wasm && fileName.endsWith('.wasm')) {
58
+ path = config.paths.wasm;
59
+ } else if (config.paths && config.paths.data && (fileName.endsWith('.data.txt') || fileName.endsWith('.data'))) {
60
+ path = config.paths.data;
61
+ }
62
+
63
+ let prefix = '';
64
+ if (config.path) {
65
+ prefix = config.path;
66
+ if (prefix.slice(-1) !== '/') prefix += '/';
67
+ } else {
68
+ prefix = `${__dirname}/`;
69
+ }
70
+
71
+ let output = prefix + path;
72
+ if (output.endsWith('.data')) output += '.txt';
73
+ return output;
74
+ },
75
+
76
+ preRun: [
77
+ ({ ENV }) => {
78
+ if (ENV && config && config.env) {
79
+ Object.entries(config.env).forEach(([key, value]) => {
80
+ // eslint-disable-next-line no-param-reassign
81
+ ENV[key] = value;
82
+ });
83
+ }
84
+ },
85
+ ],
86
+ onRuntimeInitialized() {
87
+ if (config.onRuntimeInitialized) config.onRuntimeInitialized(m);
88
+ },
89
+ getPreloadedPackage(packageName) {
90
+ // eslint-disable-next-line global-require
91
+ const a = require('fs').readFileSync(`./${packageName}`, { flag: 'r' }).buffer;
92
+ return a;
93
+ },
94
+ getFileBytes(path) {
95
+ if (!path) return new Uint8Array();
96
+ return m.FS.readFile(path, { encoding: 'binary' });
97
+ },
98
+ getFileList(path = 'virtual') {
99
+ const contents = path.split('/').reduce((accumulator, currentValue) => accumulator.contents[currentValue], m.FS.root).contents;
100
+ const fileList = [];
101
+ Object.keys(contents).forEach((name) => {
102
+ const obj = contents[name];
103
+ if (obj.usedBytes) fileList.push({ path: `/${path}/${name}`, size: obj.usedBytes });
104
+ else if (obj.contents) fileList.push(...m.getFileList(`${path}/${name}`));
105
+ });
106
+ return fileList;
107
+ },
108
+
109
+ toArray(vector) {
110
+ const output = [];
111
+ for (let i = 0; i < vector.size(); i += 1) {
112
+ output.push(vector.get(i));
113
+ }
114
+ return output;
115
+ },
116
+ toVector(VectorClass, array = []) {
117
+ const vector = new VectorClass();
118
+ array.forEach((item) => {
119
+ vector.push_back(item);
120
+ });
121
+ return vector;
122
+ },
123
+ };
124
+ Module(m).then(resolve).catch(reject);
125
+ });
126
+
127
+ return cppJsPromise;
128
+ }
129
+
130
+ export default initCppJs;
package/src/bin.js CHANGED
@@ -4,65 +4,140 @@ import { Command, Argument } from 'commander';
4
4
  import fs from 'fs';
5
5
  import glob from 'glob';
6
6
  import { createDir } from './utils/createTempDir.js';
7
-
8
- const packageJSON = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
7
+ import getPathInfo from './utils/getPathInfo.js';
9
8
 
10
9
  import CppjsCompiler from './index.js';
11
10
 
11
+ const packageJSON = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
12
+
12
13
  const program = new Command();
13
14
 
14
15
  program
15
- .name('cpp.js')
16
- .description('Compile c++ files to webassembly.')
17
- .version(packageJSON.version)
18
- .showHelpAfterError();
16
+ .name('cpp.js')
17
+ .description('Compile c++ files to webassembly.')
18
+ .version(packageJSON.version)
19
+ .showHelpAfterError();
19
20
 
20
21
  const commandGenerate = program.command('generate')
21
- .description('Generate app or lib.')
22
- .addArgument(new Argument('<type>', 'Generation type').choices(['app', 'lib']))
23
- .option('-b, --base <base>', 'base path')
24
- .option('-p, --platform <platform>', 'platform (wasm)', 'wasm', ['wasm'])
25
- .option('-o, --output <string>', 'Output path');
22
+ .description('Generate app or lib.')
23
+ .addArgument(new Argument('<type>', 'Generation type').choices(['app', 'lib']))
24
+ .option('-b, --base <base>', 'base path')
25
+ .option('-p, --platform <platform>', 'platform (all)', 'all', ['all', 'wasm', 'android', 'ios'])
26
+ .option('-o, --output <string>', 'Output path');
27
+
28
+ const commandRun = program.command('run')
29
+ .description('Run docker application');
30
+
31
+ const commandPostInstall = program.command('postinstall')
32
+ .description('npm postinstall');
26
33
 
27
34
  program.parse(process.argv);
28
35
 
29
36
  switch (program.args[0]) {
30
- case 'generate':
37
+ case 'generate': {
31
38
  const type = commandGenerate.args[0];
32
39
  const { output, platform, base } = commandGenerate.opts();
33
40
  generate(type, platform, output, base);
34
41
  break;
42
+ }
43
+ case 'run': {
44
+ const [programName, ...params] = commandRun.args;
45
+ run(programName, params);
46
+ break;
47
+ }
48
+ case 'postinstall': {
49
+ postInstall();
50
+ break;
51
+ }
35
52
  default:
36
53
  break;
37
54
  }
38
55
 
39
- function generate(type, platform, output, base) {
40
- if (type === 'lib') generateLib(platform, output, base);
56
+ function postInstall() {
57
+ const projectPath = process.env.PWD;
58
+ if (!fs.existsSync(`${projectPath}/cppjs.config.js`) && !fs.existsSync(`${projectPath}/cppjs.config.cjs`) && !fs.existsSync(`${projectPath}/cppjs.config.mjs`)) {
59
+ return;
60
+ }
61
+
62
+ const cppjs = new CppjsCompiler();
63
+ const name = cppjs?.config?.general?.name;
64
+ let dist = cppjs?.config?.paths?.output;
65
+ dist = dist ? getPathInfo(dist, projectPath).relative : null;
66
+
67
+ if (!name || !dist) {
68
+ return;
69
+ }
70
+
71
+ cppjs?.config?.export?.libName?.forEach((fileName) => {
72
+ if (fs.existsSync(`${projectPath}/${fileName}.xcframework`) || !fs.existsSync(`${dist}/prebuilt/${fileName}.xcframework`)) {
73
+ return;
74
+ }
75
+ fs.symlinkSync(`${dist}/prebuilt/${fileName}.xcframework`, `${projectPath}/${fileName}.xcframework`);
76
+ });
77
+ }
78
+
79
+ function run(programName, params) {
80
+ const compiler = new CppjsCompiler();
81
+ compiler.run(programName, params);
41
82
  }
42
83
 
43
- function generateLib(platform, output, base) {
84
+ function generate(type, platform) {
85
+ const compiler2 = new CppjsCompiler();
86
+ if (type === 'lib') {
87
+ if (platform === 'all' || platform === 'wasm') {
88
+ if (!fs.existsSync(`${compiler2.config.paths.output}/prebuilt/Emscripten-x86_64`)) {
89
+ generateWasmLib();
90
+ }
91
+ }
92
+ if (platform === 'wasm') return;
93
+ const platforms = {
94
+ all: ['Android-arm64-v8a', 'iOS-iphoneos', 'iOS-iphonesimulator'],
95
+ android: ['Android-arm64-v8a'],
96
+ ios: ['iOS-iphoneos', 'iOS-iphonesimulator'],
97
+ };
98
+ platforms[platform].forEach((p) => {
99
+ if (!fs.existsSync(`${compiler2.config.paths.output}/prebuilt/${p}`)) {
100
+ const compiler = new CppjsCompiler(p);
101
+ compiler.createLib();
102
+ }
103
+ });
104
+ if (platform === 'all' || platform === 'ios') {
105
+ compiler2.finishBuild();
106
+ }
107
+ }
108
+
109
+ const distCmakeContent = fs.readFileSync(`${compiler2.config.paths.cli}/assets/dist.cmake`, { encoding: 'utf8', flag: 'r' })
110
+ .replace('___PROJECT_NAME___', compiler2.config.general.name).replace('___PROJECT_LIBS___', compiler2.config.export.libName.join(';'));
111
+ fs.writeFileSync(`${compiler2.config.paths.output}/prebuilt/CMakeLists.txt`, distCmakeContent);
112
+ }
113
+
114
+ async function generateWasmLib() {
44
115
  let headers = [];
45
116
 
46
117
  const compiler = new CppjsCompiler();
47
- compiler.config.paths.header.forEach(headerPath => {
48
- headers.push(glob.sync("**/*.h", { absolute: true, cwd: headerPath }));
118
+ compiler.config.paths.header.forEach((headerPath) => {
119
+ headers.push(glob.sync('**/*.h', { absolute: true, cwd: headerPath }));
49
120
  });
50
- headers = headers.filter(path => !!path.toString()).flat();
121
+ headers = headers.filter((path) => !!path.toString()).flat();
51
122
 
52
- headers.forEach(header => {
123
+ headers.forEach((header) => {
53
124
  compiler.findOrCreateInterfaceFile(header);
54
125
  });
55
126
 
56
127
  compiler.createBridge();
57
- compiler.createWasm({ cc: ['-O3'] });
58
- createDir('prebuilt', compiler.config.paths.output);
59
- fs.copyFileSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.js`, `${compiler.config.paths.output}/${compiler.config.general.name}.js`);
128
+ await compiler.createWasm({ cc: ['-O3'] });
129
+ createDir('prebuilt/Emscripten-x86_64', compiler.config.paths.output);
130
+ fs.copyFileSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.node.js`, `${compiler.config.paths.output}/${compiler.config.general.name}.node.js`);
131
+ fs.copyFileSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.browser.js`, `${compiler.config.paths.output}/${compiler.config.general.name}.browser.js`);
60
132
  fs.copyFileSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.wasm`, `${compiler.config.paths.output}/${compiler.config.general.name}.wasm`);
61
- fs.rmSync(`${compiler.config.paths.output}/prebuilt`, { recursive: true, force: true });
62
- fs.renameSync(`${compiler.config.paths.temp}/prebuilt`, `${compiler.config.paths.output}/prebuilt`);
133
+ if (fs.existsSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.data.txt`)) {
134
+ fs.copyFileSync(`${compiler.config.paths.temp}/${compiler.config.general.name}.data.txt`, `${compiler.config.paths.output}/${compiler.config.general.name}.data.txt`);
135
+ }
136
+
137
+ fs.renameSync(`${compiler.config.paths.temp}/lib/`, `${compiler.config.paths.output}/prebuilt/Emscripten-x86_64/lib`);
138
+ fs.renameSync(`${compiler.config.paths.temp}/include/`, `${compiler.config.paths.output}/prebuilt/Emscripten-x86_64/include`);
63
139
 
64
- const distCmakeContent = fs.readFileSync(`${compiler.config.paths.cli}/assets/dist.cmake`, { encoding: 'utf8', flag: 'r' })
65
- .replace('___PROJECT_NAME___', compiler.config.general.name);
66
- fs.writeFileSync(`${compiler.config.paths.output}/prebuilt/CMakeLists.txt`, distCmakeContent);
140
+ fs.rmSync(`${compiler.config.paths.output}/data`, { recursive: true, force: true });
141
+ fs.renameSync(`${compiler.config.paths.temp}/data`, `${compiler.config.paths.output}/data`);
67
142
  fs.rmSync(compiler.config.paths.temp, { recursive: true, force: true });
68
143
  }
@@ -0,0 +1,63 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ /* eslint-disable no-restricted-syntax */
3
+ import fs from 'fs';
4
+ import { rollup } from 'rollup';
5
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
6
+ import commonjs from '@rollup/plugin-commonjs';
7
+ import virtual from '@rollup/plugin-virtual';
8
+
9
+ const nodeLibs = {
10
+ fs: 'export default {};',
11
+ path: 'export default {};',
12
+ string_decoder: 'export default {};',
13
+ buffer: 'export default {};',
14
+ crypto: 'export default {};',
15
+ stream: 'export default {};',
16
+ };
17
+
18
+ const options = {
19
+ browser: {
20
+ plugins: [virtual(nodeLibs), nodeResolve(), commonjs({ transformMixedEsModules: true, ignoreTryCatch: 'remove' })],
21
+ output: {
22
+ file: 'browser',
23
+ format: 'umd',
24
+ name: 'initCppJs',
25
+ },
26
+ },
27
+ node: {
28
+ plugins: [nodeResolve(), commonjs()],
29
+ output: {
30
+ file: 'node',
31
+ format: 'umd',
32
+ name: 'initCppJs',
33
+ },
34
+ },
35
+ };
36
+
37
+ export default async function buildJS(compiler, input, type) {
38
+ const entryJS = `${compiler.config.paths.cli}/assets/${type}.js`;
39
+ const env = JSON.stringify(compiler.getData('env', null, type));
40
+ const systemConfig = `export default {
41
+ env: ${env},
42
+ paths: {
43
+ wasm: '${compiler.config.general.name}.wasm',
44
+ data: '${compiler.config.general.name}.data.txt'
45
+ }
46
+ }`;
47
+ let file = input;
48
+ if (input.endsWith('.js')) {
49
+ file = input.substring(0, input.length - 3);
50
+ }
51
+ // fs.renameSync(input, `${input}.raw.js`);
52
+ const option = options[type];
53
+ option.plugins = [virtual({
54
+ 'cpp.js/systemConfig': systemConfig,
55
+ 'cpp.js/module': `export { default } from '${input}';`,
56
+ }), ...option.plugins];
57
+ option.input = entryJS;
58
+ option.output.file = `${file}.${option.output.file}.js`;
59
+ const bundle = await rollup(option);
60
+ await bundle.write(option.output);
61
+ // fs.rmSync(`${input}.raw.js`, { force: true });
62
+ }
63
+ //
@@ -1,32 +1,31 @@
1
1
  import glob from 'glob';
2
- import { execFileSync } from 'child_process';
3
- import pullDockerImage, { getDockerImage } from '../utils/pullDockerImage.js';
4
2
  import getBaseInfo from '../utils/getBaseInfo.js';
5
3
  import getPathInfo from '../utils/getPathInfo.js';
6
- import getOsUserAndGroupId from '../utils/getOsUserAndGroupId.js';
4
+ import run from './run.js';
7
5
 
8
- export default function createBridge(compiler) {
9
- pullDockerImage();
6
+ const platform = 'Emscripten-x86_64';
10
7
 
8
+ export default function createBridge(compiler) {
11
9
  const bridges = [];
12
- compiler.interfaces.forEach(filePath => {
10
+ compiler.interfaces.forEach((filePath) => {
13
11
  const input = getPathInfo(filePath, compiler.config.paths.base);
14
- const output = getPathInfo(compiler.config.paths.temp+'/bridge', compiler.config.paths.base);
15
- const projectPath = getPathInfo(compiler.config.paths.project, compiler.config.paths.base);
12
+ const output = getPathInfo(`${compiler.config.paths.temp}/bridge`, compiler.config.paths.base);
16
13
  const base = getBaseInfo(compiler.config.paths.base);
17
14
 
18
15
  const includePath = [
19
- ...glob.sync("node_modules/cppjs-lib-*/include", { absolute: true, cwd: compiler.config.paths.project }),
20
- ...glob.sync("node_modules/cppjs-lib-*/node_modules/cppjs-lib-*/include", { absolute: true, cwd: compiler.config.paths.project }),
16
+ ...compiler.config.getAllDependencies().map((d) => `${d.paths.output}/prebuilt/${platform}/include`),
17
+ ...compiler.config.getAllDependencies().map((d) => `${d.paths.output}/prebuilt/${platform}/swig`),
21
18
  ...compiler.config.paths.header,
22
- ].filter(path => !!path.toString()).map(path => `-I/live/${getPathInfo(path, compiler.config.paths.base).relative}`);
19
+ ].filter((path) => !!path.toString()).map((path) => `-I/tmp/cppjs/live/${getPathInfo(path, compiler.config.paths.base).relative}`);
20
+
21
+ run(compiler, 'swig', [
22
+ '-c++',
23
+ '-embind',
24
+ '-o', `/tmp/cppjs/live/${output.relative}/${filePath.split('/').at(-1)}.cpp`,
25
+ ...includePath,
26
+ `/tmp/cppjs/live/${input.relative}`,
27
+ ]);
23
28
 
24
- const options = { cwd: output.absolute, stdio : 'pipe' };
25
- const args = [
26
- "run", "--user", getOsUserAndGroupId(), "-v", `${base.withoutSlash}:/live`, getDockerImage(),
27
- "swig", "-c++", '-emscripten', '-o', `/live/${output.relative}/${filePath.split('/').at(-1)}.cpp`, ...includePath, `/live/${input.relative}`
28
- ];
29
- execFileSync("docker", args, options);
30
29
  bridges.push(`${base.withSlash}${output.relative}/${filePath.split('/').at(-1)}.cpp`);
31
30
  });
32
31
  return bridges;
@@ -0,0 +1,44 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import getCmakeParams from './getCmakeParams.js';
4
+ import getPathInfo from '../utils/getPathInfo.js';
5
+
6
+ const cpuCount = os.cpus().length - 1;
7
+
8
+ export default function createLib(compiler) {
9
+ if (!compiler.platform) return;
10
+
11
+ let platformParams = [];
12
+ switch (compiler.platform) {
13
+ case 'Emscripten-x86_64':
14
+ platformParams = ['-DBUILD_TYPE=STATIC'];
15
+ break;
16
+ case 'Android-arm64-v8a':
17
+ platformParams = ['-DBUILD_TYPE=SHARED'];
18
+ break;
19
+ case 'iOS-iphoneos':
20
+ platformParams = ['-DBUILD_TYPE=STATIC'];
21
+ break;
22
+ case 'iOS-iphonesimulator':
23
+ platformParams = ['-DBUILD_TYPE=STATIC'];
24
+ break;
25
+ default:
26
+ }
27
+
28
+ const libdir = `${getPathInfo(compiler.config.paths.output, compiler.config.paths.base).relative}/prebuilt/${compiler.platform}`;
29
+ const basePlatform = compiler.platform.split('-', 1)[0];
30
+ const params = getCmakeParams(compiler.config, '/tmp/cppjs/live/', true, false);
31
+ compiler.run(null, [
32
+ basePlatform === 'iOS' ? 'ios-cmake' : 'cmake', '/tmp/cppjs/cmake',
33
+ '-DCMAKE_BUILD_TYPE=Release', ...platformParams,
34
+ `-DCMAKE_INSTALL_PREFIX=/tmp/cppjs/live/${libdir}`, `-DPROJECT_NAME=${compiler.config.general.name}`,
35
+ ...params,
36
+ ]);
37
+ if (basePlatform === 'iOS') {
38
+ compiler.run(null, ['ios-cmake', '--build', '.', '--config', 'Release', '--target', 'install']);
39
+ } else {
40
+ compiler.run(null, ['make', `-j${cpuCount}`, 'install']);
41
+ }
42
+
43
+ fs.rmSync(compiler.config.paths.temp, { recursive: true, force: true });
44
+ }