Skip to content

Commit 9c01e2b

Browse files
committed
Enhancements for CDS compilation retry
1 parent c5cdb06 commit 9c01e2b

28 files changed

+3965
-2799
lines changed

extractors/cds/tools/cds-extractor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
logPerformanceTrackingStop,
1818
setSourceRootDirectory,
1919
} from './src/logging';
20-
import { installDependencies } from './src/packageManager';
20+
import { cacheInstallDependencies } from './src/packageManager';
2121
import { validateArguments } from './src/utils';
2222

2323
// Validate the script arguments.
@@ -151,7 +151,7 @@ try {
151151
}
152152

153153
logPerformanceTrackingStart('Dependency Installation');
154-
const projectCacheDirMap = installDependencies(dependencyGraph, sourceRoot, codeqlExePath);
154+
const projectCacheDirMap = cacheInstallDependencies(dependencyGraph, sourceRoot, codeqlExePath);
155155
logPerformanceTrackingStop('Dependency Installation');
156156

157157
// Check if dependency installation resulted in any usable project mappings

extractors/cds/tools/dist/cds-extractor.bundle.js

Lines changed: 1743 additions & 1795 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extractors/cds/tools/dist/cds-extractor.bundle.js.map

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extractors/cds/tools/src/cds/compiler/compile.ts

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import { BasicCdsProject } from '../parser/types';
1919
*
2020
* @param cdsFilePath The path to the CDS file to compile, relative to the `sourceRoot`.
2121
* @param sourceRoot The source root directory scanned by the CDS extractor.
22-
* CRITICAL: All spawned processes must use this as their cwd to ensure paths in generated
23-
* JSON are relative to sourceRoot.
22+
* CRITICAL: All spawned processes will use the project base directory as their `cwd` to
23+
* ensure that paths in generated JSON are relative to the project base directory.
2424
*
2525
* @param cdsCommand The actual shell command to use for `cds compile`.
2626
* @param cacheDir Full path to the cache directory where dependencies are stored.
@@ -47,8 +47,11 @@ export function compileCdsToJson(
4747
const cdsVersion = getCdsVersion(cdsCommand, cacheDir);
4848
const versionInfo = cdsVersion ? `with CDS v${cdsVersion}` : '';
4949

50-
// CRITICAL: Create spawn options with sourceRoot as cwd to ensure correct path generation
51-
const spawnOptions = createSpawnOptions(sourceRoot, cdsCommand, cacheDir);
50+
// Calculate project base directory for consistent working directory
51+
const projectBaseDir = join(sourceRoot, projectDir);
52+
53+
// Create spawn options with project base directory as cwd.
54+
const spawnOptions = createSpawnOptions(projectBaseDir, cdsCommand, cacheDir);
5255

5356
// Throw an error if projectDir cannot be found in the projectMap.
5457
if (!projectMap || !projectDir || !projectMap.has(projectDir)) {
@@ -60,7 +63,7 @@ export function compileCdsToJson(
6063
const project = projectMap.get(projectDir);
6164
const relativePath = relative(sourceRoot, resolvedCdsFilePath);
6265

63-
// Check if this is a project-level compilation marker
66+
// Check if this is a project-level compilation marker.
6467
if (shouldUseProjectLevelCompilation(project)) {
6568
return compileProjectLevel(
6669
resolvedCdsFilePath,
@@ -72,7 +75,7 @@ export function compileCdsToJson(
7275
);
7376
}
7477

75-
// Check if this file is in the list of files to compile for this project
78+
// Check if this file is in the list of files to compile for this project.
7679
if (!shouldCompileIndividually(project, relativePath)) {
7780
cdsExtractorLog(
7881
'info',
@@ -107,13 +110,13 @@ export function compileCdsToJson(
107110

108111
/**
109112
* Handles project-level compilation for CAP projects with typical directory structure.
110-
* CRITICAL: Uses the project directory as cwd and calculates paths relative to project directory.
113+
* CRITICAL: Uses the project base directory as cwd and calculates paths relative to project base directory.
111114
*
112115
* @param resolvedCdsFilePath The resolved CDS file path that triggered this compilation
113116
* @param sourceRoot The source root directory
114117
* @param projectDir The project directory (relative to sourceRoot)
115118
* @param cdsCommand The CDS command to use
116-
* @param spawnOptions Pre-configured spawn options with sourceRoot as cwd
119+
* @param spawnOptions Pre-configured spawn options with project base directory as cwd
117120
* @param versionInfo Version information for logging
118121
* @returns Compilation result
119122
*/
@@ -158,12 +161,12 @@ function compileProjectLevel(
158161
}
159162

160163
if (existingDirectories.length === 0) {
161-
// If no standard directories, check if there are CDS files in the root
164+
// If no standard directories, check if there are CDS files in the root of the project.
162165
const rootCdsFiles = globSync(join(projectAbsolutePath, '*.cds'));
163166
if (rootCdsFiles.length > 0) {
164167
existingDirectories.push('.');
165168
} else {
166-
// Find directories that contain CDS files
169+
// Find directories that contain `.cds` files.
167170
const cdsFileParents = new Set(
168171
allCdsFiles.map((file: string) => {
169172
const relativePath = relative(projectAbsolutePath, file);
@@ -175,28 +178,19 @@ function compileProjectLevel(
175178
}
176179
}
177180

178-
// Generate output path for the compiled model - relative to sourceRoot for consistency
179-
const relativeOutputPath = join(projectDir, 'model.cds.json');
180-
const projectJsonOutPath = join(sourceRoot, relativeOutputPath);
181+
// Generate output path for the compiled model - relative to project base directory.
182+
const projectJsonOutPath = join(sourceRoot, projectDir, 'model.cds.json');
181183

182-
// Use sourceRoot as working directory but provide project-relative paths
183-
const projectSpawnOptions: SpawnSyncOptions = {
184-
...spawnOptions,
185-
cwd: sourceRoot, // Use sourceRoot as working directory for consistency
186-
};
187-
188-
// Convert directories to be relative to sourceRoot (include project prefix)
189-
const projectRelativeDirectories = existingDirectories.map(dir =>
190-
dir === '.' ? projectDir : join(projectDir, dir),
191-
);
184+
// Convert directories to be relative to project base directory.
185+
const projectRelativeDirectories = existingDirectories;
192186

193187
const compileArgs = [
194188
'compile',
195-
...projectRelativeDirectories, // Use paths relative to sourceRoot
189+
...projectRelativeDirectories, // Use paths relative to project base directory.
196190
'--to',
197191
'json',
198192
'--dest',
199-
join(projectDir, 'model.cds.json'), // Output to specific model.cds.json file
193+
'model.cds.json', // TODO : replace `model.cds.json` with `model.<session_id>.cds.json`
200194
'--locations',
201195
'--log-level',
202196
'warn',
@@ -208,16 +202,14 @@ function compileProjectLevel(
208202
`Running compilation task for CDS project '${projectDir}': command='${cdsCommand}' args='${JSON.stringify(compileArgs)}'`,
209203
);
210204

211-
// CRITICAL: Use the project directory as cwd
212-
// Use array arguments for consistent test behavior
213-
const result = spawnSync(cdsCommand, compileArgs, projectSpawnOptions);
205+
const result = spawnSync(cdsCommand, compileArgs, spawnOptions);
214206

215207
if (result.error) {
216208
cdsExtractorLog('error', `SpawnSync error: ${result.error.message}`);
217209
throw new Error(`Error executing CDS compiler: ${result.error.message}`);
218210
}
219211

220-
// Log stderr for debugging even on success (CDS often writes warnings to stderr)
212+
// Log stderr for debugging even on success (CDS often writes warnings to stderr).
221213
if (result.stderr && result.stderr.length > 0) {
222214
cdsExtractorLog('warn', `CDS stderr output: ${result.stderr.toString()}`);
223215
}
@@ -243,13 +235,14 @@ function compileProjectLevel(
243235
);
244236
}
245237

246-
// Handle directory output if the CDS compiler generated a directory
238+
// Handle directory output if the CDS compiler generated a directory.
247239
if (dirExists(projectJsonOutPath)) {
248240
cdsExtractorLog(
249241
'info',
250242
`CDS compiler generated JSON to output directory: ${projectJsonOutPath}`,
251243
);
252-
// Recursively rename all .json files to have a .cds.json extension
244+
// Recursively rename generated .json files to have a .cds.json extension
245+
// TODO : replace or remove this in favor of session-specific file suffixes (i.e. `.<session_id>.cds.json`).
253246
recursivelyRenameJsonFiles(projectJsonOutPath);
254247
} else {
255248
cdsExtractorLog('info', `CDS compiler generated JSON to file: ${projectJsonOutPath}`);
@@ -278,19 +271,20 @@ function compileProjectLevel(
278271
function compileRootFileAsProject(
279272
resolvedCdsFilePath: string,
280273
sourceRoot: string,
281-
_projectDir: string,
274+
projectDir: string,
282275
cdsCommand: string,
283276
spawnOptions: SpawnSyncOptions,
284277
_versionInfo: string,
285278
): CdsCompilationResult {
286-
// Calculate relative path for the output file
287-
const relativeCdsPath = relative(sourceRoot, resolvedCdsFilePath);
279+
// Calculate project base directory and file path relative to project
280+
const projectBaseDir = join(sourceRoot, projectDir);
281+
const relativeCdsPath = relative(projectBaseDir, resolvedCdsFilePath);
288282
const cdsJsonOutPath = `${resolvedCdsFilePath}.json`;
289283

290284
// Use project-aware compilation with specific file target
291285
const compileArgs = [
292286
'compile',
293-
relativeCdsPath, // Compile the specific file relative to sourceRoot
287+
relativeCdsPath, // Compile the specific file relative to project base directory
294288
'--to',
295289
'json',
296290
'--dest',
@@ -362,20 +356,20 @@ function compileRootFileAsProject(
362356

363357
/**
364358
* Creates spawn options for CDS compilation processes.
365-
* CRITICAL: Always sets cwd to sourceRoot to ensure generated JSON paths are relative to sourceRoot.
359+
* CRITICAL: Always sets cwd to project base directory to ensure generated JSON paths are relative to project base directory.
366360
*
367-
* @param sourceRoot The source root directory - used as cwd for all spawned processes
361+
* @param projectBaseDir The project base directory (where package.json is located) - used as cwd for all spawned processes
368362
* @param cdsCommand The CDS command to determine if we need Node.js environment setup
369363
* @param cacheDir Optional cache directory for dependencies
370364
* @returns Spawn options configured for CDS compilation
371365
*/
372366
function createSpawnOptions(
373-
sourceRoot: string,
367+
projectBaseDir: string,
374368
cdsCommand: string,
375369
cacheDir?: string,
376370
): SpawnSyncOptions {
377371
const spawnOptions: SpawnSyncOptions = {
378-
cwd: sourceRoot, // CRITICAL: Always use sourceRoot as cwd to ensure correct path generation
372+
cwd: projectBaseDir, // CRITICAL: Always use project base directory as cwd to ensure correct path generation
379373
shell: false, // Use shell=false to ensure proper argument handling for paths with spaces
380374
stdio: 'pipe',
381375
env: { ...process.env },

extractors/cds/tools/src/cds/compiler/graph.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { determineCdsCommand } from './command';
22
import { compileCdsToJson } from './compile';
33
import { orchestrateRetryAttempts } from './retry';
44
import { CompilationAttempt, CompilationTask, CompilationConfig } from './types';
5+
import { updateCdsDependencyGraphStatus } from './validator';
56
import { cdsExtractorLog, generateStatusReport } from '../../logging';
67
import { CdsDependencyGraph, CdsProject } from '../parser/types';
78

@@ -145,7 +146,6 @@ function executeCompilationTask(
145146

146147
if (compilationAttempt.result.success) {
147148
task.status = 'success';
148-
dependencyGraph.statusSummary.successfulCompilations++;
149149
return;
150150
}
151151

@@ -156,7 +156,6 @@ function executeCompilationTask(
156156

157157
task.status = 'failed';
158158
task.errorSummary = lastError?.message || 'Compilation failed';
159-
dependencyGraph.statusSummary.failedCompilations++;
160159

161160
// Note: Diagnostics are deferred until after retry phase completes
162161
// to implement "Silent Success" - only add diagnostics for definitively failed tasks
@@ -254,13 +253,15 @@ export function orchestrateCompilation(
254253
planCompilationTasks(dependencyGraph, projectCacheDirMap);
255254
executeCompilationTasks(dependencyGraph, codeqlExePath);
256255

256+
// CENTRALIZED STATUS UPDATE: Establish post-initial-compilation state
257+
updateCdsDependencyGraphStatus(dependencyGraph, dependencyGraph.sourceRootDir, 'initial');
258+
257259
// Phase 2: Retry orchestration
258260
cdsExtractorLog('info', 'Starting retry orchestration phase...');
259-
const retryResults = orchestrateRetryAttempts(
260-
dependencyGraph,
261-
projectCacheDirMap,
262-
codeqlExePath,
263-
);
261+
const retryResults = orchestrateRetryAttempts(dependencyGraph, codeqlExePath);
262+
263+
// CENTRALIZED STATUS UPDATE: Final validation and status synchronization
264+
updateCdsDependencyGraphStatus(dependencyGraph, dependencyGraph.sourceRootDir, 'final');
264265

265266
// Log retry results
266267
if (retryResults.totalTasksRequiringRetry > 0) {
@@ -280,7 +281,7 @@ export function orchestrateCompilation(
280281
dependencyGraph.statusSummary.overallSuccess = !hasFailures;
281282
dependencyGraph.currentPhase = hasFailures ? 'failed' : 'completed';
282283

283-
// Generate and log a "Post-Compilation" status report, aka before the JavaScript extractor runs.
284+
// Phase 3: Status reporting (now guaranteed to be accurate)
284285
const statusReport = generateStatusReport(dependencyGraph);
285286
cdsExtractorLog('info', 'CDS Extractor Status Report : Post-Compilation...\n' + statusReport);
286287
} catch (error) {

extractors/cds/tools/src/cds/compiler/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ export { compileCdsToJson } from './compile';
33
export { orchestrateCompilation } from './graph';
44
export { findProjectForCdsFile } from './project';
55
export { orchestrateRetryAttempts } from './retry';
6+
export { identifyTasksRequiringRetry, validateOutputFile, validateTaskOutputs } from './validator';
67
export { getCdsVersion } from './version';
7-
export { validateOutputFile, validateTaskOutputs, identifyTasksRequiringRetry } from './validator';
8-
export { installFullDependencies } from './installer';

0 commit comments

Comments
 (0)