File: update_ctest_path.py

package info (click to toggle)
onnxruntime 1.23.2%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 340,756 kB
  • sloc: cpp: 3,222,136; python: 188,267; ansic: 114,318; asm: 37,927; cs: 36,849; java: 10,962; javascript: 6,811; pascal: 4,126; sh: 2,996; xml: 705; objc: 281; makefile: 67
file content (132 lines) | stat: -rw-r--r-- 5,437 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python3

# We have some Azure DevOps pipelines that each has two parts: the first part
# builds source code into binary then uploads it, then the second part downloads
# the binary and runs tests. Because the second part may need GPUs. We use
# CMake and CTest to run tests. However, in the first part cmake generates a
# file called CTestTestfile.cmake. And it has hardcoded file paths. This
# script is to update the absolute paths.

import argparse
import os
import re
import sys
from pathlib import Path


def update_ctest_paths(file_path: Path, new_repo_dir: Path, new_build_dir: Path):
    """
    Finds and replaces hardcoded source and build directory paths in a
    CTestTestfile.cmake file by processing it line-by-line.

    Args:
        file_path: The path to the CTestTestfile.cmake file.
        new_repo_dir: The path to the new repository root directory.
        new_build_dir: The path to the new build directory.
    """
    print(f"Processing file: {file_path}")

    # --- Step 1: Find old paths from the file header ---
    header_src_pattern = re.compile(r"# Source directory: (.*)")
    header_build_pattern = re.compile(r"# Build directory: (.*)")

    old_src_dir = None
    old_build_dir = None

    with file_path.open("r", encoding="utf-8") as f:
        # The paths are usually in the first few lines
        for line in f:
            if not old_src_dir and (src_match := header_src_pattern.match(line)):
                # The path in the file points to a subdirectory (e.g., .../s/cmake)
                # The actual source root is its parent.
                cmake_subdir = Path(src_match.group(1).strip())
                old_src_dir = str(cmake_subdir.parent.as_posix())
                print(f"Found CMake source sub-directory: '{cmake_subdir}'")
            elif not old_build_dir and (build_match := header_build_pattern.match(line)):
                old_build_dir = build_match.group(1).strip()

            if old_src_dir and old_build_dir:
                break

    if not old_src_dir or not old_build_dir:
        print(f"Error: Could not find old source/build directory paths in {file_path}", file=sys.stderr)
        sys.exit(1)

    print(f"Deduced old source root: '{old_src_dir}'")
    print(f"Found old build directory: '{old_build_dir}'")

    # --- Step 2: Pre-compile regex for path replacement ---
    # Normalize new paths to use forward slashes, which is CMake's preference.
    new_src_posix = new_repo_dir.as_posix()
    new_build_posix = new_build_dir.as_posix()

    print(f"Replacing with new source directory: '{new_src_posix}'")
    print(f"Replacing with new build directory: '{new_build_posix}'")

    # We need to handle both forward-slash (e.g., C:/path) and
    # escaped backslash (e.g., C:\\path) variants of the old paths.
    # We use re.escape to ensure path characters are treated literally.
    build_fwd_re = re.compile(re.escape(old_build_dir))
    build_bwd_re = re.compile(re.escape(old_build_dir.replace("/", "\\\\")))
    src_fwd_re = re.compile(re.escape(old_src_dir))
    src_bwd_re = re.compile(re.escape(old_src_dir.replace("/", "\\\\")))

    # --- Step 3: Process file line-by-line and write to a temp file ---
    temp_file_path = file_path.with_suffix(f"{file_path.suffix}.tmp")
    try:
        with file_path.open("r", encoding="utf-8") as infile, temp_file_path.open("w", encoding="utf-8") as outfile:
            for input_line in infile:
                # It's safer to replace the longer, more specific build path first.
                line = build_fwd_re.sub(new_build_posix, input_line)
                line = build_bwd_re.sub(new_build_posix, line)
                line = src_fwd_re.sub(new_src_posix, line)
                line = src_bwd_re.sub(new_src_posix, line)
                outfile.write(line)
    except OSError as e:
        print(f"Error during file processing: {e}", file=sys.stderr)
        sys.exit(1)

    # --- Step 4: Atomically replace the original file with the temp file ---
    try:
        os.replace(temp_file_path, file_path)
    except OSError as e:
        print(f"Error replacing file: {e}", file=sys.stderr)
        # Clean up the temp file if replacement fails
        if temp_file_path.exists():
            os.remove(temp_file_path)
        sys.exit(1)

    print(f"Successfully updated paths in {file_path}")


SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
REPO_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", ".."))


def main():
    """Parses command-line arguments and runs the update logic."""

    parser = argparse.ArgumentParser(
        description="Update hardcoded paths in a CTestTestfile.cmake file for relocatable test execution.",
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument("ctest_file", type=Path, help="Path to the CTestTestfile.cmake file to be updated.")
    parser.add_argument(
        "new_build_dir", type=Path, help="The new build directory path (e.g., $(Pipeline.Workspace)/b)."
    )

    args = parser.parse_args()

    if not args.ctest_file.is_file():
        print(f"Error: CTest file not found at '{args.ctest_file}'", file=sys.stderr)
        sys.exit(1)

    try:
        update_ctest_paths(args.ctest_file, Path(REPO_DIR), args.new_build_dir)
    except Exception as e:
        print(f"An unexpected error occurred: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()