From f4696d43c4f2cfc598de15a3d993e9d2529d5250 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Sun, 13 Sep 2026 13:54:04 +0200 Subject: [PATCH 1/2] [test] Use the platform library extension for the test dictionaries test03_add_library_path hardcodes .so for the library it copies and loads, so on Windows the load can never succeed (the dictionary is a .dll there), and the test Makefile and setup_make build and name the dictionaries as Dict.so as well. Derive the extension from the platform once, in support.py next to the other platform facts and as soext in the Makefile, and use it everywhere the dictionaries are built or loaded. Once the load succeeds, the library is mapped into the process and Windows refuses to delete it, so the temporary directory is cleaned up with ignore_cleanup_errors. Seen on the Windows CI of ROOT's cppjit migration branch, which is currently the only Windows coverage the test suite gets. --- test/Makefile | 15 ++++++++++----- test/support.py | 4 +++- test/test_basic_api.py | 12 +++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/test/Makefile b/test/Makefile index 7f1433e..7c89130 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,11 @@ # the C++ sides of the tests live under cpp/ and build into shared # libraries next to their sources (load_reflection_info resolves the -# header as a sibling of the loaded Dict.so) +# header as a sibling of the loaded Dict library) +soext = .so +ifeq ($(OS),Windows_NT) + soext = .dll +endif + dictnames = advancedcpp \ advancedcpp2 \ conversions \ @@ -17,7 +22,7 @@ dictnames = advancedcpp \ stltypes \ templates -dicts = $(addprefix cpp/,$(addsuffix Dict.so,$(dictnames))) +dicts = $(addprefix cpp/,$(addsuffix Dict$(soext),$(dictnames))) all : $(dicts) @@ -30,11 +35,11 @@ ifeq ($(PLATFORM),Darwin) endif # a worker can load the library while another rebuilds it, so publish it whole -cpp/%Dict.so: cpp/%.cxx cpp/%.h +cpp/%Dict$(soext): cpp/%.cxx cpp/%.h $(CXX) $(cppflags) -shared -o $@.$$$$.tmp $< && mv -f $@.$$$$.tmp $@ -# convenience: `make datatypesDict.so` builds cpp/datatypesDict.so -%Dict.so: cpp/%Dict.so ; +# convenience: `make datatypesDict$(soext)` builds cpp/datatypesDict$(soext) +%Dict$(soext): cpp/%Dict$(soext) ; .PHONY: test clean diff --git a/test/support.py b/test/support.py index 667394e..94c4738 100644 --- a/test/support.py +++ b/test/support.py @@ -45,7 +45,7 @@ def setup_make(targetname, optional=False): if fcntl is not None: fcntl.flock(lockf, fcntl.LOCK_EX) popen = subprocess.Popen( - ["make", targetname + "Dict.so"], + ["make", targetname + "Dict" + soext], cwd=str(currpath), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, @@ -76,6 +76,8 @@ def setup_make(targetname, optional=False): else: IS_WINDOWS = 32 +soext = ".dll" if IS_WINDOWS else ".so" + IS_MAC_ARM = 0 IS_MAC_X86 = 0 if "darwin" in sys.platform: diff --git a/test/test_basic_api.py b/test/test_basic_api.py index 44003a4..2618e2a 100644 --- a/test/test_basic_api.py +++ b/test/test_basic_api.py @@ -4,7 +4,7 @@ import py from pytest import raises -from support import needs_dictionary, setup_make +from support import needs_dictionary, setup_make, soext # reuse the example01 currpath = py.path.local(__file__).dirpath() @@ -48,17 +48,19 @@ def test03_add_library_path(self): with raises(OSError, match="No such directory"): cppjit.add_library_path("not/a/real/path") - with tempfile.TemporaryDirectory() as tpath: + # once loaded, the copied library is mapped into the process; Windows + # then refuses to delete it, which would fail the cleanup + with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tpath: cppjit.add_library_path(tpath) # now we should actually see if load library can follow this... # first, try to load without moving to directory... with raises(RuntimeError, match="Could not load library"): - cppjit.load_library("test.so") + cppjit.load_library("test" + soext) # then copy to our rpath, and make sure it can be loaded now - shutil.copyfile(test_dct + ".so", tpath + "/test.so") - cppjit.load_library("test.so") + shutil.copyfile(test_dct + soext, tpath + "/test" + soext) + cppjit.load_library("test" + soext) def test03a_load_library_failure_reason(self): """load_library reports the loader's failure reason""" From 67116c883920a99e8b91ce3fcddd344d4513d9af Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Fri, 28 Aug 2026 18:01:29 +0200 Subject: [PATCH 2/2] [python] Do not let the Windows std::endl workaround break the import The Windows workaround declares an explicit specialization of std::endl at import time. When std::endl was already instantiated in the interpreter, as it is when cppjit is imported into a process that loaded iostream-using headers before it (ROOT's PyROOT), the declaration is ill-formed and cppdef raises SyntaxError, which aborts the import. Ignore that error: the instantiation the workaround wants already exists. --- python/cppjit/__init__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python/cppjit/__init__.py b/python/cppjit/__init__.py index 6193fc1..8df1b92 100644 --- a/python/cppjit/__init__.py +++ b/python/cppjit/__init__.py @@ -474,6 +474,11 @@ def __new__(mcs, name, this_bases, d): # - workaround (TODO: may not be needed with Clang9) -------------------------- if "win32" in sys.platform: - cppdef("""template<> - std::basic_ostream>& __cdecl std::endl>( - std::basic_ostream>&);""") + # Ill-formed if std::endl was already instantiated; then the + # instantiation exists and the workaround is unnecessary. + try: + cppdef("""template<> + std::basic_ostream>& __cdecl std::endl>( + std::basic_ostream>&);""") + except SyntaxError: + pass