Files
Vintner/cmd/vintner/completion.go
T
Cheviiot 738234186d Run wrapped tools directly as vintner <tool> ..., no PATH needed
`vintner cl ...`, `vintner msbuild ...`, etc. now work without adding
<dest>/bin/<arch> to PATH or relying on the same-directory symlinks
`install` sets up there. wrapper.Run gained an explicit binDir
parameter (empty string preserves the existing os.Executable()-based
self-location for the ordinary multi-call/symlink case) so
cmd/vintner's new runTool can point it at a resolved toolchain
directory instead.

Resolution order: VINTNER_BIN if set (same meaning as `env --bin` -
point it at a <dest>/bin/<arch> directory directly, for a non-default
--dest or a specific architecture), else <defaultToolchainDir>/bin/
<hostArch> - the layout a plain `vintner download && vintner install`
with no --dest override produces. A missing toolchain gets a clear
error pointing at both fixes, rather than bubbling up whatever
wineenv.Load's env.json error looks like.

Also fixed shell completion falling out of sync with the actual tool
list: the bash/zsh scripts previously hand-copied tool/flag names
(and had already gone stale once - --with-dxsdk was missing from the
download flag completions since it was added). The tool name list is
now generated from wrapper.ToolNames() instead of hand-maintained,
and both scripts now complete tool names too, so `vintner <TAB>`
suggests `cl`, `link`, `msbuild`, etc. alongside the management
subcommands.

Verified end-to-end with a minimal PATH (/usr/bin:/bin only, no
toolchain dir on it at all): `vintner cl /nologo hello.c` compiled
successfully, and `vintner msbuild -t:Rebuild ...` rebuilt the same
real KMDF driver verified earlier this session - both via the default
~/.vintner/bin/<hostArch> resolution, no VINTNER_BIN override needed.
2026-07-25 16:23:46 +10:00

167 lines
6.0 KiB
Go

package main
import (
"fmt"
"strings"
"github.com/Cheviiot/vintner/internal/wrapper"
)
// runCompletion prints a shell completion script for shell ("bash" or
// "zsh") to stdout, meant to be sourced directly:
//
// source <(vintner completion bash) # or add to ~/.bashrc
// source <(vintner completion zsh) # or add to ~/.zshrc
//
// The flag lists below are hand-maintained alongside download.go/env.go's
// flag.FlagSet definitions rather than generated from them - there's no
// reflection-friendly registry to walk, and the flag set rarely changes.
// The tool name list isn't hand-maintained, though (see wrapper.ToolNames):
// a hand-copied one already went stale once already for a flag
// (--with-dxsdk missing from here after being added to download.go), and a
// list of every wrapped tool has more entries and changes for the same
// reasons the flag lists do, so it's worth generating for real.
func runCompletion(args []string) int {
if len(args) != 1 {
fmt.Println("usage: vintner completion bash|zsh")
return 1
}
switch args[0] {
case "bash":
fmt.Print(bashCompletionScript())
return 0
case "zsh":
fmt.Print(zshCompletionScript())
return 0
default:
fmt.Printf("vintner completion: unsupported shell %q (want bash or zsh)\n", args[0])
return 1
}
}
const downloadFlags = "--dest --cache --major --preview --manifest --accept-license " +
"--msvc-version --sdk-version --host-arch --only-host --language " +
"--include-optional --skip-recommended --only-download --only-unpack " +
"--keep-unpack --skip-patch --list-workloads --list-components " +
"--print-deps-tree --with-wdk --with-dxsdk --architecture --ignore -h --help"
// subcommandNames lists vintner's own management subcommands (long form
// plus every short alias) - unlike the wrapped-tool names, these really are
// fixed enough to hand-maintain: adding one is rare and always touches
// main.go's dispatch switch right next to this file anyway.
const subcommandNames = "download dl install i env e version v help h completion"
func bashCompletionScript() string {
return `# vintner bash completion - eval "$(vintner completion bash)"
_vintner_complete() {
local cur cmd
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
cmd="${COMP_WORDS[1]}"
if [ "$COMP_CWORD" -eq 1 ]; then
COMPREPLY=($(compgen -W "` + subcommandNames + ` ` + strings.Join(wrapper.ToolNames(), " ") + `" -- "$cur"))
return 0
fi
case "$cmd" in
download|dl)
COMPREPLY=($(compgen -W "` + downloadFlags + `" -- "$cur"))
;;
install|i)
COMPREPLY=($(compgen -d -- "$cur"))
;;
env|e)
COMPREPLY=($(compgen -W "--bin -h --help" -- "$cur"))
;;
completion)
COMPREPLY=($(compgen -W "bash zsh" -- "$cur"))
;;
esac
return 0
}
complete -F _vintner_complete vintner
`
}
func zshCompletionScript() string {
var toolEntries strings.Builder
for _, name := range wrapper.ToolNames() {
fmt.Fprintf(&toolEntries, " %q\n", name+":run this tool directly, e.g. \"vintner "+name+" ...\"")
}
return `#compdef vintner
# vintner zsh completion - source <(vintner completion zsh)
_vintner() {
local -a subcommands
subcommands=(
'download:fetch and unpack MSVC/WinSDK/WDK'
'dl:alias for download'
'install:wire up wrappers for a downloaded MSVC'
'i:alias for install'
'env:print INCLUDE/LIB for native clang-cl/lld-link use'
'e:alias for env'
'version:print the version'
'v:alias for version'
'help:print usage'
'h:alias for help'
'completion:print a shell completion script'
` + toolEntries.String() + ` )
if (( CURRENT == 2 )); then
_describe 'command' subcommands
return
fi
case "${words[2]}" in
download|dl)
local -a flags
flags=(
'--dest[directory to install into]:directory:_files -/'
'--cache[persistent download cache directory]:directory:_files -/'
'--major[major VS version]:version:'
'--preview[use the preview/insiders channel]'
'--manifest[use a predownloaded installer manifest file]:file:_files'
'--accept-license[do not prompt for accepting the license]'
'--msvc-version[install a specific MSVC toolchain version]:version:'
'--sdk-version[install a specific Windows SDK version]:version:'
'--host-arch[host architecture]:arch:(x86 x64 arm64)'
'--only-host[only download packages matching the host architecture]'
'--language[preferred package language]:language:'
'--include-optional[include all optional dependencies]'
'--skip-recommended[skip recommended dependencies]'
'--only-download[stop after downloading package files]'
'--only-unpack[unpack without pruning to just the CLI tools]'
'--keep-unpack[keep the scratch unpack dir]'
'--skip-patch[do not apply the Wine compatibility patches]'
'--list-workloads[list available workloads and exit]'
'--list-components[list available components and exit]'
'--print-deps-tree[print the dependency tree and exit]'
'--with-wdk[also fetch the Windows Driver Kit]'
'--with-dxsdk[also fetch the DirectX SDK]'
'--architecture[target architecture]:arch:(x86 x64 arm arm64 host)'
'--ignore[package id to skip]:package id:'
'-h[show help]'
'--help[show help]'
)
_arguments $flags
;;
install|i)
_files -/
;;
env|e)
_arguments \
'--bin[bin/<arch> directory produced by install]:directory:_files -/' \
'-h[show help]' '--help[show help]'
;;
completion)
_values 'shell' bash zsh
;;
esac
}
_vintner "$@"
`
}