mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
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.
This commit is contained in:
@@ -21,6 +21,7 @@ approach: download the real MSVC/WinSDK, wrap the compiler under Wine.
|
|||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Quick start](#quick-start)
|
- [Quick start](#quick-start)
|
||||||
- [Commands](#commands)
|
- [Commands](#commands)
|
||||||
|
- [Invoking tools without PATH](#invoking-tools-without-path)
|
||||||
- [Building drivers (WDK)](#building-drivers-wdk)
|
- [Building drivers (WDK)](#building-drivers-wdk)
|
||||||
- [Building against D3DX9 (DirectX SDK)](#building-against-d3dx9-directx-sdk)
|
- [Building against D3DX9 (DirectX SDK)](#building-against-d3dx9-directx-sdk)
|
||||||
- [Automated/scripted builds](#automatedscripted-builds)
|
- [Automated/scripted builds](#automatedscripted-builds)
|
||||||
@@ -102,6 +103,9 @@ export PATH=~/.vintner/bin/x64:$PATH
|
|||||||
cl /nologo /EHsc hello.cpp
|
cl /nologo /EHsc hello.cpp
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Don't want to touch PATH? Skip step 3 and run tools through vintner
|
||||||
|
directly instead - see [Invoking tools without PATH](#invoking-tools-without-path).
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -132,6 +136,24 @@ package id or through `--with-*`. `--print-deps-tree` prints the
|
|||||||
dependency tree of whatever would actually be selected — honoring every
|
dependency tree of whatever would actually be selected — honoring every
|
||||||
other flag — without downloading anything.
|
other flag — without downloading anything.
|
||||||
|
|
||||||
|
## Invoking tools without PATH
|
||||||
|
|
||||||
|
`cl`, `link`, `msbuild`, and the rest also work as `vintner <tool>
|
||||||
|
[args...]`, with no need to add `<dest>/bin/<arch>` to `PATH` or rely on
|
||||||
|
the symlinks `install` sets up there:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vintner cl /nologo /EHsc hello.cpp
|
||||||
|
vintner msbuild MyProject.sln
|
||||||
|
```
|
||||||
|
|
||||||
|
Resolves the toolchain to use from `VINTNER_BIN` if set (same meaning as
|
||||||
|
`env --bin`: point it at a `<dest>/bin/<arch>` directory directly — useful
|
||||||
|
for a non-default `--dest`, or to pick a specific architecture when more
|
||||||
|
than one is installed), otherwise defaults to
|
||||||
|
`~/.vintner/bin/<host-arch>`, the layout a plain `vintner download &&
|
||||||
|
vintner install` with no `--dest` override produces.
|
||||||
|
|
||||||
## Building drivers (WDK)
|
## Building drivers (WDK)
|
||||||
|
|
||||||
`--with-wdk` also fetches the Windows Driver Kit: headers, import libs,
|
`--with-wdk` also fetches the Windows Driver Kit: headers, import libs,
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/vintner/internal/wrapper"
|
||||||
|
)
|
||||||
|
|
||||||
// runCompletion prints a shell completion script for shell ("bash" or
|
// runCompletion prints a shell completion script for shell ("bash" or
|
||||||
// "zsh") to stdout, meant to be sourced directly:
|
// "zsh") to stdout, meant to be sourced directly:
|
||||||
@@ -11,6 +16,11 @@ import "fmt"
|
|||||||
// The flag lists below are hand-maintained alongside download.go/env.go's
|
// The flag lists below are hand-maintained alongside download.go/env.go's
|
||||||
// flag.FlagSet definitions rather than generated from them - there's no
|
// flag.FlagSet definitions rather than generated from them - there's no
|
||||||
// reflection-friendly registry to walk, and the flag set rarely changes.
|
// 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 {
|
func runCompletion(args []string) int {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
fmt.Println("usage: vintner completion bash|zsh")
|
fmt.Println("usage: vintner completion bash|zsh")
|
||||||
@@ -18,10 +28,10 @@ func runCompletion(args []string) int {
|
|||||||
}
|
}
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "bash":
|
case "bash":
|
||||||
fmt.Print(bashCompletionScript)
|
fmt.Print(bashCompletionScript())
|
||||||
return 0
|
return 0
|
||||||
case "zsh":
|
case "zsh":
|
||||||
fmt.Print(zshCompletionScript)
|
fmt.Print(zshCompletionScript())
|
||||||
return 0
|
return 0
|
||||||
default:
|
default:
|
||||||
fmt.Printf("vintner completion: unsupported shell %q (want bash or zsh)\n", args[0])
|
fmt.Printf("vintner completion: unsupported shell %q (want bash or zsh)\n", args[0])
|
||||||
@@ -33,9 +43,16 @@ const downloadFlags = "--dest --cache --major --preview --manifest --accept-lice
|
|||||||
"--msvc-version --sdk-version --host-arch --only-host --language " +
|
"--msvc-version --sdk-version --host-arch --only-host --language " +
|
||||||
"--include-optional --skip-recommended --only-download --only-unpack " +
|
"--include-optional --skip-recommended --only-download --only-unpack " +
|
||||||
"--keep-unpack --skip-patch --list-workloads --list-components " +
|
"--keep-unpack --skip-patch --list-workloads --list-components " +
|
||||||
"--print-deps-tree --with-wdk --architecture --ignore -h --help"
|
"--print-deps-tree --with-wdk --with-dxsdk --architecture --ignore -h --help"
|
||||||
|
|
||||||
var bashCompletionScript = `# vintner bash completion - eval "$(vintner completion bash)"
|
// 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() {
|
_vintner_complete() {
|
||||||
local cur cmd
|
local cur cmd
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
@@ -43,7 +60,7 @@ _vintner_complete() {
|
|||||||
cmd="${COMP_WORDS[1]}"
|
cmd="${COMP_WORDS[1]}"
|
||||||
|
|
||||||
if [ "$COMP_CWORD" -eq 1 ]; then
|
if [ "$COMP_CWORD" -eq 1 ]; then
|
||||||
COMPREPLY=($(compgen -W "download dl install i env e version v help h completion" -- "$cur"))
|
COMPREPLY=($(compgen -W "` + subcommandNames + ` ` + strings.Join(wrapper.ToolNames(), " ") + `" -- "$cur"))
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -65,8 +82,15 @@ _vintner_complete() {
|
|||||||
}
|
}
|
||||||
complete -F _vintner_complete vintner
|
complete -F _vintner_complete vintner
|
||||||
`
|
`
|
||||||
|
}
|
||||||
|
|
||||||
var zshCompletionScript = `#compdef 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 zsh completion - source <(vintner completion zsh)
|
||||||
|
|
||||||
_vintner() {
|
_vintner() {
|
||||||
@@ -83,7 +107,7 @@ _vintner() {
|
|||||||
'help:print usage'
|
'help:print usage'
|
||||||
'h:alias for help'
|
'h:alias for help'
|
||||||
'completion:print a shell completion script'
|
'completion:print a shell completion script'
|
||||||
)
|
` + toolEntries.String() + ` )
|
||||||
|
|
||||||
if (( CURRENT == 2 )); then
|
if (( CURRENT == 2 )); then
|
||||||
_describe 'command' subcommands
|
_describe 'command' subcommands
|
||||||
@@ -115,6 +139,7 @@ _vintner() {
|
|||||||
'--list-components[list available components and exit]'
|
'--list-components[list available components and exit]'
|
||||||
'--print-deps-tree[print the dependency tree and exit]'
|
'--print-deps-tree[print the dependency tree and exit]'
|
||||||
'--with-wdk[also fetch the Windows Driver Kit]'
|
'--with-wdk[also fetch the Windows Driver Kit]'
|
||||||
|
'--with-dxsdk[also fetch the DirectX SDK]'
|
||||||
'--architecture[target architecture]:arch:(x86 x64 arm arm64 host)'
|
'--architecture[target architecture]:arch:(x86 x64 arm arm64 host)'
|
||||||
'--ignore[package id to skip]:package id:'
|
'--ignore[package id to skip]:package id:'
|
||||||
'-h[show help]'
|
'-h[show help]'
|
||||||
@@ -138,3 +163,4 @@ _vintner() {
|
|||||||
|
|
||||||
_vintner "$@"
|
_vintner "$@"
|
||||||
`
|
`
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/vintner/internal/wrapper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestCompletionScriptsAreSyntacticallyValid catches the easy way to break
|
// TestCompletionScriptsAreSyntacticallyValid catches the easy way to break
|
||||||
@@ -15,8 +17,8 @@ func TestCompletionScriptsAreSyntacticallyValid(t *testing.T) {
|
|||||||
shell string
|
shell string
|
||||||
script string
|
script string
|
||||||
}{
|
}{
|
||||||
{"bash", bashCompletionScript},
|
{"bash", bashCompletionScript()},
|
||||||
{"zsh", zshCompletionScript},
|
{"zsh", zshCompletionScript()},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.shell, func(t *testing.T) {
|
t.Run(tc.shell, func(t *testing.T) {
|
||||||
if _, err := exec.LookPath(tc.shell); err != nil {
|
if _, err := exec.LookPath(tc.shell); err != nil {
|
||||||
@@ -31,6 +33,24 @@ func TestCompletionScriptsAreSyntacticallyValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCompletionScriptsListEveryTool guards against the exact staleness bug
|
||||||
|
// found and fixed alongside this test: a hand-copied tool/flag list here
|
||||||
|
// drifting from the real set in internal/wrapper (or download.go's flags)
|
||||||
|
// as tools/flags get added. Every current tool name must appear in both
|
||||||
|
// generated scripts.
|
||||||
|
func TestCompletionScriptsListEveryTool(t *testing.T) {
|
||||||
|
bash := bashCompletionScript()
|
||||||
|
zsh := zshCompletionScript()
|
||||||
|
for _, name := range wrapper.ToolNames() {
|
||||||
|
if !strings.Contains(bash, name) {
|
||||||
|
t.Errorf("bash completion script doesn't mention tool %q", name)
|
||||||
|
}
|
||||||
|
if !strings.Contains(zsh, name+":") {
|
||||||
|
t.Errorf("zsh completion script doesn't mention tool %q", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunCompletionUnknownShell(t *testing.T) {
|
func TestRunCompletionUnknownShell(t *testing.T) {
|
||||||
if code := runCompletion([]string{"fish"}); code != 1 {
|
if code := runCompletion([]string{"fish"}); code != 1 {
|
||||||
t.Errorf("runCompletion([\"fish\"]) = %d, want 1", code)
|
t.Errorf("runCompletion([\"fish\"]) = %d, want 1", code)
|
||||||
|
|||||||
+9
-8
@@ -1,9 +1,9 @@
|
|||||||
// Command vintner cross compiles with the real MSVC toolchain on Linux
|
// Command vintner cross compiles with the real MSVC toolchain on Linux
|
||||||
// via Wine. It's a multi-call binary that behaves as `cl`, `link`, `lib`,
|
// via Wine. It's a multi-call binary that behaves as `cl`, `link`, `lib`,
|
||||||
// `rc`, `midl`, `mt`, `dumpbin`, `msbuild`, etc. when invoked under one of
|
// `rc`, `midl`, `mt`, `dumpbin`, `msbuild`, etc. when invoked under one of
|
||||||
// those names (via symlinks set up by `vintner install`), and
|
// those names (via symlinks set up by `vintner install`) or as
|
||||||
// otherwise exposes the `download`/`install`/`env`/`version` management
|
// `vintner <tool> ...` directly (see runTool), and otherwise exposes the
|
||||||
// subcommands.
|
// `download`/`install`/`env`/`version` management subcommands.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -24,11 +24,8 @@ func main() {
|
|||||||
base := filepath.Base(os.Args[0])
|
base := filepath.Base(os.Args[0])
|
||||||
name := strings.TrimSuffix(strings.ToLower(base), ".exe")
|
name := strings.TrimSuffix(strings.ToLower(base), ".exe")
|
||||||
|
|
||||||
if _, ok := wrapper.Tools[name]; ok {
|
if wrapper.IsTool(name) {
|
||||||
os.Exit(wrapper.Run(name, os.Args[1:]))
|
os.Exit(wrapper.Run(name, os.Args[1:], ""))
|
||||||
}
|
|
||||||
if name == "cmd" || name == "findstr" {
|
|
||||||
os.Exit(wrapper.Run(name, os.Args[1:]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(runCLI(os.Args[1:]))
|
os.Exit(runCLI(os.Args[1:]))
|
||||||
@@ -40,6 +37,10 @@ func runCLI(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if wrapper.IsTool(args[0]) {
|
||||||
|
return runTool(args[0], args[1:])
|
||||||
|
}
|
||||||
|
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "download", "dl":
|
case "download", "dl":
|
||||||
return runDownload(args[1:])
|
return runDownload(args[1:])
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/vintner/internal/wrapper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runTool dispatches `vintner <tool> [args...]` (cl, link, msbuild, ...)
|
||||||
|
// directly, without needing <dest>/bin/<arch> on PATH or a same-directory
|
||||||
|
// symlink pointing back at this binary.
|
||||||
|
//
|
||||||
|
// Resolves which toolchain bin dir to use from VINTNER_BIN if set (same
|
||||||
|
// meaning as `env --bin`: point it directly at a <dest>/bin/<arch>
|
||||||
|
// directory - useful for a non-default --dest, or to pick a specific
|
||||||
|
// architecture when more than one is installed), else defaults to
|
||||||
|
// <defaultToolchainDir>/bin/<hostArch>, the layout a plain `vintner
|
||||||
|
// download && vintner install` with no --dest override produces.
|
||||||
|
func runTool(tool string, args []string) int {
|
||||||
|
binDir := os.Getenv("VINTNER_BIN")
|
||||||
|
if binDir == "" {
|
||||||
|
def, err := defaultToolchainDir()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "vintner:", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
binDir = filepath.Join(def, "bin", detectHostArch())
|
||||||
|
}
|
||||||
|
if fi, err := os.Stat(binDir); err != nil || !fi.IsDir() {
|
||||||
|
fmt.Fprintf(os.Stderr,
|
||||||
|
"vintner: no installed toolchain found at %s\n"+
|
||||||
|
"Run `vintner download --accept-license && vintner install` first, "+
|
||||||
|
"or set VINTNER_BIN to an existing <dest>/bin/<arch> directory.\n",
|
||||||
|
binDir)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return wrapper.Run(tool, args, binDir)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunToolReportsMissingToolchain(t *testing.T) {
|
||||||
|
t.Setenv("VINTNER_BIN", filepath.Join(t.TempDir(), "does-not-exist"))
|
||||||
|
if code := runTool("cl", nil); code != 1 {
|
||||||
|
t.Errorf("runTool with a nonexistent VINTNER_BIN = %d, want 1", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunToolUsesVINTNERBinOverDefault(t *testing.T) {
|
||||||
|
// A directory that exists but has no env.json - past the "toolchain
|
||||||
|
// found at all" check, into wrapper.Run's own (already-tested)
|
||||||
|
// env.json-loading error path. Confirms VINTNER_BIN is actually being
|
||||||
|
// read and passed through, without needing a full fake toolchain.
|
||||||
|
t.Setenv("VINTNER_BIN", t.TempDir())
|
||||||
|
if code := runTool("cl", nil); code != 1 {
|
||||||
|
t.Errorf("runTool with an empty VINTNER_BIN dir = %d, want 1 (from the missing env.json)", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-4
@@ -84,7 +84,10 @@ has many, including --with-wdk, --with-dxsdk, --list-workloads,
|
|||||||
Language: set VINTNER_LANG=ru (or LANG=ru_RU...) for Russian output.
|
Language: set VINTNER_LANG=ru (or LANG=ru_RU...) for Russian output.
|
||||||
Completion: source <(vintner completion bash) # or zsh
|
Completion: source <(vintner completion bash) # or zsh
|
||||||
|
|
||||||
Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly:
|
Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly,
|
||||||
|
or skip PATH and run them through vintner itself - "vintner cl ...",
|
||||||
|
"vintner msbuild ...", etc. (set VINTNER_BIN to a <dir>/bin/<arch> if it's
|
||||||
|
not the default ~/.vintner/bin/<host-arch>):
|
||||||
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
||||||
`,
|
`,
|
||||||
RU: `vintner — кросс-компиляция настоящим MSVC на Linux через Wine
|
RU: `vintner — кросс-компиляция настоящим MSVC на Linux через Wine
|
||||||
@@ -104,9 +107,13 @@ Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly:
|
|||||||
|
|
||||||
--dest/[каталог] по умолчанию — ~/.vintner.
|
--dest/[каталог] по умолчанию — ~/.vintner.
|
||||||
Язык: установите VINTNER_LANG=en (или LANG=en_US...) для вывода на английском.
|
Язык: установите VINTNER_LANG=en (или LANG=en_US...) для вывода на английском.
|
||||||
|
Можно не трогать PATH: "vintner cl ...", "vintner msbuild ..." и т.д. работают
|
||||||
|
напрямую (VINTNER_BIN — если каталог не стандартный ~/.vintner/bin/<hostarch>).
|
||||||
Автодополнение: source <(vintner completion bash) # или zsh
|
Автодополнение: source <(vintner completion bash) # или zsh
|
||||||
|
|
||||||
После установки добавьте <dir>/bin/<arch> в PATH и вызывайте инструменты напрямую:
|
После установки добавьте <dir>/bin/<arch> в PATH и вызывайте инструменты
|
||||||
|
напрямую, либо не трогая PATH — «vintner cl ...», «vintner msbuild ...»
|
||||||
|
и т.д.:
|
||||||
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
cl, link, lib, ml, ml64, mc, midl, mt, rc, dumpbin, msbuild, nmake, armasm, armasm64, cmd, findstr
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@@ -124,8 +131,8 @@ Once installed, add <dir>/bin/<arch> to PATH and invoke the tools directly:
|
|||||||
RU: "Каталог не указан, используется значение по умолчанию: %s",
|
RU: "Каталог не указан, используется значение по умолчанию: %s",
|
||||||
},
|
},
|
||||||
"install.done": {
|
"install.done": {
|
||||||
EN: "Done. Add %s to PATH to use cl, link, lib, ...",
|
EN: "Done. Add %s to PATH to use cl, link, lib, ... directly, or run them as \"vintner cl\", \"vintner link\", etc. without touching PATH.",
|
||||||
RU: "Готово. Добавьте %s в PATH, чтобы использовать cl, link, lib и т.д.",
|
RU: "Готово. Добавьте %s в PATH, чтобы использовать cl, link, lib и т.д. напрямую, либо запускайте их как «vintner cl», «vintner link» и т.д., не трогая PATH.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"env.usage": {
|
"env.usage": {
|
||||||
|
|||||||
+20
-8
@@ -30,7 +30,15 @@ const toolRelayName = "toolrelay.exe"
|
|||||||
|
|
||||||
// Run executes the named multi-call tool with args, exactly as the original
|
// Run executes the named multi-call tool with args, exactly as the original
|
||||||
// bash wrappers would, and returns the process exit code.
|
// bash wrappers would, and returns the process exit code.
|
||||||
func Run(tool string, args []string) int {
|
//
|
||||||
|
// binDir is the <dest>/bin/<arch> directory holding env.json for this
|
||||||
|
// invocation. Pass "" for the ordinary multi-call case (invoked as `cl`,
|
||||||
|
// `link`, etc. via a same-directory symlink) to have it resolved from the
|
||||||
|
// running binary's own location; a non-empty value is for `vintner <tool>
|
||||||
|
// ...` direct dispatch (see cmd/vintner's runTool), which isn't running
|
||||||
|
// from inside any particular <dest>/bin/<arch> and so has nothing to
|
||||||
|
// resolve on its own.
|
||||||
|
func Run(tool string, args []string, binDir string) int {
|
||||||
if nativeTools[tool] {
|
if nativeTools[tool] {
|
||||||
return runNative(tool, args)
|
return runNative(tool, args)
|
||||||
}
|
}
|
||||||
@@ -41,19 +49,23 @@ func Run(tool string, args []string) int {
|
|||||||
return 127
|
return 127
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scriptDir := binDir
|
||||||
|
if scriptDir == "" {
|
||||||
// os.Executable() (backed by /proc/self/exe on Linux) fully resolves
|
// os.Executable() (backed by /proc/self/exe on Linux) fully resolves
|
||||||
// symlinks, unlike os.Args[0]: not every shell passes a PATH-resolved
|
// symlinks, unlike os.Args[0]: not every shell passes a
|
||||||
// absolute path as argv[0] (some just pass the bare command name), which
|
// PATH-resolved absolute path as argv[0] (some just pass the bare
|
||||||
// would make an argv[0]-based lookup resolve against the caller's cwd
|
// command name), which would make an argv[0]-based lookup resolve
|
||||||
// instead of the actual install dir. `install` sets each arch dir up
|
// against the caller's cwd instead of the actual install dir.
|
||||||
// with its own local copy of the binary precisely so this resolves to
|
// `install` sets each arch dir up with its own local copy of the
|
||||||
// <dest>/bin/<arch>, not <dest>/bin.
|
// binary precisely so this resolves to <dest>/bin/<arch>, not
|
||||||
|
// <dest>/bin.
|
||||||
exePath, err := os.Executable()
|
exePath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "vintner:", err)
|
fmt.Fprintln(os.Stderr, "vintner:", err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
scriptDir := filepath.Dir(exePath)
|
scriptDir = filepath.Dir(exePath)
|
||||||
|
}
|
||||||
|
|
||||||
cfg, err := wineenv.Load(scriptDir)
|
cfg, err := wineenv.Load(scriptDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
// filtering its output.
|
// filtering its output.
|
||||||
package wrapper
|
package wrapper
|
||||||
|
|
||||||
import "github.com/Cheviiot/vintner/internal/wineenv"
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/Cheviiot/vintner/internal/wineenv"
|
||||||
|
)
|
||||||
|
|
||||||
// dirKind selects which install directory a tool's real .exe lives in.
|
// dirKind selects which install directory a tool's real .exe lives in.
|
||||||
type dirKind int
|
type dirKind int
|
||||||
@@ -46,6 +50,32 @@ var Tools = map[string]spec{
|
|||||||
// nativeTools are handled entirely without Wine.
|
// nativeTools are handled entirely without Wine.
|
||||||
var nativeTools = map[string]bool{"cmd": true, "findstr": true}
|
var nativeTools = map[string]bool{"cmd": true, "findstr": true}
|
||||||
|
|
||||||
|
// IsTool reports whether name is a recognized wrapped tool - either a
|
||||||
|
// Wine-hosted one in Tools or a native shim in nativeTools. Shared between
|
||||||
|
// the ordinary multi-call dispatch (invoked *as* one of these names via a
|
||||||
|
// same-directory symlink) and `vintner <tool> ...` direct dispatch, so both
|
||||||
|
// recognize exactly the same set of names.
|
||||||
|
func IsTool(name string) bool {
|
||||||
|
_, ok := Tools[name]
|
||||||
|
return ok || nativeTools[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToolNames returns every recognized tool name, sorted - Tools and
|
||||||
|
// nativeTools combined. Used to generate shell completion without a
|
||||||
|
// separate hand-maintained list that could drift from the real set (as
|
||||||
|
// happened once already with a stale --with-dxsdk completion entry).
|
||||||
|
func ToolNames() []string {
|
||||||
|
names := make([]string, 0, len(Tools)+len(nativeTools))
|
||||||
|
for name := range Tools {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
for name := range nativeTools {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
func (s spec) exeDir(p *wineenv.Paths) string {
|
func (s spec) exeDir(p *wineenv.Paths) string {
|
||||||
switch s.dir {
|
switch s.dir {
|
||||||
case dirSDK:
|
case dirSDK:
|
||||||
|
|||||||
@@ -43,3 +43,21 @@ func TestEveryToolHasAnExeName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsTool(t *testing.T) {
|
||||||
|
for name := range Tools {
|
||||||
|
if !IsTool(name) {
|
||||||
|
t.Errorf("IsTool(%q) = false, want true (it's in Tools)", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name := range nativeTools {
|
||||||
|
if !IsTool(name) {
|
||||||
|
t.Errorf("IsTool(%q) = false, want true (it's in nativeTools)", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range []string{"download", "install", "env", "version", "help", "completion", "frobnicate", ""} {
|
||||||
|
if IsTool(name) {
|
||||||
|
t.Errorf("IsTool(%q) = true, want false", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user