11import type { CancellationToken } from "vscode" ;
22import { Uri , window } from "vscode" ;
3- import { relative , join , sep , basename } from "path" ;
3+ import { join , sep , basename , relative } from "path" ;
44import { dump , load } from "js-yaml" ;
55import { copy , writeFile , readFile , mkdirp } from "fs-extra" ;
66import type { DirectoryResult } from "tmp-promise" ;
@@ -34,7 +34,6 @@ import {
3434 QLPACK_FILENAMES ,
3535 QLPACK_LOCK_FILENAMES ,
3636} from "../common/ql" ;
37- import { tryGetQueryMetadata } from "../codeql-cli/query-metadata" ;
3837import type { QlPackFile } from "../packaging/qlpack-file" ;
3938import { expandShortPaths } from "../common/short-paths" ;
4039import type { QlPackDetails } from "./ql-pack-details" ;
@@ -56,10 +55,7 @@ async function generateQueryPack(
5655 qlPackDetails : QlPackDetails ,
5756 tmpDir : RemoteQueryTempDir ,
5857) : Promise < string > {
59- const queryFile = qlPackDetails . queryFile ;
60-
6158 const originalPackRoot = qlPackDetails . qlPackRootPath ;
62- const packRelativePath = relative ( originalPackRoot , queryFile ) ;
6359 const workspaceFolders = getOnDiskWorkspaceFolders ( ) ;
6460 const extensionPacks = await getExtensionPacksToInject (
6561 cliServer ,
@@ -80,12 +76,7 @@ async function generateQueryPack(
8076 // Synthesize a query pack for the query.
8177 // copy only the query file to the query pack directory
8278 // and generate a synthetic query pack
83- await createNewQueryPack (
84- queryFile ,
85- queryPackDir ,
86- qlPackDetails . language ,
87- packRelativePath ,
88- ) ;
79+ await createNewQueryPack ( qlPackDetails , queryPackDir ) ;
8980 // Clear the cliServer cache so that the previous qlpack text is purged from the CLI.
9081 await cliServer . clearCache ( ) ;
9182
@@ -94,13 +85,7 @@ async function generateQueryPack(
9485 } else if ( ! cliSupportsMrvaPackCreate ) {
9586 // We need to copy the query pack to a temporary directory and then fix it up to work with MRVA.
9687 queryPackDir = tmpDir . queryPackDir ;
97- await copyExistingQueryPack (
98- cliServer ,
99- originalPackRoot ,
100- queryFile ,
101- queryPackDir ,
102- packRelativePath ,
103- ) ;
88+ await copyExistingQueryPack ( cliServer , qlPackDetails , queryPackDir ) ;
10489
10590 // We should already have all the dependencies available, but these older versions of the CLI
10691 // have a bug where they will not search `--additional-packs` during validation in `codeql pack bundle`.
@@ -125,10 +110,23 @@ async function generateQueryPack(
125110
126111 let precompilationOpts : string [ ] ;
127112 if ( cliSupportsMrvaPackCreate ) {
113+ if (
114+ qlPackDetails . queryFiles . length > 1 &&
115+ ! ( await cliServer . cliConstraints . supportsPackCreateWithMultipleQueries ( ) )
116+ ) {
117+ throw new Error (
118+ `Installed CLI version does not allow creating a MRVA pack with multiple queries` ,
119+ ) ;
120+ }
121+
122+ const queryOpts = qlPackDetails . queryFiles . flatMap ( ( q ) => [
123+ "--query" ,
124+ join ( queryPackDir , relative ( qlPackDetails . qlPackRootPath , q ) ) ,
125+ ] ) ;
126+
128127 precompilationOpts = [
129128 "--mrva" ,
130- "--query" ,
131- join ( queryPackDir , packRelativePath ) ,
129+ ...queryOpts ,
132130 // We need to specify the extension packs as dependencies so that they are included in the MRVA pack.
133131 // The version range doesn't matter, since they'll always be found by source lookup.
134132 ...extensionPacks . map ( ( p ) => `--extension-pack=${ p } @*` ) ,
@@ -166,23 +164,26 @@ async function generateQueryPack(
166164}
167165
168166async function createNewQueryPack (
169- queryFile : string ,
167+ qlPackDetails : QlPackDetails ,
170168 queryPackDir : string ,
171- language : string | undefined ,
172- packRelativePath : string ,
173169) {
174- void extLogger . log ( `Copying ${ queryFile } to ${ queryPackDir } ` ) ;
175- const targetQueryFileName = join ( queryPackDir , packRelativePath ) ;
176- await copy ( queryFile , targetQueryFileName ) ;
170+ for ( const queryFile of qlPackDetails . queryFiles ) {
171+ void extLogger . log ( `Copying ${ queryFile } to ${ queryPackDir } ` ) ;
172+ const relativeQueryPath = relative ( qlPackDetails . qlPackRootPath , queryFile ) ;
173+ const targetQueryFileName = join ( queryPackDir , relativeQueryPath ) ;
174+ await copy ( queryFile , targetQueryFileName ) ;
175+ }
176+
177177 void extLogger . log ( "Generating synthetic query pack" ) ;
178178 const syntheticQueryPack = {
179179 name : QUERY_PACK_NAME ,
180180 version : "0.0.0" ,
181181 dependencies : {
182- [ `codeql/${ language } -all` ] : "*" ,
182+ [ `codeql/${ qlPackDetails . language } -all` ] : "*" ,
183183 } ,
184- defaultSuite : generateDefaultSuite ( packRelativePath ) ,
184+ defaultSuite : generateDefaultSuite ( qlPackDetails ) ,
185185 } ;
186+
186187 await writeFile (
187188 join ( queryPackDir , FALLBACK_QLPACK_FILENAME ) ,
188189 dump ( syntheticQueryPack ) ,
@@ -191,12 +192,16 @@ async function createNewQueryPack(
191192
192193async function copyExistingQueryPack (
193194 cliServer : CodeQLCliServer ,
194- originalPackRoot : string ,
195- queryFile : string ,
195+ qlPackDetails : QlPackDetails ,
196196 queryPackDir : string ,
197- packRelativePath : string ,
198197) {
199- const toCopy = await cliServer . packPacklist ( originalPackRoot , false ) ;
198+ const originalPackRoot = qlPackDetails . qlPackRootPath ;
199+ const queryFiles = qlPackDetails . queryFiles ;
200+
201+ const toCopy = await cliServer . packPacklist (
202+ qlPackDetails . qlPackRootPath ,
203+ false ,
204+ ) ;
200205
201206 // Also include query files that contain extensible predicates. These query files are not
202207 // needed for the query to run, but they are needed for the query pack to pass deep validation
@@ -216,7 +221,7 @@ async function copyExistingQueryPack(
216221 [
217222 // also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
218223 ...QLPACK_LOCK_FILENAMES . map ( ( f ) => join ( originalPackRoot , f ) ) ,
219- queryFile ,
224+ ... queryFiles ,
220225 ] . forEach ( ( absolutePath ) => {
221226 if ( absolutePath ) {
222227 toCopy . push ( absolutePath ) ;
@@ -241,7 +246,7 @@ async function copyExistingQueryPack(
241246
242247 void extLogger . log ( `Copied ${ copiedCount } files to ${ queryPackDir } ` ) ;
243248
244- await fixPackFile ( queryPackDir , packRelativePath ) ;
249+ await fixPackFile ( queryPackDir , qlPackDetails ) ;
245250}
246251
247252interface RemoteQueryTempDir {
@@ -284,8 +289,6 @@ interface PreparedRemoteQuery {
284289 actionBranch : string ;
285290 base64Pack : string ;
286291 repoSelection : RepositorySelection ;
287- queryFile : string ;
288- queryMetadata : QueryMetadata | undefined ;
289292 controllerRepo : Repository ;
290293 queryStartTime : number ;
291294}
@@ -298,8 +301,12 @@ export async function prepareRemoteQueryRun(
298301 token : CancellationToken ,
299302 dbManager : DbManager ,
300303) : Promise < PreparedRemoteQuery > {
301- if ( ! qlPackDetails . queryFile . endsWith ( ".ql" ) ) {
302- throw new UserCancellationException ( "Not a CodeQL query file." ) ;
304+ for ( const queryFile of qlPackDetails . queryFiles ) {
305+ if ( ! queryFile . endsWith ( ".ql" ) ) {
306+ throw new UserCancellationException (
307+ `Not a CodeQL query file: ${ queryFile } ` ,
308+ ) ;
309+ }
303310 }
304311
305312 progress ( {
@@ -351,17 +358,13 @@ export async function prepareRemoteQueryRun(
351358 message : "Sending request" ,
352359 } ) ;
353360
354- const queryFile = qlPackDetails . queryFile ;
355361 const actionBranch = getActionBranch ( ) ;
356362 const queryStartTime = Date . now ( ) ;
357- const queryMetadata = await tryGetQueryMetadata ( cliServer , queryFile ) ;
358363
359364 return {
360365 actionBranch,
361366 base64Pack,
362367 repoSelection,
363- queryFile,
364- queryMetadata,
365368 controllerRepo,
366369 queryStartTime,
367370 } ;
@@ -379,11 +382,11 @@ export async function prepareRemoteQueryRun(
379382 * to `*` versions.
380383 *
381384 * @param queryPackDir The directory containing the query pack
382- * @param packRelativePath The relative path to the query pack from the root of the query pack
385+ * @param qlPackDetails The details of the original QL pack
383386 */
384387async function fixPackFile (
385388 queryPackDir : string ,
386- packRelativePath : string ,
389+ qlPackDetails : QlPackDetails ,
387390) : Promise < void > {
388391 const packPath = await getQlPackFilePath ( queryPackDir ) ;
389392
@@ -397,7 +400,7 @@ async function fixPackFile(
397400 }
398401 const qlpack = load ( await readFile ( packPath , "utf8" ) ) as QlPackFile ;
399402
400- updateDefaultSuite ( qlpack , packRelativePath ) ;
403+ updateDefaultSuite ( qlpack , qlPackDetails ) ;
401404 removeWorkspaceRefs ( qlpack ) ;
402405
403406 await writeFile ( packPath , dump ( qlpack ) ) ;
@@ -461,19 +464,23 @@ async function addExtensionPacksAsDependencies(
461464 await writeFile ( qlpackFile , dump ( syntheticQueryPack ) ) ;
462465}
463466
464- function updateDefaultSuite ( qlpack : QlPackFile , packRelativePath : string ) {
467+ function updateDefaultSuite ( qlpack : QlPackFile , qlPackDetails : QlPackDetails ) {
465468 delete qlpack . defaultSuiteFile ;
466- qlpack . defaultSuite = generateDefaultSuite ( packRelativePath ) ;
469+ qlpack . defaultSuite = generateDefaultSuite ( qlPackDetails ) ;
467470}
468471
469- function generateDefaultSuite ( packRelativePath : string ) {
472+ function generateDefaultSuite ( qlPackDetails : QlPackDetails ) {
473+ const queries = qlPackDetails . queryFiles . map ( ( query ) => {
474+ const relativePath = relative ( qlPackDetails . qlPackRootPath , query ) ;
475+ return {
476+ query : relativePath . replace ( / \\ / g, "/" ) ,
477+ } ;
478+ } ) ;
470479 return [
471480 {
472481 description : "Query suite for variant analysis" ,
473482 } ,
474- {
475- query : packRelativePath . replace ( / \\ / g, "/" ) ,
476- } ,
483+ ...queries ,
477484 ] ;
478485}
479486
0 commit comments