@@ -25,7 +25,17 @@ export interface CppBuildTaskDefinition extends TaskDefinition {
2525 label : string ; // The label appears in tasks.json file.
2626 command : string | util . IQuotedString ;
2727 args : ( string | util . IQuotedString ) [ ] ;
28- options : cp . ExecOptions | cp . SpawnOptions | undefined ;
28+ options : cp . ExecOptions | undefined ;
29+ windows ?: CppBuildTaskPlatformOverride ;
30+ linux ?: CppBuildTaskPlatformOverride ;
31+ osx ?: CppBuildTaskPlatformOverride ;
32+ }
33+
34+ interface CppBuildTaskPlatformOverride {
35+ command ?: string | util . IQuotedString ;
36+ args ?: ( string | util . IQuotedString ) [ ] ;
37+ options ?: cp . ExecOptions | undefined ;
38+ problemMatcher ?: string | string [ ] ;
2939}
3040
3141export class CppBuildTask extends Task {
@@ -50,7 +60,7 @@ export class CppBuildTaskProvider implements TaskProvider {
5060 const execution : ProcessExecution | ShellExecution | CustomExecution | undefined = _task . execution ;
5161 if ( ! execution ) {
5262 const definition : CppBuildTaskDefinition = < any > _task . definition ;
53- _task = this . getTask ( definition . command , false , definition . args ? definition . args : [ ] , definition , _task . detail ) ;
63+ _task = this . getTask ( definition , _task . detail ) ;
5464 return _task ;
5565 }
5666 return undefined ;
@@ -59,7 +69,7 @@ export class CppBuildTaskProvider implements TaskProvider {
5969 public resolveInsiderTask ( _task : CppBuildTask ) : CppBuildTask | undefined {
6070 const definition : CppBuildTaskDefinition = < any > _task . definition ;
6171 definition . label = definition . label . replace ( ext . configPrefix , "" ) ;
62- _task = this . getTask ( definition . command , false , definition . args ? definition . args : [ ] , definition , _task . detail ) ;
72+ _task = this . getTask ( definition , _task . detail ) ;
6373 return _task ;
6474 }
6575
@@ -152,83 +162,118 @@ export class CppBuildTaskProvider implements TaskProvider {
152162 return emptyTasks ;
153163 }
154164
155- // Create a build task per compiler path
165+ // Create a build task per compiler path.
156166 const result : CppBuildTask [ ] = [ ] ;
157167
158- // Task for valid user compiler path setting
168+ // Task for valid user compiler path setting.
159169 if ( isCompilerValid && userCompilerPath ) {
160- result . push ( this . getTask ( userCompilerPath , appendSourceToName , userCompilerPathAndArgs ?. allCompilerArgs ) ) ;
170+ result . push ( this . generateTask ( userCompilerPath , appendSourceToName , userCompilerPathAndArgs ?. allCompilerArgs ) ) ;
161171 }
162172
163- // Tasks for known compiler paths
173+ // Tasks for known compiler paths.
164174 if ( knownCompilerPaths ) {
165- result . push ( ...knownCompilerPaths . map < Task > ( compilerPath => this . getTask ( compilerPath , appendSourceToName , undefined ) ) ) ;
175+ result . push ( ...knownCompilerPaths . map < CppBuildTask > ( compilerPath => this . generateTask ( compilerPath , appendSourceToName , undefined ) ) ) ;
166176 }
167177
168178 return result ;
169179 }
170180
171- private getTask : ( compilerPath : string | util . IQuotedString , appendSourceToName : boolean , compilerArgs ?: ( string | util . IQuotedString ) [ ] , definition ?: CppBuildTaskDefinition , detail ?: string ) => Task = ( compilerPath : string | util . IQuotedString , appendSourceToName : boolean , compilerArgs ?: ( string | util . IQuotedString ) [ ] , definition ?: CppBuildTaskDefinition , detail ?: string ) => {
181+ private generateTask ( compilerPath : string | util . IQuotedString , appendSourceToName : boolean , compilerArgs ?: ( string | util . IQuotedString ) [ ] ) : CppBuildTask {
172182 const compilerPathString : string = util . isString ( compilerPath ) ? compilerPath : compilerPath . value ;
173- const compilerPathBase : string = path . basename ( compilerPathString ) ;
174- const isCl : boolean = compilerPathBase . toLowerCase ( ) === "cl.exe" ;
175- const isClang : boolean = ! isCl && compilerPathBase . toLowerCase ( ) . includes ( "clang" ) ;
176- // Double-quote the command if needed.
177- const resolvedCompilerPathString : string = isCl ? compilerPathBase : compilerPathString ;
178- let resolvedCompilerPath : string | util . IQuotedString = compilerPath ;
179- if ( isCl ) {
180- resolvedCompilerPath = compilerPathBase ;
181- }
182-
183- if ( ! definition ) {
184- const isWindows : boolean = os . platform ( ) === 'win32' ;
185- const taskLabel : string = ( ( appendSourceToName && ! compilerPathBase . startsWith ( ext . configPrefix ) ) ?
186- ext . configPrefix : "" ) + compilerPathBase + " " + localize ( "build.active.file" , "build active file" ) ;
187- const programName : string = util . defaultExePath ( ) ;
188- let args : ( string | util . IQuotedString ) [ ] = isCl ?
189- [ '/Zi' , '/EHsc' , '/nologo' , `/Fe${ programName } ` , '${file}' ] :
190- isClang ?
191- [ '-fcolor-diagnostics' , '-fansi-escape-codes' , '-g' , '${file}' , '-o' , programName ] :
192- [ '-fdiagnostics-color=always' , '-g' , '${file}' , '-o' , programName ] ;
193-
194- if ( compilerArgs && compilerArgs . length > 0 ) {
195- args = args . concat ( compilerArgs ) ;
196- }
197- const cwd : string = isWindows && ! isCl && ! process . env . PATH ?. includes ( path . dirname ( compilerPathString ) ) ? path . dirname ( compilerPathString ) : "${fileDirname}" ;
198- const options : cp . ExecOptions | cp . SpawnOptions | undefined = { cwd : cwd } ;
199- definition = {
200- type : CppBuildTaskProvider . CppBuildScriptType ,
201- label : taskLabel ,
202- command : compilerPath ,
203- args : args ,
204- options : options
205- } ;
206- if ( isCl ) {
207- definition . command = compilerPathBase ;
208- }
183+ const compilerName : string = path . basename ( compilerPathString ) ;
184+ const isCl : boolean = compilerName . toLowerCase ( ) === "cl.exe" ;
185+ const isClang : boolean = ! isCl && compilerName . toLowerCase ( ) . includes ( "clang" ) ;
186+
187+ const isWindows : boolean = os . platform ( ) === 'win32' ;
188+ const taskLabel : string = ( ( appendSourceToName && ! compilerName . startsWith ( ext . configPrefix ) ) ?
189+ ext . configPrefix : "" ) + compilerName + " " + localize ( "build.active.file" , "build active file" ) ;
190+ const programName : string = util . defaultExePath ( ) ;
191+ let args : ( string | util . IQuotedString ) [ ] = isCl ?
192+ [ '/Zi' , '/EHsc' , '/nologo' , `/Fe${ programName } ` , '${file}' ] :
193+ isClang ?
194+ [ '-fcolor-diagnostics' , '-fansi-escape-codes' , '-g' , '${file}' , '-o' , programName ] :
195+ [ '-fdiagnostics-color=always' , '-g' , '${file}' , '-o' , programName ] ;
196+
197+ if ( compilerArgs && compilerArgs . length > 0 ) {
198+ args = args . concat ( compilerArgs ) ;
209199 }
200+ const cwd : string = isWindows && ! isCl && ! process . env . PATH ?. includes ( path . dirname ( compilerPathString ) ) ? path . dirname ( compilerPathString ) : "${fileDirname}" ;
201+ const options : cp . ExecOptions | undefined = { cwd : cwd } ;
202+ const definition : CppBuildTaskDefinition = {
203+ type : CppBuildTaskProvider . CppBuildScriptType ,
204+ label : taskLabel ,
205+ command : isCl ? compilerName : compilerPath ,
206+ args : args ,
207+ options : options
208+ } ;
209+
210+ return this . getTask ( definition ) ;
211+ }
212+
213+ private getTask ( definition : CppBuildTaskDefinition , detail ?: string ) : CppBuildTask {
214+ const platformDefinition : CppBuildTaskDefinition = this . applyPlatformOverrides ( definition ) ;
215+ const command : string = util . isString ( platformDefinition . command ) ? platformDefinition . command : platformDefinition . command . value ;
216+ const compilerName : string = path . basename ( command ) ;
217+ const isCl : boolean = compilerName . toLowerCase ( ) === "cl.exe" ;
218+ const isClang : boolean = ! isCl && compilerName . toLowerCase ( ) . includes ( "clang" ) ;
210219
211220 const editor : TextEditor | undefined = window . activeTextEditor ;
212221 const folder : WorkspaceFolder | undefined = editor ? workspace . getWorkspaceFolder ( editor . document . uri ) : undefined ;
213222
214- const taskUsesActiveFile : boolean = definition . args . some ( arg => {
223+ const taskUsesActiveFile : boolean = platformDefinition . args . some ( arg => {
215224 if ( util . isString ( arg ) ) {
216225 return arg . indexOf ( '${file}' ) >= 0 ;
217226 }
218227 return arg . value . indexOf ( '${file}' ) >= 0 ;
219228 } ) ; // Need to check this before ${file} is resolved
220229 const scope : WorkspaceFolder | TaskScope = folder ? folder : TaskScope . Workspace ;
221- const task : CppBuildTask = new Task ( definition , scope , definition . label , ext . CppSourceStr ,
222- new CustomExecution ( async ( resolvedDefinition : TaskDefinition ) : Promise < Pseudoterminal > =>
223- // When the task is executed, this callback will run. Here, we setup for running the task.
224- new CustomBuildTaskTerminal ( resolvedCompilerPath , resolvedDefinition . args , resolvedDefinition . options , { taskUsesActiveFile, insertStd : isClang && os . platform ( ) === 'darwin' } )
225- ) , isCl ? '$msCompile' : '$gcc' ) ;
230+ const customExecution : CustomExecution = new CustomExecution ( async ( resolvedDefinition : TaskDefinition ) : Promise < Pseudoterminal > => {
231+ // When the task is executed, this callback will run. Here, we setup for running the task.
232+ // Apply platform-specific overrides (windows/linux/osx) at execution time so that VS Code
233+ // can still match the task definition by its original shape during the resolve phase.
234+ const effectiveDefinition : CppBuildTaskDefinition = this . applyPlatformOverrides ( resolvedDefinition as CppBuildTaskDefinition ) ;
235+ const effectiveArgs : ( string | util . IQuotedString ) [ ] = effectiveDefinition . args ? effectiveDefinition . args : [ ] ;
236+ return new CustomBuildTaskTerminal (
237+ effectiveDefinition . command ,
238+ effectiveArgs ,
239+ effectiveDefinition . options ,
240+ { taskUsesActiveFile, insertStd : isClang && os . platform ( ) === 'darwin' }
241+ ) ;
242+ } ) ;
243+ const task : CppBuildTask = new CppBuildTask ( definition , scope , definition . label , ext . CppSourceStr , customExecution , isCl ? '$msCompile' : '$gcc' ) ;
226244
227245 task . group = TaskGroup . Build ;
228- task . detail = detail ? detail : localize ( "compiler.details" , "compiler:" ) + " " + resolvedCompilerPathString ;
246+ task . detail = detail ? detail : localize ( "compiler.details" , "compiler:" ) + " " + ( isCl ? compilerName : command ) ;
229247
230248 return task ;
231- } ;
249+ }
250+
251+ private applyPlatformOverrides ( definition : CppBuildTaskDefinition ) : CppBuildTaskDefinition {
252+ const platform : NodeJS . Platform = os . platform ( ) ;
253+ let platformOverride : CppBuildTaskPlatformOverride | undefined ;
254+
255+ if ( platform === 'win32' ) {
256+ platformOverride = definition . windows ;
257+ } else if ( platform === 'linux' ) {
258+ platformOverride = definition . linux ;
259+ } else if ( platform === 'darwin' ) {
260+ platformOverride = definition . osx ;
261+ }
262+
263+ if ( ! platformOverride ) {
264+ return definition ;
265+ }
266+
267+ const mergedDefinition : CppBuildTaskDefinition = {
268+ ...definition ,
269+ command : platformOverride . command ?? definition . command ,
270+ args : platformOverride . args ?? definition . args ,
271+ options : platformOverride . options ?? definition . options ,
272+ problemMatcher : platformOverride . problemMatcher ?? definition . problemMatcher
273+ } ;
274+
275+ return mergedDefinition ;
276+ }
232277
233278 public async getJsonTasks ( ) : Promise < CppBuildTask [ ] > {
234279 const rawJson : any = await this . getRawTasksJson ( ) ;
@@ -244,7 +289,7 @@ export class CppBuildTaskProvider implements TaskProvider {
244289 args : task . args ,
245290 options : task . options
246291 } ;
247- const cppBuildTask : CppBuildTask = new Task ( definition , TaskScope . Workspace , task . label , ext . CppSourceStr ) ;
292+ const cppBuildTask : CppBuildTask = new CppBuildTask ( definition , TaskScope . Workspace , task . label , ext . CppSourceStr ) ;
248293 cppBuildTask . detail = task . detail ;
249294 cppBuildTask . existing = true ;
250295 if ( util . isObject ( task . group ) && task . group . isDefault ) {
0 commit comments