|
| 1 | +import { join } from 'path'; |
| 2 | + |
| 3 | +import { sync as globSync } from 'glob'; |
| 4 | + |
| 5 | +import { determineCdsCommand } from './src/cds'; |
| 6 | +import { orchestrateCompilation } from './src/cds/compiler/graph'; |
1 | 7 | import {
|
2 |
| - buildCdsProjectDependencyGraph, |
3 |
| - compileCdsToJson, |
4 |
| - determineCdsCommand, |
5 |
| - findProjectForCdsFile, |
6 |
| -} from './src/cds'; |
7 |
| -import { CdsProjectMapWithDebugSignals } from './src/cds/parser/types'; |
| 8 | + handleDebugParserMode, |
| 9 | + handleDebugCompilerMode, |
| 10 | + isDebugMode, |
| 11 | + isDebugParserMode, |
| 12 | + isDebugCompilerMode, |
| 13 | +} from './src/cds/parser/debugUtils'; |
| 14 | +import { buildEnhancedCdsProjectDependencyGraph } from './src/cds/parser/graph'; |
8 | 15 | import { runJavaScriptExtractor } from './src/codeql';
|
9 | 16 | import { addCompilationDiagnostic } from './src/diagnostics';
|
10 | 17 | import { configureLgtmIndexFilters, setupAndValidateEnvironment } from './src/environment';
|
@@ -66,103 +73,143 @@ cdsExtractorLog(
|
66 | 73 | `CodeQL CDS extractor using run mode '${runMode}' for scan of project source root directory '${sourceRoot}'.`,
|
67 | 74 | );
|
68 | 75 |
|
69 |
| -// Using the new project-aware approach to find CDS projects and their dependencies |
70 |
| -cdsExtractorLog('info', 'Detecting CDS projects and analyzing their structure...'); |
| 76 | +// Using the enhanced project-aware approach to find CDS projects and their dependencies |
| 77 | +cdsExtractorLog('info', 'Building enhanced CDS project dependency graph...'); |
| 78 | + |
| 79 | +// Build the enhanced dependency graph using the new enhanced parser |
| 80 | +let dependencyGraph; |
71 | 81 |
|
72 |
| -// Build the project dependency graph using the project-aware parser |
73 |
| -// Pass the script directory (__dirname) to support debug-parser mode internally |
74 |
| -const projectMap = buildCdsProjectDependencyGraph(sourceRoot, runMode, __dirname); |
| 82 | +try { |
| 83 | + dependencyGraph = buildEnhancedCdsProjectDependencyGraph(sourceRoot, runMode, __dirname); |
| 84 | + |
| 85 | + cdsExtractorLog( |
| 86 | + 'info', |
| 87 | + `Enhanced dependency graph created with ${dependencyGraph.projects.size} projects and ${dependencyGraph.statusSummary.totalCdsFiles} CDS files`, |
| 88 | + ); |
75 | 89 |
|
76 |
| -// Cast to the interface with debug signals to properly handle debug mode |
77 |
| -const typedProjectMap = projectMap as CdsProjectMapWithDebugSignals; |
| 90 | + // Handle debug modes early - these modes should exit after completing their specific tasks |
| 91 | + if (isDebugParserMode(runMode)) { |
| 92 | + const debugSuccess = handleDebugParserMode(dependencyGraph, sourceRoot, __dirname); |
| 93 | + process.exit(debugSuccess ? 0 : 1); |
| 94 | + } |
78 | 95 |
|
79 |
| -// Check if we're in debug-parser mode and should exit (based on signals from buildCdsProjectDependencyGraph) |
80 |
| -if (typedProjectMap.__debugParserSuccess) { |
81 |
| - cdsExtractorLog('info', 'Debug parser mode completed successfully.'); |
82 |
| - process.exit(0); |
83 |
| -} else if (typedProjectMap.__debugParserFailure) { |
84 |
| - cdsExtractorLog('warn', 'No CDS projects found. Cannot generate debug information.'); |
| 96 | + // Log details about discovered projects for debugging |
| 97 | + if (dependencyGraph.projects.size > 0) { |
| 98 | + for (const [projectDir, project] of dependencyGraph.projects.entries()) { |
| 99 | + cdsExtractorLog( |
| 100 | + 'info', |
| 101 | + `Enhanced Project: ${projectDir}, Status: ${project.status}, CDS files: ${project.cdsFiles.length}, Files to compile: ${project.cdsFilesToCompile.length}`, |
| 102 | + ); |
| 103 | + } |
| 104 | + } else { |
| 105 | + cdsExtractorLog( |
| 106 | + 'warn', |
| 107 | + 'No CDS projects were detected. This may indicate an issue with project detection logic.', |
| 108 | + ); |
| 109 | + // Let's also try to find CDS files directly as a backup check |
| 110 | + try { |
| 111 | + const allCdsFiles = Array.from( |
| 112 | + new Set([ |
| 113 | + ...globSync(join(sourceRoot, '**/*.cds'), { |
| 114 | + ignore: ['**/node_modules/**', '**/.git/**'], |
| 115 | + }), |
| 116 | + ]), |
| 117 | + ); |
| 118 | + cdsExtractorLog( |
| 119 | + 'info', |
| 120 | + `Direct search found ${allCdsFiles.length} CDS files in the source tree.`, |
| 121 | + ); |
| 122 | + if (allCdsFiles.length > 0) { |
| 123 | + cdsExtractorLog( |
| 124 | + 'info', |
| 125 | + `Sample CDS files: ${allCdsFiles.slice(0, 5).join(', ')}${allCdsFiles.length > 5 ? ', ...' : ''}`, |
| 126 | + ); |
| 127 | + } |
| 128 | + } catch (globError) { |
| 129 | + cdsExtractorLog('warn', `Could not perform direct CDS file search: ${String(globError)}`); |
| 130 | + } |
| 131 | + } |
| 132 | +} catch (error) { |
| 133 | + cdsExtractorLog('error', `Failed to build enhanced dependency graph: ${String(error)}`); |
| 134 | + // Exit with error since we can't continue without a proper dependency graph |
85 | 135 | process.exit(1);
|
86 | 136 | }
|
87 | 137 |
|
88 | 138 | // Install dependencies of discovered CAP/CDS projects
|
89 |
| -cdsExtractorLog( |
90 |
| - 'info', |
91 |
| - 'Ensuring dependencies are installed in cache for required CDS compiler versions...', |
92 |
| -); |
93 |
| -const projectCacheDirMap = installDependencies(projectMap, sourceRoot, codeqlExePath); |
| 139 | +cdsExtractorLog('info', 'Installing dependencies for discovered CDS projects...'); |
| 140 | + |
| 141 | +const projectCacheDirMap = installDependencies(dependencyGraph, sourceRoot, codeqlExePath); |
94 | 142 |
|
95 | 143 | const cdsFilePathsToProcess: string[] = [];
|
96 | 144 |
|
97 | 145 | cdsExtractorLog('info', 'Extracting CDS files from discovered projects...');
|
98 | 146 |
|
99 |
| -// Use the project map to collect all `.cds` files from each project. |
| 147 | +// Use the enhanced dependency graph to collect all `.cds` files from each project. |
100 | 148 | // We want to "extract" all `.cds` files from all projects so that we have a copy
|
101 | 149 | // of each `.cds` source file in the CodeQL database.
|
102 |
| -for (const [, project] of projectMap.entries()) { |
| 150 | +for (const project of dependencyGraph.projects.values()) { |
103 | 151 | cdsFilePathsToProcess.push(...project.cdsFiles);
|
104 | 152 | }
|
105 | 153 |
|
106 |
| -cdsExtractorLog('info', 'Processing CDS files to JSON ...'); |
| 154 | +cdsExtractorLog('info', 'Processing CDS files to JSON using enhanced compilation orchestration...'); |
107 | 155 |
|
108 |
| -// Collect files that need compilation, handling project-level compilation |
109 |
| -const cdsFilesToCompile: string[] = []; |
110 |
| -const projectsForProjectLevelCompilation = new Set<string>(); |
| 156 | +// Check if we're running in debug mode |
| 157 | +if (isDebugMode(runMode)) { |
| 158 | + cdsExtractorLog( |
| 159 | + 'info', |
| 160 | + `Running in ${runMode} mode - enhanced debug information will be collected...`, |
| 161 | + ); |
| 162 | +} |
111 | 163 |
|
112 |
| -for (const [projectDir, project] of projectMap.entries()) { |
113 |
| - if (project.cdsFilesToCompile.includes('__PROJECT_LEVEL_COMPILATION__')) { |
114 |
| - // This project needs project-level compilation |
115 |
| - projectsForProjectLevelCompilation.add(projectDir); |
116 |
| - // We'll only compile one file per project to trigger project-level compilation |
117 |
| - // Use the first CDS file as a representative |
118 |
| - if (project.cdsFiles.length > 0) { |
119 |
| - cdsFilesToCompile.push(project.cdsFiles[0]); |
120 |
| - } |
121 |
| - } else { |
122 |
| - // Normal individual file compilation |
123 |
| - cdsFilesToCompile.push(...project.cdsFilesToCompile); |
124 |
| - } |
| 164 | +// Initialize CDS command cache early to avoid repeated testing during compilation |
| 165 | +// This is a critical optimization that avoids testing commands for every single file |
| 166 | +try { |
| 167 | + determineCdsCommand(undefined, sourceRoot); |
| 168 | + cdsExtractorLog('info', 'CDS command cache initialized successfully'); |
| 169 | +} catch (error) { |
| 170 | + cdsExtractorLog('warn', `CDS command cache initialization failed: ${String(error)}`); |
| 171 | + // Continue anyway - individual calls will handle fallbacks |
125 | 172 | }
|
126 | 173 |
|
127 | 174 | cdsExtractorLog(
|
128 | 175 | 'info',
|
129 |
| - `Found ${cdsFilePathsToProcess.length} total CDS files, ${cdsFilesToCompile.length} files to compile (${projectsForProjectLevelCompilation.size} project-level compilations)`, |
| 176 | + `Found ${cdsFilePathsToProcess.length} total CDS files, ${dependencyGraph.statusSummary.totalCdsFiles} CDS files in dependency graph`, |
130 | 177 | );
|
131 | 178 |
|
132 |
| -// Evaluate each `.cds` source file that should be compiled to JSON. |
133 |
| -for (const rawCdsFilePath of cdsFilesToCompile) { |
134 |
| - try { |
135 |
| - // Find which project this CDS file belongs to, to use the correct cache directory |
136 |
| - const projectDir = findProjectForCdsFile(rawCdsFilePath, sourceRoot, projectMap); |
137 |
| - const cacheDir = projectDir ? projectCacheDirMap.get(projectDir) : undefined; |
138 |
| - |
139 |
| - // Determine the CDS command to use based on the cache directory for this specific file |
140 |
| - const cdsCommand = determineCdsCommand(cacheDir); |
141 |
| - |
142 |
| - // Use resolved path directly instead of passing through getArg |
143 |
| - // Pass the project dependency information to enable project-aware compilation |
144 |
| - const compilationResult = compileCdsToJson( |
145 |
| - rawCdsFilePath, |
146 |
| - sourceRoot, |
147 |
| - cdsCommand, |
148 |
| - cacheDir, |
149 |
| - projectMap, |
150 |
| - projectDir, |
151 |
| - ); |
| 179 | +try { |
| 180 | + // Use the new orchestrated compilation approach with debug awareness |
| 181 | + orchestrateCompilation(dependencyGraph, projectCacheDirMap, codeqlExePath, isDebugMode(runMode)); |
152 | 182 |
|
153 |
| - if (!compilationResult.success && compilationResult.message) { |
154 |
| - cdsExtractorLog( |
155 |
| - 'error', |
156 |
| - `adding diagnostic for source file=${rawCdsFilePath} : ${compilationResult.message} ...`, |
157 |
| - ); |
158 |
| - addCompilationDiagnostic(rawCdsFilePath, compilationResult.message, codeqlExePath); |
159 |
| - } |
160 |
| - } catch (errorMessage) { |
| 183 | + // Check if we should exit for debug modes after successful compilation |
| 184 | + if (isDebugCompilerMode(runMode)) { |
| 185 | + const debugSuccess = handleDebugCompilerMode(dependencyGraph, runMode); |
| 186 | + process.exit(debugSuccess ? 0 : 1); |
| 187 | + } |
| 188 | + |
| 189 | + // Handle compilation failures for normal mode |
| 190 | + if (!dependencyGraph.statusSummary.overallSuccess) { |
161 | 191 | cdsExtractorLog(
|
162 | 192 | 'error',
|
163 |
| - `adding diagnostic for source file=${rawCdsFilePath} : ${String(errorMessage)} ...`, |
| 193 | + `Compilation completed with failures: ${dependencyGraph.statusSummary.failedCompilations} failed out of ${dependencyGraph.statusSummary.totalCompilationTasks} total tasks`, |
| 194 | + ); |
| 195 | + |
| 196 | + // Add diagnostics for critical errors |
| 197 | + for (const error of dependencyGraph.errors.critical) { |
| 198 | + cdsExtractorLog('error', `Critical error in ${error.phase}: ${error.message}`); |
| 199 | + } |
| 200 | + |
| 201 | + // Don't exit with error - let the JavaScript extractor run on whatever was compiled |
| 202 | + } |
| 203 | +} catch (error) { |
| 204 | + cdsExtractorLog('error', `Compilation orchestration failed: ${String(error)}`); |
| 205 | + |
| 206 | + // Add diagnostic for the overall failure |
| 207 | + if (cdsFilePathsToProcess.length > 0) { |
| 208 | + addCompilationDiagnostic( |
| 209 | + cdsFilePathsToProcess[0], // Use first file as representative |
| 210 | + `Compilation orchestration failed: ${String(error)}`, |
| 211 | + codeqlExePath, |
164 | 212 | );
|
165 |
| - addCompilationDiagnostic(rawCdsFilePath, String(errorMessage), codeqlExePath); |
166 | 213 | }
|
167 | 214 | }
|
168 | 215 |
|
|
0 commit comments