@@ -52,16 +52,29 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
5252 Detect if a file belongs to this database type and extract bundle key.
5353
5454 Args:
55- path: Path to the file
55+ path: Path to the file (must be under prefix_root)
5656 prefix_root: Root of the prefix for relative path computation
5757
5858 Returns:
5959 Bundle key (e.g., 'gfx1100', 'gfx11', 'gfx12_0') if file matches,
6060 None otherwise. Bundle keys correspond to entries in the
6161 rocm-bootstrap hierarchy.
62+
63+ Raises:
64+ ValueError: If path is not under prefix_root (caller bug).
6265 """
6366 pass
6467
68+ def _relative_path (self , path : Path , prefix_root : Path ) -> str :
69+ """Compute forward-slash relative path, raising on bad input."""
70+ try :
71+ return path .relative_to (prefix_root ).as_posix ()
72+ except ValueError :
73+ raise ValueError (
74+ f"{ self .name ()} handler: path { path } is not under "
75+ f"prefix_root { prefix_root } "
76+ ) from None
77+
6578 def should_move (self , path : Path ) -> bool :
6679 """
6780 Determine if this file should be moved to architecture-specific artifact.
@@ -88,32 +101,26 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
88101
89102 Pattern: lib/rocblas/library/*_gfx*.{co,hsaco,dat}
90103 """
91- try :
92- rel_path = path .relative_to (prefix_root )
93- # Use as_posix() for consistent forward-slash paths on all platforms
94- path_str = rel_path .as_posix ()
95-
96- # Check if it's in rocblas/library directory
97- if "rocblas/library" not in path_str :
98- return None
99-
100- # Check file extension
101- if path .suffix not in [".co" , ".hsaco" , ".dat" ]:
102- return None
103-
104- # Extract architecture from filename
105- # Look for patterns like _gfx1100, _gfx1101, gfx1102, etc.
106- match = _GFX_ARCH_PATTERN .search (path .name )
107- if match :
108- return match .group (0 )
109-
110- # Some .dat files don't have architecture suffix but are generic
111- # We don't move those
104+ path_str = self ._relative_path (path , prefix_root )
105+
106+ # Check if it's in rocblas/library directory
107+ if "rocblas/library" not in path_str :
112108 return None
113109
114- except (ValueError , AttributeError ):
110+ # Check file extension
111+ if path .suffix not in [".co" , ".hsaco" , ".dat" ]:
115112 return None
116113
114+ # Extract architecture from filename
115+ # Look for patterns like _gfx1100, _gfx1101, gfx1102, etc.
116+ match = _GFX_ARCH_PATTERN .search (path .name )
117+ if match :
118+ return match .group (0 )
119+
120+ # Some .dat files don't have architecture suffix but are generic
121+ # We don't move those
122+ return None
123+
117124
118125class HipBLASLtHandler (DatabaseHandler ):
119126 """Handler for hipBLASLt kernel files."""
@@ -127,29 +134,23 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
127134
128135 Pattern: lib/hipblaslt/library/*_gfx*.{co,hsaco,dat}
129136 """
130- try :
131- rel_path = path .relative_to (prefix_root )
132- # Use as_posix() for consistent forward-slash paths on all platforms
133- path_str = rel_path .as_posix ()
134-
135- # Check if it's in hipblaslt/library directory
136- if "hipblaslt/library" not in path_str :
137- return None
138-
139- # Check file extension
140- if path .suffix not in [".co" , ".hsaco" , ".dat" ]:
141- return None
142-
143- # Extract architecture from filename
144- match = _GFX_ARCH_PATTERN .search (path .name )
145- if match :
146- return match .group (0 )
137+ path_str = self ._relative_path (path , prefix_root )
147138
139+ # Check if it's in hipblaslt/library directory
140+ if "hipblaslt/library" not in path_str :
148141 return None
149142
150- except (ValueError , AttributeError ):
143+ # Check file extension
144+ if path .suffix not in [".co" , ".hsaco" , ".dat" ]:
151145 return None
152146
147+ # Extract architecture from filename
148+ match = _GFX_ARCH_PATTERN .search (path .name )
149+ if match :
150+ return match .group (0 )
151+
152+ return None
153+
153154
154155class HipSparseLtHandler (DatabaseHandler ):
155156 """Handler for hipSPARSELt Tensile kernel files."""
@@ -163,25 +164,20 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
163164
164165 Pattern: lib/hipsparselt/library/*_gfx*.{co,hsaco,dat}
165166 """
166- try :
167- rel_path = path .relative_to (prefix_root )
168- path_str = rel_path .as_posix ()
169-
170- if "hipsparselt/library" not in path_str :
171- return None
172-
173- if path .suffix not in [".co" , ".hsaco" , ".dat" ]:
174- return None
175-
176- match = _GFX_ARCH_PATTERN .search (path .name )
177- if match :
178- return match .group (0 )
167+ path_str = self ._relative_path (path , prefix_root )
179168
169+ if "hipsparselt/library" not in path_str :
180170 return None
181171
182- except ( ValueError , AttributeError ) :
172+ if path . suffix not in [ ".co" , ".hsaco" , ".dat" ] :
183173 return None
184174
175+ match = _GFX_ARCH_PATTERN .search (path .name )
176+ if match :
177+ return match .group (0 )
178+
179+ return None
180+
185181
186182class AotritonHandler (DatabaseHandler ):
187183 """Handler for AOTriton kernel image directories.
@@ -219,22 +215,18 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
219215 Returns:
220216 Bundle key (e.g., 'gfx11', 'gfx12_0', 'gfx942') or None.
221217 """
222- try :
223- rel_path = path .relative_to (prefix_root )
224- path_parts = rel_path .parts
218+ path_str = self ._relative_path (path , prefix_root )
219+ path_parts = Path (path_str ).parts
225220
226- for i , part in enumerate (path_parts [:- 1 ]):
227- if part == "aotriton.images" and i + 1 < len (path_parts ):
228- arch_dir = path_parts [i + 1 ]
229- if arch_dir .startswith ("amd-gfx" ):
230- raw_name = arch_dir [4 :] # strip "amd-" prefix
231- return self ._BUNDLE_MAP .get (raw_name , raw_name )
232- break
233-
234- return None
221+ for i , part in enumerate (path_parts [:- 1 ]):
222+ if part == "aotriton.images" and i + 1 < len (path_parts ):
223+ arch_dir = path_parts [i + 1 ]
224+ if arch_dir .startswith ("amd-gfx" ):
225+ raw_name = arch_dir [4 :] # strip "amd-" prefix
226+ return self ._BUNDLE_MAP .get (raw_name , raw_name )
227+ break
235228
236- except (ValueError , AttributeError , IndexError ):
237- return None
229+ return None
238230
239231
240232class MIOpenHandler (DatabaseHandler ):
@@ -261,34 +253,29 @@ def detect(self, path: Path, prefix_root: Path) -> Optional[str]:
261253
262254 Pattern: share/miopen/db/gfx*.{db.txt,fdb.txt,model}
263255 """
264- try :
265- rel_path = path .relative_to (prefix_root )
266- path_str = rel_path .as_posix ()
267-
268- if "miopen/db" not in path_str :
269- return None
270-
271- if not path .is_file ():
272- return None
256+ path_str = self ._relative_path (path , prefix_root )
273257
274- # Match .model, .db.txt, .fdb.txt, .OpenCL.fdb.txt, .HIP.fdb.txt
275- name = path .name
276- if not (
277- name .endswith (".model" )
278- or name .endswith (".db.txt" )
279- or name .endswith (".fdb.txt" )
280- ):
281- return None
282-
283- match = _MIOPEN_ARCH_PATTERN .search (name )
284- if match :
285- return match .group (0 )
258+ if "miopen/db" not in path_str :
259+ return None
286260
261+ if not path .is_file ():
287262 return None
288263
289- except (ValueError , AttributeError ):
264+ # Match .model, .db.txt, .fdb.txt, .OpenCL.fdb.txt, .HIP.fdb.txt
265+ name = path .name
266+ if not (
267+ name .endswith (".model" )
268+ or name .endswith (".db.txt" )
269+ or name .endswith (".fdb.txt" )
270+ ):
290271 return None
291272
273+ match = _MIOPEN_ARCH_PATTERN .search (name )
274+ if match :
275+ return match .group (0 )
276+
277+ return None
278+
292279
293280# Registry of available handlers
294281AVAILABLE_HANDLERS = {
0 commit comments