Skip to content

Commit 21fea47

Browse files
committed
Initial commit
. . . . . . . . . .
0 parents  commit 21fea47

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2045
-0
lines changed

.editorconfig

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
indent_style = space
10+
indent_size = 4
11+
max_line_length = 150
12+
13+
[*.md]
14+
trim_trailing_whitespace = false

.gitignore

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# See http://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# compiled output
4+
dist
5+
tmp
6+
/out-tsc
7+
8+
# dependencies
9+
node_modules
10+
11+
# IDEs and editors
12+
/.idea
13+
.project
14+
.classpath
15+
.c9/
16+
*.launch
17+
.settings/
18+
*.sublime-workspace
19+
20+
# IDE - VSCode
21+
.vscode/*
22+
!.vscode/settings.json
23+
!.vscode/tasks.json
24+
!.vscode/launch.json
25+
!.vscode/extensions.json
26+
27+
# misc
28+
/.sass-cache
29+
/connect.lock
30+
/coverage
31+
/libpeerconnection.log
32+
npm-debug.log
33+
yarn-error.log
34+
testem.log
35+
/typings
36+
37+
# System Files
38+
.DS_Store
39+
Thumbs.db

.prettierignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Add files here to ignore them from prettier formatting
2+
3+
/dist
4+
/coverage

.prettierrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"singleQuote": true
3+
}

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -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.

README.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# cpp.js
2+
3+
cpp.js is a framework for building tiny, blazingly fast binaries for all major desktop, mobile and web platforms. Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface. The backend of the application is a cpp-sourced binary with an API that the front-end can interact with.
4+
5+
## Guide
6+
### Installation
7+
- Install and start docker. Add user to docker group.
8+
- Install plugin according to the builder used. (rollup-plugin-cppjs, vite-plugin-cppjs)
9+
- Add plugin to builder config. (e.g: vite.config.js)
10+
11+
### Basic Usage
12+
- Create native folder in the project root.
13+
- Create .h and .cpp files as you like.
14+
- If you want to use a library, just install it. (e.g: yarn add cppjs-lib-sample-web)
15+
16+
Check out the [Sample app](https://github.com/bugra9/cpp.js/tree/main/packages/cppjs-sample-vite-vue).
17+
18+
## TODO
19+
20+
### Urgent
21+
- [x] Implement swig-emscripten integration
22+
- [x] Add overloading support using parameter types for embind
23+
- [x] Crete and upload docker image
24+
- [x] Implement swig interface generator
25+
- [x] Implement cpp-js bridge file generator
26+
- [x] Implement wasm generator
27+
- [x] Create rollup plugin
28+
- [x] Create vite plugin for development
29+
- [ ] Create webpack plugin
30+
- [ ] Create turbopack plugin
31+
- [x] Create vite + vue example
32+
- [ ] Create vite + react example
33+
- [ ] Create webpack + react example
34+
- [ ] Handle other cpp formats (h, hpp, hh, c, cpp, cc, cxx)
35+
- [ ] Use user defined interface file if available.
36+
- [x] Use user defined CMakeLists.txt if available.
37+
- [ ] Implement Hot Module Replacement (HMR) for the Vite plugin.
38+
39+
### Important
40+
- [ ] Implement swig-jsi integration
41+
- [ ] Implement cpp-jsi bridge file generator
42+
- [ ] Implement React Native integration
43+
- [ ] Implement cpp-node bridge file generator
44+
- [ ] Implement Electron integration
45+
- [ ] Create documentation
46+
47+
### Libs
48+
- [ ] Create CartoMobileSDK cppjs web library
49+
- [ ] Create gdal3.js cppjs web library

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"devDependencies": {
3+
"cpp.js": "workspace:^",
4+
"prettier": "^2.6.2"
5+
}
6+
}

packages/cppjs-app-cli/.mocharc.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
spec:
2+
- 'src/**/*.spec.js'
3+
- 'test/*.spec.js'
Original file line numberDiff line numberDiff line change
@@ -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")

packages/cppjs-app-cli/package.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "cpp.js",
3+
"version": "0.1.0",
4+
"license": "MIT",
5+
"type": "module",
6+
"scripts": {
7+
"test": "mocha"
8+
},
9+
"bin": {
10+
"cpp.js": "./src/bin.js"
11+
},
12+
"main": "/src/index.js",
13+
"dependencies": {
14+
"commander": "^9.4.1",
15+
"glob": "^8.0.3"
16+
},
17+
"devDependencies": {
18+
"mocha": "^10.2.0",
19+
"cppjs-lib-sample-web": "workspace:^"
20+
}
21+
}

packages/cppjs-app-cli/src/bin.js

+39
Original file line numberDiff line numberDiff line change
@@ -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+
}
+18
Original file line numberDiff line numberDiff line change
@@ -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+
}
Original file line numberDiff line numberDiff line change
@@ -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+
});
+52
Original file line numberDiff line numberDiff line change
@@ -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+
}
+14
Original file line numberDiff line numberDiff line change
@@ -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+
}
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)