UNPKG

@snyk/java-call-graph-builder

Version:

Tool for building a call graph for JVM ecosystem (Maven, Gradle...)

161 lines 7.14 kB
"use strict"; 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