44
55import logging
66import os
7- from pathlib import Path
87from dataclasses import dataclass , field
98import pickle
109from typing import Iterator
1413from sbom .cmd .incbin_parser import parse_incbin
1514from sbom .cmd .cmd_file_parser import CmdFile , parse_cmd_file
1615import sbom .errors as sbom_errors
16+ from sbom .path_utils import PathStr , is_relative_to
1717from .hardcoded_dependencies import get_hardcoded_dependencies
1818
1919
2020@dataclass
2121class CmdGraphNode :
22- absolute_path : Path
22+ absolute_path : PathStr
2323 cmd_file : CmdFile | None = None
2424 children : list ["CmdGraphNode" ] = field (default_factory = list ["CmdGraphNode" ])
2525
@@ -29,20 +29,20 @@ class CmdGraph:
2929 roots : list [CmdGraphNode ] = field (default_factory = list [CmdGraphNode ])
3030
3131
32- def build_cmd_graph (root_paths : list [Path ], output_tree : Path , src_tree : Path , log_depth : int = 0 ) -> CmdGraph :
33- node_cache : dict [Path , CmdGraphNode ] = {}
32+ def build_cmd_graph (root_paths : list [PathStr ], output_tree : PathStr , src_tree : PathStr , log_depth : int = 0 ) -> CmdGraph :
33+ node_cache : dict [PathStr , CmdGraphNode ] = {}
3434 root_nodes = [
35- build_cmd_graph_node (root_path , output_tree , src_tree , node_cache , log_depth = log_depth )
35+ build_cmd_graph_node (str ( root_path ), str ( output_tree ), str ( src_tree ) , node_cache , log_depth = log_depth )
3636 for root_path in root_paths
3737 ]
3838 return CmdGraph (root_nodes )
3939
4040
4141def build_cmd_graph_node (
42- root_path : Path ,
43- output_tree : Path ,
44- src_tree : Path ,
45- cache : dict [Path , CmdGraphNode ] | None = None ,
42+ root_path : PathStr ,
43+ output_tree : PathStr ,
44+ src_tree : PathStr ,
45+ cache : dict [PathStr , CmdGraphNode ] | None = None ,
4646 depth : int = 0 ,
4747 log_depth : int = 0 ,
4848) -> CmdGraphNode :
@@ -64,7 +64,7 @@ def build_cmd_graph_node(
6464 if cache is None :
6565 cache = {}
6666
67- root_path_absolute = Path ( os .path .normpath (output_tree / root_path ))
67+ root_path_absolute = os .path .normpath (os . path . join ( output_tree , root_path ))
6868 if root_path_absolute in cache .keys ():
6969 if depth <= log_depth :
7070 logging .info (f"Reuse Node: { ' ' * depth } { root_path } " )
@@ -73,12 +73,12 @@ def build_cmd_graph_node(
7373 if depth <= log_depth :
7474 logging .info (f"Build Node: { ' ' * depth } { root_path } " )
7575 cmd_path = _to_cmd_path (root_path_absolute )
76- cmd_file = parse_cmd_file (cmd_path ) if cmd_path . exists () else None
76+ cmd_file = parse_cmd_file (cmd_path ) if os . path . exists (cmd_path ) else None
7777 node = CmdGraphNode (root_path_absolute , cmd_file )
7878 cache [root_path_absolute ] = node
7979
80- if not root_path_absolute . exists ():
81- if root_path_absolute . is_relative_to (output_tree ) or root_path_absolute . is_relative_to (src_tree ):
80+ if not os . path . exists (root_path_absolute ):
81+ if is_relative_to (root_path_absolute , output_tree ) or is_relative_to (root_path_absolute , src_tree ):
8282 sbom_errors .log (f"Skip parsing '{ root_path_absolute } ' because file does not exist" )
8383 else :
8484 logging .warning (f"Skip parsing { root_path_absolute } because file does not exist" )
@@ -88,7 +88,7 @@ def build_cmd_graph_node(
8888 child_paths = get_hardcoded_dependencies (root_path_absolute , output_tree , src_tree )
8989 if cmd_file is not None :
9090 child_paths += _parse_cmd_file (cmd_file , output_tree , src_tree , root_path )
91- if node .absolute_path .suffix == ".S" :
91+ if node .absolute_path .endswith ( ".S" ) :
9292 child_paths += _parse_incbin (node .absolute_path , output_tree , src_tree , root_path )
9393
9494 # Create child nodes
@@ -99,17 +99,19 @@ def build_cmd_graph_node(
9999 return node
100100
101101
102- def _parse_cmd_file (cmd_file : CmdFile , output_tree : Path , src_tree : Path , root_artifact : Path ) -> list [Path ]:
103- input_files = parse_commands (cmd_file .savedcmd )
102+ def _parse_cmd_file (
103+ cmd_file : CmdFile , output_tree : PathStr , src_tree : PathStr , root_artifact : PathStr
104+ ) -> list [PathStr ]:
105+ input_files : list [PathStr ] = [str (p ) for p in parse_commands (cmd_file .savedcmd )]
104106 if cmd_file .deps :
105- input_files += parse_deps (cmd_file .deps )
107+ input_files += [ str ( p ) for p in parse_deps (cmd_file .deps )]
106108 input_files = _expand_resolve_files (input_files , output_tree )
107109
108- child_paths : list [Path ] = []
109- working_directory : Path | None = None
110+ child_paths : list [PathStr ] = []
111+ working_directory : PathStr | None = None
110112 for input_file in input_files :
111113 if os .path .isabs (input_file ):
112- child_paths .append (Path ( os .path .relpath (input_file , output_tree ) ))
114+ child_paths .append (os .path .relpath (input_file , output_tree ))
113115 continue
114116
115117 if working_directory is None :
@@ -122,15 +124,17 @@ def _parse_cmd_file(cmd_file: CmdFile, output_tree: Path, src_tree: Path, root_a
122124 )
123125 return []
124126
125- child_paths .append (Path ( os .path .normpath (working_directory / input_file )))
127+ child_paths .append (os .path .normpath (os . path . join ( working_directory , input_file )))
126128
127129 # Remove root output from the input_files to prevent cycles.
128130 # Some multi stage commands create an output and pass it as input to the next command, e.g., objcopy.
129131 child_paths = [child_path for child_path in child_paths if child_path != root_artifact ]
130132 return child_paths
131133
132134
133- def _parse_incbin (assembly_path : Path , output_tree : Path , src_tree : Path , root_output_in_tree : Path ) -> list [Path ]:
135+ def _parse_incbin (
136+ assembly_path : PathStr , output_tree : PathStr , src_tree : PathStr , root_output_in_tree : PathStr
137+ ) -> list [PathStr ]:
134138 incbin_paths = parse_incbin (assembly_path )
135139 if len (incbin_paths ) == 0 :
136140 return []
@@ -140,11 +144,11 @@ def _parse_incbin(assembly_path: Path, output_tree: Path, src_tree: Path, root_o
140144 f"Skip children of node { root_output_in_tree } because no working directory for { incbin_paths [0 ]} could be found"
141145 )
142146 return []
143- return [Path ( os .path .normpath (working_directory / incbin_path )) for incbin_path in incbin_paths ]
147+ return [os .path .normpath (os . path . join ( working_directory , incbin_path )) for incbin_path in incbin_paths ]
144148
145149
146150def iter_cmd_graph (cmd_graph : CmdGraph | CmdGraphNode ) -> Iterator [CmdGraphNode ]:
147- visited : set [Path ] = set ()
151+ visited : set [PathStr ] = set ()
148152 node_stack : list [CmdGraphNode ] = cmd_graph .roots .copy () if isinstance (cmd_graph , CmdGraph ) else [cmd_graph ]
149153 while len (node_stack ) > 0 :
150154 node = node_stack .pop (0 )
@@ -156,20 +160,20 @@ def iter_cmd_graph(cmd_graph: CmdGraph | CmdGraphNode) -> Iterator[CmdGraphNode]
156160 yield node
157161
158162
159- def save_cmd_graph (node : CmdGraph , path : Path ) -> None :
163+ def save_cmd_graph (node : CmdGraph , path : PathStr ) -> None :
160164 with open (path , "wb" ) as f :
161165 pickle .dump (node , f )
162166
163167
164- def load_cmd_graph (path : Path ) -> CmdGraph :
168+ def load_cmd_graph (path : PathStr ) -> CmdGraph :
165169 with open (path , "rb" ) as f :
166170 return pickle .load (f )
167171
168172
169173def build_or_load_cmd_graph (
170- root_paths : list [Path ], output_tree : Path , src_tree : Path , cmd_graph_path : Path
174+ root_paths : list [PathStr ], output_tree : PathStr , src_tree : PathStr , cmd_graph_path : PathStr
171175) -> CmdGraph :
172- if cmd_graph_path . exists ():
176+ if os . path . exists (cmd_graph_path ):
173177 logging .info ("Load cmd graph" )
174178 cmd_graph = load_cmd_graph (cmd_graph_path )
175179 else :
@@ -179,43 +183,46 @@ def build_or_load_cmd_graph(
179183 return cmd_graph
180184
181185
182- def _to_cmd_path (path : Path ) -> Path :
183- return path .parent / f".{ path .name } .cmd"
186+ def _to_cmd_path (path : PathStr ) -> PathStr :
187+ name = os .path .basename (path )
188+ return path .removesuffix (name ) + f".{ name } .cmd"
184189
185190
186- def _get_working_directory (input_file : Path , output_tree : Path , src_tree : Path , root_artifact : Path ) -> Path | None :
191+ def _get_working_directory (
192+ input_file : PathStr , output_tree : PathStr , src_tree : PathStr , root_artifact : PathStr
193+ ) -> PathStr | None :
187194 """
188195 Input paths in .cmd files are often relative paths but it is unclear to which original working directory these paths are relative to.
189196 This function heuristically estimates the working directory for a given input_file and returns the working directory relative to the output tree.
190197 """
191198
192- relative_to_cmd_file = ( output_tree / root_artifact . parent / input_file ). exists ( )
193- relative_to_output_tree = ( output_tree / input_file ). exists ( )
194- relative_to_tools_objtool = str ( root_artifact ) .startswith ("tools/objtool/arch/x86" )
195- relative_to_tools_lib_subcmd = str ( root_artifact ) .startswith ("tools/objtool/libsubcmd" )
199+ relative_to_cmd_file = os . path . exists ( os . path . join ( output_tree , os . path . dirname ( root_artifact ), input_file ))
200+ relative_to_output_tree = os . path . exists ( os . path . join ( output_tree , input_file ))
201+ relative_to_tools_objtool = root_artifact .startswith ("tools/objtool/arch/x86" )
202+ relative_to_tools_lib_subcmd = root_artifact .startswith ("tools/objtool/libsubcmd" )
196203
197204 if relative_to_cmd_file :
198- return root_artifact . parent
205+ return os . path . dirname ( root_artifact )
199206 elif relative_to_output_tree :
200- return Path ( "." )
207+ return "."
201208 elif relative_to_tools_objtool :
202209 # Input path relative to `tools/objtool` (e.g., `tools/objtool/arch/x86/special.o` has input `arch/x86/special.c`)
203- return Path (os .path .relpath (src_tree , output_tree )) / "tools/objtool"
210+ return os . path . join (os .path .relpath (src_tree , output_tree ), "tools/objtool" )
204211 elif relative_to_tools_lib_subcmd :
205212 # Input path relative to `tools/lib/subcmd` (e.g., `tools/objtool/libsubcmd/.sigchain.o` has input `subcmd-util.h` which lives in `tools/lib/subcmd/subcmd-util.h`)
206- return Path (os .path .relpath (src_tree , output_tree )) / "tools/lib/subcmd"
213+ return os . path . join (os .path .relpath (src_tree , output_tree ), "tools/lib/subcmd" )
207214
208215 return None
209216
210217
211- def _expand_resolve_files (input_files : list [Path ], output_tree : Path ) -> list [Path ]:
212- expanded_input_files : list [Path ] = []
218+ def _expand_resolve_files (input_files : list [PathStr ], output_tree : PathStr ) -> list [PathStr ]:
219+ expanded_input_files : list [PathStr ] = []
213220 for input_file in input_files :
214221 input_file_str = str (input_file )
215222 if not input_file_str .startswith ("@" ):
216223 expanded_input_files .append (input_file )
217224 continue
218- with open (output_tree / input_file_str [1 :], "r" ) as f :
219- resolve_file_content = [Path ( line .strip () ) for line in f .readlines () if line .strip ()]
225+ with open (os . path . join ( output_tree , input_file_str [1 :]) , "r" ) as f :
226+ resolve_file_content = [line .strip () for line in f .readlines () if line .strip ()]
220227 expanded_input_files += _expand_resolve_files (resolve_file_content , output_tree )
221228 return expanded_input_files
0 commit comments