cpp.js 0.1.0

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.
package/.mocharc.yaml ADDED
@@ -0,0 +1,3 @@
1
+ spec:
2
+ - 'src/**/*.spec.js'
3
+ - 'test/*.spec.js'
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Buğra Sarı
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ cmake_minimum_required(VERSION 3.1)
2
+ project(cppjs)
3
+
4
+ file(GLOB SRC_FILES "${BASE_DIR}/native/**/*.cpp" "${BASE_DIR}/native/*.cpp" "${BRIDGE_DIR}/*.i.cpp")
5
+ add_library(cppjs STATIC ${SRC_FILES})
6
+
7
+ file(GLOB HEADERS "${BASE_DIR}/node_modules/cppjs-lib-*/include")
8
+ message("... ${SRC_FILES} . ${HEADERS};${BASE_DIR}/native")
9
+ include_directories("${HEADERS};${BASE_DIR}/native")
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "cpp.js",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "bin": {
7
+ "cpp.js": "./src/bin.js"
8
+ },
9
+ "main": "/src/index.js",
10
+ "dependencies": {
11
+ "commander": "^9.4.1",
12
+ "glob": "^8.0.3"
13
+ },
14
+ "devDependencies": {
15
+ "mocha": "^10.2.0",
16
+ "cppjs-lib-sample-web": "^0.1.0"
17
+ },
18
+ "scripts": {
19
+ "test": "mocha"
20
+ }
21
+ }
package/src/bin.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ import packageJSON from '../package.json' assert { type: 'json' };
4
+ import { Command, Argument } from 'commander';
5
+
6
+ import { createBridge, findCMakeListsFile, findOrCreateInterfaceFile, createWasm } from './index.js';
7
+ import { createTempDir } from './utils.js';
8
+
9
+ const program = new Command();
10
+ program
11
+ .name('cpp.js')
12
+ .description('Compile c++ files to webassembly.')
13
+ .version(packageJSON.version)
14
+ .showHelpAfterError();
15
+
16
+ program.addArgument(new Argument('<type>', 'Creation type').choices(['bridge', 'wasm', 'all']));
17
+ program.argument('<input>', 'Input paths');
18
+ program
19
+ .option('-o, --output <string>', 'Output path');
20
+
21
+ program.parse(process.argv);
22
+ const options = program.opts();
23
+ const args = program.args;
24
+
25
+ const [type, ...inputs]= args;
26
+ main(type, inputs);
27
+
28
+ async function main(type, inputs, output = createTempDir('a'+Math.random())) {
29
+ if (type === 'bridge' || type === 'all') {
30
+ const interfaceFile = findOrCreateInterfaceFile(process.cwd()+'/'+inputs[0], output);
31
+ createBridge(interfaceFile, output);
32
+ }
33
+
34
+ if (type === 'wasm' || type === 'all') {
35
+ const cMakeListsFilePath = findCMakeListsFile();
36
+ createWasm(cMakeListsFilePath, output, output, { cc: ['-O3'] });
37
+ }
38
+ console.log('output: ' + output);
39
+ }
@@ -0,0 +1,18 @@
1
+ import glob from 'glob';
2
+ import { execFileSync } from 'child_process';
3
+ import pullDockerImage from './pullDockerImage.js';
4
+
5
+ export default function createBridge(filePath, outputPath) {
6
+ pullDockerImage();
7
+ const includePath = [
8
+ glob.sync("node_modules/cppjs-lib-*-web/include", { absolute: true })
9
+ ].map(path => `-I${path}`);
10
+
11
+ const options = { cwd: outputPath, stdio : 'pipe' };
12
+ const args = [
13
+ "run", "-v", `${outputPath}:/output`, "-v", "/:/live", "bugra9/cpp.js",
14
+ "swig", "-c++", '-emscripten', '-o', `/output/${filePath.split('/').at(-1)}.cpp`, ...includePath, `/live${filePath}`
15
+ ];
16
+ execFileSync("docker", args, options);
17
+ return `${outputPath}/${filePath.split('/').at(-1)}.cpp`;
18
+ }
@@ -0,0 +1,52 @@
1
+ import { assert } from 'chai';
2
+ import fs from 'fs';
3
+ import p, {dirname} from 'path';
4
+ import * as url from 'node:url';
5
+ import { tmpdir } from "os";
6
+ import createBridge from './createBridge.js';
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const temp = __filename.split('/');
10
+ temp.pop();
11
+ temp.pop();
12
+ const __dirname = temp.join('/');
13
+
14
+ export function createTempDir(folder) {
15
+ let path = p.join(tmpdir(), "cppjs-app-cli-test");
16
+ if (folder) path = p.join(path, folder);
17
+
18
+ if (fs.existsSync(path)) fs.rmSync(path, { recursive: true, force: true });
19
+ fs.mkdirSync(path, { recursive: true });
20
+
21
+ return path;
22
+ }
23
+
24
+ describe('createBridge', function () {
25
+ let tempdir;
26
+ before(async function () {
27
+ tempdir = createTempDir();
28
+ });
29
+
30
+ it('createBridge', async function () {
31
+ const path = __dirname+'/test/data/sample.i';
32
+ const bridgeFilePath = createBridge(path, tempdir);
33
+ const bridgeFileData = fs.readFileSync(bridgeFilePath, 'utf8');
34
+
35
+ const startIndex = bridgeFileData.indexOf('#include <emscripten/bind.h>');
36
+ const bridgeFileDataTrim = bridgeFileData.substring(startIndex, bridgeFileData.length).trim();
37
+
38
+ const expectedContent =
39
+ `#include <emscripten/bind.h>
40
+
41
+ #include "sample.h"
42
+
43
+ EMSCRIPTEN_BINDINGS(Sample) {
44
+ emscripten::class_<Sample>("Sample")
45
+ .constructor<>()
46
+ .function("t", &Sample::t)
47
+ ;
48
+ }
49
+ `.trim();
50
+ assert.equal(bridgeFileDataTrim, expectedContent);
51
+ });
52
+ });
@@ -0,0 +1,52 @@
1
+ import { execFileSync } from 'child_process';
2
+ import * as url from 'node:url';
3
+ import pullDockerImage from './pullDockerImage.js';
4
+
5
+ const __filename = url.fileURLToPath(import.meta.url);
6
+ const temp = __filename.split('/');
7
+ temp.pop();
8
+ temp.pop();
9
+ const __dirname = temp.join('/');
10
+
11
+ export default function createWasm(cMakeFilePath, outputPath, tempPath, options = {}) {
12
+ pullDockerImage();
13
+ cmake(cMakeFilePath, tempPath);
14
+ make(tempPath);
15
+ cc(tempPath, outputPath, options.cc);
16
+
17
+ return outputPath;
18
+ }
19
+
20
+ function cmake(cMakeFilePath, outputPath) {
21
+ let cMakeParentPath = cMakeFilePath.split('/');
22
+ cMakeParentPath.pop();
23
+ cMakeParentPath = cMakeParentPath.join('/');
24
+
25
+ const args = [
26
+ "run", "-v", `${outputPath}:/output`, "-v", "/:/live", "--workdir", "/output", "bugra9/cpp.js",
27
+ "emcmake", "cmake", "/live"+cMakeParentPath, "-DCMAKE_INSTALL_PREFIX=/output", `-DBASE_DIR=/live${process.cwd()}`, '-DBRIDGE_DIR=/output',
28
+ ];
29
+ const options = { cwd: outputPath, stdio : 'pipe' };
30
+ execFileSync("docker", args, options);
31
+ return outputPath;
32
+ }
33
+
34
+ function make(outputPath) {
35
+ const args = [
36
+ "run", "-v", `${outputPath}:/output`, "-v", "/:/live", "--workdir", "/output", "bugra9/cpp.js",
37
+ "emmake", "make"
38
+ ];
39
+ const options = { cwd: outputPath, stdio : 'pipe' };
40
+ execFileSync("docker", args, options);
41
+ return outputPath;
42
+ }
43
+
44
+ function cc(tempPath, outputPath, flags = []) {
45
+ const args = [
46
+ "run", "-v", `${tempPath}:/tempPath`, "-v", `${outputPath}:/output`, "-v", `${__dirname}:/cli`, "-v", "/:/live", "bugra9/cpp.js",
47
+ "emcc", "-lembind", "-Wl,--whole-archive", '/tempPath/libcppjs.a', ...flags, "-s", "WASM=1", "-s", "MODULARIZE=1", '-o', '/output/cpp.js', '--extern-post-js', '/cli/src/extern-post.js'
48
+ ];
49
+ const options = { cwd: tempPath, stdio : 'pipe' };
50
+ execFileSync("docker", args, options);
51
+ return outputPath;
52
+ }
@@ -0,0 +1,14 @@
1
+ export default async function then(cb) {
2
+ const m = {
3
+ locateFile(path) {
4
+ if(path.endsWith('.wasm')) {
5
+ return "cpp.wasm";
6
+ }
7
+ return path;
8
+ },
9
+ onRuntimeInitialized() {
10
+ cb(this);
11
+ }
12
+ };
13
+ Module(m);
14
+ }
@@ -0,0 +1,17 @@
1
+ import fs from 'fs';
2
+ import glob from 'glob';
3
+ import {dirname} from 'path';
4
+ import * as url from 'node:url';
5
+
6
+ export const __filename = url.fileURLToPath(import.meta.url);
7
+ export const __dirname = dirname(dirname(__filename)+'..');
8
+
9
+ export default function findCMakeListsFile() {
10
+ let temp = glob.sync("CMakeLists.txt", { absolute: true });
11
+ if (temp.length === 0) {
12
+ temp = glob.sync("*/CMakeLists.txt", { absolute: true, ignore: ['node_modules/*', 'dist/*', 'build/*'] });
13
+ }
14
+
15
+ if (temp.length > 0) return temp[0];
16
+ return __dirname + '/assets/CMakeLists.txt';
17
+ }
@@ -0,0 +1,34 @@
1
+ import { assert } from 'chai';
2
+ import fs from 'fs';
3
+ import p from 'path';
4
+ import * as url from 'node:url';
5
+ import { tmpdir } from "os";
6
+ import findCMakeListsFile from './findCMakeListsFile.js';
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const temp = __filename.split('/');
10
+ temp.pop();
11
+ temp.pop();
12
+ const __dirname = temp.join('/');
13
+
14
+ export function createTempDir(folder) {
15
+ let path = p.join(tmpdir(), "cppjs-app-cli-test");
16
+ if (folder) path = p.join(path, folder);
17
+
18
+ if (fs.existsSync(path)) fs.rmSync(path, { recursive: true, force: true });
19
+ fs.mkdirSync(path, { recursive: true });
20
+
21
+ return path;
22
+ }
23
+
24
+ describe('findCMakeListsFile', function () {
25
+ let tempdir;
26
+ before(async function () {
27
+ tempdir = createTempDir();
28
+ });
29
+
30
+ it('find CMakeLists.txt file', async function () {
31
+ const path = findCMakeListsFile(tempdir);
32
+ assert.equal(path, __dirname + '/assets/CMakeLists.txt');
33
+ });
34
+ });
@@ -0,0 +1,33 @@
1
+ import fs from 'fs';
2
+
3
+ export default function findOrCreateInterfaceFile(filePath, outputPath) {
4
+ const temp = filePath.match(/^(.*)\..+?$/);
5
+ if (temp.length < 2) return null;
6
+
7
+ const filePathWithoutExt = temp[1];
8
+ const interfaceFile = filePathWithoutExt + '.i';
9
+
10
+ if (fs.existsSync(interfaceFile)) return interfaceFile;
11
+
12
+ const fileName = filePathWithoutExt.split('/').at(-1);
13
+ const fileNameWithExt = filePath.split('/').at(-1);
14
+
15
+ const content =
16
+ `#ifndef _${fileName.toUpperCase()}_I
17
+ #define _${fileName.toUpperCase()}_I
18
+
19
+ %module ${fileName.toUpperCase()}
20
+
21
+ %{
22
+ #include "${fileNameWithExt}"
23
+ %}
24
+
25
+ %include "/live${filePath}"
26
+
27
+ #endif
28
+ `;
29
+
30
+ const outputFilePath = outputPath+'/'+fileName+'.i';
31
+ fs.writeFileSync(outputFilePath, content);
32
+ return outputFilePath;
33
+ }
@@ -0,0 +1,49 @@
1
+ import { assert } from 'chai';
2
+ import fs from 'fs';
3
+ import p, {dirname} from 'path';
4
+ import * as url from 'node:url';
5
+ import { tmpdir } from "os";
6
+ import findOrCreateInterfaceFile from './findOrCreateInterfaceFile.js';
7
+
8
+ const __filename = url.fileURLToPath(import.meta.url);
9
+ const temp = __filename.split('/');
10
+ temp.pop();
11
+ temp.pop();
12
+ const __dirname = temp.join('/');
13
+
14
+ export function createTempDir(folder) {
15
+ let path = p.join(tmpdir(), "cppjs-app-cli-test");
16
+ if (folder) path = p.join(path, folder);
17
+
18
+ if (fs.existsSync(path)) fs.rmSync(path, { recursive: true, force: true });
19
+ fs.mkdirSync(path, { recursive: true });
20
+
21
+ return path;
22
+ }
23
+
24
+ describe('findOrCreateInterfaceFile', function () {
25
+ let tempdir;
26
+ before(async function () {
27
+ tempdir = createTempDir();
28
+ });
29
+
30
+ it('find interface file', async function () {
31
+ const path = __dirname+'/test/data/sample.h';
32
+ const interfaceFile = findOrCreateInterfaceFile(path, tempdir);
33
+ assert.equal(interfaceFile, path.replace('.h', '.i'));
34
+ });
35
+
36
+ it('create interface file', async function () {
37
+ const path = __dirname+'/test/data/sample2.h';
38
+ const interfaceFile = findOrCreateInterfaceFile(path, tempdir);
39
+ const interfaceFileData = fs.readFileSync(interfaceFile, 'utf8');
40
+
41
+ const path2 = __dirname+'/test/data/sample2.ei';
42
+ const interfaceFileData2 = fs.readFileSync(path2, 'utf8');
43
+
44
+ assert.equal(
45
+ interfaceFileData.trim().replace(__dirname + '/test/data/', ''),
46
+ interfaceFileData2.trim().replace(__dirname + '/test/data/', '')
47
+ );
48
+ });
49
+ });
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { default as createBridge } from './createBridge.js';
2
+ export { default as findCMakeListsFile } from './findCMakeListsFile.js';
3
+ export { default as findOrCreateInterfaceFile } from './findOrCreateInterfaceFile.js';
4
+ export { default as createWasm } from './createWasm.js';
@@ -0,0 +1,17 @@
1
+ import { execFileSync } from 'child_process';
2
+
3
+ export default function pullDockerImage() {
4
+ const isImageExist = execFileSync("docker", ["images", "-q", "bugra9/cpp.js"], {encoding: 'utf-8'}).trim() !== '';
5
+
6
+ if (!isImageExist) {
7
+ console.log('');
8
+ console.log('===========================================================');
9
+ console.log('============= Downloading the docker image... =============');
10
+ console.log('===========================================================');
11
+ console.log('');
12
+ execFileSync("docker", ["pull", "bugra9/cpp.js"], {stdio: 'inherit'});
13
+ console.log('');
14
+ console.log('===========================================================');
15
+ console.log('');
16
+ }
17
+ }
package/src/utils.js ADDED
@@ -0,0 +1,56 @@
1
+ import fs from 'fs';
2
+ import p, {dirname} from 'path';
3
+ import { tmpdir } from "os";
4
+ import * as url from 'node:url';
5
+
6
+ export const __filename = url.fileURLToPath(import.meta.url);
7
+ export const __dirname = dirname(__filename);
8
+
9
+ let config;
10
+ export const mainPath = process.cwd();
11
+
12
+ export function createTempDir(folder) {
13
+ let path = p.join(tmpdir(), "cppjs-app-cli");
14
+ if (folder) path = p.join(path, folder);
15
+
16
+ if (fs.existsSync(path)) fs.rmdirSync(path);
17
+ fs.mkdirSync(path, { recursive: true });
18
+
19
+ return path;
20
+ }
21
+
22
+ export function getFilesGivenDir(dir, options = {}) {
23
+ return new Promise((resolve, reject) => {
24
+ fs.readdir(dir, (err, files) => {
25
+ if (err) reject(err);
26
+
27
+ let filteredFiles = files;
28
+ if (options.ext && Array.isArray(options.ext) && options.ext.length > 0) {
29
+ const regex = new RegExp(`\.(${options.ext.join('|')})$`, 'g');
30
+ filteredFiles = filteredFiles.filter(file => regex.test(file));
31
+ }
32
+ if (options.filter) filteredFiles = filteredFiles.filter(options.filter);
33
+ resolve(filteredFiles);
34
+ });
35
+ });
36
+ }
37
+
38
+ export async function getConfig(projectPath) {
39
+ if (config) return config;
40
+ if (!projectPath) projectPath = mainPath;
41
+
42
+ config = {
43
+ projectPath,
44
+ modulesPath: projectPath,
45
+ modulesTempPath: createTempDir('modules'),
46
+ bridgesTempPath: createTempDir('bridges'),
47
+ output: `${projectPath}/dist`,
48
+ };
49
+
50
+ const configFilePath = `${projectPath}/.cppjs.config.js`;
51
+ if (fs.existsSync(configFilePath)) {
52
+ config = Object.assign({}, config, (await import(configFilePath)).default);
53
+ }
54
+
55
+ return config;
56
+ }
@@ -0,0 +1 @@
1
+ #include "sample.h"
@@ -0,0 +1,10 @@
1
+ #ifndef _SAMPLE_H
2
+ #define _SAMPLE_H
3
+
4
+ class Sample {
5
+ public:
6
+ Sample();
7
+ void t();
8
+ };
9
+
10
+ #endif
@@ -0,0 +1,12 @@
1
+ #ifndef _SAMPLE_I
2
+ #define _SAMPLE_I
3
+
4
+ %module Sample
5
+
6
+ %{
7
+ #include "sample.h"
8
+ %}
9
+
10
+ %include "sample.h"
11
+
12
+ #endif
File without changes
@@ -0,0 +1,12 @@
1
+ #ifndef _SAMPLE2_I
2
+ #define _SAMPLE2_I
3
+
4
+ %module SAMPLE2
5
+
6
+ %{
7
+ #include "sample2.h"
8
+ %}
9
+
10
+ %include "sample2.h"
11
+
12
+ #endif
@@ -0,0 +1,10 @@
1
+ #ifndef _SAMPLE2_H
2
+ #define _SAMPLE2_H
3
+
4
+ class Sample2 {
5
+ public:
6
+ Sample2();
7
+ void y();
8
+ };
9
+
10
+ #endif