Skip to content

Commit 15e6b8b

Browse files
committed
Generate CMakeLists.txt with frameworks enabled
1 parent 84e12d4 commit 15e6b8b

File tree

4 files changed

+102
-18
lines changed

4 files changed

+102
-18
lines changed

packages/gyp-to-cmake/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
},
2424
"dependencies": {
2525
"@react-native-node-api/cli-utils": "0.1.0",
26-
"gyp-parser": "^1.0.4"
26+
"gyp-parser": "^1.0.4",
27+
"pkg-dir": "^8.0.0",
28+
"read-pkg": "^9.0.1"
2729
}
2830
}

packages/gyp-to-cmake/src/cli.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import assert from "node:assert/strict";
12
import fs from "node:fs";
23
import path from "node:path";
4+
import { packageDirectorySync } from "pkg-dir";
5+
import { readPackageSync } from "read-pkg";
6+
37
import {
48
Command,
9+
Option,
510
prettyPath,
611
wrapAction,
712
} from "@react-native-node-api/cli-utils";
@@ -12,23 +17,22 @@ import {
1217
type GypToCmakeListsOptions,
1318
} from "./transformer.js";
1419

15-
export type TransformOptions = Omit<
16-
GypToCmakeListsOptions,
17-
"gyp" | "projectName"
18-
> & {
20+
export type TransformOptions = Omit<GypToCmakeListsOptions, "gyp"> & {
1921
disallowUnknownProperties: boolean;
20-
projectName?: string;
2122
};
2223

2324
export function generateProjectName(gypPath: string) {
24-
return path.dirname(gypPath).replaceAll(path.sep, "-");
25+
const packagePath = packageDirectorySync({ cwd: path.dirname(gypPath) });
26+
assert(packagePath, "Expected the binding.gyp file to be inside a package");
27+
const packageJson = readPackageSync({ cwd: packagePath });
28+
return packageJson.name;
2529
}
2630

2731
export function transformBindingGypFile(
2832
gypPath: string,
2933
{
3034
disallowUnknownProperties,
31-
projectName = generateProjectName(gypPath),
35+
projectName,
3236
...restOfOptions
3337
}: TransformOptions,
3438
) {
@@ -49,19 +53,27 @@ export function transformBindingGypFile(
4953

5054
export function transformBindingGypsRecursively(
5155
directoryPath: string,
52-
options: TransformOptions,
56+
options: Omit<TransformOptions, "projectName">,
5357
) {
5458
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
5559
for (const entry of entries) {
5660
const fullPath = path.join(directoryPath, entry.name);
5761
if (entry.isDirectory()) {
5862
transformBindingGypsRecursively(fullPath, options);
5963
} else if (entry.isFile() && entry.name === "binding.gyp") {
60-
transformBindingGypFile(fullPath, options);
64+
transformBindingGypFile(fullPath, {
65+
...options,
66+
projectName: generateProjectName(fullPath),
67+
});
6168
}
6269
}
6370
}
6471

72+
const projectNameOption = new Option(
73+
"--project-name <name>",
74+
"Project name to use in CMakeLists.txt",
75+
).default(undefined, "Uses name from the surrounding package.json");
76+
6577
export const program = new Command("gyp-to-cmake")
6678
.description("Transform binding.gyp to CMakeLists.txt")
6779
.option(
@@ -70,7 +82,12 @@ export const program = new Command("gyp-to-cmake")
7082
)
7183
.option("--weak-node-api", "Link against the weak-node-api library", false)
7284
.option("--define-napi-version", "Define NAPI_VERSION for all targets", false)
85+
.option(
86+
"--no-apple-framework",
87+
"Disable emitting target properties to produce Apple frameworks",
88+
)
7389
.option("--cpp <version>", "C++ standard version", "17")
90+
.addOption(projectNameOption)
7491
.argument(
7592
"[path]",
7693
"Path to the binding.gyp file or directory to traverse recursively",
@@ -80,19 +97,30 @@ export const program = new Command("gyp-to-cmake")
8097
wrapAction(
8198
(
8299
targetPath: string,
83-
{ pathTransforms, cpp, defineNapiVersion, weakNodeApi },
100+
{
101+
pathTransforms,
102+
cpp,
103+
defineNapiVersion,
104+
weakNodeApi,
105+
appleFramework,
106+
projectName,
107+
},
84108
) => {
85-
const options: TransformOptions = {
109+
const options: Omit<TransformOptions, "projectName"> = {
86110
unsupportedBehaviour: "throw",
87111
disallowUnknownProperties: false,
88112
transformWinPathsToPosix: pathTransforms,
89113
compileFeatures: cpp ? [`cxx_std_${cpp}`] : [],
90114
defineNapiVersion,
91115
weakNodeApi,
116+
appleFramework,
92117
};
93118
const stat = fs.statSync(targetPath);
94119
if (stat.isFile()) {
95-
transformBindingGypFile(targetPath, options);
120+
transformBindingGypFile(targetPath, {
121+
...options,
122+
projectName: projectName ?? generateProjectName(targetPath),
123+
});
96124
} else if (stat.isDirectory()) {
97125
transformBindingGypsRecursively(targetPath, options);
98126
} else {

packages/gyp-to-cmake/src/transformer.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type GypToCmakeListsOptions = {
1515
compileFeatures?: string[];
1616
defineNapiVersion?: boolean;
1717
weakNodeApi?: boolean;
18+
appleFramework?: boolean;
1819
};
1920

2021
function isCmdExpansion(value: string) {
@@ -26,6 +27,10 @@ function escapeSpaces(source: string) {
2627
return source.replace(/ /g, "\\ ");
2728
}
2829

30+
function escapeBundleIdentifier(identifier: string) {
31+
return identifier.replace(/[^A-Za-z0-9.-]/g, "-");
32+
}
33+
2934
/**
3035
* @see {@link https://github.com/cmake-js/cmake-js?tab=readme-ov-file#usage} for details on the template used
3136
* @returns The contents of a CMakeLists.txt file
@@ -39,6 +44,7 @@ export function bindingGypToCmakeLists({
3944
transformWinPathsToPosix = true,
4045
defineNapiVersion = true,
4146
weakNodeApi = false,
47+
appleFramework = true,
4248
compileFeatures = [],
4349
}: GypToCmakeListsOptions): string {
4450
function mapExpansion(value: string): string[] {
@@ -113,10 +119,58 @@ export function bindingGypToCmakeLists({
113119
escapedIncludes.push("${CMAKE_JS_INC}");
114120
}
115121

116-
lines.push(
117-
`add_library(${targetName} SHARED ${escapedSources.join(" ")})`,
118-
`set_target_properties(${targetName} PROPERTIES PREFIX "" SUFFIX ".node")`,
119-
);
122+
function setTargetPropertiesLines(
123+
properties: Record<string, string>,
124+
indent = "",
125+
): string[] {
126+
return [
127+
`${indent}set_target_properties(${targetName} PROPERTIES`,
128+
...Object.entries(properties).map(
129+
([key, value]) => `${indent} ${key} ${value}`,
130+
),
131+
`${indent} )`,
132+
];
133+
}
134+
135+
lines.push(`add_library(${targetName} SHARED ${escapedSources.join(" ")})`);
136+
137+
if (appleFramework) {
138+
lines.push(
139+
"",
140+
'option(BUILD_APPLE_FRAMEWORK "Wrap addon in an Apple framework" ON)',
141+
"",
142+
"if(APPLE AND BUILD_APPLE_FRAMEWORK)",
143+
...setTargetPropertiesLines(
144+
{
145+
FRAMEWORK: "TRUE",
146+
MACOSX_FRAMEWORK_IDENTIFIER: escapeBundleIdentifier(
147+
`${projectName}.${targetName}`,
148+
),
149+
MACOSX_FRAMEWORK_SHORT_VERSION_STRING: "1.0",
150+
MACOSX_FRAMEWORK_BUNDLE_VERSION: "1.0",
151+
XCODE_ATTRIBUTE_SKIP_INSTALL: "NO",
152+
},
153+
" ",
154+
),
155+
"elseif(APPLE)",
156+
...setTargetPropertiesLines(
157+
{
158+
PREFIX: '""',
159+
SUFFIX: ".node",
160+
},
161+
" ",
162+
),
163+
"endif()",
164+
"",
165+
);
166+
} else {
167+
lines.push(
168+
...setTargetPropertiesLines({
169+
PREFIX: "",
170+
SUFFIX: ".node",
171+
}),
172+
);
173+
}
120174

121175
if (libraries.length > 0) {
122176
lines.push(

packages/node-addon-examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"scripts": {
1313
"copy-examples": "tsx scripts/copy-examples.mts",
14-
"gyp-to-cmake": "gyp-to-cmake --weak-node-api --apple-framework .",
14+
"gyp-to-cmake": "gyp-to-cmake --weak-node-api .",
1515
"build": "tsx scripts/build-examples.mts",
1616
"copy-and-build": "node --run copy-examples && node --run gyp-to-cmake && node --run build",
1717
"verify": "tsx scripts/verify-prebuilds.mts",

0 commit comments

Comments
 (0)