5

WSL makes it easy to get gcc and other Linux tools. You can use a script to quickly configure a WSL development environment, so if you mess something up you can quickly destroy it and start over again with minimal cost. Thus I would like to run tasks on WSL instead of on Windows itself.

In Sublime3 for example, I can build C code using the WSL installation of GCC by simply creating a new build system with the following code:

{
  "cmd" : ["bash", "-c", "gcc ${file_name} -o ${file_base_name} && ./${file_base_name}"],
  "shell": true,
  "working_dir": "${file_path}",
}

I have been trying to figure out how to replicate this on VS Code with no luck so far. It seems as if at least one other person has managed to get this working so it should be possible.

It seems that the proper way is to configure a tasks.json file. The relevant lines seem to be:

"command": "bash.exe",
    "args": ["-c", "'insert code here'"]

It seems that this tells the powershell to run bash -c 'insert code here'. Can anyone come up with the right command?


Some Background:

${file} is a a variable representing the active file in VSCode. Here is the list of all predefined variables.

wslpath is a new bash command shipping in Windows 1803 which takes a Windows path and returns the wsl path e.g.:

$ wslpath "c:\Users\Evan\Google Drive\Development\Programming Languages\file.c"
/mnt/c/Users/Evan/Google Drive/Development/Programming Languages/file.c

My Attempts:

I tried writing a script to do it, but ran into issues. The tasks.json syntax to call myscript should be something like this (note that we pass the file name to the script so that the script can pass it to GCC).

"args": ["-c", "'myscript.sh \"${file}\"'"]

or as per this answer

"command": "/path/to/script.sh"
"args": ["\"${file}\""]

myscript might look something like this:

#!/bin/bash
echo "File Path is here $1"
x=$(wslpath "$1")
gcc "$x" -o a.out
./a.out

VS Code Docs

Evan Rosica
  • 1,182
  • 1
  • 12
  • 22

5 Answers5

4

It can be done without the script:

 "command": "gcc '$(wslpath '${file}')' && ./a.out"

${file} would be parsed before the command parsed in bash.

idanp
  • 973
  • 12
  • 18
  • This doesn't work. Its getting confused by the spaces in my path name. I get the following error: `> Executing task: gcc $(wslpath 'c:\Users\Evan\Google Drive\Development\Programming Languages\C\sandbox.c') < gcc: error: /mnt/c/Users/Evan/Google: No such file or directory gcc: error: Drive/Development/Programming: No such file or directory gcc: error: Languages/C/sandbox.c: No such file or directory gcc: fatal error: no input files compilation terminated. The terminal process terminated with exit code: 1` – Evan Rosica May 26 '18 at 20:46
  • I figured it out; you just need an extra set of escaped quotes `\"` to handle this case: `"command": "gcc \"$(wslpath '${file}')\" && echo done"` – Evan Rosica May 26 '18 at 20:58
  • And then to run the file too it's simply: `"gcc \"$(wslpath '${file}')\" -o a.out &&./a.out` – Evan Rosica May 26 '18 at 21:15
  • Worked for me without the extra quote. On vs-code 1.23.1 and windows 1803. Added the run executable command o answer. – idanp May 27 '18 at 11:02
  • Did you have any spaces in your path name? – Evan Rosica May 27 '18 at 15:33
2

here is another quick solution of how to solve the problem using the bash command in the task.json file in vscode. You can pretty much take this and insert the commands that you need. You can also chain and nest commands if you need that.

{
    "version": "2.0.0",
    "tasks": [
        {
            // link current file. Uses one or more object.o files
            "label": "link_only_cpp_17",
            "type": "shell",
            "command": "bash", // call bash
            "args": [
                "-c", // read commands from string
                "\"g++ -g -o a $(ls | grep -F .o | tr '\\n' ' ')\"" // link all files ending with .o
            ],
            "problemMatcher": []
        }
    ]
}

This is an example from my task.json file. It shows that you can use the bash command and pass arbitrary code to it. For this you have to use the -c flag (read code from a string) and then pass your code as a string (next argument). In this example, the g++ links all files that contain .o. These files are found with the help of $(ls | grep -F .o | tr '\\n' ' '). Be aware that you have to use $(), otherwise, your code will not be evaluated.

As far as I know, you cannot use a vscode macro (e.g. ${file}) in the command that you pass to bash, since this is a string literal. This is why I am using a and not the file name. If you know a way to do this, let me know :)

Works on vscode version 1.38.1.

User12547645
  • 6,955
  • 3
  • 38
  • 69
1

To avoid changing the default shell, have it working within Powershell, and avoid long escape sequences, using a separate file is preferable.

Launcher:

{
            "label": "command name",
            "type": "shell",
            "command": ".\\command_name.bat",
            "problemMatcher": []
}

Batch file:

bash.exe -c "ssh -T bastion \"for proxy in bunch of different hosts ; do echo \\\$proxy ; ssh \\\$proxy -T -oBatchMode=yes -oStrictHostKeyChecking=no -oConnectTimeout=3 \\\"command --with --parameters\\\" ; done\""
Iron Bishop
  • 1,749
  • 1
  • 7
  • 16
1

A simple workaround: prepend wsl before the command. screenshot

Bartleby
  • 1,144
  • 1
  • 13
  • 14
0

I figured it out. Firstly, make sure your user settings are configured to use bash:

"terminal.integrated.shell.windows": "C:\\Windows\\sysnative\\bash.exe"

Note "C:\\Windows\\System32\\bash.exe" also seems to work fine.


Next your tasks.json should contain these lines:

"type": "shell",                                        //Uses the default shee (make sure this is set to bash)
"command": "'./path to/your/script.sh'",                //Unix Style Path to the Script (remember, bash is your shell reading this).
"args": ["\"${file}\""],                                //Pass the path to the source code as an arg 
"useWSL": true,                                         //This was suggested somewhere. The script runs fine without it though.... Not sure what this does. 

Finally, here is the script. Be sure to change your line endings to LF. The script does not work otherwise.

#!/bin/bash
echo "Running Script"
echo "Windows File Path $1"           #The Windows file path we passed in as an argument
wslpath=$(wslpath "$1")               #Convert the Windows path to a Unix Style Path
echo "wslpath is $wslpath"
gcc "$wslpath"                        #Compile the file
echo "done" 
./a.out                               #execute the compiled file

Then opening any C file and hitting Ctrl + Shift + B and selecting the script name from the build task menu will compile the C file to a.out in the current directory.

Evan Rosica
  • 1,182
  • 1
  • 12
  • 22