current commit
261027.10 · tools: update gyp-next to 0.22.4
#!/usr/bin/env python3

"""Integration test"""

import os
import re
import shutil
import subprocess
import sys
import unittest

import gyp

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

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


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)

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

    try:
        test.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

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

    def test_make(self) -> None:
        rc = gyp.main(
            [
                "-f",
                "make",
                "--depth",
                fixture_dir,
                "--generator-output",
                "out",
                gyp_file,
            ]
        )
        assert rc == 0

        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

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


class TestGypMac(unittest.TestCase):
    # The prefix header of each target includes a header generated by its
    # action (pch_action) or by its dependency (pch_dep). Each output is built
    # on its own from a clean build, so it fails unless it waits for that header.
    pch_targets = ("pch_action", "pch_dep")
    pch_langs = ("c", "cc", "m", "mm")

    def setUp(self) -> None:
        if sysname != "darwin":
            self.skipTest("macOS-only test")
        shutil.rmtree(os.path.join(fixture_dir, "out"), ignore_errors=True)

    def test_make_precompiled_prefix_header(self) -> None:
        rc = gyp.main(
            [
                "-f",
                "make",
                "--depth",
                fixture_dir,
                "--generator-output",
                "out",
                pch_prefix_gyp_file,
            ]
        )
        assert rc == 0

        makefile_dir = os.path.join(fixture_dir, "out")
        # CXXFLAGS that a C compile rejects check the C gch doesn't get them.
        env = {**os.environ, "CXXFLAGS": "-std=c++17"}
        for target in self.pch_targets:
            obj = f"out/Default/obj.target/{target}"
            gchs = [f"{obj}/pch-{lang}/{target}.h.gch" for lang in self.pch_langs]
            for output in [*gchs, f"{obj}/pch_asm.o"]:
                shutil.rmtree(os.path.join(makefile_dir, "out"), ignore_errors=True)
                subprocess.run(
                    ["make", "-C", makefile_dir, output], env=env, check=True
                )

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

        build_dir = os.path.join(fixture_dir, "out", "Default")
        for target in self.pch_targets:
            gchs = [f"obj/{target}.{target}.h-{lang}.gch" for lang in self.pch_langs]
            for output in [*gchs, f"obj/{target}.pch_asm.o"]:
                subprocess.run(["ninja", "-C", build_dir, "-t", "clean"], check=True)
                subprocess.run(["ninja", "-C", build_dir, output], check=True)


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")

    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