44import argparse
55import importlib .util
66import os
7+ import shlex
8+ import shutil
9+ import sys
710from pathlib import Path
811import subprocess
912import tempfile
@@ -65,6 +68,9 @@ def prepare(self, root, compiler_status=0):
6568import json, os, pathlib, sys
6669with open(os.environ['INVOCATIONS'], 'a') as stream:
6770 stream.write(json.dumps(sys.argv[1:]) + '\\ n')
71+ if any(mode in sys.argv for mode in ('-r', '-Wl,-r', '-c', '-S', '-E', '-fsyntax-only', '-static')) and any(arg.startswith('-Wl,-rpath,') for arg in sys.argv):
72+ sys.stderr.write('rpath requires a final dynamic link\\ n')
73+ sys.exit(73)
6874if '-o' in sys.argv:
6975 output = pathlib.Path(sys.argv[sys.argv.index('-o') + 1])
7076 output.write_text('#!/bin/sh\\ nexit 0\\ n')
@@ -91,13 +97,36 @@ def test_wrapper_selects_matching_library_and_preserves_arguments(self):
9197 self .assertEqual (result .returncode , 0 , result .stdout + result .stderr )
9298 selected = dict (line .split ('=' , 1 ) for line in Path (env ['GITHUB_ENV' ]).read_text ().splitlines ())
9399 self .assertEqual (selected ['V3_CC' ], str (llvm / 'bin/clang' ))
94- result = subprocess .run ([selected ['V3_CXX' ], '-c' , ' path with spaces.cpp' ], env = env )
100+ result = subprocess .run ([selected ['V3_CXX' ], 'path with spaces.cpp' , '-o' , str ( root / 'consumer' ) ], env = env )
95101 self .assertEqual (result .returncode , 0 )
96102 calls = [json .loads (line ) for line in Path (env ['INVOCATIONS' ]).read_text ().splitlines ()]
97103 self .assertIn ('-std=c++20' , calls [1 ])
98104 self .assertIn ('-L' + str (llvm / 'lib/c++' ), calls [1 ])
99105 self .assertIn ('-Wl,-rpath,' + str (llvm / 'lib/c++' ), calls [1 ])
100- self .assertEqual (calls [- 1 ][- 2 :], ['-c' , 'path with spaces.cpp' ])
106+ self .assertEqual (calls [- 1 ][- 3 :], ['path with spaces.cpp' , '-o' , str (root / 'consumer' )])
107+ result = subprocess .run ([selected ['V3_CXX' ], '-dynamiclib' , 'input.o' ], env = env )
108+ self .assertEqual (result .returncode , 0 )
109+ calls = [json .loads (line ) for line in Path (env ['INVOCATIONS' ]).read_text ().splitlines ()]
110+ self .assertIn ('-L' + str (llvm / 'lib/c++' ), calls [- 1 ])
111+ self .assertIn ('-Wl,-rpath,' + str (llvm / 'lib/c++' ), calls [- 1 ])
112+
113+ def test_nonfinal_modes_do_not_receive_runtime_linker_flags (self ):
114+ with tempfile .TemporaryDirectory () as directory :
115+ root = Path (directory )
116+ result , env , llvm = self .prepare (root )
117+ self .assertEqual (result .returncode , 0 , result .stdout + result .stderr )
118+ selected = dict (line .split ('=' , 1 ) for line in Path (env ['GITHUB_ENV' ]).read_text ().splitlines ())
119+ for mode in ('-r' , '-Wl,-r' , '-c' , '-S' , '-E' , '-fsyntax-only' , '-static' ):
120+ with self .subTest (mode = mode ):
121+ result = subprocess .run ([selected ['V3_CXX' ], mode , 'path with spaces.cpp' ],
122+ env = env , capture_output = True , text = True )
123+ self .assertEqual (result .returncode , 0 , result .stdout + result .stderr )
124+ calls = [json .loads (line ) for line in Path (env ['INVOCATIONS' ]).read_text ().splitlines ()]
125+ self .assertIn ('-stdlib=libc++' , calls [- 1 ])
126+ self .assertIn ('-D_LIBCPP_DISABLE_AVAILABILITY' , calls [- 1 ])
127+ self .assertNotIn ('-L' + str (llvm / 'lib/c++' ), calls [- 1 ])
128+ self .assertNotIn ('-Wl,-rpath,' + str (llvm / 'lib/c++' ), calls [- 1 ])
129+ self .assertEqual (calls [- 1 ][- 2 :], [mode , 'path with spaces.cpp' ])
101130
102131 def test_missing_cancellation_support_stops_before_exporting_toolchain (self ):
103132 with tempfile .TemporaryDirectory () as directory :
@@ -106,5 +135,46 @@ def test_missing_cancellation_support_stops_before_exporting_toolchain(self):
106135 self .assertFalse (Path (env ['GITHUB_ENV' ]).exists ())
107136
108137
138+ @unittest .skipIf (os .name == 'nt' , 'competing compiler and symlink fixtures use POSIX paths' )
139+ class InstalledPackageFixtureEnvironment (unittest .TestCase ):
140+ def test_fixture_entrypoint_honors_quoted_cxx_command (self ):
141+ with tempfile .TemporaryDirectory () as directory :
142+ root = Path (directory )
143+ selected = root / 'selected compiler.py'
144+ selected .write_text ("""import json, os, sys
145+ if sys.argv[1] != '--selected-toolchain':
146+ sys.exit(72)
147+ with open(os.environ['INVOCATIONS'], 'a') as stream:
148+ stream.write(json.dumps(sys.argv[2:]) + '\\ n')
149+ os.execv(os.environ['REAL_CXX'], [os.environ['REAL_CXX']] + sys.argv[2:])
150+ """ )
151+ competing = root / 'c++'
152+ competing .write_text ('#!/bin/sh\n printf "unselected compiler invoked\\ n" >&2\n exit 71\n ' )
153+ competing .chmod (0o755 )
154+ env = dict (os .environ , CXX = shlex .join ([sys .executable , str (selected ), '--selected-toolchain' ]),
155+ PATH = str (root ) + os .pathsep + os .environ ['PATH' ],
156+ INVOCATIONS = str (root / 'calls.jsonl' ), REAL_CXX = shutil .which ('c++' ))
157+ result = subprocess .run ([sys .executable , str (ROOT / 'scripts/test-v3-installed-package.py' ),
158+ 'AuditTest.test_native_public_api_is_available_through_umbrella' ,
159+ 'AuditTest.test_current_umbrella_has_no_backend_types' ],
160+ env = env , capture_output = True , text = True )
161+ self .assertEqual (result .returncode , 0 , result .stdout + result .stderr )
162+ calls = [json .loads (line ) for line in Path (env ['INVOCATIONS' ]).read_text ().splitlines ()]
163+ self .assertTrue (any ('-fsyntax-only' in call for call in calls ))
164+ self .assertTrue (any ('-E' in call for call in calls ))
165+
166+ def test_pe_fixture_accepts_canonical_path_under_symlinked_tmpdir (self ):
167+ with tempfile .TemporaryDirectory () as directory :
168+ root = Path (directory )
169+ target = root / 'real'
170+ target .mkdir ()
171+ alias = root / 'alias'
172+ alias .symlink_to (target , target_is_directory = True )
173+ result = subprocess .run ([sys .executable , str (ROOT / 'scripts/test-v3-installed-package.py' ),
174+ 'AuditTest.test_pe_resolution_uses_explicit_roots_and_rejects_msys' ],
175+ env = dict (os .environ , TMPDIR = str (alias )), capture_output = True , text = True )
176+ self .assertEqual (result .returncode , 0 , result .stdout + result .stderr )
177+
178+
109179if __name__ == '__main__' :
110180 unittest .main ()
0 commit comments