Skip to content

Commit 7e58207

Browse files
committed
Rename CDS extractor entrypoint and refactor args
Renames the entrypoint to the CDS extractor script and refactors its arguments in order to support using different "run modes" for the extractor, including: - "autobuild" : work-in-progress, just a stub right now; - "debug-parser" : using for debugging CDS project & file parsing; - "index-files" : legacy mode, useful for backwards compatibility; Updates the usage (help) message for the script to represent the required arguments for each of the currently planned run modes. Adds support for the "debug-parser" run mode, which debugs to a file under the `extractors/cds/tools/out/debug/` directory. Useful for in-progress rewrite of the CDS extractor to be more performant when running and more useful in terms of yielding a CodeQL database that allows for high-precision query results for CDS projects/queries.
1 parent e4c1ff0 commit 7e58207

21 files changed

Lines changed: 977 additions & 409 deletions

extractors/cds/tools/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Ignore files create just for debugging the CDS extractor.
2+
debug/
3+
14
# Ignore the entire "out" directory as this is for the .js and .js.map files
25
# which are generated by the `tsc` build process. In the current project config,
36
# we require the platform-specific "index-files" shell/cmd script to run the
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { join } from 'path';
2+
3+
import {
4+
buildCdsProjectDependencyGraph,
5+
compileCdsToJson,
6+
determineCdsCommand,
7+
writeParserDebugInfo,
8+
} from './src/cds';
9+
import { runJavaScriptExtractor } from './src/codeql';
10+
import { addCompilationDiagnostic } from './src/diagnostics';
11+
import { configureLgtmIndexFilters, setupAndValidateEnvironment } from './src/environment';
12+
import { getCdsFilePathsToProcess } from './src/filesystem';
13+
import { installDependencies } from './src/packageManager';
14+
import { RunMode, validateArguments } from './src/utils';
15+
16+
// Validate arguments to this script.
17+
// The first argument we pass is the expected run mode, which will be extracted from process.argv[2]
18+
// This will determine the correct minimum argument count for validation
19+
const validationResult = validateArguments(process.argv, RunMode.AUTOBUILD);
20+
if (!validationResult.isValid) {
21+
console.warn(validationResult.usageMessage);
22+
// Exit with an error code on invalid use of this script.
23+
process.exit(1);
24+
}
25+
26+
// Get the validated and sanitized arguments
27+
const { runMode, sourceRoot, responseFile } = validationResult.args!;
28+
29+
// Handle debug parser mode
30+
if (runMode === (RunMode.DEBUG_PARSER as string)) {
31+
console.log('Running CDS Parser in debug mode...');
32+
console.log(`Source Root Directory: ${sourceRoot}`);
33+
34+
// Use the project-aware parser as the main entry point
35+
const scriptDir = __dirname;
36+
const projectMap = buildCdsProjectDependencyGraph(sourceRoot, runMode, scriptDir);
37+
38+
if (projectMap.size === 0) {
39+
console.warn('No CDS projects found. Cannot generate debug information.');
40+
process.exit(1);
41+
}
42+
43+
// Check if debug information was successfully written
44+
if (runMode === 'debug-parser' && !writeParserDebugInfo(projectMap, sourceRoot, scriptDir)) {
45+
console.warn('Failed to write debug information.');
46+
process.exit(1);
47+
}
48+
49+
console.log('Debug parser process completed successfully.');
50+
process.exit(0);
51+
} else if (runMode === (RunMode.AUTOBUILD as string)) {
52+
console.log('Autobuild mode is not implemented yet.');
53+
process.exit(1);
54+
}
55+
56+
// Force this script, and any process it spawns, to use the project (source) root
57+
// directory as the current working directory.
58+
process.chdir(sourceRoot);
59+
60+
console.log(
61+
`INFO: CodeQL CDS extractor using run mode '${runMode}' for scan of project source root directory '${sourceRoot}'.`,
62+
);
63+
64+
// Setup the environment and validate all requirements.
65+
const {
66+
success: envSetupSuccess,
67+
errorMessages,
68+
codeqlExePath,
69+
autobuildScriptPath,
70+
platformInfo,
71+
} = setupAndValidateEnvironment(sourceRoot);
72+
73+
if (!envSetupSuccess) {
74+
const codeqlExe = platformInfo.isWindows ? 'codeql.exe' : 'codeql';
75+
console.warn(
76+
`'${codeqlExe} database index-files --language cds' terminated early due to: ${errorMessages.join(
77+
', ',
78+
)}.`,
79+
);
80+
// Exit with an error code when environment setup fails.
81+
process.exit(1);
82+
}
83+
84+
// Only process response file for INDEX_FILES mode
85+
let cdsFilePathsToProcess: string[] = [];
86+
if (runMode === (RunMode.INDEX_FILES as string)) {
87+
// Validate response file and get the full paths of CDS files to process.
88+
const filePathsResult = getCdsFilePathsToProcess(responseFile, platformInfo);
89+
if (!filePathsResult.success) {
90+
console.warn(filePathsResult.errorMessage);
91+
// Exit with an error if unable to get a list of `.cds` file paths to process.
92+
process.exit(1);
93+
}
94+
95+
// Get the validated list of CDS files to process
96+
cdsFilePathsToProcess = filePathsResult.cdsFilePaths;
97+
}
98+
99+
// Using the new project-aware approach to find CDS projects and their dependencies
100+
console.log('Detecting CDS projects and analyzing their structure...');
101+
102+
// Build the project dependency graph using the project-aware parser
103+
const projectMap = buildCdsProjectDependencyGraph(sourceRoot, runMode);
104+
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);
120+
121+
// Determine the CDS command to use.
122+
const cdsCommand = determineCdsCommand();
123+
124+
console.log('Processing CDS files to JSON ...');
125+
126+
// Compile each `.cds` file to create a `.cds.json` file.
127+
for (const rawCdsFilePath of cdsFilePathsToProcess) {
128+
try {
129+
// Use resolved path directly instead of passing through getArg
130+
const compilationResult = compileCdsToJson(rawCdsFilePath, sourceRoot, cdsCommand);
131+
132+
if (!compilationResult.success && compilationResult.message) {
133+
console.error(
134+
`ERROR: adding diagnostic for source file=${rawCdsFilePath} : ${compilationResult.message} ...`,
135+
);
136+
addCompilationDiagnostic(rawCdsFilePath, compilationResult.message, codeqlExePath);
137+
}
138+
} catch (errorMessage) {
139+
console.error(
140+
`ERROR: adding diagnostic for source file=${rawCdsFilePath} : ${String(errorMessage)} ...`,
141+
);
142+
addCompilationDiagnostic(rawCdsFilePath, String(errorMessage), codeqlExePath);
143+
}
144+
}
145+
146+
// Configure the "LGTM" index filters for proper extraction.
147+
configureLgtmIndexFilters();
148+
149+
// Run CodeQL's JavaScript extractor to process the compiled JSON files.
150+
const extractorResult = runJavaScriptExtractor(sourceRoot, autobuildScriptPath, codeqlExePath);
151+
if (!extractorResult.success && extractorResult.error) {
152+
console.error(`Error running JavaScript extractor: ${extractorResult.error}`);
153+
}
154+
155+
// Use the `cds-extractor.js` name in the log message as that is the name of the script
156+
// that is actually run by the `codeql database index-files` command. This TypeScript
157+
// file is where the code/logic is edited/implemented, but the runnable script is
158+
// generated by the TypeScript compiler and is named `cds-extractor.js`.
159+
console.log(`Completed run of cds-extractor.js script for CDS extractor.`);

0 commit comments

Comments
 (0)