Why I Started Using This Tool

Once the Makefile worked, the slowest part of my day wasn't writing C. It was the ritual around it. Twice I spent several minutes chasing a "bug" I had already fixed, because I forgot to copy the new binary to the board. So the second task in the chain does the copy for me, and it always builds first.

What It Does

{
  "label": "deploy",
  "type": "shell",
  "command": "scp",
  "args": [
    "-o", "StrictHostKeyChecking=accept-new",
    "${config:bin}",
    "${config:user}@${config:host}:${config:remoteDir}/${config:bin}"
  ],
  "dependsOn": ["build"],
  "problemMatcher": []
}
  • scp means "secure copy". It copies a file to another machine over SSH. Windows 10 and 11 include the OpenSSH client, so scp and ssh usually work from PowerShell with nothing extra installed.
  • args: instead of cramming everything into one command string, each argument is passed separately. VS Code then handles the quoting, which matters once paths have spaces in them.
  • -o StrictHostKeyChecking=accept-new: the first time you connect to a new machine, SSH stops and asks "Are you sure you want to continue connecting (yes/no)?" In an automated task nobody's there to type "yes", so the task just hangs. accept-new trusts a board it has never seen and saves its fingerprint, but still refuses if a known board's fingerprint changes, which is the case that actually protects you.
  • The source, ${config:bin}, is the binary on the PC. A plain filename works because VS Code runs tasks from the workspace folder by default, which is also where the Makefile puts its output.
  • The destination, user@host:path, becomes root@192.168.0.83:/home/root/main with my settings: log in as root on that IP, and put the file at /home/root/main.
  • dependsOn: ["build"]: always build before copying.

A correction if you're following along from #3: there I called the board's IP setting target. This task uses ${config:host}. The names have to match exactly, so either rename target to host in settings.json (that's what I did, since "host" is what SSH calls the machine you connect to), or change the tasks to ${config:target}. If they don't match, the address comes out broken, something like root@:/home/root/main, and scp fails.

A DE10-Nano gotcha: if you re-flash the SD card, the board gets a new SSH key and SSH refuses to connect. Remove the old entry with ssh-keygen -R 192.168.0.83.

Set up SSH keys early. scp here and ssh in the next task each log in separately, so with a password you'll type it twice every run. Run ssh-keygen on the PC once, then add the contents of your public key file (id_ed25519.pub, for example) to /home/root/.ssh/authorized_keys on the board. After that, the chain runs with no prompts.

Final Verdict

Running deploy now builds and copies in one go, and it stops if the build fails, so the board can't end up with a half-updated binary. The two things to watch are the setting names (my target vs host mix-up cost me a confusing scp error) and SSH keys. Next is the last link: a gdbserver task that starts the program on the board, paused and waiting for the debugger.