main
py 179 lines 6.23 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import argparse
4 import json
5 import multiprocessing
6 import os
7 import sys
8 from shutil import which
9
10 try:
11 # Try to get CUDA_HOME from PyTorch installation, which is the
12 # most reliable source of truth for vLLM's build.
13 from torch.utils.cpp_extension import CUDA_HOME
14 except ImportError:
15 print("Warning: PyTorch not found. Falling back to CUDA_HOME environment variable.")
16 CUDA_HOME = os.environ.get("CUDA_HOME")
17
18
19 def get_python_executable():
20 """Get the current Python executable, which is used to run this script."""
21 return sys.executable
22
23
24 def get_cpu_cores():
25 """Get the number of CPU cores."""
26 return multiprocessing.cpu_count()
27
28
29 def generate_presets(output_path="CMakeUserPresets.json", force_overwrite=False):
30 """Generates the CMakeUserPresets.json file."""
31 print("Attempting to detect your system configuration...")
32
33 # Detect NVCC
34 nvcc_path = None
35 if CUDA_HOME:
36 prospective_path = os.path.join(CUDA_HOME, "bin", "nvcc")
37 if os.path.exists(prospective_path):
38 nvcc_path = prospective_path
39 print(f"Found nvcc via torch.utils.cpp_extension.CUDA_HOME: {nvcc_path}")
40
41 if not nvcc_path:
42 nvcc_path = which("nvcc")
43 if nvcc_path:
44 print(f"Found nvcc in PATH: {nvcc_path}")
45
46 if not nvcc_path:
47 nvcc_path_input = input(
48 "Could not automatically find 'nvcc'. Please provide the full "
49 "path to nvcc (e.g., /usr/local/cuda/bin/nvcc): "
50 )
51 nvcc_path = nvcc_path_input.strip()
52 print(f"Using NVCC path: {nvcc_path}")
53
54 # Detect Python executable
55 python_executable = get_python_executable()
56 if python_executable:
57 print(f"Found Python via sys.executable: {python_executable}")
58 else:
59 python_executable_prompt = (
60 "Could not automatically find Python executable. Please provide "
61 "the full path to your Python executable for vLLM development "
62 "(typically from your virtual environment, e.g., "
63 "/home/user/venvs/vllm/bin/python): "
64 )
65 python_executable = input(python_executable_prompt).strip()
66 if not python_executable:
67 raise ValueError(
68 "Could not determine Python executable. Please provide it manually."
69 )
70
71 print(f"Using Python executable: {python_executable}")
72
73 # Get CPU cores
74 cpu_cores = get_cpu_cores()
75 nvcc_threads = min(4, cpu_cores)
76 cmake_jobs = max(1, cpu_cores // nvcc_threads)
77 print(
78 f"Detected {cpu_cores} CPU cores. "
79 f"Setting NVCC_THREADS={nvcc_threads} and CMake jobs={cmake_jobs}."
80 )
81
82 # Get vLLM project root (assuming this script is in vllm/tools/)
83 project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
84 print(f"VLLM project root detected as: {project_root}")
85
86 # Ensure python_executable path is absolute or resolvable
87 if not os.path.isabs(python_executable) and which(python_executable):
88 python_executable = os.path.abspath(which(python_executable))
89 elif not os.path.isabs(python_executable):
90 print(
91 f"Warning: Python executable '{python_executable}' is not an "
92 "absolute path and not found in PATH. CMake might not find it."
93 )
94
95 cache_variables = {
96 "CMAKE_CUDA_COMPILER": nvcc_path,
97 "CMAKE_BUILD_TYPE": "Release",
98 "VLLM_PYTHON_EXECUTABLE": python_executable,
99 "CMAKE_INSTALL_PREFIX": "${sourceDir}",
100 "CMAKE_CUDA_FLAGS": "",
101 "NVCC_THREADS": str(nvcc_threads),
102 }
103
104 # Detect compiler cache
105 if which("sccache"):
106 print("Using sccache for compiler caching.")
107 for launcher in ("C", "CXX", "CUDA", "HIP"):
108 cache_variables[f"CMAKE_{launcher}_COMPILER_LAUNCHER"] = "sccache"
109 elif which("ccache"):
110 print("Using ccache for compiler caching.")
111 for launcher in ("C", "CXX", "CUDA", "HIP"):
112 cache_variables[f"CMAKE_{launcher}_COMPILER_LAUNCHER"] = "ccache"
113 else:
114 print("No compiler cache ('ccache' or 'sccache') found.")
115
116 configure_preset = {
117 "name": "release",
118 "binaryDir": "${sourceDir}/cmake-build-release",
119 "cacheVariables": cache_variables,
120 }
121 if which("ninja"):
122 print("Using Ninja generator.")
123 configure_preset["generator"] = "Ninja"
124 cache_variables["CMAKE_JOB_POOLS"] = f"compile={cmake_jobs}"
125 else:
126 print("Ninja not found, using default generator. Build may be slower.")
127
128 presets = {
129 "version": 6,
130 # Keep in sync with CMakeLists.txt and requirements/build/cuda.txt
131 "cmakeMinimumRequired": {"major": 3, "minor": 26, "patch": 1},
132 "configurePresets": [configure_preset],
133 "buildPresets": [
134 {
135 "name": "release",
136 "configurePreset": "release",
137 "jobs": cmake_jobs,
138 }
139 ],
140 }
141
142 output_file_path = os.path.join(project_root, output_path)
143
144 if os.path.exists(output_file_path):
145 if force_overwrite:
146 print(f"Overwriting existing file '{output_file_path}'")
147 else:
148 overwrite = (
149 input(f"'{output_file_path}' already exists. Overwrite? (y/N): ")
150 .strip()
151 .lower()
152 )
153 if overwrite != "y":
154 print("Generation cancelled.")
155 return
156
157 try:
158 with open(output_file_path, "w") as f:
159 json.dump(presets, f, indent=4)
160 print(f"Successfully generated '{output_file_path}'")
161 print("\nTo use this preset:")
162 print(f"1. Ensure you are in the vLLM root directory: cd {project_root}")
163 print("2. Initialize CMake: cmake --preset release")
164 print("3. Build+install: cmake --build --preset release --target install")
165
166 except OSError as e:
167 print(f"Error writing file: {e}")
168
169
170 if __name__ == "__main__":
171 parser = argparse.ArgumentParser()
172 parser.add_argument(
173 "--force-overwrite",
174 action="store_true",
175 help="Force overwrite existing CMakeUserPresets.json without prompting",
176 )
177
178 args = parser.parse_args()
179 generate_presets(force_overwrite=args.force_overwrite)