Skip to content

Commit 577bb2c

Browse files
committed
Generate CMakeLists.txt with frameworks enabled
1 parent ad66739 commit 577bb2c

File tree

3 files changed

+101
-17
lines changed

3 files changed

+101
-17
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,5 +1,9 @@
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,
59
prettyPath,
@@ -13,23 +17,22 @@ import {
1317
type GypToCmakeListsOptions,
1418
} from "./transformer.js";
1519

16-
export type TransformOptions = Omit<
17-
GypToCmakeListsOptions,
18-
"gyp" | "projectName"
19-
> & {
20+
export type TransformOptions = Omit<GypToCmakeListsOptions, "gyp"> & {
2021
disallowUnknownProperties: boolean;
21-
projectName?: string;
2222
};
2323

2424
export function generateProjectName(gypPath: string) {
25-
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;
2629
}
2730

2831
export function transformBindingGypFile(
2932
gypPath: string,
3033
{
3134
disallowUnknownProperties,
32-
projectName = generateProjectName(gypPath),
35+
projectName,
3336
...restOfOptions
3437
}: TransformOptions,
3538
) {
@@ -50,19 +53,27 @@ export function transformBindingGypFile(
5053

5154
export function transformBindingGypsRecursively(
5255
directoryPath: string,
53-
options: TransformOptions,
56+
options: Omit<TransformOptions, "projectName">,
5457
) {
5558
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
5659
for (const entry of entries) {
5760
const fullPath = path.join(directoryPath, entry.name);
5861
if (entry.isDirectory()) {
5962
transformBindingGypsRecursively(fullPath, options);
6063
} else if (entry.isFile() && entry.name === "binding.gyp") {
61-
transformBindingGypFile(fullPath, options);
64+
transformBindingGypFile(fullPath, {
65+
...options,
66+
projectName: generateProjectName(fullPath),
67+
});
6268
}
6369
}
6470
}
6571

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+
6677
export const program = new Command("gyp-to-cmake")
6778
.description("Transform binding.gyp to CMakeLists.txt")
6879
.option(
@@ -71,7 +82,13 @@ export const program = new Command("gyp-to-cmake")
7182
)
7283
.option("--weak-node-api", "Link against the weak-node-api library", false)
7384
.option("--define-napi-version", "Define NAPI_VERSION for all targets", false)
85+
.option(
86+
"--apple-framework",
87+
"Set target properties to produce Apple frameworks",
88+
true,
89+
)
7490
.option("--cpp <version>", "C++ standard version", "17")
91+
.addOption(projectNameOption)
7592
.argument(
7693
"[path]",
7794
"Path to the binding.gyp file or directory to traverse recursively",
@@ -81,19 +98,30 @@ export const program = new Command("gyp-to-cmake")
8198
wrapAction(
8299
(
83100
targetPath: string,
84-
{ pathTransforms, cpp, defineNapiVersion, weakNodeApi },
101+
{
102+
pathTransforms,
103+
cpp,
104+
defineNapiVersion,
105+
weakNodeApi,
106+
appleFramework,
107+
projectName,
108+
},
85109
) => {
86-
const options: TransformOptions = {
110+
const options: Omit<TransformOptions, "projectName"> = {
87111
unsupportedBehaviour: "throw",
88112
disallowUnknownProperties: false,
89113
transformWinPathsToPosix: pathTransforms,
90114
compileFeatures: cpp ? [`cxx_std_${cpp}`] : [],
91115
defineNapiVersion,
92116
weakNodeApi,
117+
appleFramework,
93118
};
94119
const stat = fs.statSync(targetPath);
95120
if (stat.isFile()) {
96-
transformBindingGypFile(targetPath, options);
121+
transformBindingGypFile(targetPath, {
122+
...options,
123+
projectName: projectName ?? generateProjectName(targetPath),
124+
});
97125
} else if (stat.isDirectory()) {
98126
transformBindingGypsRecursively(targetPath, options);
99127
} 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(

0 commit comments

Comments
 (0)