I’ve set up a tasks.json file for building a project on multiple platforms. All platforms see the same content of the project repository. This is done either via disk sharing, because of running another platform in a VM, or via sync with the Git repository.
So far so good, they all see the same task.json. However some command lines are rather long and those long lines are identical for most part.
for example:
"rm -rf build; mkdir build; cd build; ../configure --with-bash-malloc=no CFLAGS="-O3 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free" LDFLAGS=-L/usr/local/lib LIBS="-ltcmalloc -lcurl" CC=clang
Similar lines are there for the different platforms.
The configure part is always the same for the different platforms, so it would be nice to factor out this common part. Thus the question is if it is possible to define your own variables, so you can use them similar to ${workspaceRoot}
.
Thus define somewhere
"win_dir": "build_windows",
"linux_dir": "build",
"osx_dir": "build_osx",
"configure": "../configure --with-bash-malloc=no CFLAGS="-O3 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free" LDFLAGS=-L/usr/local/lib LIBS="-ltcmalloc -lcurl" CC=clang"
And then write
"tasks": [
{
"taskName": "configure",
"command": "bash",
"windows": {
"args": ["-c", "rm -rf ${win_dir}; mkdir ${win_dir}; cd ${win_dir}; ${configure}"]
},
"linux": {
"args": ["-c", "rm -rf ${linux_dir}; mkdir ${linux_dir}; cd ${linux_dir}; ${configure}"]
},
"osx": {
"args": ["-c", "rm -rf ${osx_dir}; mkdir ${osx_dir}; cd ${osx_dir}; ${configure}"]
},
"isBuildCommand": true,
"problemMatcher": "$make-compile"
},
... others tasks using the variables
When making changes to the build directory or arguments passed to configure etc, then the tasks.json file needs only editing at one place, instead of many.
Perhaps it is already possible but I’m unable to find out how. I tried to do something with the declares block, but that seems to be hard tied to problemMatcher. You can find some examples, but I could not find clear documentation of of the elements of the tasks.json file and how they interact.
Perhaps I’m missing something, please educate me!
1
Adam Parkin’s answer won’t work because, at least on windows, the shell will not substitute environment variables given as arguments. ${env:...}
variables as suggested in a comment on that answer won’t be substituted using environment variables set in tasks.json
itself, only preexisting ones. You can however add custom settings in settings.json
, and reference those in tasks.json
using ${config:...}
.
e.g. settings.json
:
{
"win_dir": "build_windows",
"linux_dir": "build",
"osx_dir": "build_osx",
"configure": "../configure --with-bash-malloc=no CFLAGS="-O3 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free" LDFLAGS=-L/usr/local/lib LIBS="-ltcmalloc -lcurl" CC=clang"
}
in tasks.json
:
{
"tasks": [
{
"taskName": "configure",
"command": "bash",
"windows": {
"args": ["-c", "rm -rf ${config:win_dir}; mkdir ${config:win_dir}; cd ${config:win_dir}; ${config:configure}"]
},
"linux": {
"args": ["-c", "rm -rf ${config:linux_dir}; mkdir ${config:linux_dir}; cd ${config:linux_dir}; ${config:configure}"]
},
"osx": {
"args": ["-c", "rm -rf ${config:osx_dir}; mkdir ${config:osx_dir}; cd ${config:osx_dir}; ${config:configure}"]
},
"isBuildCommand": true,
"problemMatcher": "$make-compile"
},
// ... other tasks using the variables
]
}
2
Use tasks.options.env
to set per-task environment
As documented in the VSCode custom tasks page, environment variables can be set either at the task level or the file level, depending on how broadly those values should be applied.
Task-level Example:
{
"version": "2.0.0",
"tasks": [
{
"label": "Test with passing variable",
"type": "shell",
"options": {
"env": {
"GREETING_VAR": "Hello"
}
},
"command": "echo ${GREETING_VAR}, World!"
}
]
}
File-level Example:
{
"version": "2.0.0",
"options": {
"env": {
"GREETING_VAR": "Hello"
}
},
"tasks": [
{
"label": "Test with passing variable",
"type": "shell",
"command": "echo ${GREETING_VAR}, World!"
}
]
}
More details regarding options
in tasks may be found here.
1
Thus the question is if it is possible to define your own variables, so you can use them similar to ${workspaceRoot}.
You could define environment variables in your tasks.json:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"options": {
"env": {
"win_dir": "build_windows",
"linux_dir": "build",
"osx_dir": "build_osx",
"configure": "../configure --with-bash-malloc=no CFLAGS="-O3 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free" LDFLAGS=-L/usr/local/lib LIBS="-ltcmalloc -lcurl" CC=clang"
}
},
"tasks": [
{
"label": "Example",
"type": "shell",
"command": "echo win_dir is $env:win_dir"
},
]
}
With that, you could then also use the environment matching to refer to the relevant environment variables.
3
I am taking a different approach.
tasks.json
{
"version": "2.0.0",
"params":{
"git_version":"2.30.0",
"node_version":"14.13.6",
"python_version":"3.8"
},
"tasks": [
{
"label":"Process Task.json",
"type":"shell",
"command":"python process_tasks.py",
"group":"build",
"isBackground":true
},
{
"label":"Test process_tasks.py",
"type":"shell",
"command":"echo $[params.git_version]",
"group":"test",
"presentation": {
"reveal": "always"
}
}
]
}
Rather than making env variables, we can follow the flowing steps:
Step 1:
Make a task in tasks.json as follows
{
"label":"Process Task.json",
"type":"shell",
"command":"python process_tasks.py",
"group":"build",
"isBackground":true
},
Step 2:
process_tasks.py is a Python file that will replace variables in tasks.json with the actual value:
import json
import os
import re
if __name__ == "__main__":
# Get the path to the JSON file
json_path = os.path.join(".vscode/tasks.json")
with open(json_path, "r") as f:
data = json.load(f)
with open(json_path, "r") as f:
lines = f.readlines()
new_lines = []
#regex to find text between $[]
regex = re.compile(r"$[(.*?)]")
for line in lines:
#regex in line:
match = regex.search(line)
if match:
#get the text between $[]
text = match.group(1)
keys = text.split(".")
buffer_data = data
for key in keys:
buffer_data = buffer_data[key]
#replace the text with the value of the environment variable
line = line.replace(f"$[{text}]", buffer_data)
new_lines.append(line)
with open(json_path, "w") as f:
f.writelines(new_lines)
Step 3:
Add a test task to verify your result
{
"label":"Test process_tasks.py",
"type":"shell",
"command":"echo $[params.git_version]",
"group":"test",
"presentation": {
"reveal": "always"
}
},
Note:
Making this “Process Task.json” as a global task and adding the correct path of the process.py file in the build task will reduce a lot of work.
Thus we can define our own variables inside tasks.json and access them using $[params.git_version]
.
After executing the “Process Task.json” task, all variables in $[]
format will be replaced by its corresponding value.
0