Skip to content

Commit ea19649

Browse files
committed
CDS extractor tests for compiler & packageManager
Adds more extensive unit tests of CDS extractor code related to the use of the `cds` compiler. Adds unit tests for CDS extractor functions in "projectMapping.ts".
1 parent af80a68 commit ea19649

File tree

8 files changed

+839
-69
lines changed

8 files changed

+839
-69
lines changed

extractors/cds/tools/cds-extractor.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { join } from 'path';
2-
31
import {
42
buildCdsProjectDependencyGraph,
53
compileCdsToJson,
64
determineCdsCommand,
5+
findProjectForCdsFile,
76
writeParserDebugInfo,
87
} from './src/cds';
98
import { runJavaScriptExtractor } from './src/codeql';
@@ -102,21 +101,9 @@ console.log('Detecting CDS projects and analyzing their structure...');
102101
// Build the project dependency graph using the project-aware parser
103102
const projectMap = buildCdsProjectDependencyGraph(sourceRoot, runMode);
104103

105-
// Extract all package.json directories that need dependency installation
106-
const packageJsonDirs = new Set<string>();
107-
projectMap.forEach(project => {
108-
if (
109-
project.packageJson &&
110-
(project.packageJson.dependencies?.['@sap/cds'] ||
111-
project.packageJson.devDependencies?.['@sap/cds'])
112-
) {
113-
packageJsonDirs.add(join(sourceRoot, project.projectDir));
114-
}
115-
});
116-
117-
// Install node dependencies in each directory.
118-
console.log('Pre-installing required CDS compiler versions ...');
119-
installDependencies(packageJsonDirs, codeqlExePath);
104+
// Install dependencies using the new caching approach
105+
console.log('Installing required CDS compiler versions using cached approach...');
106+
const projectCacheDirMap = installDependencies(projectMap, sourceRoot, codeqlExePath);
120107

121108
// Determine the CDS command to use.
122109
const cdsCommand = determineCdsCommand();
@@ -126,8 +113,15 @@ console.log('Processing CDS files to JSON ...');
126113
// Compile each `.cds` file to create a `.cds.json` file.
127114
for (const rawCdsFilePath of cdsFilePathsToProcess) {
128115
try {
116+
// Find which project this CDS file belongs to, to use the correct cache directory
117+
const projectDir = findProjectForCdsFile(rawCdsFilePath, sourceRoot, projectMap);
118+
const cacheDir = projectDir ? projectCacheDirMap.get(projectDir) : undefined;
119+
if (cacheDir) {
120+
console.log(`Using cache directory for ${rawCdsFilePath}: ${cacheDir}`);
121+
}
122+
129123
// Use resolved path directly instead of passing through getArg
130-
const compilationResult = compileCdsToJson(rawCdsFilePath, sourceRoot, cdsCommand);
124+
const compilationResult = compileCdsToJson(rawCdsFilePath, sourceRoot, cdsCommand, cacheDir);
131125

132126
if (!compilationResult.success && compilationResult.message) {
133127
console.error(

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { execFileSync, spawnSync, SpawnSyncReturns } from 'child_process';
2-
import { resolve } from 'path';
1+
import { execFileSync, spawnSync, SpawnSyncOptions } from 'child_process';
2+
import { resolve, join, delimiter } from 'path';
33

44
import { CdsCompilationResult } from './types';
55
import { fileExists, dirExists, recursivelyRenameJsonFiles } from '../../filesystem';
@@ -29,12 +29,14 @@ export function determineCdsCommand(): string {
2929
* @param cdsFilePath Path to the CDS file
3030
* @param sourceRoot The source root directory
3131
* @param cdsCommand The CDS command to use
32+
* @param cacheDir Optional path to a directory containing installed dependencies
3233
* @returns Result of the compilation
3334
*/
3435
export function compileCdsToJson(
3536
cdsFilePath: string,
3637
sourceRoot: string,
3738
cdsCommand: string,
39+
cacheDir?: string,
3840
): CdsCompilationResult {
3941
try {
4042
const resolvedCdsFilePath = resolve(cdsFilePath);
@@ -45,7 +47,25 @@ export function compileCdsToJson(
4547
const cdsJsonOutPath = `${resolvedCdsFilePath}.json`;
4648
console.log(`Processing CDS file ${resolvedCdsFilePath} to ${cdsJsonOutPath} ...`);
4749

48-
const result: SpawnSyncReturns<Buffer> = spawnSync(
50+
// Prepare spawn options
51+
const spawnOptions: SpawnSyncOptions = {
52+
cwd: sourceRoot,
53+
shell: true,
54+
stdio: 'pipe',
55+
};
56+
57+
// If a cache directory is provided, set NODE_PATH to use that cache
58+
if (cacheDir) {
59+
const nodePath = join(cacheDir, 'node_modules');
60+
spawnOptions.env = {
61+
...process.env,
62+
NODE_PATH: `${nodePath}${delimiter}${process.env.NODE_PATH ?? ''}`,
63+
PATH: `${join(nodePath, '.bin')}${delimiter}${process.env.PATH}`,
64+
};
65+
console.log(`Using cached dependencies from: ${cacheDir}`);
66+
}
67+
68+
const result = spawnSync(
4969
cdsCommand,
5070
[
5171
'compile',
@@ -58,7 +78,7 @@ export function compileCdsToJson(
5878
'--log-level',
5979
'warn',
6080
],
61-
{ cwd: sourceRoot, shell: true, stdio: 'pipe' },
81+
spawnOptions,
6282
);
6383

6484
if (result.error) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './functions';
22
export * from './types';
3+
export * from './projectMapping';
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Helper functions for mapping CDS files to their projects and cache directories
3+
*/
4+
5+
/**
6+
* Find the project directory for a CDS file
7+
* @param cdsFilePath Path to the CDS file
8+
* @param sourceRoot Source root directory
9+
* @param projectMap Map of project directories to project objects
10+
* @returns The project directory the file belongs to, or undefined if not found
11+
*/
12+
export function findProjectForCdsFile(
13+
cdsFilePath: string,
14+
sourceRoot: string,
15+
projectMap: Map<string, { cdsFiles: string[] }>,
16+
): string | undefined {
17+
// Get the relative path to the project directory for this CDS file
18+
const relativeCdsFilePath = cdsFilePath.startsWith(sourceRoot)
19+
? cdsFilePath.substring(sourceRoot.length + 1)
20+
: cdsFilePath;
21+
22+
// Find the project this file belongs to
23+
for (const [projectDir, project] of projectMap.entries()) {
24+
if (
25+
project.cdsFiles.some(
26+
cdsFile => cdsFile === relativeCdsFilePath || relativeCdsFilePath.startsWith(projectDir),
27+
)
28+
) {
29+
return projectDir;
30+
}
31+
}
32+
33+
return undefined;
34+
}

0 commit comments

Comments
 (0)