Skip to content

Commit 6588c5c

Browse files
authored
feat: added possibility to start module with C++ code enabled (#30)
1 parent 87f5a39 commit 6588c5c

File tree

15 files changed

+151
-11
lines changed

15 files changed

+151
-11
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ If you want to create your own React Native module, scaffolding the project can
1414
- [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) and [Husky](https://github.com/typicode/husky) pre-configured
1515
- Bob pre-configured to compile your files
1616
- CircleCI pre-configured to run tests on the CI
17+
- Use C++ code to boost performance of your modules
1718

1819
<img src="assets/bob-create.gif" width="480px" height="auto">
1920

@@ -26,6 +27,7 @@ Bob can build code for following targets:
2627
- Flow definitions (copies .js files to .flow files)
2728
- TypeScript definitions (uses `tsc` to generate declaration files)
2829
- Android AAR files
30+
- C++ modules
2931

3032
## Why
3133

src/create.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import pack from '../package.json';
1212

1313
const TEMPLATE = path.resolve(__dirname, '../templates/library');
1414
const BINARIES = /(gradlew|\.(jar|keystore|png|jpg|gif))$/;
15+
const CPP_FILES = path.resolve(__dirname, '../templates/cppLibrary');
1516

1617
export default async function create(argv: yargs.Arguments<any>) {
1718
const folder = path.join(process.cwd(), argv.name);
@@ -51,6 +52,7 @@ export default async function create(argv: yargs.Arguments<any>) {
5152
authorEmail,
5253
authorUrl,
5354
githubUrl: repo,
55+
useCpp,
5456
} = (await inquirer.prompt([
5557
{
5658
type: 'input',
@@ -118,13 +120,20 @@ export default async function create(argv: yargs.Arguments<any>) {
118120
},
119121
validate: input => /^https?:\/\//.test(input) || 'Must be a valid URL',
120122
},
123+
{
124+
type: 'confirm',
125+
name: 'useCpp',
126+
message: 'Does your library use C++ code?',
127+
default: false,
128+
},
121129
])) as {
122130
slug: string;
123131
description: string;
124132
authorName: string;
125133
authorEmail: string;
126134
authorUrl: string;
127135
githubUrl: string;
136+
useCpp: boolean;
128137
};
129138

130139
const project = slug.replace(/^(react-native-|@[^/]+\/)/, '');
@@ -143,6 +152,7 @@ export default async function create(argv: yargs.Arguments<any>) {
143152
.slice(1)}`,
144153
package: slug.replace(/[^a-z0-9]/g, '').toLowerCase(),
145154
podspec: slug.replace(/[^a-z0-9]+/g, '-').replace(/^-/, ''),
155+
useCpp,
146156
},
147157
author: {
148158
name: authorName,
@@ -176,6 +186,9 @@ export default async function create(argv: yargs.Arguments<any>) {
176186
};
177187

178188
await copyDir(TEMPLATE, folder);
189+
if (options.project.useCpp) {
190+
await copyDir(CPP_FILES, folder);
191+
}
179192

180193
try {
181194
await spawn.sync(
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cmake_minimum_required(VERSION 3.4.1)
2+
3+
set (CMAKE_VERBOSE_MAKEFILE ON)
4+
set (CMAKE_CXX_STANDARD 11)
5+
6+
add_library(cpp
7+
SHARED
8+
../cpp/example.cpp
9+
cpp-adapter.cpp
10+
)
11+
12+
# Specifies a path to native header files.
13+
include_directories(
14+
../cpp
15+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <jni.h>
2+
#include "example.h"
3+
4+
extern "C"
5+
JNIEXPORT jint JNICALL
6+
Java_com_<%= project.package %>_<%= project.name %>Module_nativeMultiply(JNIEnv *env, jclass type, jint a, jint b) {
7+
return example::multiply(a, b);
8+
}

templates/cppLibrary/cpp/example.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "example.h"
2+
3+
namespace example {
4+
int multiply(int a, int b) {
5+
return a * b;
6+
}
7+
}

templates/cppLibrary/cpp/example.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef EXAMPLE_H
2+
#define EXAMPLE_H
3+
4+
namespace example {
5+
int multiply(int a, int b);
6+
}
7+
8+
#endif /* EXAMPLE_H */

templates/library/<%= project.podspec %>.podspec

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ Pod::Spec.new do |s|
1313
s.platforms = { :ios => "9.0" }
1414
s.source = { :git => "<%= repo %>.git", :tag => "#{s.version}" }
1515

16-
s.source_files = "ios/**/*.{h,m}"
16+
s.source_files = "ios/**/*.{h,m,mm}"
1717

18+
<% if(project.useCpp==true){ %>
19+
s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{h,cpp}"
20+
<% } else{ %>
21+
s.source_files = "ios/**/*.{h,m,mm}"
22+
<% } %>
1823
s.dependency "React"
1924
end

templates/library/android/build.gradle

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,22 @@ android {
3333
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
3434
versionCode 1
3535
versionName "1.0"
36+
<%if (project.useCpp==true) {%>
37+
externalNativeBuild {
38+
cmake {
39+
cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
40+
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
41+
}
42+
}
43+
<%}%>
44+
}
45+
<%if (project.useCpp==true) {%>
46+
externalNativeBuild {
47+
cmake {
48+
path "CMakeLists.txt"
49+
}
3650
}
51+
<%}%>
3752
buildTypes {
3853
release {
3954
minifyEnabled false

templates/library/android/src/main/java/com/<%= project.package %>/<%= project.name %>Module.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,23 @@ class <%= project.name %>Module(reactContext: ReactApplicationContext) : ReactCo
1717
fun getDeviceName(promise: Promise) {
1818
promise.resolve(android.os.Build.MODEL)
1919
}
20+
<%if (project.useCpp==true) {%>
21+
22+
@ReactMethod
23+
fun multiply(a: Int, b: Int, promise: Promise) {
24+
promise.resolve(nativeMultiply(a, b));
25+
}
26+
27+
external fun nativeMultiply(a: Int, b: Int): Int;
28+
29+
companion object
30+
{
31+
32+
// Used to load the 'native-lib' library on application startup.
33+
init
34+
{
35+
System.loadLibrary("cpp")
36+
}
37+
}
38+
<%}%>
2039
}

templates/library/example/src/App.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,24 @@ import <%= project.name %> from '<%= project.slug %>';
44

55
export default function App() {
66
const [deviceName, setDeviceName] = React.useState('');
7+
<%if (project.useCpp==true) {%>
8+
const [value, setValue] = React.useState();
9+
<%}%>
10+
711

812
React.useEffect(() => {
913
<%= project.name %>.getDeviceName().then(setDeviceName);
14+
<%if (project.useCpp==true) {%>
15+
<%= project.name %>.multiply(2, 3).then(setValue);
16+
<%}%>
1017
}, []);
1118

1219
return (
1320
<View style={styles.container}>
1421
<Text>Device name: {deviceName}</Text>
22+
<%if (project.useCpp==true) {%>
23+
<Text>C++ mulitply value: {value}</Text>
24+
<%}%>
1525
</View>
1626
);
1727
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
#import <React/RCTBridgeModule.h>
22

3+
<%if (project.useCpp==true) {%>
4+
#ifdef __cplusplus
5+
6+
#import "example.h"
7+
8+
#endif
9+
<%}%>
10+
311
@interface <%= project.name %> : NSObject <RCTBridgeModule>
412

513
@end

templates/library/ios/<%= project.name %>.m renamed to templates/library/ios/<%= project.name %>.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@ @implementation <%= project.name %>
1515
resolve(deviceInfo.name);
1616
}
1717

18+
<%if (project.useCpp==true) {%>
19+
RCT_EXPORT_METHOD(multiply:(nonnull NSNumber*)a withB:(nonnull NSNumber*)b resolver:(RCTPromiseResolveBlock)resolve
20+
withReject:(RCTPromiseRejectBlock)reject)
21+
{
22+
long result = example::multiply([a longValue], [b longValue]);
23+
24+
resolve(@(result));
25+
}
26+
<%}%>
27+
1828
@end

templates/library/ios/<%= project.name %>.xcodeproj/project.pbxproj

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
B3E7B58A1CC2AC0600A0062D /* <%= project.name %>.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* <%= project.name %>.m */; };
10+
<%if (project.useCpp==true) {%>
11+
5E46D8CD2428F78900513E24 /* example.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E46D8CB2428F78900513E24 /* example.cpp */; };
12+
<%}%>
13+
5E555C0D2413F4C50049A1A2 /* <%= project.name %>.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* <%= project.name %>.mm */; };
1114
/* End PBXBuildFile section */
1215

1316
/* Begin PBXCopyFilesBuildPhase section */
@@ -24,8 +27,12 @@
2427

2528
/* Begin PBXFileReference section */
2629
134814201AA4EA6300B7C361 /* lib<%= project.name %>.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = lib<%= project.name %>.a; sourceTree = BUILT_PRODUCTS_DIR; };
30+
<%if (project.useCpp==true) {%>
31+
5E46D8CB2428F78900513E24 /* example.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = example.cpp; path = ../cpp/example.cpp; sourceTree = "<group>"; };
32+
5E46D8CC2428F78900513E24 /* example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = example.h; path = ../cpp/example.h; sourceTree = "<group>"; };
33+
<%}%>
2734
B3E7B5881CC2AC0600A0062D /* <%= project.name %>.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = <%= project.name %>.h; sourceTree = "<group>"; };
28-
B3E7B5891CC2AC0600A0062D /* <%= project.name %>.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = <%= project.name %>.m; sourceTree = "<group>"; };
35+
B3E7B5891CC2AC0600A0062D /* <%= project.name %>.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = <%= project.name %>.mm; sourceTree = "<group>"; };
2936
/* End PBXFileReference section */
3037

3138
/* Begin PBXFrameworksBuildPhase section */
@@ -50,8 +57,12 @@
5057
58B511D21A9E6C8500147676 = {
5158
isa = PBXGroup;
5259
children = (
60+
<%if (project.useCpp==true) {%>
61+
5E46D8CB2428F78900513E24 /* example.cpp */,
62+
5E46D8CC2428F78900513E24 /* example.h */,
63+
<%}%>
5364
B3E7B5881CC2AC0600A0062D /* <%= project.name %>.h */,
54-
B3E7B5891CC2AC0600A0062D /* <%= project.name %>.m */,
65+
B3E7B5891CC2AC0600A0062D /* <%= project.name %>.mm */,
5566
134814211AA4EA7D00B7C361 /* Products */,
5667
);
5768
sourceTree = "<group>";
@@ -95,6 +106,7 @@
95106
developmentRegion = English;
96107
hasScannedForEncodings = 0;
97108
knownRegions = (
109+
English,
98110
en,
99111
);
100112
mainGroup = 58B511D21A9E6C8500147676;
@@ -112,7 +124,10 @@
112124
isa = PBXSourcesBuildPhase;
113125
buildActionMask = 2147483647;
114126
files = (
115-
B3E7B58A1CC2AC0600A0062D /* <%= project.name %>.m in Sources */,
127+
<%if (project.useCpp==true) {%>
128+
5E46D8CD2428F78900513E24 /* example.cpp in Sources */,
129+
<%}%>
130+
5E555C0D2413F4C50049A1A2 /* <%= project.name %>.mm in Sources */,
116131
);
117132
runOnlyForDeploymentPostprocessing = 0;
118133
};
@@ -216,7 +231,7 @@
216231
isa = XCBuildConfiguration;
217232
buildSettings = {
218233
HEADER_SEARCH_PATHS = (
219-
"$(inherited)",
234+
"$(inherited)",
220235
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
221236
"$(SRCROOT)/../../../React/**",
222237
"$(SRCROOT)/../../react-native/React/**",

templates/library/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { NativeModules } from 'react-native';
22

33
type <%= project.name %>Type = {
44
getDeviceName(): Promise<string>;
5+
<%if (project.useCpp==true) {%>
6+
multiply(a: number, b: number): Promise<number>;
7+
<%}%>
58
};
69

710
const { <%= project.name %> } = NativeModules;

yarn.lock

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,13 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
22832283
dependencies:
22842284
ms "2.0.0"
22852285

2286+
debug@^3.2.6:
2287+
version "3.2.6"
2288+
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
2289+
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
2290+
dependencies:
2291+
ms "^2.1.1"
2292+
22862293
decamelize-keys@^1.0.0:
22872294
version "1.1.0"
22882295
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
@@ -5338,11 +5345,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
53385345
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
53395346
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
53405347

5341-
safe-buffer@~5.2.0:
5342-
version "5.2.0"
5343-
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
5344-
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
5345-
53465348
safe-regex@^1.1.0:
53475349
version "1.1.0"
53485350
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"

0 commit comments

Comments
 (0)