New-VSCodeTask.ps1
<#PSScriptInfo .VERSION 1.0.1 .AUTHOR Roman Kuzmin .COPYRIGHT (c) 2011-2017 Roman Kuzmin .TAGS Invoke, Task, Invoke-Build, VSCode .GUID b8b2b532-28f6-443a-b0b1-079a66dd4ce3 .LICENSEURI http://www.apache.org/licenses/LICENSE-2.0 .PROJECTURI https://github.com/nightroman/Invoke-Build #> <# .Synopsis Makes VSCode tasks from Invoke-Build tasks .Description The script makes VSCode tasks from Invoke-Build tasks. It creates tasks.cmd and tasks.json in .\.vscode. The existing files are overridden. Change to the VSCode workspace directory before invoking the script. Do not edit tasks.json directly. Edit the build script instead. When you add, remove, rename, reorder tasks, change tags or Invoke-Build location then regenerate VSCode tasks. The default task becomes a so called VSCode build task (Ctrl+Shift+B). The default task is '.' if it exists, otherwise it is the first task. To invoke another task from VSCode, use Ctrl+Shift+P and type 'Run Task'. Then type a task name or select it from the opened list of your tasks. Or simply define a shortcut in keybindings.json, for example: // show the task list { "key": "ctrl+k k", "command": "workbench.action.tasks.runTask" } Only tasks with certain names are included. They contain alphanumeric characters, '_', '.', and '-', with the first character other than '-'. In order to invoke some tasks in a console host outside VSCode specify the tag #ConsoleHost in a comment preceding the task definition. Note that all parent tasks in the task trees get this tag automatically. .Parameter BuildFile Specifies the build script path, absolute or relative. By default it is the standard default script in the current location, i.e. the workspace root. .Parameter InvokeBuild Specifies the Invoke-Build.ps1 path, absolute or relative. If it is not specified then the script tries to find it in the workspace directory recursively. If it is not found then 'Invoke-Build.ps1' is used, i.e. the script is expected to be in the path. .Example New-VSCodeTask This command binds to the default build script in the workspace root and Invoke-Build.ps1 either in the workspace root or subdirectory or in the path. .Example New-VSCodeTask .\Scripts\Build.ps1 .\Tools\Invoke-Build\Invoke-Build.ps1 This command binds to the relative build and engine script paths. The second argument may be omitted, Invoke-Build.ps1 will be discovered (it is needed if there are several versions of it for some reason). #> [CmdletBinding()] param( [string]$BuildFile, [string]$InvokeBuild ) function Add-Text($Text) {$null = $out.Append($Text)} function Add-Line($Text) {$null = $out.AppendLine($Text)} $Comments = @{} function Test-ConsoleHost($Task) { $info = $Task.InvocationInfo $file = $info.ScriptName if (!($data = $Comments[$file])) { $Comments[$file] = $data = @{} foreach($_ in [System.Management.Automation.PSParser]::Tokenize((Get-Content -LiteralPath $file), [ref]$null)) { if ($_.Type -eq 'Comment') {$data[$_.EndLine] = $_.Content} } } for($n = $info.ScriptLineNumber; --$n -ge 1 -and ($c = $data[$n])) { if ($c -match '#ConsoleHost\b') { return 1 } } foreach($j in $Task.Jobs) {if ($j -is [string]) { if (Test-ConsoleHost $all[$j]) { return 1 } }} } ### main trap {$PSCmdlet.ThrowTerminatingError($_)} $ErrorActionPreference = 'Stop' # resolve missing Invoke-Build.ps1 if (!$InvokeBuild) { $InvokeBuild2 = @(Get-ChildItem . -Name -Recurse -Include Invoke-Build.ps1) $InvokeBuild = if ($InvokeBuild2) {".\$($InvokeBuild2[0])"} else {'Invoke-Build.ps1'} } # get all tasks and the default task $all = & $InvokeBuild ?? -File $BuildFile $dot = if ($all['.']) {'.'} else {$all.Item(0).Name} # ensure .vscode if (!(Test-Path .vscode)) { $null = mkdir .vscode } ### tasks.cmd $InvokeBuild2 = $InvokeBuild.Replace("'", "''") $BuildFile2 = if ($BuildFile) {" -File '{0}'" -f $BuildFile.Replace("'", "''")} $out = @' @rem Do not edit! This file is generated by New-VSCodeTask.ps1 @echo off if "%1" == "!" goto start chcp 65001 > nul PowerShell.exe -NoProfile -ExecutionPolicy Bypass "& '{0}'{1} %1" exit :start shift start PowerShell.exe -NoExit -NoProfile -ExecutionPolicy Bypass "& '{0}'{1} %1" '@ -f $InvokeBuild2, $BuildFile2 Set-Content .\.vscode\tasks.cmd $out ### tasks.json $out = New-Object System.Text.StringBuilder Add-Line '// Do not edit! This file is generated by New-VSCodeTask.ps1' Add-Line '// Modify the build script instead and regenerate this file.' Add-Line '{' Add-Line ' "version": "0.1.0",' Add-Line ' "command": ".\\.vscode\\tasks.cmd",' Add-Line ' "suppressTaskName": false,' Add-Line ' "showOutput": "always",' Add-Line ' "tasks": [' foreach($task in $all.Values) { $name = $task.Name if ($name -match '[^\w\.\-]|^-') { continue } Add-Line ' {' if ($name -eq $dot) { Add-Line ' "isBuildCommand": true,' } if (Test-ConsoleHost $task) { Add-Line ' "suppressTaskName": true,' Add-Line (' "args": ["!", "{0}"],' -f $name) } Add-Line (' "taskName": "{0}"' -f $name) Add-Line ' },' } Add-Line ' {' Add-Line ' "taskName": "?"' Add-Line ' }' Add-Line ' ]' Add-Text '}' Set-Content .\.vscode\tasks.json $out.ToString() -Encoding UTF8 |