xref: /aosp_15_r20/external/pytorch/tools/build_with_debinfo.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1*da0073e9SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*da0073e9SAndroid Build Coastguard Worker# Tool quickly rebuild one or two files with debug info
3*da0073e9SAndroid Build Coastguard Worker# Mimics following behavior:
4*da0073e9SAndroid Build Coastguard Worker# - touch file
5*da0073e9SAndroid Build Coastguard Worker# - ninja -j1 -v -n torch_python | sed -e 's/-O[23]/-g/g' -e 's#\[[0-9]\+\/[0-9]\+\] \+##' |sh
6*da0073e9SAndroid Build Coastguard Worker# - Copy libs from build/lib to torch/lib folder
7*da0073e9SAndroid Build Coastguard Worker
8*da0073e9SAndroid Build Coastguard Workerfrom __future__ import annotations
9*da0073e9SAndroid Build Coastguard Worker
10*da0073e9SAndroid Build Coastguard Workerimport subprocess
11*da0073e9SAndroid Build Coastguard Workerimport sys
12*da0073e9SAndroid Build Coastguard Workerfrom pathlib import Path
13*da0073e9SAndroid Build Coastguard Workerfrom typing import Any
14*da0073e9SAndroid Build Coastguard Worker
15*da0073e9SAndroid Build Coastguard Worker
16*da0073e9SAndroid Build Coastguard WorkerPYTORCH_ROOTDIR = Path(__file__).resolve().parent.parent
17*da0073e9SAndroid Build Coastguard WorkerTORCH_DIR = PYTORCH_ROOTDIR / "torch"
18*da0073e9SAndroid Build Coastguard WorkerTORCH_LIB_DIR = TORCH_DIR / "lib"
19*da0073e9SAndroid Build Coastguard WorkerBUILD_DIR = PYTORCH_ROOTDIR / "build"
20*da0073e9SAndroid Build Coastguard WorkerBUILD_LIB_DIR = BUILD_DIR / "lib"
21*da0073e9SAndroid Build Coastguard Worker
22*da0073e9SAndroid Build Coastguard Worker
23*da0073e9SAndroid Build Coastguard Workerdef check_output(args: list[str], cwd: str | None = None) -> str:
24*da0073e9SAndroid Build Coastguard Worker    return subprocess.check_output(args, cwd=cwd).decode("utf-8")
25*da0073e9SAndroid Build Coastguard Worker
26*da0073e9SAndroid Build Coastguard Worker
27*da0073e9SAndroid Build Coastguard Workerdef parse_args() -> Any:
28*da0073e9SAndroid Build Coastguard Worker    from argparse import ArgumentParser
29*da0073e9SAndroid Build Coastguard Worker
30*da0073e9SAndroid Build Coastguard Worker    parser = ArgumentParser(description="Incremental build PyTorch with debinfo")
31*da0073e9SAndroid Build Coastguard Worker    parser.add_argument("--verbose", action="store_true")
32*da0073e9SAndroid Build Coastguard Worker    parser.add_argument("files", nargs="*")
33*da0073e9SAndroid Build Coastguard Worker    return parser.parse_args()
34*da0073e9SAndroid Build Coastguard Worker
35*da0073e9SAndroid Build Coastguard Worker
36*da0073e9SAndroid Build Coastguard Workerdef get_lib_extension() -> str:
37*da0073e9SAndroid Build Coastguard Worker    if sys.platform == "linux":
38*da0073e9SAndroid Build Coastguard Worker        return "so"
39*da0073e9SAndroid Build Coastguard Worker    if sys.platform == "darwin":
40*da0073e9SAndroid Build Coastguard Worker        return "dylib"
41*da0073e9SAndroid Build Coastguard Worker    raise RuntimeError(f"Usupported platform {sys.platform}")
42*da0073e9SAndroid Build Coastguard Worker
43*da0073e9SAndroid Build Coastguard Worker
44*da0073e9SAndroid Build Coastguard Workerdef create_symlinks() -> None:
45*da0073e9SAndroid Build Coastguard Worker    """Creates symlinks from build/lib to torch/lib"""
46*da0073e9SAndroid Build Coastguard Worker    if not TORCH_LIB_DIR.exists():
47*da0073e9SAndroid Build Coastguard Worker        raise RuntimeError(f"Can't create symlinks as {TORCH_LIB_DIR} does not exist")
48*da0073e9SAndroid Build Coastguard Worker    if not BUILD_LIB_DIR.exists():
49*da0073e9SAndroid Build Coastguard Worker        raise RuntimeError(f"Can't create symlinks as {BUILD_LIB_DIR} does not exist")
50*da0073e9SAndroid Build Coastguard Worker    for torch_lib in TORCH_LIB_DIR.glob(f"*.{get_lib_extension()}"):
51*da0073e9SAndroid Build Coastguard Worker        if torch_lib.is_symlink():
52*da0073e9SAndroid Build Coastguard Worker            continue
53*da0073e9SAndroid Build Coastguard Worker        build_lib = BUILD_LIB_DIR / torch_lib.name
54*da0073e9SAndroid Build Coastguard Worker        if not build_lib.exists():
55*da0073e9SAndroid Build Coastguard Worker            raise RuntimeError(f"Can't find {build_lib} corresponding to {torch_lib}")
56*da0073e9SAndroid Build Coastguard Worker        torch_lib.unlink()
57*da0073e9SAndroid Build Coastguard Worker        torch_lib.symlink_to(build_lib)
58*da0073e9SAndroid Build Coastguard Worker
59*da0073e9SAndroid Build Coastguard Worker
60*da0073e9SAndroid Build Coastguard Workerdef has_build_ninja() -> bool:
61*da0073e9SAndroid Build Coastguard Worker    return (BUILD_DIR / "build.ninja").exists()
62*da0073e9SAndroid Build Coastguard Worker
63*da0073e9SAndroid Build Coastguard Worker
64*da0073e9SAndroid Build Coastguard Workerdef is_devel_setup() -> bool:
65*da0073e9SAndroid Build Coastguard Worker    output = check_output([sys.executable, "-c", "import torch;print(torch.__file__)"])
66*da0073e9SAndroid Build Coastguard Worker    return output.strip() == str(TORCH_DIR / "__init__.py")
67*da0073e9SAndroid Build Coastguard Worker
68*da0073e9SAndroid Build Coastguard Worker
69*da0073e9SAndroid Build Coastguard Workerdef create_build_plan() -> list[tuple[str, str]]:
70*da0073e9SAndroid Build Coastguard Worker    output = check_output(
71*da0073e9SAndroid Build Coastguard Worker        ["ninja", "-j1", "-v", "-n", "torch_python"], cwd=str(BUILD_DIR)
72*da0073e9SAndroid Build Coastguard Worker    )
73*da0073e9SAndroid Build Coastguard Worker    rc = []
74*da0073e9SAndroid Build Coastguard Worker    for line in output.split("\n"):
75*da0073e9SAndroid Build Coastguard Worker        if not line.startswith("["):
76*da0073e9SAndroid Build Coastguard Worker            continue
77*da0073e9SAndroid Build Coastguard Worker        line = line.split("]", 1)[1].strip()
78*da0073e9SAndroid Build Coastguard Worker        if line.startswith(": &&") and line.endswith("&& :"):
79*da0073e9SAndroid Build Coastguard Worker            line = line[4:-4]
80*da0073e9SAndroid Build Coastguard Worker        line = line.replace("-O2", "-g").replace("-O3", "-g")
81*da0073e9SAndroid Build Coastguard Worker        name = line.split("-o ", 1)[1].split(" ")[0]
82*da0073e9SAndroid Build Coastguard Worker        rc.append((name, line))
83*da0073e9SAndroid Build Coastguard Worker    return rc
84*da0073e9SAndroid Build Coastguard Worker
85*da0073e9SAndroid Build Coastguard Worker
86*da0073e9SAndroid Build Coastguard Workerdef main() -> None:
87*da0073e9SAndroid Build Coastguard Worker    if sys.platform == "win32":
88*da0073e9SAndroid Build Coastguard Worker        print("Not supported on Windows yet")
89*da0073e9SAndroid Build Coastguard Worker        sys.exit(-95)
90*da0073e9SAndroid Build Coastguard Worker    if not is_devel_setup():
91*da0073e9SAndroid Build Coastguard Worker        print(
92*da0073e9SAndroid Build Coastguard Worker            "Not a devel setup of PyTorch, please run `python3 setup.py develop --user` first"
93*da0073e9SAndroid Build Coastguard Worker        )
94*da0073e9SAndroid Build Coastguard Worker        sys.exit(-1)
95*da0073e9SAndroid Build Coastguard Worker    if not has_build_ninja():
96*da0073e9SAndroid Build Coastguard Worker        print("Only ninja build system is supported at the moment")
97*da0073e9SAndroid Build Coastguard Worker        sys.exit(-1)
98*da0073e9SAndroid Build Coastguard Worker    args = parse_args()
99*da0073e9SAndroid Build Coastguard Worker    for file in args.files:
100*da0073e9SAndroid Build Coastguard Worker        if file is None:
101*da0073e9SAndroid Build Coastguard Worker            continue
102*da0073e9SAndroid Build Coastguard Worker        Path(file).touch()
103*da0073e9SAndroid Build Coastguard Worker    build_plan = create_build_plan()
104*da0073e9SAndroid Build Coastguard Worker    if len(build_plan) == 0:
105*da0073e9SAndroid Build Coastguard Worker        return print("Nothing to do")
106*da0073e9SAndroid Build Coastguard Worker    if len(build_plan) > 100:
107*da0073e9SAndroid Build Coastguard Worker        print("More than 100 items needs to be rebuild, run `ninja torch_python` first")
108*da0073e9SAndroid Build Coastguard Worker        sys.exit(-1)
109*da0073e9SAndroid Build Coastguard Worker    for idx, (name, cmd) in enumerate(build_plan):
110*da0073e9SAndroid Build Coastguard Worker        print(f"[{idx + 1 } / {len(build_plan)}] Building {name}")
111*da0073e9SAndroid Build Coastguard Worker        if args.verbose:
112*da0073e9SAndroid Build Coastguard Worker            print(cmd)
113*da0073e9SAndroid Build Coastguard Worker        subprocess.check_call(["sh", "-c", cmd], cwd=BUILD_DIR)
114*da0073e9SAndroid Build Coastguard Worker    create_symlinks()
115*da0073e9SAndroid Build Coastguard Worker
116*da0073e9SAndroid Build Coastguard Worker
117*da0073e9SAndroid Build Coastguard Workerif __name__ == "__main__":
118*da0073e9SAndroid Build Coastguard Worker    main()
119