Skip to content

Commit 0c8b6b5

Browse files
partoufclaude
andcommitted
Improve build test artifact detection and cmake package install
- Prefer simple artifact filenames (1 dot) over versioned ones in link.txt - Derive link targets from artifacts instead of raw cmake target names - Enable package_install for static/shared/cshared cmake libraries so cmake --install runs and places artifacts properly - Detect generated headers in build dir and inform user about package_install - Make --debug conditional on debug flag in build test commands - Use --dest to place ce_install staging in wizard temp directory - Preserve temp directory on build test failure for debugging - Search all staging dirs for artifacts, warn about empty ones - Abort on missing link libraries instead of creating PRs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 793b865 commit 0c8b6b5

3 files changed

Lines changed: 188 additions & 36 deletions

File tree

cli/main.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ def process_cpp_library(
123123
build_result = cpp_handler.run_build_test(library_id, config.version)
124124
if not build_result.success:
125125
click.echo("❌ Build test failed.", err=True)
126+
if build_result.staging_dir:
127+
git_mgr.keep_temp = True
128+
click.echo(
129+
f" Build directory: {build_result.staging_dir}",
130+
err=True,
131+
)
126132
if is_auto_mode:
127133
click.echo(
128134
" 💡 Hint: Use --build-test=no to skip build testing "
@@ -143,6 +149,14 @@ def process_cpp_library(
143149
f" ⚠️ Missing: {', '.join(build_result.missing_links)}",
144150
err=True,
145151
)
152+
if build_result.staging_dir:
153+
git_mgr.keep_temp = True
154+
click.echo(
155+
f" Build directory: {build_result.staging_dir}",
156+
err=True,
157+
)
158+
click.echo("Aborting.", err=True)
159+
return
146160

147161
# Show diffs if verify or dry_run flag is set
148162
if verify or dry_run:
@@ -292,6 +306,12 @@ def process_rust_library(
292306
build_result = rust_handler.run_build_test(config.name, config.version)
293307
if not build_result.success:
294308
click.echo("❌ Build test failed.", err=True)
309+
if build_result.staging_dir:
310+
git_mgr.keep_temp = True
311+
click.echo(
312+
f" Build directory: {build_result.staging_dir}",
313+
err=True,
314+
)
295315
if is_auto_mode:
296316
click.echo(
297317
" 💡 Hint: Use --build-test=no to skip build testing "
@@ -460,6 +480,12 @@ def process_c_library(
460480
build_result = c_handler.run_build_test(library_id, config.version)
461481
if not build_result.success:
462482
click.echo("❌ Build test failed.", err=True)
483+
if build_result.staging_dir:
484+
git_mgr.keep_temp = True
485+
click.echo(
486+
f" Build directory: {build_result.staging_dir}",
487+
err=True,
488+
)
463489
if is_auto_mode:
464490
click.echo(
465491
" 💡 Hint: Use --build-test=no to skip build testing "
@@ -480,6 +506,14 @@ def process_c_library(
480506
f" ⚠️ Missing: {', '.join(build_result.missing_links)}",
481507
err=True,
482508
)
509+
if build_result.staging_dir:
510+
git_mgr.keep_temp = True
511+
click.echo(
512+
f" Build directory: {build_result.staging_dir}",
513+
err=True,
514+
)
515+
click.echo("Aborting.", err=True)
516+
return
483517

484518
# Show diffs if verify or dry_run flag is set
485519
if verify or dry_run:
@@ -800,6 +834,12 @@ def process_fortran_library(
800834
build_result = fortran_handler.run_build_test(library_id, config.version)
801835
if not build_result.success:
802836
click.echo("❌ Build test failed.", err=True)
837+
if build_result.staging_dir:
838+
git_mgr.keep_temp = True
839+
click.echo(
840+
f" Build directory: {build_result.staging_dir}",
841+
err=True,
842+
)
803843
if is_auto_mode:
804844
click.echo(
805845
" 💡 Hint: Use --build-test=no to skip build testing "
@@ -989,6 +1029,12 @@ def process_go_library(
9891029
build_result = go_handler.run_build_test(library_id, config.version)
9901030
if not build_result.success:
9911031
click.echo("Build test failed.", err=True)
1032+
if build_result.staging_dir:
1033+
git_mgr.keep_temp = True
1034+
click.echo(
1035+
f" Build directory: {build_result.staging_dir}",
1036+
err=True,
1037+
)
9921038
if is_auto_mode:
9931039
click.echo(
9941040
" Hint: Use --build-test=no to skip build testing "
@@ -1264,13 +1310,20 @@ def main(
12641310
exit(1)
12651311

12661312
# Set package install based on type and flag
1267-
if config.library_type == LibraryType.PACKAGED_HEADERS:
1268-
# packaged-headers always uses package installation
1313+
if config.library_type in (
1314+
LibraryType.PACKAGED_HEADERS,
1315+
LibraryType.STATIC,
1316+
LibraryType.SHARED,
1317+
LibraryType.CSHARED,
1318+
):
12691319
config.package_install = True
1270-
click.echo("✓ Using CMake package installation (default for packaged-headers)")
1320+
click.echo(
1321+
f"✓ Using CMake package installation"
1322+
f" (default for {config.library_type.value})"
1323+
)
12711324
elif package_install:
12721325
config.package_install = True
1273-
click.echo("✓ Using CMake package installation for headers")
1326+
click.echo("✓ Using CMake package installation")
12741327

12751328
config.validate_versions_and_exit_on_missing()
12761329
else:

core/build_tester.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,17 @@ def get_latest_compiler(
257257

258258

259259
def _find_staging_dirs(output: str) -> list[str]:
260-
"""Extract staging directory paths from build output."""
261-
# Pattern: /tmp/ce-cefs-temp/staging/<uuid>
262-
pattern = r"/tmp/ce-cefs-temp/staging/[a-f0-9-]+"
263-
matches = re.findall(pattern, output)
264-
# Return unique paths
265-
return list(set(matches))
260+
"""Extract staging directory paths from build output.
261+
262+
Returns unique paths, preferring directories that contain an install/
263+
subdirectory or build artifacts over empty ones.
264+
"""
265+
# Pattern: <any-path>/staging/<uuid>
266+
pattern = r"/\S+/staging/[a-f0-9-]+"
267+
matches = list(set(re.findall(pattern, output)))
268+
# Sort so dirs with content (install/ or any subdirs) come first
269+
matches.sort(key=lambda d: (not Path(d, "install").exists(), d))
270+
return matches
266271

267272

268273
def _list_artifacts(install_dir: Path) -> list[str]:
@@ -424,11 +429,13 @@ def run_build_test(
424429
library_spec = f"libraries/{language}/{library_id} {version}"
425430

426431
# Build the command
427-
# Command: bin/ce_install --debug --dry-run --keep-staging --force-traditional build
428-
# --temp-install --buildfor <compiler_id> '<library_spec>'
432+
# bin/ce_install [--debug] --dry-run --dest <dir> --keep-staging
433+
# --force-traditional build --temp-install --buildfor <id> '<spec>'
434+
dest_path = str(infra_path.parent / "build-test")
429435
subcommand = [
430-
"--debug",
431436
"--dry-run",
437+
"--dest",
438+
dest_path,
432439
"--keep-staging",
433440
"--force-traditional",
434441
"build",
@@ -437,6 +444,8 @@ def run_build_test(
437444
compiler_id,
438445
library_spec,
439446
]
447+
if debug:
448+
subcommand.insert(0, "--debug")
440449

441450
logger.info(f"Running build test for {library_id} {version} with {compiler_id}...")
442451
logger.info(f"Command: bin/ce_install {' '.join(subcommand)}")
@@ -445,29 +454,53 @@ def run_build_test(
445454

446455
output = f"{result.stdout}\n{result.stderr}".strip()
447456

457+
# Parse staging directories from output
458+
staging_dirs = _find_staging_dirs(output)
459+
staging_dir = staging_dirs[0] if staging_dirs else None
460+
448461
if result.returncode != 0:
449462
logger.error(f"Build test failed with exit code {result.returncode}")
463+
if staging_dir:
464+
logger.error(f"Build directory preserved at: {staging_dir}")
450465
if debug:
451466
logger.debug(f"Output: {output}")
452467
return BuildTestResult(
453468
success=False,
454469
message=f"Build test failed: {output}",
455470
compiler_id=compiler_id,
471+
staging_dir=staging_dir,
456472
)
457-
458-
# Parse staging directories from output
459-
staging_dirs = _find_staging_dirs(output)
460-
staging_dir = staging_dirs[0] if staging_dirs else None
461473
install_dir = None
462474
artifacts: list[str] = []
463475

464-
if staging_dir:
465-
# Check for install directory
466-
install_path = Path(staging_dir) / "install"
476+
# Search all staging dirs for artifacts
477+
for sdir in staging_dirs:
478+
staging_path = Path(sdir)
479+
# Check for install directory (used when package_install is set)
480+
install_path = staging_path / "install"
467481
if install_path.exists():
468-
install_dir = str(install_path)
469-
artifacts = _list_artifacts(install_path)
470-
logger.info(f"Found {len(artifacts)} artifacts in {install_dir}")
482+
found = _list_artifacts(install_path)
483+
if found:
484+
install_dir = str(install_path)
485+
artifacts = found
486+
logger.info(f"Found {len(found)} artifacts in {install_dir}")
487+
else:
488+
logger.warning(f"Empty install directory in {sdir}")
489+
continue
490+
# Fall back to searching build subdirs (no package_install)
491+
found_in_subdir = False
492+
for subdir in sorted(staging_path.iterdir()):
493+
if subdir.is_dir():
494+
found = _list_artifacts(subdir)
495+
if found:
496+
if not artifacts:
497+
install_dir = str(subdir)
498+
artifacts = found
499+
logger.info(f"Found {len(found)} artifacts in {subdir}")
500+
found_in_subdir = True
501+
break
502+
if not found_in_subdir:
503+
logger.warning(f"No build artifacts found in {sdir}")
471504

472505
# Verify link libraries
473506
static_links, shared_links = _get_expected_link_libraries(infra_path, library_id, language)
@@ -716,9 +749,11 @@ def run_rust_build_test(
716749
library_spec = f"libraries/rust/{crate_name} {version}"
717750

718751
# Build the command
752+
dest_path = str(infra_path.parent / "build-test")
719753
subcommand = [
720-
"--debug",
721754
"--dry-run",
755+
"--dest",
756+
dest_path,
722757
"--keep-staging",
723758
"--force-traditional",
724759
"build",
@@ -727,6 +762,8 @@ def run_rust_build_test(
727762
compiler_id,
728763
library_spec,
729764
]
765+
if debug:
766+
subcommand.insert(0, "--debug")
730767

731768
logger.info(f"Running Rust build test for {crate_name} {version} with {compiler_id}...")
732769
logger.info(f"Command: bin/ce_install {' '.join(subcommand)}")
@@ -979,8 +1016,10 @@ def run_fortran_build_test(
9791016

9801017
# Build the command - note: we can't use --dry-run for fpm libraries
9811018
# as they need to be "installed" (source extracted) to verify
1019+
dest_path = str(infra_path.parent / "build-test")
9821020
subcommand = [
983-
"--debug",
1021+
"--dest",
1022+
dest_path,
9841023
"--keep-staging",
9851024
"--force-traditional",
9861025
"build",
@@ -989,6 +1028,8 @@ def run_fortran_build_test(
9891028
compiler_id,
9901029
library_spec,
9911030
]
1031+
if debug:
1032+
subcommand.insert(0, "--debug")
9921033

9931034
logger.info(f"Running Fortran build test for {library_id} {version}...")
9941035
logger.info(f"Command: bin/ce_install {' '.join(subcommand)}")
@@ -1224,8 +1265,10 @@ def run_go_build_test(
12241265
library_spec = f"libraries/go/{library_id} {version}"
12251266

12261267
# Build the command
1268+
dest_path = str(infra_path.parent / "build-test")
12271269
subcommand = [
1228-
"--debug",
1270+
"--dest",
1271+
dest_path,
12291272
"--keep-staging",
12301273
"--force-traditional",
12311274
"build",
@@ -1234,6 +1277,8 @@ def run_go_build_test(
12341277
compiler_id,
12351278
library_spec,
12361279
]
1280+
if debug:
1281+
subcommand.insert(0, "--debug")
12371282

12381283
logger.info(f"Running Go build test for {library_id} {version} with {compiler_id}...")
12391284
logger.info(f"Command: bin/ce_install {' '.join(subcommand)}")

0 commit comments

Comments
 (0)