From 28eb6f6bae3fb6a1d9d6485d71cfe02e85d3f3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BA=A7n=20=C4=90=C3=ACnh=20Huy?= Date: Sun, 23 Aug 2026 14:06:37 +0700 Subject: [PATCH] feat(gyp-to-cmake): support cflags --- .changeset/gyp-cflags.md | 7 +++ packages/gyp-to-cmake/src/gyp.test.ts | 43 +++++++++++++++++ packages/gyp-to-cmake/src/gyp.ts | 16 ++++++- packages/gyp-to-cmake/src/transformer.test.ts | 46 +++++++++++++++++++ packages/gyp-to-cmake/src/transformer.ts | 11 ++++- 5 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 .changeset/gyp-cflags.md diff --git a/.changeset/gyp-cflags.md b/.changeset/gyp-cflags.md new file mode 100644 index 00000000..271a6841 --- /dev/null +++ b/.changeset/gyp-cflags.md @@ -0,0 +1,7 @@ +--- +"gyp-to-cmake": minor +--- + +Translate target-level `cflags` from `binding.gyp` into private CMake compile +options. The parser now validates that `cflags` is an array of strings, and the +generated options preserve command expansion and escaped spaces. diff --git a/packages/gyp-to-cmake/src/gyp.test.ts b/packages/gyp-to-cmake/src/gyp.test.ts index 0734e25c..e948937e 100644 --- a/packages/gyp-to-cmake/src/gyp.test.ts +++ b/packages/gyp-to-cmake/src/gyp.test.ts @@ -38,4 +38,47 @@ describe("gyp.assertRoot", () => { assertBinding(input); assert(Array.isArray(input.targets)); }); + + it("should accept target cflags", () => { + assert.doesNotThrow(() => { + assertBinding( + { + targets: [ + { + target_name: "addon", + sources: ["addon.cc"], + cflags: ["-fPIC", "-Wall"], + }, + ], + }, + true, + ); + }); + }); + + it("should reject malformed target cflags", () => { + assert.throws(() => { + assertBinding({ + targets: [ + { + target_name: "addon", + sources: ["addon.cc"], + cflags: "-fPIC", + }, + ], + }); + }, /Expected 'cflags' to be an array/); + + assert.throws(() => { + assertBinding({ + targets: [ + { + target_name: "addon", + sources: ["addon.cc"], + cflags: ["-fPIC", 42], + }, + ], + }); + }, /Expected all cflags to be strings/); + }); }); diff --git a/packages/gyp-to-cmake/src/gyp.ts b/packages/gyp-to-cmake/src/gyp.ts index c1d17341..37f650f2 100644 --- a/packages/gyp-to-cmake/src/gyp.ts +++ b/packages/gyp-to-cmake/src/gyp.ts @@ -8,6 +8,7 @@ export type GypTarget = { sources: string[]; include_dirs?: string[]; defines?: string[]; + cflags?: string[]; }; export type GypBinding = { @@ -49,8 +50,21 @@ export function assertTarget( "Expected all include_dirs to be strings", ); } + if ("cflags" in target) { + const { cflags } = target; + assert(Array.isArray(cflags), "Expected 'cflags' to be an array"); + assert( + cflags.every((flag) => typeof flag === "string"), + "Expected all cflags to be strings", + ); + } if (disallowUnknownProperties) { - assertNoExtraProperties(target, ["target_name", "sources", "include_dirs"]); + assertNoExtraProperties(target, [ + "target_name", + "sources", + "include_dirs", + "cflags", + ]); } } diff --git a/packages/gyp-to-cmake/src/transformer.test.ts b/packages/gyp-to-cmake/src/transformer.test.ts index a43a794a..2f20a50b 100644 --- a/packages/gyp-to-cmake/src/transformer.test.ts +++ b/packages/gyp-to-cmake/src/transformer.test.ts @@ -126,6 +126,50 @@ describe("bindingGypToCmakeLists", () => { }); }); + describe("cflags", () => { + it("should add cflags as target-specific compile options", () => { + const output = bindingGypToCmakeLists({ + projectName: "some-project", + gyp: { + targets: [ + { + target_name: "foo", + sources: ["foo.cc"], + cflags: ["-fPIC", "-Wall", "-DNAME=value with space"], + }, + ], + }, + }); + + assert( + output.includes( + "target_compile_options(foo PRIVATE -fPIC -Wall -DNAME=value\\ with\\ space)", + ), + `Expected output to include target_compile_options:\n${output}`, + ); + }); + + it("should expand cflags command output into compile options", () => { + const output = bindingGypToCmakeLists({ + projectName: "some-project", + gyp: { + targets: [ + { + target_name: "foo", + sources: ["foo.cc"], + cflags: [" { const gyp = { targets: [{ target_name: "addon", sources: ["addon.cc"] }], @@ -174,6 +218,7 @@ describe("bindingGypToCmakeLists", () => { sources: ["addon.cc"], include_dirs: ["include"], defines: ["FOO"], + cflags: ["-fPIC"], }, ], }, @@ -186,6 +231,7 @@ describe("bindingGypToCmakeLists", () => { "target_link_libraries(some-project-addon PRIVATE weak-node-api)", "target_include_directories(some-project-addon PRIVATE include)", "target_compile_definitions(some-project-addon PRIVATE FOO)", + "target_compile_options(some-project-addon PRIVATE -fPIC)", "target_compile_features(some-project-addon PRIVATE cxx_std_17)", ]) { assert( diff --git a/packages/gyp-to-cmake/src/transformer.ts b/packages/gyp-to-cmake/src/transformer.ts index de4c0773..d55643c8 100644 --- a/packages/gyp-to-cmake/src/transformer.ts +++ b/packages/gyp-to-cmake/src/transformer.ts @@ -95,10 +95,9 @@ export function bindingGypToCmakeLists({ } for (const target of gyp.targets) { - const { target_name: targetName, defines = [] } = target; + const { target_name: targetName, defines = [], cflags = [] } = target; // TODO: Handle "conditions" - // TODO: Handle "cflags" // TODO: Handle "ldflags" const escapedSources = target.sources @@ -116,6 +115,8 @@ export function bindingGypToCmakeLists({ .map(transformPath) .map(escapeSpaces); + const escapedCflags = cflags.flatMap(mapExpansion).map(escapeSpaces); + const libraries = []; if (weakNodeApi) { libraries.push("weak-node-api"); @@ -216,6 +217,12 @@ export function bindingGypToCmakeLists({ ); } + if (escapedCflags.length > 0) { + lines.push( + `target_compile_options(${actualTargetName} PRIVATE ${escapedCflags.join(" ")})`, + ); + } + if (compileFeatures.length > 0) { lines.push( `target_compile_features(${actualTargetName} PRIVATE ${compileFeatures.join(" ")})`,