cpp.js 1.0.0-beta.3 → 1.0.0-beta.31

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.
Files changed (60) hide show
  1. package/README.md +104 -0
  2. package/package.json +24 -14
  3. package/src/{functions/buildJS.js → actions/buildJs.js} +7 -6
  4. package/src/actions/buildWasm.js +68 -0
  5. package/src/actions/createInterface.js +128 -0
  6. package/src/actions/createLib.js +104 -0
  7. package/src/actions/createXCFramework.js +44 -0
  8. package/src/actions/getAllBridges.js +8 -0
  9. package/src/actions/getCmakeParameters.js +56 -0
  10. package/src/actions/getData.js +28 -0
  11. package/src/actions/getDependLibs.js +19 -0
  12. package/src/{functions → actions}/run.js +98 -58
  13. package/src/assets/CMakeLists.txt +14 -10
  14. package/src/assets/commonBridges.cpp +16 -0
  15. package/src/assets/dist.cmake +1 -1
  16. package/src/assets/ios.toolchain.cmake +4 -4
  17. package/src/bin.js +255 -96
  18. package/src/index.js +13 -63
  19. package/src/integration/getCppJsScript.js +91 -0
  20. package/src/integration/getDependFilePath.js +34 -0
  21. package/src/state/calculateDependencyParameters.js +64 -0
  22. package/src/state/index.js +81 -0
  23. package/src/state/loadConfig.js +103 -0
  24. package/src/utils/downloadAndExtractFile.js +36 -0
  25. package/src/utils/fixPackageName.js +7 -0
  26. package/src/utils/getAbsolutePath.js +14 -0
  27. package/src/utils/{findCMakeListsFile.js → getCMakeListsFilePath.js} +5 -4
  28. package/src/utils/getOsUserAndGroupId.js +1 -1
  29. package/src/utils/getParentPath.js +12 -0
  30. package/src/utils/hash.js +20 -0
  31. package/src/utils/loadJs.js +32 -0
  32. package/src/utils/loadJson.js +16 -0
  33. package/src/utils/pullDockerImage.js +2 -2
  34. package/src/utils/systemKeys.js +18 -0
  35. package/src/utils/writeJson.js +9 -0
  36. package/.mocharc.yaml +0 -3
  37. package/src/functions/createBridge.js +0 -37
  38. package/src/functions/createLib.js +0 -44
  39. package/src/functions/createWasm.js +0 -67
  40. package/src/functions/findOrCreateInterfaceFile.js +0 -68
  41. package/src/functions/finishBuild.js +0 -43
  42. package/src/functions/getCmakeParams.js +0 -106
  43. package/src/functions/getData.js +0 -36
  44. package/src/functions/getLibs.js +0 -33
  45. package/src/functions/specs/createBridge.spec.js +0 -51
  46. package/src/functions/specs/findOrCreateInterfaceFile.spec.js +0 -49
  47. package/src/utils/createTempDir.js +0 -15
  48. package/src/utils/getBaseInfo.js +0 -15
  49. package/src/utils/getCliPath.js +0 -12
  50. package/src/utils/getConfig.js +0 -170
  51. package/src/utils/getDirName.js +0 -8
  52. package/src/utils/getPathInfo.js +0 -10
  53. package/src/utils/specs/findCMakeListsFile.spec.js +0 -34
  54. package/src/utils/specs/utils.spec.js +0 -81
  55. package/test/data/sample.cpp +0 -1
  56. package/test/data/sample.h +0 -10
  57. package/test/data/sample.i +0 -12
  58. package/test/data/sample2.ei +0 -12
  59. package/test/data/sample2.h +0 -10
  60. /package/{test/data/sample2.cpp → src/assets/cppjsEmptySource.cpp} +0 -0
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ <div align="center">
2
+ <a href="https://cpp.js.org">
3
+ <picture>
4
+ <img alt="Next.js logo" src="https://cpp.js.org/img/logo.png" height="128">
5
+ </picture>
6
+ </a>
7
+ <h1>Cpp.js</h1>
8
+ <p align="center">
9
+ <strong>Bind C++ to JavaScript with no extra code.</strong><br>
10
+ WebAssembly & React Native
11
+ </p>
12
+
13
+ <a href="https://www.npmjs.com/package/cpp.js"><img alt="NPM version" src="https://img.shields.io/npm/v/cpp.js?style=for-the-badge" /></a>
14
+ <a href="https://github.com/bugra9/cpp.js/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/bugra9/cpp.js?style=for-the-badge" /></a>
15
+ <a href="https://github.com/bugra9/cpp.js/discussions"><img alt="Discussions" src="https://img.shields.io/github/discussions/bugra9/cpp.js?style=for-the-badge" /></a>
16
+ <a href="https://github.com/bugra9/cpp.js/issues"><img alt="Issues" src="https://img.shields.io/github/issues/bugra9/cpp.js?style=for-the-badge" /></a>
17
+ <br />
18
+ <img alt="Linux Build" src="https://img.shields.io/github/actions/workflow/status/bugra9/cpp.js/build-linux.yml?branch=main&style=for-the-badge&label=Linux%20Build">
19
+ <img alt="Macos Build" src="https://img.shields.io/github/actions/workflow/status/bugra9/cpp.js/build-macos.yml?branch=main&style=for-the-badge&label=Macos%20Build">
20
+ </div>
21
+
22
+ <h3 align="center">
23
+ <a href="https://cpp.js.org/docs/guide/getting-started/introduction">Getting Started</a>
24
+ <span> · </span>
25
+ <a href="https://cpp.js.org/docs/guide/integrate-into-existing-project/overview">Integrate Into Existing Project</a>
26
+ <span> · </span>
27
+ <a href="https://cpp.js.org/docs/api/cpp-bindings/overview">C++ Bindings</a>
28
+ <span> · </span>
29
+ <a href="https://cpp.js.org/showcase">Showcase</a>
30
+ </h3>
31
+
32
+ ## Basic Usage
33
+ **src/index.js**
34
+ ```js
35
+ import { initCppJs } from './native/Factorial.h';
36
+
37
+ const { Factorial } = await initCppJs();
38
+ const factorial = new Factorial(99999);
39
+ const result = factorial.calculate();
40
+ console.log(result);
41
+ ```
42
+
43
+ **src/native/Factorial.h**
44
+ ```c++
45
+ class Factorial {
46
+ private:
47
+ int number;
48
+
49
+ public:
50
+ Factorial(int num) : number(num) {}
51
+
52
+ int calculate() {
53
+ if (number < 0) return -1;
54
+
55
+ int result = 1;
56
+ for (int i = 2; i <= number; i++) {
57
+ result *= i;
58
+ }
59
+ return result;
60
+ }
61
+ };
62
+ ```
63
+
64
+ ## Prerequisites
65
+ To begin building your project with Cpp.js, you’ll first need to install a few dependencies:
66
+
67
+ - Docker
68
+ - Node.js version 18 or higher
69
+ - CMake version 3.28 or higher (only required for Mobile development)
70
+ - Xcode (only required for iOS development)
71
+ - Cocoapods (only required for iOS development)
72
+
73
+ ## Create a New Project
74
+ To set up a new cpp.js project with a minimal starter structure, execute the following command in your terminal:
75
+ ```sh
76
+ npm create cpp.js@latest
77
+ ```
78
+ ## Integrate Into Existing Project
79
+ Integrate cpp.js seamlessly into your existing projects using the appropriate packages for your development needs. Refer to the documentation links for detailed integration guides.
80
+
81
+ | Platform | Package(s) | Documentation |
82
+ | -------- | ---------- | ------------- |
83
+ | Standalone | [cpp.js](https://www.npmjs.com/package/cpp.js) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/standalone) |
84
+ | Webpack | [@cpp.js/plugin-webpack](https://www.npmjs.com/package/@cpp.js/plugin-webpack), [@cpp.js/plugin-webpack-loader](https://www.npmjs.com/package/@cpp.js/plugin-webpack-loader) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/webpack) |
85
+ | Rollup | [@cpp.js/plugin-rollup](https://www.npmjs.com/package/@cpp.js/plugin-rollup) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/rollup) |
86
+ | Vite | [@cpp.js/plugin-vite](https://www.npmjs.com/package/@cpp.js/plugin-vite) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/vite) |
87
+ | Rspack | [@cpp.js/plugin-webpack](https://www.npmjs.com/package/@cpp.js/plugin-webpack), [@cpp.js/plugin-webpack-loader](https://www.npmjs.com/package/@cpp.js/plugin-webpack-loader) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/rspack) |
88
+ | Create React App (CRA) | [@cpp.js/plugin-webpack](https://www.npmjs.com/package/@cpp.js/plugin-webpack), [@cpp.js/plugin-webpack-loader](https://www.npmjs.com/package/@cpp.js/plugin-webpack-loader) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/create-react-app) |
89
+ | React Native | [@cpp.js/plugin-react-native](https://www.npmjs.com/package/@cpp.js/plugin-react-native), [@cpp.js/plugin-react-native-ios-helper](https://www.npmjs.com/package/@cpp.js/plugin-react-native-ios-helper), [@cpp.js/plugin-metro](https://www.npmjs.com/package/@cpp.js/plugin-metro), [@cpp.js/core-embind-jsi](https://www.npmjs.com/package/@cpp.js/core-embind-jsi) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/react-native) |
90
+ | Expo | [@cpp.js/plugin-react-native](https://www.npmjs.com/package/@cpp.js/plugin-react-native), [@cpp.js/plugin-react-native-ios-helper](https://www.npmjs.com/package/@cpp.js/plugin-react-native-ios-helper), [@cpp.js/plugin-metro](https://www.npmjs.com/package/@cpp.js/plugin-metro), [@cpp.js/core-embind-jsi](https://www.npmjs.com/package/@cpp.js/core-embind-jsi) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/expo) |
91
+ | Node.js | [cpp.js](https://www.npmjs.com/package/cpp.js) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/nodejs) |
92
+ | Cloudflare Workers | [cpp.js](https://www.npmjs.com/package/cpp.js) | [Learn](https://cpp.js.org/docs/guide/integrate-into-existing-project/cloudflare-worker) |
93
+
94
+ ## Features
95
+ - [Calling C++ from JavaScript](https://cpp.js.org/docs/guide/features/calling-cpp-from-javascript)
96
+ - [Packages](https://cpp.js.org/docs/guide/features/packages)
97
+ - [Plugins](https://cpp.js.org/docs/guide/features/plugins)
98
+ - [Assets](https://cpp.js.org/docs/guide/features/assets)
99
+ - [Working with a Monorepo](https://cpp.js.org/docs/guide/features/monorepo)
100
+
101
+ ## License
102
+ [MIT](https://github.com/bugra9/cpp.js/blob/main/LICENSE)
103
+
104
+ Copyright (c) 2024, Buğra Sarı
package/package.json CHANGED
@@ -1,29 +1,39 @@
1
1
  {
2
2
  "name": "cpp.js",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.31",
4
4
  "license": "MIT",
5
- "type": "module",
6
5
  "homepage": "https://cpp.js.org",
7
6
  "repository": "https://github.com/bugra9/cpp.js.git",
7
+ "description": "Bind C++ to JavaScript without extra code. Cpp.js supports WebAssembly and React Native for cross-platform compatibility.",
8
+ "type": "module",
8
9
  "bin": {
9
10
  "cpp.js": "./src/bin.js",
10
11
  "cppjs": "./src/bin.js"
11
12
  },
12
13
  "main": "src/index.js",
13
14
  "dependencies": {
14
- "@rollup/plugin-commonjs": "^26.0.1",
15
- "@rollup/plugin-node-resolve": "^15.2.3",
15
+ "@rollup/plugin-commonjs": "^28.0.2",
16
+ "@rollup/plugin-json": "^6.1.0",
17
+ "@rollup/plugin-node-resolve": "^16.0.0",
16
18
  "@rollup/plugin-virtual": "^3.0.2",
17
19
  "commander": "^12.1.0",
18
- "glob": "^8.0.3",
19
- "rollup": "^4.18.0",
20
- "rollup-plugin-uglify": "^6.0.4",
21
- "upath": "^2.0.1"
22
- },
23
- "devDependencies": {
24
- "mocha": "^10.2.0"
20
+ "glob": "^11.0.0",
21
+ "rollup": "^4.29.1",
22
+ "upath": "^2.0.1",
23
+ "decompress": "^4.2.1",
24
+ "follow-redirects": "^1.15.9",
25
+ "replace": "^1.2.2"
25
26
  },
26
- "scripts": {
27
- "test": "mocha"
28
- }
27
+ "keywords": [
28
+ "webassembly",
29
+ "react-native",
30
+ "bind",
31
+ "c++",
32
+ "emscripten",
33
+ "embind",
34
+ "swig",
35
+ "android",
36
+ "ios",
37
+ "mobile"
38
+ ]
29
39
  }
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  /* eslint-disable no-restricted-syntax */
3
- import fs from 'fs';
4
3
  import { rollup } from 'rollup';
5
4
  import { nodeResolve } from '@rollup/plugin-node-resolve';
6
5
  import commonjs from '@rollup/plugin-commonjs';
7
6
  import virtual from '@rollup/plugin-virtual';
7
+ import state from '../state/index.js';
8
+ import getData from './getData.js';
8
9
 
9
10
  const nodeLibs = {
10
11
  fs: 'export default {};',
@@ -34,14 +35,14 @@ const options = {
34
35
  },
35
36
  };
36
37
 
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));
38
+ export default async function buildJS(input, type) {
39
+ const entryJS = `${state.config.paths.cli}/assets/${type}.js`;
40
+ const env = JSON.stringify(getData('env', 'Emscripten-x86_64', type));
40
41
  const systemConfig = `export default {
41
42
  env: ${env},
42
43
  paths: {
43
- wasm: '${compiler.config.general.name}.wasm',
44
- data: '${compiler.config.general.name}.data.txt'
44
+ wasm: '${state.config.general.name}.wasm',
45
+ data: '${state.config.general.name}.data.txt'
45
46
  }
46
47
  }`;
47
48
  let file = input;
@@ -0,0 +1,68 @@
1
+ import fs from 'node:fs';
2
+ import run from './run.js';
3
+ import getDependLibs from './getDependLibs.js';
4
+ import getData from './getData.js';
5
+ import buildJs from './buildJs.js';
6
+ import state from '../state/index.js';
7
+
8
+ export default async function buildWasm(type, isProd = false) {
9
+ const buildType = isProd ? 'Release' : 'Debug';
10
+ const libs = [
11
+ ...getDependLibs(),
12
+ `${state.config.paths.build}/Source-${buildType}/Emscripten-x86_64/lib${state.config.general.name}.a`,
13
+ `${state.config.paths.build}/Bridge-${buildType}/Emscripten-x86_64/lib${state.config.general.name}.a`,
14
+ ];
15
+
16
+ if (type === 'browser') {
17
+ console.log('wasm compiling for browser...');
18
+ const t0 = performance.now();
19
+ const data = Object.entries(getData('data', 'Emscripten-x86_64', 'browser')).map(([key, value]) => ['--preload-file', `${key.replaceAll('@', '@@')}@${value}`]).flat();
20
+ run('emcc', [
21
+ '-lembind', '-Wl,--whole-archive',
22
+ ...libs, ...(isProd ? ['-O3'] : []),
23
+ '-s', 'WASM=1', '-s', 'MODULARIZE=1', '-s', 'DYNAMIC_EXECUTION=0',
24
+ '-s', 'RESERVED_FUNCTION_POINTERS=200', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-s', 'FORCE_FILESYSTEM=1',
25
+ '-s', 'ALLOW_MEMORY_GROWTH=1',
26
+ '-s', 'EXPORTED_RUNTIME_METHODS=["FS", "ENV"]',
27
+ '-o', `${state.config.paths.build}/${state.config.general.name}.js`,
28
+ ...data,
29
+ ]);
30
+ const t1 = performance.now();
31
+ console.log('wasm compiled for browser...', Math.round(t1 - t0));
32
+ console.log('js compiling for browser...');
33
+ await buildJs(`${state.config.paths.build}/${state.config.general.name}.js`, 'browser');
34
+ const t2 = performance.now();
35
+ console.log('js compiled for browser...', Math.round(t2 - t1));
36
+ }
37
+
38
+ if (type === 'node') {
39
+ console.log('wasm compiling for node...');
40
+ run('emcc', [
41
+ '-lembind', '-Wl,--whole-archive', '-lnodefs.js',
42
+ ...libs, ...(isProd ? ['-O3'] : []),
43
+ '-s', 'WASM=1', '-s', 'MODULARIZE=1', '-s', 'DYNAMIC_EXECUTION=0',
44
+ '-s', 'RESERVED_FUNCTION_POINTERS=200', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-s', 'FORCE_FILESYSTEM=1', '-s', 'NODERAWFS',
45
+ '-s', 'ALLOW_MEMORY_GROWTH=1',
46
+ '-s', 'EXPORTED_RUNTIME_METHODS=["FS", "ENV", "NODEFS"]',
47
+ '-o', `${state.config.paths.build}/${state.config.general.name}.js`,
48
+ ]);
49
+ console.log('wasm compiled for node...');
50
+ console.log('js compiling for node...');
51
+ await buildJs(`${state.config.paths.build}/${state.config.general.name}.js`, 'node');
52
+ console.log('js compiled for node...');
53
+
54
+ Object.entries(getData('data', 'Emscripten-x86_64', 'node')).forEach(([key, value]) => {
55
+ if (fs.existsSync(key)) {
56
+ const dAssetPath = `${state.config.paths.build}/data/${value}`;
57
+ if (!fs.existsSync(dAssetPath)) {
58
+ fs.mkdirSync(dAssetPath, { recursive: true });
59
+ fs.cpSync(key, dAssetPath, { recursive: true });
60
+ }
61
+ }
62
+ });
63
+ }
64
+
65
+ if (fs.existsSync(`${state.config.paths.build}/${state.config.general.name}.data`)) {
66
+ fs.renameSync(`${state.config.paths.build}/${state.config.general.name}.data`, `${state.config.paths.build}/${state.config.general.name}.data.txt`);
67
+ }
68
+ }
@@ -0,0 +1,128 @@
1
+ /* eslint-disable prefer-destructuring */
2
+ import fs from 'node:fs';
3
+ import upath from 'upath';
4
+ import state, { saveCache } from '../state/index.js';
5
+ import { getFileHash } from '../utils/hash.js';
6
+ import run from './run.js';
7
+
8
+ export default function createBridgeFile(headerOrModuleFilePath) {
9
+ const interfaceFilePath = upath.resolve(headerOrModuleFilePath);
10
+ if (!fs.existsSync(`${state.config.paths.build}/interface`)) {
11
+ fs.mkdirSync(`${state.config.paths.build}/interface`, { recursive: true });
12
+ }
13
+ if (!fs.existsSync(`${state.config.paths.build}/bridge`)) {
14
+ fs.mkdirSync(`${state.config.paths.build}/bridge`, { recursive: true });
15
+ }
16
+ const interfaceFile = createInterfaceFile(interfaceFilePath);
17
+ return createBridgeFileFromInterfaceFile(interfaceFile);
18
+ }
19
+
20
+ function createInterfaceFile(headerOrModuleFilePath) {
21
+ if (!headerOrModuleFilePath) {
22
+ return null;
23
+ }
24
+ const fileHash = getFileHash(headerOrModuleFilePath);
25
+ if (state.cache.hashes[headerOrModuleFilePath] === fileHash) {
26
+ return state.cache.interfaces[headerOrModuleFilePath];
27
+ }
28
+
29
+ const moduleRegex = new RegExp(`.(${state.config.ext.module.join('|')})$`);
30
+ const isModule = moduleRegex.test(headerOrModuleFilePath);
31
+ if (isModule) {
32
+ const newPath = `${state.config.paths.build}/interface/${headerOrModuleFilePath.split('/').pop()}`;
33
+ fs.copyFileSync(headerOrModuleFilePath, newPath);
34
+ state.cache.interfaces[headerOrModuleFilePath] = newPath;
35
+ state.cache.hashes[headerOrModuleFilePath] = fileHash;
36
+ saveCache();
37
+ return newPath;
38
+ }
39
+
40
+ const headerPaths = (state.config.dependencyParameters?.pathsOfCmakeDepends?.split(';') || [])
41
+ .filter((d) => d.startsWith(state.config.paths.base));
42
+
43
+ const temp2 = headerPaths
44
+ .map((p) => headerOrModuleFilePath.match(new RegExp(`^${p}/.*?/include/(.*?)$`, 'i')))
45
+ .filter((p) => p && p.length === 2);
46
+
47
+ const temp = headerOrModuleFilePath.match(/^(.*)\..+?$/);
48
+ if (temp.length < 2) return null;
49
+
50
+ const filePathWithoutExt = temp[1];
51
+ const interfaceFile = `${filePathWithoutExt}.i`;
52
+
53
+ if (fs.existsSync(interfaceFile)) {
54
+ const newPath = `${state.config.paths.build}/interface/${interfaceFile.split('/').at(-1)}`;
55
+ fs.copyFileSync(interfaceFile, newPath);
56
+ state.cache.interfaces[headerOrModuleFilePath] = newPath;
57
+ state.cache.hashes[headerOrModuleFilePath] = fileHash;
58
+ saveCache();
59
+ return newPath;
60
+ }
61
+
62
+ const fileName = filePathWithoutExt.split('/').at(-1);
63
+
64
+ let headerPath = state.config.paths.header.find((path) => headerOrModuleFilePath.startsWith(path));
65
+ if (headerPath) headerPath = headerOrModuleFilePath.substr(headerPath.length + 1);
66
+ else if (temp2 && temp2.length > 0) headerPath = temp2[0][1];
67
+ else headerPath = headerOrModuleFilePath.split('/').at(-1);
68
+
69
+ const content = `#ifndef _${fileName.toUpperCase()}_I
70
+ #define _${fileName.toUpperCase()}_I
71
+
72
+ %module ${fileName.toUpperCase()}
73
+
74
+ %{
75
+ #include "${headerPath}"
76
+ %}
77
+
78
+ %feature("shared_ptr");
79
+ %feature("polymorphic_shared_ptr");
80
+
81
+ %include "${headerPath}"
82
+
83
+ #endif
84
+ `;
85
+ const outputFilePath = `${state.config.paths.build}/interface/${fileName}.i`;
86
+ fs.writeFileSync(outputFilePath, content);
87
+
88
+ state.cache.interfaces[headerOrModuleFilePath] = outputFilePath;
89
+ state.cache.hashes[headerOrModuleFilePath] = fileHash;
90
+ saveCache();
91
+
92
+ return outputFilePath;
93
+ }
94
+
95
+ function createBridgeFileFromInterfaceFile(interfaceFilePath) {
96
+ if (!interfaceFilePath) {
97
+ return null;
98
+ }
99
+
100
+ const fileHash = getFileHash(interfaceFilePath);
101
+ if (state.cache.hashes[interfaceFilePath] === fileHash) {
102
+ return state.cache.bridges[interfaceFilePath];
103
+ }
104
+
105
+ const allHeaders = state.config.dependencyParameters.headerPathWithDepends.split(';');
106
+
107
+ let includePath = [
108
+ ...state.config.allDependencies.map((d) => `${d.paths.output}/prebuilt/Emscripten-x86_64/include`),
109
+ ...state.config.allDependencies.map((d) => `${d.paths.output}/prebuilt/Emscripten-x86_64/swig`),
110
+ ...state.config.paths.header,
111
+ ...allHeaders,
112
+ ].filter((path) => !!path.toString()).map((path) => `-I${path}`);
113
+ includePath = [...new Set(includePath)];
114
+
115
+ run('swig', [
116
+ '-c++',
117
+ '-embind',
118
+ '-o', `${state.config.paths.build}/bridge/${interfaceFilePath.split('/').at(-1)}.cpp`,
119
+ ...includePath,
120
+ interfaceFilePath,
121
+ ]);
122
+
123
+ state.cache.bridges[interfaceFilePath] = `${state.config.paths.build}/bridge/${interfaceFilePath.split('/').at(-1)}.cpp`;
124
+ state.cache.hashes[interfaceFilePath] = fileHash;
125
+ saveCache();
126
+
127
+ return state.cache.bridges[interfaceFilePath];
128
+ }
@@ -0,0 +1,104 @@
1
+ import os from 'node:os';
2
+ import fs from 'node:fs';
3
+ import replace from 'replace';
4
+ import run from './run.js';
5
+ import getCmakeParameters from './getCmakeParameters.js';
6
+ import state from '../state/index.js';
7
+
8
+ const cpuCount = os.cpus().length - 1;
9
+ const sharedPlatforms = ['Android'];
10
+
11
+ export default function createLib(platform, fileType, options = {}) {
12
+ if (!platform || !options || typeof options !== 'object' || Array.isArray(options)) {
13
+ throw new Error('invalid platform or options');
14
+ }
15
+
16
+ const basePlatform = platform.split('-', 1)[0];
17
+ if (basePlatform === 'iOS' && process.platform !== 'darwin') {
18
+ return;
19
+ }
20
+
21
+ const buildType = options.isProd ? 'Release' : 'Debug';
22
+ const platformPrefix = `${fileType ? `${fileType}-` : ''}${buildType}`;
23
+ const libdir = `${state.config.paths.build}/${platformPrefix}/prebuilt/${platform}`;
24
+ const buildPath = `${state.config.paths.build}/${platformPrefix}/${platform}`;
25
+
26
+ const buildEnv = { params: [] };
27
+ let buildParams;
28
+ const depPaths = state.config.allDependencyPaths[platform];
29
+ if (state.config.build) {
30
+ const { getBuildParams, getExtraLibs } = state.config.build;
31
+ buildEnv.console = true;
32
+ const ext = sharedPlatforms.includes(basePlatform) ? 'so' : 'a';
33
+ buildParams = getBuildParams ? getBuildParams(platform, depPaths, ext, buildPath) : [];
34
+ if (state.config.build?.buildType !== 'configure') {
35
+ const cmakeBuildType = sharedPlatforms.includes(basePlatform) ? 'SHARED' : 'STATIC';
36
+ buildParams.push(`-DCMAKE_PREFIX_PATH=${libdir}`, `-DCMAKE_FIND_ROOT_PATH=${libdir}`, `-DBUILD_TYPE=${cmakeBuildType}`);
37
+ }
38
+
39
+ const cFlags = Object.values(depPaths).map((d) => `-I${d.header}`).join(' ');
40
+ const ldFlags = Object.values(depPaths).map((d) => `-L${d.libPath}`).join(' ');
41
+ let dependLibs = '';
42
+ if (state.config.build?.buildType === 'configure') {
43
+ dependLibs = Object.keys(depPaths).map((d) => `-l${d}`).join(' ');
44
+ }
45
+
46
+ const extraLibs = getExtraLibs ? getExtraLibs(platform) : [];
47
+
48
+ buildEnv.params.push('-e', `CFLAGS=${cFlags}`);
49
+ buildEnv.params.push('-e', `CPPFLAGS=${cFlags}`);
50
+ buildEnv.params.push('-e', `LDFLAGS=${ldFlags} ${extraLibs.join(' ')}`);
51
+ // buildEnv.params.push('-e', `LIBS=${dependLibs} ${extraLibs.join(' ')}`);
52
+
53
+ state.config.build.env?.forEach((e) => {
54
+ buildEnv.params.push('-e', e);
55
+ });
56
+ } else {
57
+ buildParams = getCmakeParameters(platform, options);
58
+ }
59
+
60
+ console.log(`${platform} is compiling...`);
61
+ const t0 = performance.now();
62
+ const cmakeDir = state.config.build ? `${state.config.paths.build}/source` : state.config.paths.cmakeDir;
63
+
64
+ if (state.config.build?.beforeRun) {
65
+ const dataList = state.config.build?.beforeRun(cmakeDir);
66
+ dataList.forEach((data) => {
67
+ run(data.program, data.parameters || [], platformPrefix, platform, buildEnv);
68
+ });
69
+ }
70
+
71
+ if (!options.bypassCmake) {
72
+ if (state.config.build?.buildType === 'configure') {
73
+ fs.cpSync(cmakeDir, buildPath, { recursive: true });
74
+ if (state.config.build?.sourceReplaceList) {
75
+ state.config.build.sourceReplaceList(platform, depPaths)?.forEach(({ regex, replacement, paths }) => {
76
+ replace({
77
+ regex, replacement, paths: paths.map((p) => `${buildPath}/${p}`), recursive: false, silent: true,
78
+ });
79
+ });
80
+ }
81
+ run(null, [
82
+ './configure',
83
+ ...buildParams,
84
+ `--prefix=${libdir}`,
85
+ ], platformPrefix, platform, buildEnv);
86
+ } else {
87
+ run(null, [
88
+ basePlatform === 'iOS' && state.config.build?.useIOSCMake ? 'ios-cmake' : 'cmake', cmakeDir,
89
+ `-DCMAKE_BUILD_TYPE=${buildType}`,
90
+ `-DCMAKE_INSTALL_PREFIX=${libdir}`,
91
+ ...buildParams,
92
+ ], platformPrefix, platform, buildEnv);
93
+ }
94
+ }
95
+ const t1 = performance.now();
96
+ if (basePlatform === 'iOS' && state.config.build?.buildType !== 'configure') {
97
+ const iOSCMakeBuilder = state.config.build?.useIOSCMake ? 'ios-cmake' : 'cmake';
98
+ run(null, [iOSCMakeBuilder, '--build', '.', '-j', cpuCount, '--config', buildType, '--target', 'install'], platformPrefix, platform, { console: buildEnv.console });
99
+ } else {
100
+ run(null, ['make', `-j${cpuCount}`, 'install'], platformPrefix, platform, { console: buildEnv.console });
101
+ }
102
+ const t2 = performance.now();
103
+ console.log(`${platform} compiled`, platformPrefix, `full time: ${Math.round(t2 - t0)}ms`, `cmake: ${Math.round(t1 - t0)}ms`, `build: ${Math.round(t2 - t1)}ms`, `core: ${cpuCount}`);
104
+ }
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs';
2
+ import upath from 'upath';
3
+ import { execFileSync } from 'node:child_process';
4
+ import state from '../state/index.js';
5
+
6
+ export default function createXCFramework(overrideConfig = null) {
7
+ if (process.platform !== 'darwin') {
8
+ return;
9
+ }
10
+
11
+ const output = overrideConfig?.paths?.output || state.config.paths.output;
12
+ const projectPath = overrideConfig?.paths?.project || state.config.paths.project;
13
+ const libName = overrideConfig?.export?.libName || state.config.export.libName;
14
+
15
+ if (
16
+ !fs.existsSync(`${output}/prebuilt/iOS-iphoneos/lib`)
17
+ || !fs.existsSync(`${output}/prebuilt/iOS-iphonesimulator/lib`)
18
+ ) return;
19
+
20
+ const options = {
21
+ cwd: projectPath,
22
+ stdio: 'inherit',
23
+ };
24
+
25
+ const relativeOutput = upath.relative(projectPath, output);
26
+
27
+ libName.forEach((fileName) => {
28
+ if (!fs.existsSync(`${projectPath}/${fileName}.xcframework`)) {
29
+ const params = [
30
+ '-create-xcframework',
31
+ '-library', `${relativeOutput}/prebuilt/iOS-iphoneos/lib/lib${fileName}.a`,
32
+ '-headers', `${relativeOutput}/prebuilt/iOS-iphoneos/include`,
33
+ '-library', `${relativeOutput}/prebuilt/iOS-iphonesimulator/lib/lib${fileName}.a`,
34
+ '-headers', `${relativeOutput}/prebuilt/iOS-iphonesimulator/include`,
35
+ '-output', `${fileName}.xcframework`,
36
+ ];
37
+ execFileSync('xcodebuild', params, options);
38
+ }
39
+
40
+ if (!fs.existsSync(`${output}/prebuilt/${fileName}.xcframework.zip`)) {
41
+ execFileSync('zip', ['-y', '-r', `${relativeOutput}/prebuilt/${fileName}.xcframework.zip`, `${fileName}.xcframework`], options);
42
+ }
43
+ });
44
+ }
@@ -0,0 +1,8 @@
1
+ import { glob } from 'glob';
2
+ import state from '../state/index.js';
3
+
4
+ export default function getAllBridges() {
5
+ return [
6
+ ...glob.sync(`${state.config.paths.build}/bridge/*.i.cpp`, { absolute: true }),
7
+ ];
8
+ }
@@ -0,0 +1,56 @@
1
+ import state from '../state/index.js';
2
+
3
+ export default function getCmakeParameters(platform, options = {}) {
4
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
5
+ throw new Error('invalid options');
6
+ }
7
+
8
+ const params = [];
9
+ // if (isBuildBridge) params.push('-DBUILD_BRIDGE=TRUE');
10
+
11
+ const dependParams = state.config.dependencyParameters;
12
+ const externalNativeGlob = [
13
+ ...(options.nativeGlob || []),
14
+ ];
15
+ const externalBridgeGlob = [
16
+ `${state.config.paths.cli}/assets/commonBridges.cpp`,
17
+ ...(options.bridgeGlob || []),
18
+ ];
19
+ const nativeGlob = [
20
+ `${state.config.paths.cli}/assets/cppjsEmptySource.cpp`,
21
+ ...(dependParams.nativeGlob || []),
22
+ ];
23
+ const headerGlob = [
24
+ ...(dependParams.headerGlob || []),
25
+ ...(options.headerGlob || []),
26
+ ];
27
+
28
+ const headerDirs = [
29
+ dependParams.headerPathWithDepends,
30
+ ...(options.headerDirs || []),
31
+ ];
32
+
33
+ const basePlatform = platform.split('-', 1)[0];
34
+ const sharedPlatforms = ['Android'];
35
+ const buildType = sharedPlatforms.includes(basePlatform) ? 'SHARED' : 'STATIC';
36
+ const otherBuildType = buildType === 'STATIC' ? 'SHARED' : 'STATIC';
37
+
38
+ params.push(...[
39
+ `-DPROJECT_NAME=${options.name || state.config.general.name}`,
40
+ `-DBASE_DIR=${state.config.paths.project}`,
41
+ `-DEXTERNAL_NATIVE_GLOB=${externalNativeGlob.join(';')}`,
42
+ `-DEXTERNAL_BRIDGE_GLOB=${externalBridgeGlob.join(';')}`,
43
+ `-DNATIVE_GLOB=${nativeGlob.join(';')}`,
44
+ `-DHEADER_GLOB=${headerGlob.join(';')}`,
45
+ `-DHEADER_DIR=${headerDirs.join(';')}`,
46
+ `-DDEPENDS_CMAKE_PATHS=${dependParams.pathsOfCmakeDepends}`,
47
+ `-DDEPENDS_CMAKE_NAMES=${dependParams.nameOfCmakeDepends}`,
48
+ `-DBRIDGE_DIR=${state.config.paths.build}/bridge`,
49
+ `-DBUILD_TYPE=${buildType}`,
50
+ `-DBUILD_${otherBuildType}_LIBS=OFF`,
51
+ // '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache',
52
+ ...(options.buildSource !== false ? ['-DBUILD_SOURCE=TRUE'] : []),
53
+ ]);
54
+
55
+ return params;
56
+ }
@@ -0,0 +1,28 @@
1
+ /* eslint-disable default-param-last */
2
+ /* eslint-disable no-param-reassign */
3
+ import state from '../state/index.js';
4
+
5
+ function getRecursiveData(obj, config, dependency, field, platform, subPlatform) {
6
+ const platformName = subPlatform ? `${platform}-${subPlatform}` : platform;
7
+ if (dependency?.platform?.[platformName]?.[field]) {
8
+ Object.entries(dependency.platform[platformName][field]).forEach(([dKey, value]) => {
9
+ if (field === 'data') {
10
+ const key = `${dependency.paths.project}/dist/prebuilt/${platform}/${dKey}`;
11
+ obj[key] = value;
12
+ } else {
13
+ obj[dKey] = value;
14
+ }
15
+ });
16
+ }
17
+
18
+ dependency.dependencies.forEach((dep) => {
19
+ getRecursiveData(obj, config, dep, field, platform, subPlatform);
20
+ });
21
+ }
22
+
23
+ export default function getData(field, platform = 'Emscripten-x86_64', subPlatform) {
24
+ const output = {};
25
+ getRecursiveData(output, state.config, state.config, field, platform, subPlatform);
26
+
27
+ return output;
28
+ }
@@ -0,0 +1,19 @@
1
+ import { glob } from 'glob';
2
+ import state from '../state/index.js';
3
+
4
+ export default function getDependLibs() {
5
+ let dependLibs = [
6
+ ...glob.sync(`${state.config.paths.build}/Source-Release/Emscripten-x86_64/dependencies/**/*.a`, { absolute: true, cwd: state.config.paths.project }),
7
+ ];
8
+ state.config.dependencyParameters.cmakeDepends.forEach((d) => {
9
+ if (d.export.libName) {
10
+ d.export.libName.forEach((fileName) => {
11
+ if (d.platform['Emscripten-x86_64'].ignoreLibName?.includes(fileName)) return;
12
+ dependLibs.push(...glob.sync(`${d.paths.output}/prebuilt/Emscripten-x86_64/lib/lib${fileName}.a`, { absolute: true, cwd: d.paths.project }));
13
+ });
14
+ }
15
+ });
16
+
17
+ dependLibs = [...new Set(dependLibs)];
18
+ return dependLibs;
19
+ }