|
| 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