Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Sibikrish3000/gowinbridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GoWinBridge

Go License: GPL

A Go library and CLI for executing Windows binaries from WSL with pure Go path translation, codepage sanitization, interactive PTY support, environment bridging, shim generation, and bounded concurrency.

Why?

Running Windows binaries from WSL via os/exec works, but lacks:

  • Path translation — Linux paths like ./file.txt don't resolve on the Windows side
  • Encoding handling — Windows tools output CP1252/UTF-16LE, Go assumes UTF-8
  • Interactive support — REPLs like python.exe and TUI apps break with buffered I/O
  • Environment bridging — Windows processes don't inherit Linux env vars without WSLENV
  • Signal propagation — Ctrl+C in your terminal won't kill the Windows child process
  • Concurrency control — Spawning dozens of Windows processes simultaneously is expensive

GoWinBridge solves all of these behind a clean Go API and a ready-to-use CLI.

Architecture

cmd/winrun/                CLI entry point (flags, signal handling, shim subcommand)
  │
  ├── pkg/workerpool/        Bounded worker pool (configurable concurrency)
  │     │
  │     └── pkg/bridge/        Core executor
  │           ├── exec.go          CommandContext, .exe resolution, buffered + interactive modes
  │           ├── encoding.go      CP1252/UTF-16LE/BE → UTF-8 decoder middleware
  │           ├── env.go           WSLENV formatting with value-based heuristics
  │           └── config.go        CommandConfig / Output types
  │
  └── internal/wsl/          WSL detection & path translation
        ├── detect.go            WSL1 vs WSL2 detection (cached singleton)
        └── path.go              Pure Go path resolver (/proc/mounts + string manipulation)

Installation

One-line Installer (Recommended)

curl -sL https://raw.githubusercontent.com/sibikrish3000/gowinbridge/main/install.sh | sh

From Source

# Clone
git clone https://github.com/sibikrish3000/gowinbridge.git
cd gowinbridge

# Build the CLI
go build -o winrun ./cmd/winrun

# Or install via Go
go install github.com/sibikrish3000/gowinbridge/cmd/winrun@latest

CLI Usage

# Basic execution
winrun -- cmd.exe /c echo hello

# Auto-convert Linux paths to Windows paths
winrun --convert-paths -- cmd.exe /c type ./go.mod

# Handle Windows codepage output (legacy tools outputting CP1252)
winrun --encoding cp1252 -- cmd.exe /c chcp

# Interactive mode for REPLs (auto-detected for python, node, mysql, etc.)
winrun --interactive -- python.exe

# Environment variable tunneling (flags auto-detected from values)
winrun --env MY_VAR=hello --env MY_PATH=/home/user --tunnel-env -- cmd.exe /c echo %MY_VAR%

# Concurrent execution with timeout
winrun --concurrency 4 --timeout 30s -- powershell.exe -Command Get-Process

# Version info
winrun --version

Flags

Flag Default Description
--concurrency N NumCPU Max concurrent Windows process executions
--convert-paths false Auto-detect and convert file path arguments to Windows format
--encoding ENC "" (UTF-8) Output encoding: utf8, cp1252, utf16le, utf16be, auto
--interactive false Run in interactive/PTY mode (bypasses output capture)
--env KEY=VAL Set environment variable (repeatable)
--tunnel-env false Enable WSLENV tunneling for --env vars
--timeout DURATION 0 (none) Max execution time (e.g., 30s, 5m)
--version Print version information and exit

Shim Generator

Create transparent wrapper scripts so Windows tools behave like native Linux binaries:

# Install a shim — creates ~/.local/bin/docker that proxies to docker.exe
winrun shim install docker.exe --as docker

# Install with custom bin directory
winrun shim install code.exe --as code --bin-dir /usr/local/bin

# Auto-derive name from binary (docker.exe → docker)
winrun shim install docker.exe

# List installed shims
winrun shim list

# Remove a shim
winrun shim remove docker

Each shim is a shell script:

#!/bin/sh
# Generated by winrun shim install
# Binary: docker.exe
exec winrun --convert-paths -- docker.exe "$@"

Library Usage

Basic Execution

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/sibikrish3000/gowinbridge/pkg/bridge"
)

