Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions tools/gyp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## [0.22.3](https://github.com/nodejs/gyp-next/compare/v0.22.2...v0.22.3) (2026-09-29)


### Bug Fixes

* add Python 3.15 beta to the testing ([#344](https://github.com/nodejs/gyp-next/issues/344)) ([bb6522f](https://github.com/nodejs/gyp-next/commit/bb6522fbacc620da2cc382484d75c75ee35d780c))
* **ci:** add zizmor security linter for github actions ([#358](https://github.com/nodejs/gyp-next/issues/358)) ([3d4c42b](https://github.com/nodejs/gyp-next/commit/3d4c42b386582df54368c29fcd7c3121737451a2))
* **ci:** enable Python 3.15 release candidate on Windows ([#357](https://github.com/nodejs/gyp-next/issues/357)) ([e2080d2](https://github.com/nodejs/gyp-next/commit/e2080d25c31a0c1f08178a52802be7fd56848376))
* **ci:** test on npm@12 ([#359](https://github.com/nodejs/gyp-next/issues/359)) ([1729f6e](https://github.com/nodejs/gyp-next/commit/1729f6e3bf8071d95cda85129cdf4ebab0a877ef))
* **ci:** update ruff github aactio to v4.0.0 ([#348](https://github.com/nodejs/gyp-next/issues/348)) ([8d213cd](https://github.com/nodejs/gyp-next/commit/8d213cd35cec96e91dc98359ccd247404f71f27b))
* **cmake:** add a dummy source to libraries without sources ([#362](https://github.com/nodejs/gyp-next/issues/362)) ([391165e](https://github.com/nodejs/gyp-next/commit/391165e76f682a318216b8b5c6ca1de0ef5f8f24))
* **cmake:** require CMake 3.10 and quote custom command comments ([#361](https://github.com/nodejs/gyp-next/issues/361)) ([b418211](https://github.com/nodejs/gyp-next/commit/b418211c49705c0fb42177b666c140cf96d0149f))
* write pch cflags as a list instead of a map object ([#355](https://github.com/nodejs/gyp-next/issues/355)) ([fe8188c](https://github.com/nodejs/gyp-next/commit/fe8188c6af0cf53488960b0ec655b1dcc01a7c94))

## [0.22.2](https://github.com/nodejs/gyp-next/compare/v0.22.1...v0.22.2) (2026-04-22)


Expand Down
2 changes: 1 addition & 1 deletion tools/gyp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ When used as a command line utility, __gyp-next__ can also be installed with [pi
* `uv tool install gyp-next`
```
Installing to a new venv 'gyp-next'
installed package gyp-next 0.13.0, installed using Python 3.10.6
installed package gyp-next 0.13.0, installed using Python 3.14.7
These apps are now globally available
- gyp
done! ✨ 🌟 ✨
Expand Down
6 changes: 6 additions & 0 deletions tools/gyp/commitlint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'body-max-line-length': [0, 'always'],
},
};
33 changes: 14 additions & 19 deletions tools/gyp/pylib/gyp/generator/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,9 @@ def WriteActions(target_name, actions, extra_sources, extra_deps, path_to_gyp, o
output.write(path_to_gyp)
output.write("\n")

output.write(" COMMENT ")
if "message" in action:
output.write(action["message"])
else:
output.write(action_target_name)
output.write("\n")
output.write(' COMMENT "')
output.write(CMakeStringEscape(action.get("message", action_target_name)))
output.write('"\n')

output.write(" VERBATIM\n")
output.write(")\n")
Expand Down Expand Up @@ -415,12 +412,9 @@ def WriteRules(target_name, rules, extra_sources, extra_deps, path_to_gyp, outpu
output.write(path_to_gyp)
output.write("\n")

output.write(" COMMENT ")
if "message" in rule:
output.write(rule["message"])
else:
output.write(action_name)
output.write("\n")
output.write(' COMMENT "')
output.write(CMakeStringEscape(rule.get("message", action_name)))
output.write('"\n')

output.write(" VERBATIM\n")
output.write(")\n")
Expand Down Expand Up @@ -537,9 +531,9 @@ def __init__(self, ext, command):
output.write(path_to_gyp)
output.write("\n")

output.write("COMMENT Copying for ")
output.write(target_name)
output.write("\n")
output.write('COMMENT "Copying for ')
output.write(CMakeStringEscape(target_name))
output.write('"\n')

output.write("VERBATIM\n")
output.write(")\n")
Expand Down Expand Up @@ -779,8 +773,10 @@ def WriteTarget(
other_sources_name = cmake_target_name + "__other_srcs"
SetVariableList(output, other_sources_name, other_sources)

# CMake gets upset when executable targets provide no sources.
# CMake gets upset when executable or library targets provide no sources.
# http://www.cmake.org/pipermail/cmake/2010-July/038461.html
# Libraries without sources are used to aggregate dependencies; build an
# empty library for them, as the make generator does.
dummy_sources_name = None
has_sources = (
s_sources_name
Expand All @@ -789,7 +785,7 @@ def WriteTarget(
or linkable_sources_name
or other_sources_name
)
if target_type == "executable" and not has_sources:
if target_type != "none" and not has_sources:
dummy_sources_name = cmake_target_name + "__dummy_srcs"
SetVariable(
output, dummy_sources_name, "${obj}.${TOOLSET}/${TARGET}/genc/dummy.c"
Expand Down Expand Up @@ -1160,8 +1156,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, config_to_u
gyp.common.EnsureDirExists(output_file)

output = open(output_file, "w")
output.write("cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)\n")
output.write("cmake_policy(VERSION 2.8.8)\n")
output.write("cmake_minimum_required(VERSION 3.10)\n")

gyp_file, project_target, _ = gyp.common.ParseQualifiedTarget(target_list[-1])
output.write("project(")
Expand Down
105 changes: 105 additions & 0 deletions tools/gyp/pylib/gyp/generator/cmake_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3

"""Unit tests for the cmake.py file."""

import io
import types
import unittest

from gyp.generator import cmake


class TestCustomCommandComment(unittest.TestCase):
def test_ActionMessageIsQuotedAndEscaped(self):
output = io.StringIO()
action = {
"action_name": "gen",
"inputs": ["in.txt"],
"outputs": ["out.txt"],
"action": ["python", "gen.py"],
"message": 'Generating "out.txt"; (see #1)',
}
cmake.WriteActions("tgt", [action], [], [], ".", output)
self.assertIn(
' COMMENT "Generating \\"out.txt\\"\\; (see #1)"\n', output.getvalue()
)

def test_ActionWithoutMessageUsesTargetName(self):
output = io.StringIO()
action = {
"action_name": "gen",
"inputs": [],
"outputs": ["out.txt"],
"action": ["python", "gen.py"],
}
cmake.WriteActions("tgt", [action], [], [], ".", output)
self.assertIn(' COMMENT "tgt__gen"\n', output.getvalue())

def test_RuleMessageKeepsVariableReferences(self):
output = io.StringIO()
rule = {
"rule_name": "compile",
"outputs": ["${RULE_INPUT_ROOT}.o"],
"action": ["cc", "${RULE_INPUT_PATH}"],
"rule_sources": ["foo.c"],
"message": "Compiling ${RULE_INPUT_NAME}",
}
cmake.WriteRules("tgt", [rule], [], [], ".", output)
self.assertIn(' COMMENT "Compiling ${RULE_INPUT_NAME}"\n', output.getvalue())

def test_CopiesMessageIsQuoted(self):
output = io.StringIO()
copies = [{"files": ["a.txt"], "destination": "out"}]
cmake.WriteCopies("tgt", copies, [], ".", output)
self.assertIn('COMMENT "Copying for tgt"\n', output.getvalue())


class TestTargetWithoutSources(unittest.TestCase):
def _WriteTarget(self, target_type, sources=()):
qualified_target = "foo.gyp:foo#target"
spec = {
"target_name": "foo",
"type": target_type,
"toolset": "target",
"sources": list(sources),
}
output = io.StringIO()
cmake.WriteTarget(
cmake.CMakeNamer([qualified_target]),
qualified_target,
{qualified_target: spec},
"out/Default",
"Default",
types.SimpleNamespace(toplevel_dir="."),
{},
[qualified_target],
"linux",
output,
)
return output.getvalue()

def test_DummySourceForEmptyTargets(self):
for target_type, add_target in (
("executable", "add_executable(foo"),
("static_library", "add_library(foo STATIC"),
("shared_library", "add_library(foo SHARED"),
("loadable_module", "add_library(foo MODULE"),
):
with self.subTest(target_type=target_type):
output = self._WriteTarget(target_type)
self.assertIn(' file(WRITE "${foo__dummy_srcs}" "")\n', output)
self.assertIn(add_target + " ${foo__dummy_srcs})\n", output)

def test_NoDummySourceForLibraryWithSources(self):
output = self._WriteTarget("static_library", ["foo.c"])
self.assertIn("add_library(foo STATIC ${foo__c_srcs})\n", output)
self.assertNotIn("dummy", output)

def test_NoDummySourceForNoneTarget(self):
output = self._WriteTarget("none")
self.assertIn("add_custom_target(foo SOURCES)\n", output)
self.assertNotIn("dummy", output)


if __name__ == "__main__":
unittest.main()
9 changes: 7 additions & 2 deletions tools/gyp/pylib/gyp/msvs_emulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,13 +1057,18 @@ def GetFlagsModifications(
pch_output = ["/Yc" + self._PchHeader()]
if command == "cxx":
return (
[("cflags_cc", map(expand_special, cflags_cc + pch_output))],
[
(
"cflags_cc",
[expand_special(x) for x in cflags_cc + pch_output],
)
],
self.output_obj,
[],
)
elif command == "cc":
return (
[("cflags_c", map(expand_special, cflags_c + pch_output))],
[("cflags_c", [expand_special(x) for x in cflags_c + pch_output])],
self.output_obj,
[],
)
Expand Down
2 changes: 1 addition & 1 deletion tools/gyp/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "gyp-next"
version = "0.22.2"
version = "0.22.3"
authors = [
{ name="Node.js contributors", email="ryzokuken@disroot.org" },
]
Expand Down
3 changes: 1 addition & 2 deletions tools/gyp/test/fixtures/expected-darwin/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
cmake_policy(VERSION 2.8.8)
cmake_minimum_required(VERSION 3.10)
project(test)
set(configuration "Default")
enable_language(ASM)
Expand Down
3 changes: 1 addition & 2 deletions tools/gyp/test/fixtures/expected-linux/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
cmake_policy(VERSION 2.8.8)
cmake_minimum_required(VERSION 3.10)
project(test)
set(configuration "Default")
enable_language(ASM)
Expand Down
1 change: 1 addition & 0 deletions tools/gyp/test/fixtures/pch.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "pch.h"
13 changes: 13 additions & 0 deletions tools/gyp/test/fixtures/pch.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
'targets': [
{
'target_name': 'pch',
'type': 'executable',
'sources': [
'pch.cc',
],
'msvs_precompiled_header': 'pch.h',
'msvs_precompiled_source': 'pch.cc',
},
]
}
1 change: 1 addition & 0 deletions tools/gyp/test/fixtures/pch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma once
16 changes: 15 additions & 1 deletion tools/gyp/test/integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import gyp

fixture_dir = os.path.join(os.path.dirname(__file__), "fixtures")
gyp_file = os.path.join(os.path.dirname(__file__), "fixtures/integration.gyp")
gyp_file = os.path.join(fixture_dir, "integration.gyp")
pch_gyp_file = os.path.join(fixture_dir, "pch.gyp")

if sys.platform == "win32":
sysname = sys.platform
Expand Down Expand Up @@ -91,3 +92,16 @@ def test_msvs(self) -> None:

assert_file(self, "test.vcproj", "msvs/test.vcproj")
assert_file(self, "integration.sln", "msvs/integration.sln")

def test_ninja_precompiled_header(self) -> None:
try:
rc = gyp.main(["-f", "ninja", "--depth", fixture_dir, pch_gyp_file])
except ValueError as exc:
self.skipTest(str(exc))
assert rc == 0

with open(os.path.join(fixture_dir, "out/Default/obj/pch.ninja")) as in_file:
ninja_file = in_file.read()

assert "map object at" not in ninja_file
assert "/Ycpch.h" in ninja_file
Loading