import * as child_process from "child_process" ; import * as expandHome from "expand-home-dir" ; import * as fse from "fs-extra" ; import * as md5 from "md5" ; import * as path from "path" ; import * as vscode from "vscode" ; import * as which from "which" ; import { mavenOutputChannel } from "../mavenOutputChannel" ; import { mavenTerminal } from "../mavenTerminal" ; import { Settings } from "../Settings" ; import { getPathToExtensionRoot , getPathToTempFolder , getPathToWorkspaceStorage } from "./contextUtils" ; import { MavenNotFoundError } from "./errorUtils" ; import { updateLRUCommands } from "./historyUtils" ; param pomPath param options returns export async function rawEffectivePom ( pomPath , options ? ) { const outputPath = getTempFolder ( pomPath ) ; const epomPath = `${ outputPath }.epom` ; const mtimePath = `${ outputPath }.mtime` ; const cachedMTimeMs = await readFileIfExists ( mtimePath ) ; const stat = await fse . stat ( pomPath ) ; const mtimeMs = stat . mtimeMs . toString ( ) ; if ( cachedMTimeMs === mtimeMs || options ?. cacheOnly ) { return await readFileIfExists ( epomPath ) ; } await executeInBackground ( `-B -Doutput="${ epomPath }" help:effective-pom` , pomPath ) ; await fse . writeFile ( mtimePath , mtimeMs ) ; return await readFileIfExists ( epomPath ) ; } export async function rawDependencyTree ( pomPath ) { const outputPath = getTempFolder ( pomPath ) ; const dependencyGraphPath = `${ outputPath }.deps.txt` ; const outputDirectory = path . dirname ( dependencyGraphPath ) ; const outputFileName = path . basename ( dependencyGraphPath ) ; await executeInBackground ( `-N com.github.ferstl:depgraph-maven-plugin:3.3.1:graph -DgraphFormat=text -DshowDuplicates -DshowConflicts -DshowVersions -DshowGroupIds -DoutputDirectory="${ outputDirectory }" -DoutputFileName="${ outputFileName }"` , pomPath ) ; return await readFileIfExists ( path . join ( outputDirectory , outputFileName ) ) ; } export async function pluginDescription ( pluginId , pomPath ) { const outputPath = getTempFolder ( pluginId ) ; await executeInBackground ( `-B -Dapple.awt.UIElement=true -Dplugin=${ pluginId } -Doutput="${ outputPath }" help:describe` , pomPath ) ; return await readFileIfExists ( outputPath ) ; } async function executeInBackground ( mvnArgs , pomfile ? ) { const mvn = await getMaven ( pomfile ) ; if ( mvn === undefined ) { throw new MavenNotFoundError ( ) ; } const command = wrappedWithQuotes ( mvn ) ; const workspaceFolder = pomfile ? vscode . workspace . getWorkspaceFolder ( vscode . Uri . file ( pomfile ) ) : undefined ; const cwd = workspaceFolder ? path . resolve ( workspaceFolder . uri . fsPath , mvn , ".." ) : undefined ; const userArgs = Settings . Executable . options ( pomfile ) ; const mvnSettingsFile = Settings . getSettingsFilePath ( ) ; const mvnSettingsArg = mvnSettingsFile ? `-s "${ await mavenTerminal . formattedPathForTerminal ( mvnSettingsFile ) }"` : undefined ; const matched = [ mvnSettingsArg , mvnArgs , userArgs ] . filter ( Boolean ) . join ( " " ) . match ( /(?:[^\s"]+|"[^"]*")+/g ) ; const args = matched !== null ? matched : [ ] ; if ( pomfile ) { args . push ( "-f" , `"${ pomfile }"` ) ; } const spawnOptions = { cwd , env : Object . assign ( { } , process . env , Settings . getEnvironment ( pomfile ) ) , shell : true } ; return new Promise < { } > ( ( resolve , reject ) : void => { mavenOutputChannel . appendLine ( `Spawn ${ JSON . stringify ( { command , args } ) }` ) ; const proc = child_process . spawn ( command , args , spawnOptions ) ; proc . on ( "error" , ( err ) => { reject ( new Error ( `Error occurred in background process. ${ err . message }` ) ) ; } ) ; proc . on ( "exit" , ( code , signal ) => { if ( code !== null ) { if ( code === 0 ) { resolve ( code ) ; } else { reject ( new Error ( `Background process terminated with code ${ code }.` ) ) ; } } else { reject ( new Error ( `Background process killed by signal ${ signal }.` ) ) ; } } ) ; if ( proc . stdout !== null ) { proc . stdout . on ( "data" , ( chunk ) => { mavenOutputChannel . append ( chunk . toString ( ) ) ; } ) ; } if ( proc . stderr !== null ) { proc . stderr . on ( "data" , ( chunk ) => { mavenOutputChannel . append ( chunk . toString ( ) ) ; } ) ; } } ) ; } export async function executeInTerminal ( options ) { const { command , mvnPath , pomfile , cwd , env , terminalName } = options ; const workspaceFolder = pomfile ? vscode . workspace . getWorkspaceFolder ( vscode . Uri . file ( pomfile ) ) : undefined ; const mvn = mvnPath ? mvnPath : await getMaven ( pomfile ) ; if ( mvn === undefined ) { await promptToSettingMavenExecutable ( ) ; return undefined ; } const mvnString = wrappedWithQuotes ( await mavenTerminal . formattedPathForTerminal ( mvn ) ) ; const mvnSettingsFile = Settings . getSettingsFilePath ( ) ; const fullCommand = [ mvnString , mvnSettingsFile && `-s "${ await mavenTerminal . formattedPathForTerminal ( mvnSettingsFile ) }"` , command . trim ( ) , pomfile && `-f "${ await mavenTerminal . formattedPathForTerminal ( pomfile ) }"` , Settings . Executable . options ( pomfile ) ] . filter ( Boolean ) . join ( " " ) ; const name = terminalName || ( workspaceFolder ? `Maven-${ workspaceFolder . name }` : "Maven" ) ; const terminal = await mavenTerminal . runInTerminal ( fullCommand , { name , cwd , env , workspaceFolder } ) ; if ( pomfile ) { await updateLRUCommands ( command , pomfile ) ; } return terminal ; } export async function getMaven ( pomPath ? ) { const mvnPathFromSettings = Settings . Executable . path ( pomPath ) ; if ( mvnPathFromSettings ) { return expandHome ( mvnPathFromSettings ) ; } const preferMavenWrapper = Settings . Executable . preferMavenWrapper ( pomPath ) ; if ( preferMavenWrapper && pomPath && vscode . workspace . isTrusted ) { const localMvnwPath = await getLocalMavenWrapper ( path . dirname ( pomPath ) ) ; if ( localMvnwPath ) { return localMvnwPath ; } } return await defaultMavenExecutable ( ) ; } export function getEmbeddedMavenWrapper ( ) { const mvnw = isWin ( ) ? "mvnw.cmd" : "mvnw" ; return getPathToExtensionRoot ( "resources" , "maven-wrapper" , mvnw ) ; } async function getLocalMavenWrapper ( projectFolder ) { const mvnw = isWin ( ) ? "mvnw.cmd" : "mvnw" ; let current = projectFolder ; while ( path . basename ( current ) ) { const potentialMvnwPath = path . join ( current , mvnw ) ; if ( await fse . pathExists ( potentialMvnwPath ) ) { return potentialMvnwPath ; } current = path . dirname ( current ) ; const folderUri = vscode . Uri . file ( current ) ; if ( vscode . workspace . getWorkspaceFolder ( folderUri ) === undefined ) { return undefined ; } } return undefined ; } async function defaultMavenExecutable ( ) { return new Promise < string | undefined > ( ( resolve ) => { which ( "mvn" , ( _err , filepath ) => { if ( filepath ) { resolve ( "mvn" ) ; } else { mavenOutputChannel . appendLine ( "Maven executable not found in PATH." ) ; resolve ( undefined ) ; } } ) ; } ) ; } function wrappedWithQuotes ( mvn ) { if ( mvn === "mvn" ) { return mvn ; } else { return `"${ mvn }"` ; } } async function readFileIfExists ( filepath ) { if ( await fse . pathExists ( filepath ) ) { return ( await fse . readFile ( filepath ) ) . toString ( ) ; } return undefined ; } function getTempFolder ( identifier ) { const outputPath = getPathToWorkspaceStorage ( md5 ( identifier ) ) ; return outputPath ? outputPath : getPathToTempFolder ( md5 ( identifier ) ) ; } export async function promptToSettingMavenExecutable ( ) { const SETTING_MAVEN_EXECUTABLE_PATH = "maven.executable.path" ; const MESSAGE = `Maven executable not found in PATH. Please specify "${ SETTING_MAVEN_EXECUTABLE_PATH }".` ; const BUTTON_GOTO_SETTINGS = "Open Settings" ; const BUTTON_BROWSE_FOR_MAVEN = "Browse..." ; const choice = await vscode . window . showInformationMessage ( MESSAGE , BUTTON_GOTO_SETTINGS , BUTTON_BROWSE_FOR_MAVEN ) ; switch ( choice ) { case BUTTON_GOTO_SETTINGS : await vscode . commands . executeCommand ( "workbench.action.openSettings" , SETTING_MAVEN_EXECUTABLE_PATH ) ; break ; case BUTTON_BROWSE_FOR_MAVEN : const mvnPath = await browseForMavenBinary ( ) ; if ( mvnPath ) { Settings . setMavenExecutablePath ( mvnPath ) ; await vscode . window . showInformationMessage ( `Successfully set "${ SETTING_MAVEN_EXECUTABLE_PATH }" to: ${ mvnPath }` ) ; } break ; default : break ; } } async function browseForMavenBinary ( ) { const mvnFilename = isWin ( ) ? "mvn.cmd" : "mvn" ; const filters = isWin ( ) ? { Executable : [ "cmd" ] } : undefined ; const selectedUris = await vscode . window . showOpenDialog ( { openLabel : `Select ${ mvnFilename }` , canSelectMany : false , filters } ) ; if ( selectedUris === undefined ) { return undefined ; } const mvnPath = selectedUris . length > 0 && selectedUris [ 0 ] !== undefined ? selectedUris [ 0 ] . fsPath : undefined ; if ( ! mvnPath || ! mvnPath . endsWith ( mvnFilename ) ) { await vscode . window . showErrorMessage ( `Maven executable file must match with name: ${ mvnFilename }` ) ; return undefined ; } return mvnPath ; } function isWin ( ) { return process . platform . startsWith ( "win" ) ; }