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
4 changes: 4 additions & 0 deletions tools/gyp/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,7 @@ static

test/fixtures/out
*.actual
*.sln
*.vcproj
!test/fixtures/expected-win32/**/*.sln
!test/fixtures/expected-win32/**/*.vcproj
12 changes: 12 additions & 0 deletions tools/gyp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [0.22.0](https://github.com/nodejs/gyp-next/compare/v0.21.1...v0.22.0) (2026-04-02)


### Features

* Windows ARM64 target architecture support ([#331](https://github.com/nodejs/gyp-next/issues/331)) ([652a346](https://github.com/nodejs/gyp-next/commit/652a346bbd3b077a4b08a3c37d48100ce200758a))


### Bug Fixes

* drop deprecated Python module pkg_resources ([#333](https://github.com/nodejs/gyp-next/issues/333)) ([5b180d5](https://github.com/nodejs/gyp-next/commit/5b180d52d03aff062bdea1ad0209b82271c7eb4a))

## [0.21.1](https://github.com/nodejs/gyp-next/compare/v0.21.0...v0.21.1) (2026-01-24)


Expand Down
14 changes: 11 additions & 3 deletions tools/gyp/pylib/gyp/MSVSVersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def DefaultToolset(self):
def _SetupScriptInternal(self, target_arch):
"""Returns a command (with arguments) to be used to set up the
environment."""
assert target_arch in ("x86", "x64"), "target_arch not supported"
assert target_arch in ("x86", "x64", "arm64"), "target_arch not supported"
# If WindowsSDKDir is set and SetEnv.Cmd exists then we are using the
# depot_tools build tools and should run SetEnv.Cmd to set up the
# environment. The check for WindowsSDKDir alone is not sufficient because
Expand All @@ -109,8 +109,16 @@ def _SetupScriptInternal(self, target_arch):
)

# Always use a native executable, cross-compiling if necessary.
host_arch = "amd64" if is_host_arch_x64 else "x86"
msvc_target_arch = "amd64" if target_arch == "x64" else "x86"
host_arch = (
"amd64"
if is_host_arch_x64
else (
"arm64"
if os.environ.get("PROCESSOR_ARCHITECTURE") == "ARM64"
else "x86"
)
)
msvc_target_arch = {"x64": "amd64"}.get(target_arch, target_arch)
arg = host_arch
if host_arch != msvc_target_arch:
arg += "_" + msvc_target_arch
Expand Down
5 changes: 2 additions & 3 deletions tools/gyp/pylib/gyp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import shlex
import sys
import traceback
from importlib.metadata import version

import gyp.input
from gyp.common import GypError
Expand Down Expand Up @@ -491,9 +492,7 @@ def gyp_main(args):

options, build_files_arg = parser.parse_args(args)
if options.version:
import pkg_resources # noqa: PLC0415

print(f"v{pkg_resources.get_distribution('gyp-next').version}")
print(f"v{version('gyp-next')}")
return 0
build_files = build_files_arg

Expand Down
3 changes: 2 additions & 1 deletion tools/gyp/pylib/gyp/generator/ninja.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def __init__(
if flavor == "win":
# See docstring of msvs_emulation.GenerateEnvironmentFiles().
self.win_env = {}
for arch in ("x86", "x64"):
for arch in ("x86", "x64", "arm64"):
self.win_env[arch] = "environment." + arch

# Relative path from build output dir to base dir.
Expand Down Expand Up @@ -2339,6 +2339,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name
master_ninja.variable("rc", "rc.exe")
master_ninja.variable("ml_x86", "ml.exe")
master_ninja.variable("ml_x64", "ml64.exe")
master_ninja.variable("ml_arm64", "armasm64.exe")
master_ninja.variable("mt", "mt.exe")
else:
master_ninja.variable("ld", CommandWithWrapper("LINK", wrappers, ld))
Expand Down
42 changes: 26 additions & 16 deletions tools/gyp/pylib/gyp/generator/ninja_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,36 @@
from pathlib import Path

from gyp.generator import ninja
from gyp.MSVSVersion import SelectVisualStudioVersion


def _has_visual_studio():
"""Check if Visual Studio can be detected by gyp's registry-based detection."""
if not sys.platform.startswith("win"):
return False
try:
SelectVisualStudioVersion("auto", allow_fallback=False)
return True
except ValueError:
return False


class TestPrefixesAndSuffixes(unittest.TestCase):
@unittest.skipUnless(
_has_visual_studio(),
"requires Windows with a Visual Studio installation detected via the registry",
)
def test_BinaryNamesWindows(self):
# These cannot run on non-Windows as they require a VS installation to
# correctly handle variable expansion.
if sys.platform.startswith("win"):
writer = ninja.NinjaWriter(
"foo", "wee", ".", ".", "build.ninja", ".", "build.ninja", "win"
)
spec = {"target_name": "wee"}
self.assertTrue(
writer.ComputeOutputFileName(spec, "executable").endswith(".exe")
)
self.assertTrue(
writer.ComputeOutputFileName(spec, "shared_library").endswith(".dll")
)
self.assertTrue(
writer.ComputeOutputFileName(spec, "static_library").endswith(".lib")
)
writer = ninja.NinjaWriter(
"foo", "wee", ".", ".", "build.ninja", ".", "build.ninja", "win"
)
spec = {"target_name": "wee"}
for key, ext in {
"executable": ".exe",
"shared_library": ".dll",
"static_library": ".lib",
}:
self.assertTrue(writer.ComputeOutputFileName(spec, key).endswith(ext))

def test_BinaryNamesLinux(self):
writer = ninja.NinjaWriter(
Expand Down
2 changes: 1 addition & 1 deletion tools/gyp/pylib/gyp/mac_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ def _FindProvisioningProfile(self, profile, bundle_identifier):
# If the user has multiple provisioning profiles installed that can be
# used for ${bundle_identifier}, pick the most specific one (ie. the
# provisioning profile whose pattern is the longest).
selected_key = max(valid_provisioning_profiles, key=lambda v: len(v))
selected_key = max(valid_provisioning_profiles, key=len)
return valid_provisioning_profiles[selected_key]

def _LoadProvisioningProfile(self, profile_path):
Expand Down
2 changes: 1 addition & 1 deletion tools/gyp/pylib/gyp/msvs_emulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ def GenerateEnvironmentFiles(
meet your requirement (e.g. for custom toolchains), you can pass
"-G ninja_use_custom_environment_files" to the gyp to suppress file
generation and use custom environment files prepared by yourself."""
archs = ("x86", "x64")
archs = ("x86", "x64", "arm64")
if generator_flags.get("ninja_use_custom_environment_files", 0):
cl_paths = {}
for arch in archs:
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.21.1"
version = "0.22.0"
authors = [
{ name="Node.js contributors", email="ryzokuken@disroot.org" },
]
Expand Down
16 changes: 16 additions & 0 deletions tools/gyp/test/fixtures/expected-win32/msvs/integration.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{*}") = "test", "test.vcproj", "{*}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Default|Win32 = Default|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{*}.Default|Win32.ActiveCfg = Default|Win32
{*}.Default|Win32.Build.0 = Default|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions tools/gyp/test/fixtures/expected-win32/msvs/test.vcproj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="Windows-1252"?><VisualStudioProject Keyword="Win32Proj" Name="test" ProjectGUID="{*}" ProjectType="Visual C++" RootNamespace="test" Version="8.00"><Platforms><Platform Name="Win32"/></Platforms><ToolFiles/><Configurations><Configuration ConfigurationType="1" IntermediateDirectory="$(ConfigurationName)\obj\$(ProjectName)\" Name="Default|Win32" OutputDirectory="$(SolutionDir)$(ConfigurationName)\"><Tool AdditionalDependencies="$(NOINHERIT)" AdditionalLibraryDirectories="mylib" Name="VCLinkerTool" OutputFile="$(OutDir)\$(ProjectName).exe"/><Tool AdditionalIncludeDirectories="include" Name="VCCLCompilerTool" ProgramDataBaseFileName="$(IntDir)$(ProjectName)\vc80.pdb"/><Tool AdditionalIncludeDirectories="include" Name="VCResourceCompilerTool"/></Configuration></Configurations><References/><Files><File RelativePath="test.cc"/><File RelativePath="integration.gyp"/></Files><Globals/></VisualStudioProject>
71 changes: 47 additions & 24 deletions tools/gyp/test/integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,56 @@
import os
import re
import shutil
import sys
import unittest

import gyp

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

supported_sysnames = {"darwin", "linux"}
sysname = os.uname().sysname.lower()
if sys.platform == "win32":
sysname = sys.platform
else:
sysname = os.uname().sysname.lower()
expected_dir = os.path.join(fixture_dir, f"expected-{sysname}")


class TestGyp(unittest.TestCase):
def setUp(self) -> None:
if sysname not in supported_sysnames:
self.skipTest(f"Unsupported system: {sysname}")
shutil.rmtree(os.path.join(fixture_dir, "out"), ignore_errors=True)
def assert_file(test, actual, expected) -> None:
actual_filepath = os.path.join(fixture_dir, actual)
expected_filepath = os.path.join(expected_dir, expected)

with open(expected_filepath) as in_file:
in_bytes = in_file.read()
in_bytes = in_bytes.strip()
expected_bytes = re.escape(in_bytes)
expected_bytes = expected_bytes.replace("\\*", ".*")
expected_re = re.compile(expected_bytes)

def assert_file(self, actual, expected) -> None:
actual_filepath = os.path.join(fixture_dir, actual)
expected_filepath = os.path.join(expected_dir, expected)
with open(actual_filepath) as in_file:
actual_bytes = in_file.read()
actual_bytes = actual_bytes.strip()

with open(expected_filepath) as in_file:
expected_bytes = re.escape(in_file.read())
expected_bytes = expected_bytes.replace("\\*", ".*")
expected_re = re.compile(expected_bytes)
try:
test.assertRegex(actual_bytes, expected_re)
except Exception:
shutil.copyfile(actual_filepath, f"{expected_filepath}.actual")
raise

with open(actual_filepath) as in_file:
actual_bytes = in_file.read()

try:
self.assertRegex(actual_bytes, expected_re)
except Exception:
shutil.copyfile(actual_filepath, f"{expected_filepath}.actual")
raise
class TestGypUnix(unittest.TestCase):
supported_sysnames = {"darwin", "linux"}

def setUp(self) -> None:
if sysname not in TestGypUnix.supported_sysnames:
self.skipTest(f"Unsupported system: {sysname}")
shutil.rmtree(os.path.join(fixture_dir, "out"), ignore_errors=True)

def test_ninja(self) -> None:
rc = gyp.main(["-f", "ninja", "--depth", fixture_dir, gyp_file])
assert rc == 0

self.assert_file("out/Default/obj/test.ninja", "ninja/test.ninja")
assert_file(self, "out/Default/obj/test.ninja", "ninja/test.ninja")

def test_make(self) -> None:
rc = gyp.main(
Expand All @@ -61,10 +70,24 @@ def test_make(self) -> None:
)
assert rc == 0

self.assert_file("out/test.target.mk", "make/test.target.mk")
assert_file(self, "out/test.target.mk", "make/test.target.mk")

def test_cmake(self) -> None:
rc = gyp.main(["-f", "cmake", "--depth", fixture_dir, gyp_file])
assert rc == 0

self.assert_file("out/Default/CMakeLists.txt", "cmake/CMakeLists.txt")
assert_file(self, "out/Default/CMakeLists.txt", "cmake/CMakeLists.txt")


class TestGypWindows(unittest.TestCase):
def setUp(self) -> None:
if sys.platform != "win32":
self.skipTest("Windows-only test")
shutil.rmtree(os.path.join(fixture_dir, "out"), ignore_errors=True)

def test_msvs(self) -> None:
rc = gyp.main(["-f", "msvs", "--depth", fixture_dir, gyp_file])
assert rc == 0

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