@@ -34,6 +34,7 @@ import {
3434 getQlPackPath ,
3535 FALLBACK_QLPACK_FILENAME ,
3636 QLPACK_FILENAMES ,
37+ QLPACK_LOCK_FILENAMES ,
3738} from "../pure/ql" ;
3839
3940export interface QlPack {
@@ -70,42 +71,23 @@ async function generateQueryPack(
7071 const originalPackRoot = await findPackRoot ( queryFile ) ;
7172 const packRelativePath = relative ( originalPackRoot , queryFile ) ;
7273 const targetQueryFileName = join ( queryPackDir , packRelativePath ) ;
74+ const workspaceFolders = getOnDiskWorkspaceFolders ( ) ;
7375
7476 let language : string | undefined ;
77+
78+ // Check if the query is already in a query pack.
79+ // If so, copy the entire query pack to the temporary directory.
80+ // Otherwise, copy only the query file to the temporary directory
81+ // and generate a synthetic query pack.
7582 if ( await getQlPackPath ( originalPackRoot ) ) {
7683 // don't include ql files. We only want the queryFile to be copied.
77- const toCopy = await cliServer . packPacklist ( originalPackRoot , false ) ;
78-
79- // also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
80- [
81- join ( originalPackRoot , "qlpack.lock.yml" ) ,
82- join ( originalPackRoot , "codeql-pack.lock.yml" ) ,
84+ await copyExistingQueryPack (
85+ cliServer ,
86+ originalPackRoot ,
8387 queryFile ,
84- ] . forEach ( ( absolutePath ) => {
85- if ( absolutePath ) {
86- toCopy . push ( absolutePath ) ;
87- }
88- } ) ;
89-
90- let copiedCount = 0 ;
91- await copy ( originalPackRoot , queryPackDir , {
92- filter : ( file : string ) =>
93- // copy file if it is in the packlist, or it is a parent directory of a file in the packlist
94- ! ! toCopy . find ( ( f ) => {
95- // Normalized paths ensure that Windows drive letters are capitalized consistently.
96- const normalizedPath = Uri . file ( f ) . fsPath ;
97- const matches =
98- normalizedPath === file || normalizedPath . startsWith ( file + sep ) ;
99- if ( matches ) {
100- copiedCount ++ ;
101- }
102- return matches ;
103- } ) ,
104- } ) ;
105-
106- void extLogger . log ( `Copied ${ copiedCount } files to ${ queryPackDir } ` ) ;
107-
108- await fixPackFile ( queryPackDir , packRelativePath ) ;
88+ queryPackDir ,
89+ packRelativePath ,
90+ ) ;
10991
11092 language = await findLanguage ( cliServer , Uri . file ( targetQueryFileName ) ) ;
11193 } else {
@@ -114,20 +96,12 @@ async function generateQueryPack(
11496
11597 // copy only the query file to the query pack directory
11698 // and generate a synthetic query pack
117- void extLogger . log ( `Copying ${ queryFile } to ${ queryPackDir } ` ) ;
118- await copy ( queryFile , targetQueryFileName ) ;
119- void extLogger . log ( "Generating synthetic query pack" ) ;
120- const syntheticQueryPack = {
121- name : QUERY_PACK_NAME ,
122- version : "0.0.0" ,
123- dependencies : {
124- [ `codeql/${ language } -all` ] : "*" ,
125- } ,
126- defaultSuite : generateDefaultSuite ( packRelativePath ) ,
127- } ;
128- await writeFile (
129- join ( queryPackDir , FALLBACK_QLPACK_FILENAME ) ,
130- dump ( syntheticQueryPack ) ,
99+ await createNewQueryPack (
100+ queryFile ,
101+ queryPackDir ,
102+ targetQueryFileName ,
103+ language ,
104+ packRelativePath ,
131105 ) ;
132106 }
133107 if ( ! language ) {
@@ -149,12 +123,21 @@ async function generateQueryPack(
149123 precompilationOpts = [ "--no-precompile" ] ;
150124 }
151125
126+ if ( await cliServer . useExtensionPacks ( ) ) {
127+ await injectExtensionPacks ( cliServer , queryPackDir , workspaceFolders ) ;
128+ }
129+
130+ await cliServer . packInstall ( queryPackDir , {
131+ workspaceFolders,
132+ } ) ;
133+
134+ // Clear the CLI cache so that the most recent qlpack lock file is used.
135+ await cliServer . clearCache ( ) ;
136+
152137 const bundlePath = await getPackedBundlePath ( queryPackDir ) ;
153138 void extLogger . log (
154139 `Compiling and bundling query pack from ${ queryPackDir } to ${ bundlePath } . (This may take a while.)` ,
155140 ) ;
156- await cliServer . packInstall ( queryPackDir ) ;
157- const workspaceFolders = getOnDiskWorkspaceFolders ( ) ;
158141 await cliServer . packBundle (
159142 queryPackDir ,
160143 workspaceFolders ,
@@ -168,6 +151,70 @@ async function generateQueryPack(
168151 } ;
169152}
170153
154+ async function createNewQueryPack (
155+ queryFile : string ,
156+ queryPackDir : string ,
157+ targetQueryFileName : string ,
158+ language : string | undefined ,
159+ packRelativePath : string ,
160+ ) {
161+ void extLogger . log ( `Copying ${ queryFile } to ${ queryPackDir } ` ) ;
162+ await copy ( queryFile , targetQueryFileName ) ;
163+ void extLogger . log ( "Generating synthetic query pack" ) ;
164+ const syntheticQueryPack = {
165+ name : QUERY_PACK_NAME ,
166+ version : "0.0.0" ,
167+ dependencies : {
168+ [ `codeql/${ language } -all` ] : "*" ,
169+ } ,
170+ defaultSuite : generateDefaultSuite ( packRelativePath ) ,
171+ } ;
172+ await writeFile (
173+ join ( queryPackDir , FALLBACK_QLPACK_FILENAME ) ,
174+ dump ( syntheticQueryPack ) ,
175+ ) ;
176+ }
177+
178+ async function copyExistingQueryPack (
179+ cliServer : cli . CodeQLCliServer ,
180+ originalPackRoot : string ,
181+ queryFile : string ,
182+ queryPackDir : string ,
183+ packRelativePath : string ,
184+ ) {
185+ const toCopy = await cliServer . packPacklist ( originalPackRoot , false ) ;
186+
187+ [
188+ // also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
189+ ...QLPACK_LOCK_FILENAMES . map ( ( f ) => join ( originalPackRoot , f ) ) ,
190+ queryFile ,
191+ ] . forEach ( ( absolutePath ) => {
192+ if ( absolutePath ) {
193+ toCopy . push ( absolutePath ) ;
194+ }
195+ } ) ;
196+
197+ let copiedCount = 0 ;
198+ await copy ( originalPackRoot , queryPackDir , {
199+ filter : ( file : string ) =>
200+ // copy file if it is in the packlist, or it is a parent directory of a file in the packlist
201+ ! ! toCopy . find ( ( f ) => {
202+ // Normalized paths ensure that Windows drive letters are capitalized consistently.
203+ const normalizedPath = Uri . file ( f ) . fsPath ;
204+ const matches =
205+ normalizedPath === file || normalizedPath . startsWith ( file + sep ) ;
206+ if ( matches ) {
207+ copiedCount ++ ;
208+ }
209+ return matches ;
210+ } ) ,
211+ } ) ;
212+
213+ void extLogger . log ( `Copied ${ copiedCount } files to ${ queryPackDir } ` ) ;
214+
215+ await fixPackFile ( queryPackDir , packRelativePath ) ;
216+ }
217+
171218async function findPackRoot ( queryFile : string ) : Promise < string > {
172219 // recursively find the directory containing qlpack.yml or codeql-pack.yml
173220 let dir = dirname ( queryFile ) ;
@@ -329,19 +376,54 @@ async function fixPackFile(
329376 }
330377 const qlpack = load ( await readFile ( packPath , "utf8" ) ) as QlPack ;
331378
332- // update pack name
333379 qlpack . name = QUERY_PACK_NAME ;
334-
335- // update default suite
336- delete qlpack . defaultSuiteFile ;
337- qlpack . defaultSuite = generateDefaultSuite ( packRelativePath ) ;
338-
339- // remove any ${workspace} version references
380+ updateDefaultSuite ( qlpack , packRelativePath ) ;
340381 removeWorkspaceRefs ( qlpack ) ;
341382
342383 await writeFile ( packPath , dump ( qlpack ) ) ;
343384}
344385
386+ async function injectExtensionPacks (
387+ cliServer : cli . CodeQLCliServer ,
388+ queryPackDir : string ,
389+ workspaceFolders : string [ ] ,
390+ ) {
391+ const qlpackFile = await getQlPackPath ( queryPackDir ) ;
392+ if ( ! qlpackFile ) {
393+ throw new Error (
394+ `Could not find ${ QLPACK_FILENAMES . join (
395+ " or " ,
396+ ) } file in '${ queryPackDir } '`,
397+ ) ;
398+ }
399+ const syntheticQueryPack = load ( await readFile ( qlpackFile , "utf8" ) ) as QlPack ;
400+
401+ const extensionPacks = await cliServer . resolveQlpacks ( workspaceFolders , true ) ;
402+ Object . entries ( extensionPacks ) . forEach ( ( [ name , paths ] ) => {
403+ // We are guaranteed that there is at least one path found for each extension pack.
404+ // If there are multiple paths, then we have a problem. This means that there is
405+ // ambiguity in which path to use. This is an error.
406+ if ( paths . length > 1 ) {
407+ throw new Error (
408+ `Multiple versions of extension pack '${ name } ' found: ${ paths . join (
409+ ", " ,
410+ ) } `,
411+ ) ;
412+ }
413+ // Add this extension pack as a dependency. It doesn't matter which
414+ // version we specify, since we are guaranteed that the extension pack
415+ // is resolved from source at the given path.
416+ syntheticQueryPack . dependencies [ name ] = "*" ;
417+ } ) ;
418+ await writeFile ( qlpackFile , dump ( syntheticQueryPack ) ) ;
419+ await cliServer . clearCache ( ) ;
420+ }
421+
422+ function updateDefaultSuite ( qlpack : QlPack , packRelativePath : string ) {
423+ delete qlpack . defaultSuiteFile ;
424+ qlpack . defaultSuite = generateDefaultSuite ( packRelativePath ) ;
425+ }
426+
345427function generateDefaultSuite ( packRelativePath : string ) {
346428 return [
347429 {
0 commit comments