import { Task , TaskGroup , WorkspaceFolder , ShellExecution , Uri , workspace , ShellExecutionOptions , extensions } from "vscode" ; import * as path from "path" ; import * as util from "../common/utils" ; import * as log from "../common/log" ; import { configuration } from "../common/configuration" ; import { filesCache } from "../cache" ; import { TaskExplorerProvider } from "./provider" ; import { TaskExplorerDefinition } from "../taskDefinition" ; export class MakeTaskProvider extends TaskExplorerProvider implements TaskExplorerProvider { private suffixRuleTargets = /^(\.\w+|\.\w+\.\w+)$/ ; private patternRuleTargets = /^(%\.\w+|%)$/ ; private ruleTargetExp = /^([\w-.\/ ]+)\s*:[^=]/mg ; private specialTargets = new Set ( [ ".PHONY" , ".SUFFIXES" , ".DEFAULT" , ".PRECIOUS" , ".INTERMEDIATE" , ".SECONDARY" , ".SECONDEXPANSION" , ".DELETE_ON_ERROR" , ".IGNORE" , ".LOW_RESOLUTION_TIME" , ".SILENT" , ".EXPORT_ALL_VARIABLES" , ".NOTPARALLEL" , ".ONESHELL" , ".POSIX" , ".MAKE" , ] ) ; constructor ( ) { super ( "make" ) ; } public createTask ( target , cmd , folder , uri , xArgs ? , logPad = "" ) { log . methodStart ( "create make task" , 4 , logPad , true ) ; const getCommand = ( ) : string => { let make = "make" ; if ( process . platform === "win32" ) { make = "nmake" ; } if ( configuration . get ( "pathToMake" ) ) { make = configuration . get ( "pathToMake" ) ; log . value ( "   set make program from settings" , make , 5 , logPad ) ; } log . value ( "   make program" , make , 5 , logPad ) ; return make ; } ; const kind = this . getDefaultDefinition ( target , folder , uri ) ; const cwd = path . dirname ( uri . fsPath ) ; const args = [ target ] ; const options = { cwd } ; const execution = new ShellExecution ( getCommand ( ) , args , options ) ; let problemMatcher = "$gccte" ; const cPlusPlusExtension = extensions . getExtension ( "spmeesseman.vscode-taskexplorer" ) ; if ( cPlusPlusExtension ) { problemMatcher = "$gcc" ; } log . methodDone ( "create make task" , 4 , logPad , true ) ; return new Task ( kind , folder , target , "make" , execution , problemMatcher ) ; } public getDocumentPosition ( taskName , documentText ) { if ( ! taskName || ! documentText ) { return 0 ; } let idx = documentText . indexOf ( taskName + ":" ) ; if ( idx === - 1 ) { idx = documentText . indexOf ( taskName ) ; let bLine = documentText . lastIndexOf ( "\n" , idx ) + 1 ; let eLine = documentText . indexOf ( "\n" , idx ) ; if ( eLine === - 1 ) { eLine = documentText . length ; } let line = documentText . substring ( bLine , eLine ) . trim ( ) ; while ( bLine !== - 1 && bLine !== idx && idx !== - 1 && line . indexOf ( ":" ) === - 1 ) { idx = documentText . indexOf ( taskName , idx + 1 ) ; bLine = documentText . lastIndexOf ( "\n" , idx ) + 1 ; eLine = documentText . indexOf ( "\n" , idx ) ; if ( bLine !== - 1 ) { if ( eLine === - 1 ) { eLine = documentText . length ; } line = documentText . substring ( bLine , eLine ) . trim ( ) ; } } } return idx ; } private findTargets ( fsPath , logPad = "" ) { const scripts = [ ] ; log . blank ( 1 ) ; log . write ( logPad + "find makefile targets" ) ; const contents = util . readFileSync ( fsPath ) ; let match ; while ( match = this . ruleTargetExp . exec ( contents ) ) { const tgtName = match [ 1 ] ; if ( tgtName . startsWith ( "." ) ) { continue ; } if ( ! scripts . includes ( tgtName ) ) { if ( this . isNormalTarget ( tgtName ) ) { scripts . push ( tgtName ) ; log . write ( logPad + "   found makefile target" ) ; log . value ( logPad + "      name" , tgtName ) ; } } } log . write ( logPad + "find makefile targets complete" , 1 ) ; return scripts ; } public getDefaultDefinition ( target , folder , uri ) { const def = { type : "make" , script : target , target , path : util . getRelativePath ( folder , uri ) , fileName : path . basename ( uri . path ) , problemMatcher : "" , uri } ; return def ; } private isNormalTarget ( target ) { if ( this . specialTargets . has ( target ) ) { return false ; } if ( this . suffixRuleTargets . test ( target ) ) { return false ; } if ( this . patternRuleTargets . test ( target ) ) { return false ; } return true ; } public async readTasks ( logPad = "" ) { log . methodStart ( "detect make files" , 1 , logPad , true ) ; const allTasks = [ ] ; const visitedFiles = new Set ( ) ; const paths = filesCache . get ( "make" ) ; if ( workspace . workspaceFolders && paths ) { for ( const fobj of paths ) { if ( ! util . isExcluded ( fobj . uri . path ) && ! visitedFiles . has ( fobj . uri . fsPath ) ) { visitedFiles . add ( fobj . uri . fsPath ) ; const tasks = await this . readUriTasks ( fobj . uri , undefined , logPad + "   " ) ; log . write ( "   processed make file" , 3 , logPad ) ; log . value ( "      file" , fobj . uri . fsPath , 3 , logPad ) ; log . value ( "      targets in file" , tasks . length , 3 , logPad ) ; allTasks . push ( ... tasks ) ; } } } log . blank ( ) ; log . value ( "   # of make tasks" , allTasks . length , 2 , logPad ) ; log . methodDone ( "detect make files" , 1 , logPad , true ) ; return allTasks ; } public async readUriTasks ( uri , wsFolder ? , logPad = "" ) { const result = [ ] ; const folder = wsFolder || workspace . getWorkspaceFolder ( uri ) ; log . methodStart ( "read make file uri tasks" , 1 , logPad , true , [ [ "path" , uri ?. fsPath ] , [ "project folder" , folder ?. name ] ] ) ; if ( folder ) { const scripts = this . findTargets ( uri . fsPath , logPad + "   " ) ; if ( scripts ) { for ( const s of scripts ) { const task = this . createTask ( s , s , folder , uri , undefined , logPad ) ; task . group = TaskGroup . Build ; result . push ( task ) ; } } } log . methodDone ( "read make file uri tasks" , 1 , logPad ) ; return result ; } }