Skip to content

Commit 9ed9111

Browse files
committed
FEATURE-ci-darwin-canonical-fixtures: repair native fixture path and linker identities
Resolve affected fixture roots before deriving receipt and catalog identities, discover the selected success executable, and use the quoted selected compiler with native compile_probe maps. Match Darwin map options and contributing archive members while preserving exact installed archive and provenance checks. Verified all six affected Python suites (155 tests), real symlinked TMPDIR regressions, real GNU compile/archive controls, quoted compiler selection, Python byte compilation and diff checks. Darwin map-format tests are synthetic; native post-fix platform execution remains CI/v3 PR owned. Task remains In Progress pending caller validation/finalization.
1 parent 1fa030d commit 9ed9111

8 files changed

Lines changed: 164 additions & 23 deletions

‎scripts/lib/release_package_provenance.py‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import shlex
77
import os
8+
import platform
89
from native_benchmark import require, verify_artifact, parse_json
910
from package_commands import dependencies
1011
from package_matrix import check_dependencies, check_linkage
@@ -51,10 +52,18 @@ def bound(path, digest=None):
5152
row['argv'][row['argv'].index('-o') + 1] == executable]
5253
require(len(selected) == 1, 'missing actual link command')
5354
link = selected[0]['argv']
54-
require('-Wl,-Map,' + str(linkmap) in link, 'wrong link map command')
55+
map_option = '-Wl,-map,' if platform.system() == 'Darwin' else '-Wl,-Map,'
56+
require(map_option + str(linkmap) in link, 'wrong link map command')
5557
if linkage == 'static':
5658
archive = str(prefix / 'lib/libhttpserver.a')
57-
contributors = {str(Path(name).resolve()) for name in re.findall(r'([^\s()]+libhttpserver\.a)\([^()\n]+\)', linkmap.read_text())}
59+
text = linkmap.read_text()
60+
if platform.system() == 'Darwin':
61+
# ld64 lists contributing archive members in its object-file table.
62+
table = text.partition('# Object files:\n')[2].split('\n# ', 1)[0]
63+
names = re.findall(r'^\[\s*\d+\]\s+(.+libhttpserver\.a)(?:\[\d+\])?\([^()\n]+\)\s*$', table, re.M)
64+
else:
65+
names = re.findall(r'([^\s()]+libhttpserver\.a)\([^()\n]+\)', text)
66+
contributors = {str(Path(name).resolve()) for name in names}
5867
require(archive in link and contributors == {archive}, 'different or noncontributing installed static archive')
5968
compile_rows = [row for row in commands if '-c' in row['argv'] and '-o' in row['argv'] and
6069
row['argv'][row['argv'].index('-o') + 1] in link and

‎scripts/test-v3-native-benchmark.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def command(argv, *_):
122122

123123
class ReceiptTest(unittest.TestCase):
124124
def receipt(self, root, tls='no'):
125-
root = Path(root)
125+
root = Path(root).resolve()
126126
consumer = root / 'consumer'; consumer.mkdir()
127127
files = {'consumer.cpp': 'consumer', 'host_poll.hpp': 'poll', 'client.py': 'client',
128128
'fixtures/root.pem': 'certificate', 'shared': 'shared binary', 'static': 'static binary',

‎scripts/test-v3-packaged-adapters.py‎

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import importlib.util
66
import io
77
import json
8+
import os
9+
import shlex
10+
import subprocess
811
from pathlib import Path
912
import sys
1013
import tarfile
@@ -26,7 +29,7 @@ def setUp(self):
2629
self.assertIsNotNone(adapter, 'packaged adapter implementation missing')
2730
self.temp = tempfile.TemporaryDirectory()
2831
self.addCleanup(self.temp.cleanup)
29-
self.root = Path(self.temp.name)
32+
self.root = Path(self.temp.name).resolve()
3033
self.source = self.root / 'source'
3134
self.source.mkdir()
3235
(self.source / 'unit.cpp').write_text('public source\n')
@@ -99,17 +102,22 @@ def provenance(self):
99102
unit = directory / 'consumer.cpp'; unit.write_text('public source\n')
100103
executable = directory / 'static'; executable.write_text('consumer')
101104
depfile = directory / 'static-0.d'; depfile.write_text(f'static.o: {unit} {header}\n')
102-
linkmap = directory / 'static.map'; linkmap.write_text(str(library) + '(public.o)\nLOAD ' + str(library) + '\n')
105+
linkmap = directory / 'static.map'
106+
linkmap.write_text(('# Object files:\n[ 1] ' + str(library) + '[1](public.o)\n# Sections:\n')
107+
if sys.platform == 'darwin' else str(library) + '(public.o)\nLOAD ' + str(library) + '\n')
108+
compiler = os.environ.get('CXX', 'c++')
109+
compiler_argv = shlex.split(compiler)
110+
map_option = '-Wl,-map,' if sys.platform == 'darwin' else '-Wl,-Map,'
103111
obj = directory / 'static-0.o'; obj.write_text('object')
104112
log = self.root / 'command.log'; log.write_text('compile/link ok')
105-
commands = [dict(argv=['c++', '-std=c++20', '-MMD', '-MF', str(depfile), '-c', str(unit), '-o', str(obj)],
113+
commands = [dict(argv=compiler_argv + ['-std=c++20', '-MMD', '-MF', str(depfile), '-c', str(unit), '-o', str(obj)],
106114
exit=0, log=str(log), sha256=bench.artifact(log)['sha256'], bytes=log.stat().st_size),
107-
dict(argv=['c++', str(obj), str(library), '-Wl,-Map,' + str(linkmap), '-o', str(executable)],
115+
dict(argv=compiler_argv + [str(obj), str(library), map_option + str(linkmap), '-o', str(executable)],
108116
exit=0, log=str(log), sha256=bench.artifact(log)['sha256'], bytes=log.stat().st_size)]
109117
compiled = dict(private_test_headers=False, sources={str(unit): bench.artifact(unit)['sha256']},
110118
dependencies=[str(unit), str(header)], dependency_sha256={str(p): bench.artifact(p)['sha256'] for p in (unit, header)},
111119
artifact_sha256=bench.artifact(executable)['sha256'], link_map=str(linkmap))
112-
identity = dict(source=str(self.source), build=str(self.root / 'build'), prefix=str(prefix), compiler='c++',
120+
identity = dict(source=str(self.source), build=str(self.root / 'build'), prefix=str(prefix), compiler=compiler,
113121
archive={'members': {'test/installed/consumer.cpp': bench.artifact(unit)['sha256']}},
114122
package={str(p): bench.artifact(p)['sha256'] for p in (header, library)})
115123
artifacts = {str(p): bench.artifact(p) for p in directory.iterdir() if p.is_file()}
@@ -154,6 +162,29 @@ def test_empty_linkmap_and_foreign_dependency_are_rejected(self):
154162
artifacts[compiled['link_map']] = bench.artifact(compiled['link_map'])
155163
with self.assertRaises(ValueError): adapter.validate_compile(compiled, 'static', identity, artifacts, commands)
156164

165+
def test_darwin_link_map_requires_actual_selected_archive_contribution(self):
166+
from unittest.mock import patch
167+
compiled, identity, artifacts, commands = self.provenance()
168+
linkmap = Path(compiled['link_map'])
169+
archive = str(Path(identity['prefix']) / 'lib/libhttpserver.a')
170+
foreign = str(self.root / 'foreign/libhttpserver.a')
171+
commands[1]['argv'][-3] = '-Wl,-map,' + str(linkmap)
172+
with patch('platform.system', return_value='Darwin'):
173+
for member in (archive + '(public.o)', archive + '[12](public.o)'):
174+
linkmap.write_text('# Path: consumer\n# Object files:\n[ 0] linker synthesized\n[ 1] ' + member + '\n# Sections:\n# Symbols:\n')
175+
artifacts[str(linkmap)] = bench.artifact(linkmap)
176+
adapter.validate_compile(compiled, 'static', identity, artifacts, commands)
177+
for body in ('', 'LOAD ' + archive, archive + '(public.o)',
178+
'# Object files:\n[ 1] ' + foreign + '[1](public.o)\n# Sections:\n',
179+
'# Object files:\n[ 1] consumer.o\n# Sections:\n' + archive + '(unused.o)',
180+
'# Object files:\n[ 1] ' + archive + '[1](public.o)\n[ 2] ' + foreign + '[1](other.o)\n# Sections:\n'):
181+
linkmap.write_text(body); artifacts[str(linkmap)] = bench.artifact(linkmap)
182+
with self.subTest(body=body), self.assertRaisesRegex(ValueError, 'static archive'):
183+
adapter.validate_compile(compiled, 'static', identity, artifacts, commands)
184+
commands[1]['argv'][-3] = '-Wl,-Map,' + str(linkmap)
185+
with self.assertRaisesRegex(ValueError, 'link map command'):
186+
adapter.validate_compile(compiled, 'static', identity, artifacts, commands)
187+
157188
def test_artifact_catalog_rejects_pruning_and_command_tampering(self):
158189
from release_package_provenance import catalog, validate_catalog
159190
output = self.root / 'evidence'; output.mkdir()
@@ -363,6 +394,23 @@ def test_each_public_family_requires_its_compile_and_dependency_records(self):
363394
bad = copy.deepcopy(child); bad.pop('independent_client')
364395
with self.assertRaises(ValueError): provenance.validate_child_provenance(bad, identity, {}, [], family)
365396

397+
def test_static_archive_fixture_uses_selected_quoted_compiler(self):
398+
compiler = shlex.split(os.environ.get('CXX', 'c++'))
399+
wrapper = self.root / 'selected compiler.py'
400+
marker = self.root / 'compiler-commands.jsonl'
401+
wrapper.write_text('import json, os, sys\n'
402+
'with open(' + repr(str(marker)) + ', "a") as stream: stream.write(json.dumps(sys.argv[1:]) + "\\n")\n'
403+
'argv = ' + repr(compiler) + '\nos.execvp(argv[0], argv + sys.argv[1:])\n')
404+
result = subprocess.run([sys.executable, str(Path(__file__).resolve()),
405+
'AdapterContracts.test_unused_selected_static_archive_cannot_supply_link_identity'],
406+
env=dict(os.environ, CXX=shlex.join([sys.executable, str(wrapper)])),
407+
capture_output=True, text=True, timeout=30)
408+
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
409+
self.assertTrue(marker.is_file(), 'archive fixture bypassed the selected compiler')
410+
commands = [json.loads(line) for line in marker.read_text().splitlines()]
411+
self.assertEqual(sum('-c' in argv for argv in commands), 3)
412+
self.assertEqual(sum('-c' not in argv for argv in commands), 2)
413+
366414
def test_unused_selected_static_archive_cannot_supply_link_identity(self):
367415
import subprocess
368416
compiled, identity, artifacts, commands = self.provenance()
@@ -377,16 +425,17 @@ def test_unused_selected_static_archive_cannot_supply_link_identity(self):
377425
source = folder / 'implementation.cpp'; source.write_text('int implementation() { return %d; }\n' % value)
378426
obj = folder / 'implementation.o'; archive = folder / 'libhttpserver.a'
379427
archive.unlink(missing_ok=True)
380-
subprocess.run(['c++', '-c', str(source), '-o', str(obj)], check=True, capture_output=True)
428+
subprocess.run(shlex.split(identity['compiler']) + ['-c', str(source), '-o', str(obj)], check=True, capture_output=True)
381429
subprocess.run(['ar', 'rcs', str(archive), str(obj)], check=True, capture_output=True)
382430
libraries.append(str(archive))
383-
commands[0]['argv'].insert(2, '-I' + str(Path(identity['prefix']) / 'include'))
384-
commands[1]['argv'][2:3] = libraries
385-
for command in commands: subprocess.run(command['argv'], check=True, capture_output=True)
431+
from package_commands import Commands
432+
from package_integration import compile_probe
433+
command = Commands(directory)
434+
compiled = compile_probe(identity['compiler'], [unit], ['-I' + str(Path(identity['prefix']) / 'include')],
435+
libraries, directory / 'static', command, os.environ, self.source,
436+
Path(identity['build']), Path(identity['prefix']), False)
437+
commands = command.records
386438
self.assertEqual(subprocess.run([str(directory / 'static')]).returncode, 2)
387-
compiled['sources'] = {str(unit): bench.artifact(unit)['sha256']}
388-
compiled['dependency_sha256'] = {str(p): bench.artifact(p)['sha256'] for p in (unit, header)}
389-
compiled['artifact_sha256'] = bench.artifact(directory / 'static')['sha256']
390439
identity['archive']['members']['test/installed/consumer.cpp'] = compiled['sources'][str(unit)]
391440
identity['package'] = {p: bench.artifact(p)['sha256'] for p in identity['package']}
392441
artifacts = {str(p): bench.artifact(p) for p in directory.iterdir() if p.is_file()}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# FEATURE-ci-darwin-canonical-fixtures
2+
3+
Status: In Progress
4+
Base: local v3 `1fa030dd528e67218d7c0a6957371ba246679e96`
5+
6+
Repair the exact known Darwin Python fixture root aliases, executable discovery,
7+
selected compiler and native linker-map setup failures. Keep original child names,
8+
symlink/tampering controls and production provenance policies intact.
9+
10+
- [x] Reproduce known identity failures with real symlinked TMPDIR and actual validators.
11+
- [x] Resolve affected fixture roots before constructing identities/catalogs.
12+
- [x] Discover the success executable and use the selected compiler/native map format.
13+
- [x] Verify affected suites, syntax and diff; preserve negative controls.
14+
- [ ] Caller validation and finalization.
15+
16+
Evidence: primary `.debug/ci-darwin-canonical-fixtures-2shorq1l/journal.md`.
17+
Native post-fix Darwin, Windows and FreeBSD execution remains CI/v3 PR owned.
18+
No C++ investigations, runtime changes, full suite, SDK/Bash rebuilds or CI controls.
19+
20+
Caller-approved owner expansion: `scripts/lib/release_package_provenance.py`
21+
`validate_compile` now recognizes the native Darwin linker option and contributing
22+
object-file table entries. GNU handling and exact installed archive/security bindings
23+
are preserved; actual real compilation uses unchanged `package_integration.compile_probe`.
24+
25+
Verification: all six affected suites pass locally (155 tests including six real
26+
symlinked TMPDIR regressions), plus final focused reruns after test refactoring.
27+
Python byte-compilation and `git diff --check` pass. No standalone Python linter is
28+
documented; the repository cpplint command applies to C++, which is unchanged.
29+
RED/GREEN logs cover each known alias symptom, selected executable/compiler and native
30+
map grammar. No authentic retained Darwin map exists in the selected evidence tree;
31+
small ld64 format tests prove parser behavior only. Real local GNU map/archive controls
32+
prove foreign-first rejection and selected-only acceptance. Native post-fix platform
33+
checks remain incomplete and CI/v3 PR owned. Older C++ uncertainties remain closed.

‎test/integ/release_artifact_test.py‎

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def setUpClass(cls):
2626
def setUp(self):
2727
self.assertTrue(HELPER.is_file(), 'verified release-artifact identity helper missing')
2828
self.tmp = tempfile.TemporaryDirectory(); self.addCleanup(self.tmp.cleanup)
29-
self.directory = Path(self.tmp.name)
29+
self.directory = Path(self.tmp.name).resolve()
3030
self.source = self.directory / 'source'; self.source.mkdir()
3131
(self.source / 'configure.ac').write_text('packaged source')
3232
self.archive = self.directory / 'release.tar.gz'; self.archive.write_bytes(b'archive identity')
@@ -111,4 +111,44 @@ def test_distribution_adapters_accept_verified_identity_instead_of_git(self):
111111
self.assertIn('release_artifact.py', (ROOT / 'scripts' / name).read_text())
112112

113113

114+
class CanonicalFixtureContracts(unittest.TestCase):
115+
def check_fixture(self, script, *cases):
116+
with tempfile.TemporaryDirectory() as directory:
117+
root = Path(directory).resolve()
118+
target = root / 'native-tmp'; target.mkdir()
119+
alias = root / 'tmp'; alias.symlink_to(target, target_is_directory=True)
120+
self.assertNotEqual(alias, alias.resolve())
121+
result = subprocess.run([sys.executable, str(ROOT / script), *cases],
122+
env=dict(os.environ, TMPDIR=str(alias), TEMP=str(alias), TMP=str(alias)),
123+
capture_output=True, text=True, timeout=60)
124+
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
125+
126+
def test_archive_parity_through_symlinked_tmpdir(self):
127+
self.check_fixture('test/integ/release_artifact_test.py', 'ArchiveContracts')
128+
129+
def test_release_execution_through_symlinked_tmpdir(self):
130+
self.check_fixture('test/integ/release_gate_runner_test.py',
131+
'ReleaseContracts.test_packaged_families_execute_both_tls_lanes_and_independent_full_validation',
132+
'ReleaseContracts.test_packaged_family_cannot_pass_when_independent_validation_rejects',
133+
'ReleaseContracts.test_failed_stage_retains_its_original_command_log',
134+
'ReleaseContracts.test_package_receipt_survives_later_native_build_and_check',
135+
'ReleaseContracts.test_cxx_gate_bounds_each_native_test_before_waiting_for_aggregate')
136+
137+
def test_benchmark_provenance_through_symlinked_tmpdir(self):
138+
self.check_fixture('scripts/test-v3-native-benchmark.py', 'ReceiptTest')
139+
140+
def test_public_compile_and_receipts_through_symlinked_tmpdir(self):
141+
self.check_fixture('scripts/test-v3-packaged-adapters.py',
142+
'AdapterContracts.test_real_public_compile_artifacts_are_rechecked',
143+
'AdapterContracts.test_unused_selected_static_archive_cannot_supply_link_identity',
144+
'AdapterContracts.test_h1_requires_exact_versions_and_two_persistent_pipeline_responses',
145+
'AdapterContracts.test_unsafe_duplicate_and_symlink_archive_members_are_rejected')
146+
147+
def test_selected_windows_shell_through_symlinked_tmpdir(self):
148+
self.check_fixture('test/integ/release_ci_test.py', 'RequestShellContracts')
149+
150+
def test_platform_origin_and_tool_aliases_through_symlinked_tmpdir(self):
151+
self.check_fixture('test/integ/release_platform_bundle_test.py', 'PlatformBundles')
152+
153+
114154
if __name__ == '__main__': unittest.main()

‎test/integ/release_ci_test.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def setUp(self):
2121
import subprocess
2222
self.temp = tempfile.TemporaryDirectory()
2323
self.addCleanup(self.temp.cleanup)
24-
self.root = Path(self.temp.name)
24+
self.root = Path(self.temp.name).resolve()
2525
self.selected = self.root / 'MSYS tools'
2626
self.selected.mkdir()
2727
self.bash = self.selected / ('bash.exe' if os.name == 'nt' else 'bash')

0 commit comments

Comments
 (0)