func main() {
    output, err := bridge.Execute(context.Background(), bridge.CommandConfig{
        Command:      "cmd.exe",
        Args:         []string{"/c", "echo", "hello from Windows"},
        ConvertPaths: false,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(output.Stdout)
    // Output: hello from Windows
}

With Path Translation & Environment Tunneling

output, err := bridge.Execute(ctx, bridge.CommandConfig{
    Command:      "cmd.exe",
    Args:         []string{"/c", "type", "./myfile.txt"},
    ConvertPaths: true,  // ./myfile.txt → C:\Users\...\myfile.txt
    Env:          map[string]string{
        "MY_VAR":  "value",        // auto-detected as /u (plain string)
        "MY_PATH": "/home/user",   // auto-detected as /p (path)
    },
    EnvTunneling: true,  // Generates WSLENV=MY_PATH/p:MY_VAR/u
    Timeout:      30 * time.Second,
})

With Encoding (Legacy Windows Tools)

output, err := bridge.Execute(ctx, bridge.CommandConfig{
    Command:  "legacy_tool.exe",
    Args:     []string{"/report"},
    Encoding: "cp1252",  // Decode CP1252 output to UTF-8
})
// output.Stdout is now valid UTF-8, even if the tool outputs "café"

Interactive Mode (REPLs & TUI)

output, err := bridge.Execute(ctx, bridge.CommandConfig{
    Command:     "python.exe",
    Interactive: true,       // Direct stdin/stdout/stderr copy
    Stdin:       os.Stdin,   // Pass through terminal input
})

Concurrent Execution via Worker Pool

import "github.com/sibikrish3000/gowinbridge/pkg/workerpool"

pool := workerpool.NewPool(4, bridge.Execute)

pool.Submit(bridge.CommandConfig{Command: "build1.exe"})
pool.Submit(bridge.CommandConfig{Command: "build2.exe"})
pool.Submit(bridge.CommandConfig{Command: "build3.exe"})

go pool.Shutdown()

for result := range pool.Results() {
    if result.Err != nil {
        log.Printf("Error: %v", result.Err)
        continue
    }
    fmt.Printf("[%s] %s\n", result.Config.Command, result.Output.Stdout)
}

WSL Detection

import "github.com/sibikrish3000/gowinbridge/internal/wsl"

if !wsl.IsWSL() {
    log.Fatal("This tool requires WSL")
}
fmt.Printf("Running on WSL%d\n", wsl.DetectWSLVersion())

Pure Go Path Translation

import "github.com/sibikrish3000/gowinbridge/internal/wsl"

// Linux → Windows (no subprocess, <1µs)
winPath, _ := wsl.ToWindowsPath("/mnt/c/Users/test")
// winPath = "C:\Users\test"

// Non-mount paths get UNC notation
uncPath, _ := wsl.ToWindowsPath("/home/user/project")
// uncPath = "\\wsl.localhost\Ubuntu\home\user\project"

// Windows → Linux
linPath, _ := wsl.ToLinuxPath(`C:\Users\test`)
// linPath = "/mnt/c/Users/test"

Development

Prerequisites

  • Go 1.21+
  • WSL (for integration tests; unit tests run anywhere)

Project Structure

.
├── cmd/winrun/              CLI tool
│   ├── main.go                Entry point, flag parsing, shim dispatch
│   ├── shim.go                Shim install/list/remove subcommands
│   └── shim_test.go
├── internal/wsl/            WSL detection & path translation (private)
│   ├── detect.go
│   ├── detect_test.go
│   ├── path.go                Pure Go resolver (/proc/mounts parsing)
│   └── path_test.go
├── pkg/bridge/              Core executor (public API)
│   ├── config.go              CommandConfig / Output types
│   ├── encoding.go            CP1252/UTF-16LE/BE decoder middleware
│   ├── encoding_test.go
│   ├── env.go                 WSLENV formatting with value-based heuristics
│   ├── env_test.go
│   ├── exec.go                Buffered + interactive execution modes
│   └── exec_test.go
├── pkg/workerpool/          Bounded concurrency pool (public API)
│   ├── pool.go
│   └── pool_test.go
├── go.mod
├── go.sum
└── README.md

Running Tests

# All unit tests (works on any OS — WSL integration tests auto-skip)
go test ./... -v

# With race detector
go test ./... -race

# Short / unit tests only
go test ./... -short

Key Design Decisions

Decision Rationale
Pure Go path resolver Parses /proc/mounts once; eliminates wslpath subprocess overhead (~20ms → <1µs)
Value-based WSLENV heuristics inferWSLEnvFlag inspects values (not just key names) to auto-select /p, /l, /u
Dual execution modes Buffered (Scanner) for output capture; interactive (io.Copy) for REPLs and TUI apps
Encoding middleware transform.Reader wraps stdio pipes to decode CP1252/UTF-16 transparently before Scanner
sync.Once for WSL detection Avoids repeated /proc/version reads; cached after first call
sync.Map for path cache Memoizes resolved paths; concurrent-safe without locks
exec.CommandContext Ensures context cancellation (timeout / SIGINT) kills the Windows process
.exe auto-resolution Appends .exe and checks PATH if the user passes cmd instead of cmd.exe
Worker pool with injectable executor Testable concurrency engine; mock executor eliminates WSL dependency in tests
Shim scripts with marker comments Safe identification and removal; prevents accidental deletion of non-shim files

Known Gotchas

  • Binary names: Always use .exe suffix (e.g., cmd.exe, not cmd). The library attempts auto-resolution but explicit is better.
  • Path separators: Windows uses \. The library handles this via the pure Go resolver, but be careful with manual string building.
  • Zombie processes: The CLI registers SIGINT/SIGTERM handlers to cancel all in-flight Windows processes on exit.
  • WSLENV: Only works for environment variables you explicitly pass — it does not auto-export your entire shell environment.
  • Encoding: If unsure about the encoding, use --encoding auto for BOM-based detection, or --encoding cp1252 for legacy Western European tools.
  • Interactive mode: Auto-detected for python, node, mysql, psql, irb, bash. Use --interactive explicitly for other REPLs.
  • Shim PATH: Ensure ~/.local/bin is in your $PATH (add export PATH="$HOME/.local/bin:$PATH" to your shell profile).

License

GNU GENERAL PUBLIC LICENSE

About

A Go library and CLI for executing Windows binaries from WSL

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors