@snyk/java-call-graph-builder
Version:
Tool for building a call graph for JVM ecosystem (Maven, Gradle...)
161 lines • 7.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeMavenProject = exports.makeMavenModule = exports.MavenProject = exports.MavenModule = exports.parseModuleNames = exports.getDepsClassPath = exports.getOutputDir = exports.getBuildDir = exports.withOutputToTemporaryFile = void 0;
const tslib_1 = require("tslib");
require("source-map-support/register");
const path = require("path");
const fs = require("fs");
const xmlJs = require("xml-js");
const classpath_1 = require("./classpath");
const tmp = require("tmp");
const sub_process_1 = require("./sub-process");
const errors_1 = require("./errors");
const metrics_1 = require("./metrics");
const debug_1 = require("./debug");
// Low level helper functions
function withOutputToTemporaryFile(f) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// NOTE(alexmu): We have to do this little dance with output written to files
// because that seems to be the only way to get the output without having to
// parse maven logs
const file = tmp.fileSync({ discardDescriptor: true });
try {
yield f(file.name);
}
catch (e) {
debug_1.debug(`Failed to execute command with temporary file: ${e}`);
throw e;
}
try {
return fs.readFileSync(file.name, 'utf8');
}
catch (e) {
debug_1.debug(`Failed to read temporary file: ${e}`);
throw e;
}
});
}
exports.withOutputToTemporaryFile = withOutputToTemporaryFile;
function runCommand(projectDirectory, args) {
return sub_process_1.execute('mvn', args.concat(['-f', projectDirectory]), {
cwd: projectDirectory,
});
}
// Domain specific helpers
function evaluateExpression(projectDirectory, expression, customMavenArgs = []) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield withOutputToTemporaryFile((outputFile) => tslib_1.__awaiter(this, void 0, void 0, function* () {
yield runCommand(projectDirectory, [
'help:evaluate',
`-Dexpression="${expression}"`,
`-Doutput=${outputFile}`,
...customMavenArgs,
]);
}));
});
}
function getBuildDir(baseDir, customMavenArgs) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield evaluateExpression(baseDir, 'project.build.directory', customMavenArgs);
});
}
exports.getBuildDir = getBuildDir;
function getOutputDir(baseDir, customMavenArgs) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield evaluateExpression(baseDir, 'project.build.outputDirectory', customMavenArgs);
});
}
exports.getOutputDir = getOutputDir;
function getDepsClassPath(baseDir, customMavenArgs = []) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const classPath = yield withOutputToTemporaryFile((outputFile) => tslib_1.__awaiter(this, void 0, void 0, function* () {
yield runCommand(baseDir, [
'dependency:build-classpath',
`-Dmdep.outputFile=${outputFile}`,
...customMavenArgs,
]);
}));
return new classpath_1.ClassPath(classPath);
});
}
exports.getDepsClassPath = getDepsClassPath;
function parseModuleNames(modulesXml) {
const modulesSpec = xmlJs.xml2js(modulesXml, { compact: true });
if ('strings' in modulesSpec && 'string' in modulesSpec['strings']) {
debug_1.debug(`Found 'strings' in the modules XML`);
return modulesSpec['strings']['string'].map((s) => s['_text']);
}
else if ('modules' in modulesSpec) {
debug_1.debug(`Empty modules XML`);
return [];
}
else {
throw new errors_1.MalformedModulesSpecError(modulesXml);
}
}
exports.parseModuleNames = parseModuleNames;
// Maven model
class MavenModule {
constructor(baseDir, buildDirectory, outputDirectory, dependenciesClassPath) {
if ((buildDirectory === null || buildDirectory === void 0 ? void 0 : buildDirectory.length) === 0) {
throw new Error(`Empty build directory for the project in: ${baseDir}`);
}
if ((outputDirectory === null || outputDirectory === void 0 ? void 0 : outputDirectory.length) === 0) {
throw new Error(`Empty output directory for the project in: ${baseDir}`);
}
if (dependenciesClassPath === null || dependenciesClassPath === void 0 ? void 0 : dependenciesClassPath.isEmpty()) {
throw new Error(`Empty dependencies for the project in: ${baseDir}`);
}
this.baseDirectory = baseDir;
this.buildDirectory = buildDirectory;
this.outputDirectory = outputDirectory;
this.dependenciesClassPath = dependenciesClassPath;
}
getClassPath() {
debug_1.debug(`Dependencies class path: ${this.dependenciesClassPath}`);
debug_1.debug(`Output directory: ${this.outputDirectory}`);
return this.dependenciesClassPath.concat(new classpath_1.ClassPath(this.outputDirectory));
}
}
exports.MavenModule = MavenModule;
class MavenProject {
constructor(baseDir, modules) {
if ((modules === null || modules === void 0 ? void 0 : modules.length) === 0) {
throw new Error(`Empty module list for the project in: ${baseDir}`);
}
this.baseDir = baseDir;
this.modules = modules;
}
getClassPath() {
const classPaths = this.modules.map((module) => module.getClassPath());
const cp = classPaths.reduce((cp1, cp2) => cp1.concat(cp2));
debug_1.debug(`Project class path: ${cp}`);
return cp.toString();
}
}
exports.MavenProject = MavenProject;
// Factories that deal with the low level details
function makeMavenModule(baseDir, args) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const buildDir = yield getBuildDir(baseDir, args);
const outputDir = yield getOutputDir(baseDir, args);
const depsClassPath = yield metrics_1.timeIt('getMvnClassPath', () => tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield getDepsClassPath(baseDir, args);
}));
return new MavenModule(baseDir, buildDir, outputDir, depsClassPath);
});
}
exports.makeMavenModule = makeMavenModule;
function makeMavenProject(baseDir, customMavenArgs) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const modulesXml = yield evaluateExpression(baseDir, 'project.modules', customMavenArgs);
const moduleNames = parseModuleNames(modulesXml);
const modules = [yield makeMavenModule(baseDir, customMavenArgs)];
const submodules = yield Promise.all(moduleNames.map((name) => makeMavenModule(path.join(baseDir, name))));
modules.push(...submodules);
const validModules = modules.filter((module) => fs.existsSync(module.buildDirectory));
return new MavenProject(baseDir, validModules);
});
}
exports.makeMavenProject = makeMavenProject;
//# sourceMappingURL=mvn-wrapper.js.map