55from typing import Dict , Any , Iterable , Self , Optional , List
66import yaml
77from dataclasses import dataclass , fields , field
8- from enum import Enum
98import logging
109
1110logger = logging .getLogger (__name__ )
@@ -16,7 +15,7 @@ class CodeQLException(Exception):
1615
1716
1817@dataclass (kw_only = True , frozen = True , eq = True )
19- class CodeQLPack :
18+ class CodeQLPackConfig :
2019 library : bool = False
2120 name : str
2221 version : Version = Version ("0.0.0" )
@@ -53,25 +52,18 @@ def get_pack_name(self) -> str:
5352 def __hash__ (self ):
5453 return hash (f"{ self .name } @{ str (self .version )} " )
5554
56-
57- class CodeQLPackKind (Enum ):
58- QUERY_PACK = 1
59- LIBRARY_PACK = 2
60- CUSTOMIZATION_PACK = 3
61-
62-
6355@dataclass (kw_only = True , frozen = True , eq = True )
64- class ResolvedCodeQLPack ( CodeQLPack ) :
56+ class CodeQLPack :
6557 path : Path
66- kind : CodeQLPackKind
67-
68- def __hash__ (self ):
69- return CodeQLPack .__hash__ (self )
58+ config : CodeQLPackConfig
7059
60+ def __hash__ (self ) -> int :
61+ return hash (f"{ self .path } " )
7162
7263class CodeQL :
7364 def __init__ (self , codeql_path : Path ):
7465 self .codeql_path = codeql_path
66+ self ._version = None
7567
7668 def _exec (self , command : str , * args : str ) -> subprocess .CompletedProcess [str ]:
7769 logger .debug (
@@ -80,16 +72,20 @@ def _exec(self, command: str, *args: str) -> subprocess.CompletedProcess[str]:
8072 return subprocess .run (
8173 [f"{ self .codeql_path } " , command ] + [arg for arg in args ],
8274 capture_output = True ,
83- text = True ,
75+ text = True
8476 )
8577
8678 def version (self ) -> Version :
87- cp = self ._exec ("version" , "--format=json" )
88- if cp .returncode == 0 :
89- version_info = json .loads (cp .stdout )
90- return Version (version_info ["version" ])
79+ if self ._version != None :
80+ return self ._version
9181 else :
92- raise CodeQLException (f"Failed to run { cp .args } command!" )
82+ cp = self ._exec ("version" , "--format=json" )
83+ if cp .returncode == 0 :
84+ version_info = json .loads (cp .stdout )
85+ self ._version = Version (version_info ["version" ])
86+ return self ._version
87+ else :
88+ raise CodeQLException (f"Failed to run { cp .args } command!" )
9389
9490 def unpacked_location (self ) -> Path :
9591 cp = self ._exec ("version" , "--format=json" )
@@ -102,44 +98,36 @@ def unpacked_location(self) -> Path:
10298 def supports_qlx (self ) -> bool :
10399 return self .version () >= Version ("2.11.4" )
104100
105- def pack_ls (self , workspace : Path = Path .cwd ()) -> List [ResolvedCodeQLPack ]:
101+ def pack_ls (self , workspace : Path = Path .cwd ()) -> List [CodeQLPack ]:
106102 cp = self ._exec ("pack" , "ls" , "--format=json" , str (workspace ))
107103 if cp .returncode == 0 :
108104 packs : Iterable [Path ] = map (Path , json .loads (cp .stdout )["packs" ].keys ())
109105
110- def load (qlpack : Path ) -> ResolvedCodeQLPack :
111- with qlpack .open ("r" ) as qlpack_file :
112- logger .debug (f"Resolving CodeQL pack at { qlpack } ." )
113- qlpack_spec = yaml .safe_load (qlpack_file )
114- qlpack_spec ["path" ] = qlpack
115- if not "library" in qlpack_spec or not qlpack_spec ["library" ]:
116- qlpack_spec ["kind" ] = CodeQLPackKind .QUERY_PACK
117- else :
118- if (
119- qlpack_spec ["path" ].parent
120- / qlpack_spec ["name" ].replace ("-" , "_" )
121- / "Customizations.qll"
122- ).exists ():
123- qlpack_spec ["kind" ] = CodeQLPackKind .CUSTOMIZATION_PACK
124- else :
125- qlpack_spec ["kind" ] = CodeQLPackKind .LIBRARY_PACK
126- resolved_pack = ResolvedCodeQLPack .from_dict (qlpack_spec )
106+ def load (qlpack_yml_path : Path ) -> CodeQLPack :
107+ with qlpack_yml_path .open ("r" ) as qlpack_yml_file :
108+ logger .debug (f"Loading CodeQL pack configuration at { qlpack_yml_path } ." )
109+ qlpack_yml_as_dict : Dict [str , Any ] = yaml .safe_load (qlpack_yml_file )
110+ qlpack_config = CodeQLPackConfig .from_dict (qlpack_yml_as_dict )
111+ qlpack = CodeQLPack (path = qlpack_yml_path , config = qlpack_config )
127112 logger .debug (
128- f"Resolved { resolved_pack . name } with version { str (resolved_pack . version )} at { resolved_pack .path } with kind { resolved_pack . kind . name } "
113+ f"Loaded { qlpack . config . name } with version { str (qlpack . config . version )} at { qlpack .path } . "
129114 )
130- return resolved_pack
115+ return qlpack
131116
132- logger .debug (f"Resolving CodeQL packs for workspace { workspace } " )
117+ logger .debug (f"Listing CodeQL packs for workspace { workspace } " )
133118 return list (map (load , packs ))
134119 else :
135120 raise CodeQLException (f"Failed to run { cp .args } command! { cp .stderr } " )
136121
137122 def pack_bundle (
138123 self ,
139- pack : ResolvedCodeQLPack ,
124+ pack : CodeQLPack ,
140125 output_path : Path ,
141126 * additional_packs : Path ,
142127 ):
128+ if not pack .config .library :
129+ raise CodeQLException (f"Cannot bundle non-library pack { pack .config .name } !" )
130+
143131 args = ["bundle" , "--format=json" , f"--pack-path={ output_path } " ]
144132 if len (additional_packs ) > 0 :
145133 args .append (f"--additional-packs={ ':' .join (map (str ,additional_packs ))} " )
@@ -155,11 +143,14 @@ def pack_bundle(
155143
156144 def pack_create (
157145 self ,
158- pack : ResolvedCodeQLPack ,
146+ pack : CodeQLPack ,
159147 output_path : Path ,
160148 * additional_packs : Path ,
161149 ):
162- args = ["create" , "--format=json" , f"--output={ output_path } " , "--threads=0" ]
150+ if pack .config .library :
151+ raise CodeQLException (f"Cannot bundle non-query pack { pack .config .name } !" )
152+
153+ args = ["create" , "--format=json" , f"--output={ output_path } " , "--threads=0" , "--no-default-compilation-cache" ]
163154 if self .supports_qlx ():
164155 args .append ("--qlx" )
165156 if len (additional_packs ) > 0 :
0 commit comments