Doramagic Project Pack · Human Manual
go-to-wheel
Related topics: Installation, Quick Start Guide
Introduction
Related topics: Installation, Quick Start Guide
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Installation, Quick Start Guide
Introduction
Overview
go-to-wheel is a Python command-line tool that compiles Go CLI programs into Python wheels, enabling Go binaries to be distributed and installed through the Python packaging ecosystem via pip or pipx. This tool bridges the Go and Python communities by providing a seamless way to package and distribute Go applications to Python developers who are accustomed to installing tools through Python package managers. The project was created to address the gap in the ecosystem—there was no equivalent to Rust's maturin --bindings bin for Go, and go-to-wheel fills that void by providing a straightforward solution for bundling Go binaries into standard Python wheel packages.
Sources: README.md:1-10
The tool takes a Go module directory as input, cross-compiles the Go binary for multiple target platforms, and produces properly-tagged Python wheels that can be installed via standard Python package management tools. This approach allows Go developers to leverage the extensive Python packaging infrastructure for distribution, including PyPI hosting, pipx for isolated installations, and standard Python dependency resolution.
Sources: spec.md:1-15
Core Functionality
What go-to-wheel Does
At its core, go-to-wheel performs three main operations to transform a Go module into a distributable Python wheel. First, it validates that the input directory is a valid Go module containing a go.mod file. Second, it cross-compiles the Go binary for each requested target platform using environment variables GOOS and GOARCH with CGO_ENABLED=0 to produce static binaries. Third, it creates a Python package structure with a thin wrapper that executes the bundled binary, then packages everything into a wheel file following PEP 427 conventions.
Sources: spec.md:80-95
The resulting wheel can be installed with pip, which extracts the bundled Go binary and creates console entry points that make the tool available on the system PATH. This means users can install and run Go-compiled tools exactly as they would any other Python package, without needing to understand that the underlying implementation is written in Go.
Sources: README.md:55-65
How It Works
The build process follows a precise sequence of operations to ensure compatibility across all supported platforms. The tool begins by validating the input Go module directory and verifying that Go is installed and accessible. It then iterates through each requested platform, setting the appropriate environment variables and running the Go compiler with flags optimized for static binary production.
graph TD
A[Start: go-to-wheel] --> B{Validate Go Module}
B -->|go.mod exists| C[Parse Package Metadata]
B -->|No go.mod| E[Error: Not a Go module]
C --> D{For each target platform}
D --> F[Cross-compile with GOOS/GOARCH]
F --> G[CGO_ENABLED=0 for static binary]
G --> H[Generate Python wrapper]
H --> I[Create wheel structure]
I --> J[Zip into .whl file]
J --> D
D -->|All platforms done| K[Output wheels to ./dist]
K --> L[Success]Sources: spec.md:85-100
The cross-compilation step uses CGO_ENABLED=0 to ensure that the resulting binaries are fully static and have no libc dependencies, which is essential for compatibility across different Linux distributions and container environments. The -ldflags="-s -w" flags strip debug information and reduce binary size for more efficient distribution.
Sources: README.md:45-50
Supported Platforms
go-to-wheel supports a comprehensive range of target platforms across Linux, macOS, and Windows operating systems, covering both x86_64 and ARM architectures. The tool provides different wheel tags depending on whether the target uses glibc (standard Linux) or musl (Alpine Linux and similar distributions).
Platform Mapping
| Target Platform | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
| linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
| linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
| linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
| linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
| darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
| darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
| windows-amd64 | windows | amd64 | win_amd64 |
| windows-arm64 | windows | arm64 | win_arm64 |
Sources: README.md:35-45
Default Platform Behavior
By default, go-to-wheel builds wheels for all eight supported platforms, ensuring maximum compatibility for distribution. Users can optionally specify a subset of platforms using the --platforms flag with a comma-separated list of target platforms, which is useful when building for specific deployment environments or when cross-compilation toolchains are not available for all targets.
Sources: spec.md:40-45
Installation and Requirements
Prerequisites
The tool itself requires Python 3.10 or later and has no external Python dependencies—it uses only the Python standard library. For building Go binaries, Go 1.16 or later is required due to the use of go mod commands.
Sources: spec.md:115-120
Installation Methods
go-to-wheel can be installed using standard Python package installation tools:
pip install go-to-wheel
# or
pipx install go-to-wheel
Sources: README.md:20-25
After installation, Go must be available in the system PATH. The tool will automatically detect the Go binary or can be configured to use a specific path via the --go-binary option.
Command Line Interface
Basic Usage
The simplest usage of go-to-wheel requires only a path to the Go module directory:
go-to-wheel path/to/go-module
Sources: README.md:30-35
This command creates wheels in the ./dist directory for all supported platforms using default metadata values.
Command Options
| Option | Description | Default |
|---|---|---|
--name NAME | Python package name | Directory basename |
--version VERSION | Package version | 0.1.0 |
--output-dir DIR | Directory for built wheels | ./dist |
--entry-point NAME | CLI command name | Same as package name |
--platforms PLATFORMS | Comma-separated list of targets | All supported platforms |
--go-binary PATH | Path to Go binary | go |
--description TEXT | Package description | "Go binary packaged as Python wheel" |
--license LICENSE | License identifier | None |
--author AUTHOR | Author name | None |
--author-email EMAIL | Author email | None |
--url URL | Project URL | None |
--requires-python VERSION | Python version requirement | >=3.10 |
--readme PATH | Path to README markdown file | None |
--set-version-var VAR | Go variable for version via -X ldflag | None |
--ldflags FLAGS | Additional Go linker flags | None |
Sources: spec.md:25-45
Wheel Structure
Each generated wheel follows PEP 427 format with a specific internal structure that enables proper execution of the bundled Go binary.
File Structure
{package_name}-{version}-py3-none-{platform_tag}.whl
├── {package_name}/
│ ├── __init__.py
│ ├── __main__.py
│ └── bin/
│ └── {binary_name}[.exe]
├── {package_name}-{version}.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── entry_points.txt
Sources: spec.md:60-70
Python Wrapper Mechanism
The Python wrapper in __init__.py provides the execution mechanism for the bundled binary. It uses os.execvp() on Unix systems to replace the Python process with the Go binary, ensuring proper signal handling and exit code propagation. On Windows, it uses subprocess.call() to achieve similar behavior with proper signal handling.
Sources: spec.md:60-90
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
# On Windows, use subprocess to properly handle signals
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
# On Unix, exec replaces the process
os.execvp(binary, [binary] + sys.argv[1:])
Sources: spec.md:75-85
Why Python Wrapper vs .data/scripts
The specification uses a Python wrapper with console_scripts entry point rather than placing the binary directly in .data/scripts/ for several important reasons. First, it provides consistent behavior across all platforms without platform-specific edge cases. Second, it enables better error messages if the binary is missing or incompatible with the system. Third, it offers future flexibility for adding Python-side features such as version checking or update notifications. Fourth, it works seamlessly with pipx install for isolated application installations.
Sources: spec.md:90-100
Use Cases
Distributing Go Tools to Python Users
The primary use case for go-to-wheel is distributing Go CLI tools to Python developers who prefer to use pip or pipx for managing command-line tools. This is particularly valuable for tools that have natural appeal to the Python community or tools that need to be installed alongside Python packages as dependencies.
Sources: README.md:8-12
PyPI Distribution
Go binaries packaged as wheels can be uploaded to PyPI, making them available through the standard Python package index. This enables distribution to millions of Python developers who can install the tool with a single pip install command, without needing to understand Go compilation or maintain separate release artifacts.
pipx Isolation
Wheels built with go-to-wheel work seamlessly with pipx, which provides isolated Python environments for command-line tools. Users can install Go-compiled tools in isolation to avoid dependency conflicts:
pipx install ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl
Sources: README.md:60-65
Advanced Features
Version Embedding
Go-to-wheel supports embedding the package version into the Go binary at compile time using Go linker flags. This requires a var version declaration in the Go source code. When the --set-version-var option is used, the tool automatically passes the value from --version to the Go linker via the -X flag.
Sources: README.md:50-55
A typical Go pattern for version embedding:
var version = "dev"
func main() {
if os.Args[1] == "--version" {
fmt.Println(version) // prints "2.0.0" when built with --set-version-var
}
}
Sources: README.md:50-60
Custom Linker Flags
Additional Go linker flags can be passed using the --ldflags option, which are appended to the default -s -w flags. This allows for custom version strings, commit hashes, or other build-time information:
go-to-wheel ./mytool --version 2.0.0 \
--ldflags "-X main.version=2.0.0 -X main.commit=abc123"
Sources: README.md:60-65
README Integration
The --readme option allows embedding a README markdown file into the wheel's METADATA, which is displayed on the PyPI package page:
go-to-wheel ./mytool --readme README.md
Sources: spec.md:42
Development
Running Tests
The project uses pytest for testing. After cloning the repository, tests can be run with:
git clone https://github.com/simonw/go-to-wheel
cd go-to-wheel
uv run pytest
Sources: README.md:70-75
Project Architecture
The tool is implemented as a single Python module (go_to_wheel/__init__.py) with no external dependencies. The main components include argument parsing, cross-compilation orchestration, wheel file generation, and metadata file creation. This simple architecture makes the tool easy to understand, maintain, and extend.
Related Tools
go-to-wheel was inspired by similar tools in the Rust ecosystem. The primary inspiration is maturin, which provides the same functionality for Rust programs with Python bindings. Additionally, pip-binary-factory serves as a template for packaging pre-built binaries.
Sources: README.md:75-80
| Tool | Language | Repository |
|---|---|---|
| go-to-wheel | Go | simonw/go-to-wheel |
| maturin | Rust | PyO3/maturin |
| pip-binary-factory | Template | Bing-su/pip-binary-factory |
License
go-to-wheel is released under the Apache 2.0 license, allowing for both personal and commercial use with minimal restrictions.
Sources: README.md:12
Summary
go-to-wheel provides a valuable bridge between the Go and Python ecosystems by enabling Go CLI programs to be packaged and distributed as standard Python wheels. With support for eight target platforms, flexible metadata configuration, and seamless integration with pip and pipx, it offers Go developers a straightforward path to reaching Python's extensive user base. The tool's single-file implementation with no external dependencies ensures reliability and ease of installation, making it a practical choice for distributing Go tools to the Python community.
Sources: README.md:1-10
Installation
Related topics: Introduction, Development Guide
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Introduction, Development Guide
Installation
go-to-wheel is a Python tool that compiles Go CLI programs into Python wheels. Installing this tool correctly is the first step to packaging Go binaries for PyPI distribution.
Prerequisites
Before installing go-to-wheel, ensure your environment meets the following requirements:
| Requirement | Version | Description |
|---|---|---|
| Python | >= 3.10 | The tool is implemented in Python and requires this version or higher |
| Go | >= 1.16 | Required for building Go modules with go mod support |
Sources: spec.md
Python Dependencies
go-to-wheel has no external Python dependencies. The tool uses only Python's standard library for all operations:
argparse- Command-line argument parsingzipfile- Wheel creationsubprocess- Go compilation executionhashlib/base64- RECORD file hash generationtempfile/shutil- Temporary directory management
Sources: go_to_wheel/__init__.py:1-20
Go Installation Verification
To verify Go is installed and accessible:
go version
Ensure go is in your system's PATH environment variable.
Installation Methods
Via pip (Recommended for Users)
pip install go-to-wheel
Sources: README.md
This method installs go-to-wheel globally in your Python environment.
Via pipx (Recommended for CLI Tools)
pipx install go-to-wheel
Sources: README.md
pipx is preferred for CLI applications because it:
- Creates an isolated virtual environment for the tool
- Automatically manages PATH shims for the installed command
- Avoids dependency conflicts with other Python packages
Installation Workflow
graph TD
A[Choose Installation Method] --> B{pip or pipx?}
B -->|pip| C[Run pip install go-to-wheel]
B -->|pipx| D[Run pipx install go-to-wheel]
C --> E[Download from PyPI]
D --> E
E --> F[Install to Python environment]
F --> G[Create executable entry point]
G --> H[go-to-wheel ready to use]Post-Installation Verification
After installation, verify the tool is working:
go-to-wheel --version
Expected output:
go-to-wheel v0.1.0
Sources: go_to_wheel/__init__.py
Usage Requirements
Once installed, you need a Go module to package:
# Verify you have a Go module
cd path/to/your-go-module
ls go.mod
The tool will:
- Cross-compile the Go binary for multiple platforms
- Create Python wheels with proper metadata
- Bundle everything into installable packages
Sources: spec.md
Quick Start After Installation
# Build wheels for all platforms
go-to-wheel path/to/go-module
# Or with custom options
go-to-wheel ./mytool --name my-python-tool --version 1.0.0
Sources: README.md
Troubleshooting
| Issue | Solution |
|---|---|
go-to-wheel: command not found | Ensure pip's Scripts directory is in PATH, or use python -m go_to_wheel |
| Go not found | Install Go from go.dev and ensure it's in PATH |
| Python version error | Upgrade to Python 3.10 or higher |
| Permission denied | Use pip install --user or pipx install instead of system-wide install |
Sources: spec.md
Quick Start Guide
Related topics: CLI Options Reference, Usage Examples
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: CLI Options Reference, Usage Examples
Quick Start Guide
Overview
The Quick Start Guide provides the fastest path for developers to begin using go-to-wheel to compile Go CLI programs into installable Python wheels. This tool addresses a gap in the Go/Python ecosystem—there is no equivalent to Rust's maturin --bindings bin for Go. Go-to-wheel takes a Go module directory, cross-compiles it for multiple platforms, and produces properly-tagged Python wheels installable via pip or pipx. Sources: README.md:1-10
Prerequisites
Before using go-to-wheel, ensure your environment meets the following requirements:
| Requirement | Version | Notes |
|---|---|---|
| Python | >= 3.10 | Required for installation and running the tool |
| Go | >= 1.16 | Required for building Go modules Sources: spec.md:145-150 |
| Go Module | Valid go.mod | The source Go project must be a Go module |
Go must be installed and available in your system PATH. No external Python dependencies are required—go-to-wheel uses only the Python standard library. Sources: go_to_wheel/__init__.py:1-15
Installation
Install go-to-wheel using pip or pipx:
pip install go-to-wheel
# or
pipx install go-to-wheel
Verify the installation:
go-to-wheel --version
Basic Usage
Minimal Command
Build wheels for all supported platforms using the simplest invocation:
go-to-wheel path/to/go-module
This command will:
- Cross-compile the Go binary for all default platforms
- Create a Python package with a thin wrapper
- Package everything into wheels in
./distdirectory - Use the directory name as the package name with version
0.1.0Sources: README.md:35-50
Build Flow
graph TD
A[Go Module Directory] --> B[Validate go.mod exists]
B --> C[Cross-compile Go binary for each platform]
C --> D[Create Python package structure]
D --> E[Generate wheel metadata]
E --> F[Package into .whl file]
F --> G[Move to output directory]Command Options
The following table documents all available command-line options:
| --ldflags FLAGS | Additional Go linker flags | None | Sources: README.md:20-35
| Option | Description | Default |
|---|---|---|
--name NAME | Python package name | Directory basename |
--version VERSION | Package version | 0.1.0 |
--output-dir DIR | Directory for built wheels | ./dist |
--entry-point NAME | CLI command name | Same as package name |
--platforms PLATFORMS | Comma-separated list of targets | All supported platforms |
--go-binary PATH | Path to Go binary | go |
--description TEXT | Package description | "Go binary packaged as Python wheel" |
--license LICENSE | License identifier (e.g., MIT) | None |
--author AUTHOR | Author name | None |
--author-email EMAIL | Author email | None |
--url URL | Project URL | None |
--requires-python VERSION | Python version requirement | >=3.10 |
--readme PATH | Path to README markdown file for PyPI | None |
--set-version-var VAR | Go variable to set via -X ldflag | None |
Supported Platforms
Go-to-wheel supports cross-compilation to the following target platforms:
| windows-arm64 | windows | arm64 | win_arm64 | Sources: go_to_wheel/__init__.py:20-30
| Platform Identifier | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
Common Use Cases
Custom Package Name
Build wheels with a custom Python package name:
go-to-wheel ./mytool --name my-python-tool
Build for Specific Platforms Only
Reduce build time by targeting only required platforms:
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64
Embed Version into Go Binary
Pass the version to the Go binary at compile time using linker flags. First, add a version variable to your Go source:
var version = "dev"
func main() {
if os.Args[1] == "--version" {
fmt.Println(version)
}
}
Then build with:
go-to-wheel ./mytool --version 2.0.0 --set-version-var main.version
This passes -X main.version=2.0.0 to the Go linker. The flags are appended to the default -s -w, resulting in -ldflags="-s -w -X main.version=2.0.0". Sources: README.md:55-75
Full Metadata for PyPI
Publish to PyPI with complete metadata:
go-to-wheel ./mytool \
--name mytool-bin \
--version 2.0.0 \
--description "My awesome tool" \
--license MIT \
--author "Jane Doe" \
--author-email "[email protected]" \
--url "https://github.com/jane/mytool" \
--readme README.md
Custom Linker Flags
Pass arbitrary Go linker flags for additional build-time configuration:
go-to-wheel ./mytool --version 2.0.0 \
--ldflags "-X main.version=2.0.0 -X main.commit=abc123"
How It Works
The build process consists of four main steps:
graph LR
A[Cross-compile Go Binary] --> B[Create Python Package]
B --> C[Generate Metadata Files]
C --> D[Package into Wheel]Step 1: Cross-Compilation
For each target platform, go-to-wheel executes:
GOOS={goos} GOARCH={goarch} CGO_ENABLED=0 go build \
-ldflags="-s -w" \
-o {output_path} \
{go_module_path}
Key points:
CGO_ENABLED=0ensures static binaries with no libc dependency issues-ldflags="-s -w"strips debug information to reduce binary size- Windows builds automatically receive
.exeextension Sources: spec.md:100-115
Step 2: Python Package Structure
Each wheel contains a Python wrapper that executes the bundled binary:
{package_name}-{version}-py3-none-{platform_tag}.whl
├── {package_name}/
│ ├── __init__.py
│ ├── __main__.py
│ └── bin/
│ └── {binary_name}[.exe]
├── {package_name}-{version}.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── entry_points.txt
The __init__.py file contains a main() function that:
- On Windows: uses
subprocess.call()for proper signal handling - On Unix: uses
os.execvp()to replace the Python process Sources: spec.md:60-95
Step 3: Metadata Generation
Generated METADATA follows PEP 566:
Metadata-Version: 2.1
Name: {package_name}
Version: {version}
Summary: {description}
License: {license}
Author: {author}
Author-email: {author_email}
Home-page: {url}
Requires-Python: {requires_python}
The WHEEL file follows PEP 427:
Wheel-Version: 1.0
Generator: go-to-wheel {version}
Root-Is-Purelib: false
Tag: py3-none-{platform_tag}
Step 4: Entry Points
Console script entry points are defined in entry_points.txt:
[console_scripts]
{entry_point} = {package_name}:main
The tool uses Python wrapper entry points rather than .data/scripts/ because:
- Consistent behavior across all platforms
- Better error messages if binary is missing
- Future flexibility for Python-side features
- Seamless pipx compatibility Sources: spec.md:95-105
Testing Built Wheels
Install with pip
pip install ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl
Test with uv
uv run --with ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl mytool --help
Run with pipx
pipx install ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl
After installation, the Go binary is available on your PATH through the Python wrapper. Sources: README.md:100-115
Development
To contribute or customize go-to-wheel:
# Clone the repository
git clone https://github.com/simonw/go-to-wheel
cd go-to-wheel
# Run tests
uv run pytest
Package Name Validation
Package names must follow PEP 503 naming rules:
| Rule | Description |
|---|---|
| Allowed characters | Lowercase letters, digits, hyphens, underscores, periods |
| Must start with | Letter or digit |
| Normalization | Hyphens and underscores become hyphens in wheel filename |
The import name (Python package directory) follows PEP 8:
- Hyphens are replaced with underscores
- Example:
my-toolbecomesmy_tool/directory Sources: spec.md:130-140
Troubleshooting
| Issue | Solution |
|---|---|
| "No go.mod file found" | Ensure the path points to a valid Go module directory |
| "Go compilation failed" | Verify Go is installed and the module builds correctly standalone |
| Invalid package name | Follow PEP 503 naming rules (lowercase, alphanumeric with hyphens/underscores) |
See Also
- maturin - The Rust equivalent that inspired this tool
- pip-binary-factory - Template for packaging pre-built binaries
- Distributing Go binaries like sqlite-scanner through PyPI using go-to-wheel - Background article on this project Sources: README.md:120-130
Source: https://github.com/simonw/go-to-wheel / Human Manual
CLI Options Reference
Related topics: Quick Start Guide, Configuration and Metadata
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Quick Start Guide, Configuration and Metadata
CLI Options Reference
Overview
The go-to-wheel CLI provides comprehensive options for cross-compiling Go binaries into Python wheels. The command follows the structure:
go-to-wheel path/to/go-folder [options]
All options are passed as space-separated arguments after the path to the Go module directory. Sources: README.md
Complete Options Reference
Core Options
| Option | Type | Default | Description |
|---|---|---|---|
path/to/go-folder | positional | required | Path to Go module directory containing go.mod |
--name NAME | string | Directory basename | Python package name for the wheel |
--version VERSION | string | 0.1.0 | Package version string |
--output-dir DIR | string | ./dist | Directory where built wheels are written |
--entry-point NAME | string | Same as package name | CLI command name exposed after installation |
--go-binary PATH | string | go | Path to Go binary in PATH |
Sources: go_to_wheel/__init__.py:52-85
Metadata Options
| Option | Type | Default | Description |
|---|---|---|---|
--description TEXT | string | "Go binary packaged as Python wheel" | Package summary for PyPI |
--license LICENSE | string | None | SPDX license identifier (e.g., MIT, Apache-2.0) |
--author AUTHOR | string | None | Author name |
--author-email EMAIL | string | None | Author email address |
--url URL | string | None | Project homepage URL |
--requires-python VERSION | string | >=3.10 | Python version requirement |
--readme PATH | string | None | Path to README.md for PyPI long description |
Sources: go_to_wheel/__init__.py:86-115
Build Options
| Option | Type | Default | Description |
|---|---|---|---|
--platforms PLATFORMS | string | All 8 platforms | Comma-separated list of target platforms |
--ldflags FLAGS | string | None | Additional Go linker flags appended to -s -w |
--set-version-var VAR | string | None | Go variable name for version embedding via -X |
Sources: go_to_wheel/__init__.py:116-128
Supported Platforms
The following platform targets are available for cross-compilation:
| Platform Identifier | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
windows-arm64 | windows | arm64 | win_arm64 |
Sources: go_to_wheel/__init__.py:25-35
Platform Selection Syntax
Specify multiple platforms using comma-separated values:
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64,windows-amd64
Version Embedding
The --set-version-var option enables embedding the package version into the Go binary at compile time. This requires a matching variable in the Go source:
var version = "dev"
func main() {
if len(os.Args) > 1 && os.Args[1] == "--version" {
fmt.Println(version)
}
}
Build command with version embedding:
go-to-wheel ./mytool --version 2.0.0 --set-version-var main.version
This passes -X main.version=2.0.0 to the Go linker. Sources: README.md
Advanced Ldflags
The --ldflags option appends custom linker flags to the default -s -w flags (which strip debug info):
go-to-wheel ./mytool --ldflags "-X main.commit=abc123 -X main.date=2024-01-15"
The combined ldflags become: -s -w -X main.commit=abc123 -X main.date=2024-01-15
Usage Examples
Minimal Build
go-to-wheel ./mytool
Produces wheels for all 8 platforms using default settings. Sources: README.md
Custom Package Name
go-to-wheel ./mytool --name my-python-tool
Creates my-python-tool-0.1.0-py3-none-*.whl files.
PyPI-Ready Build
go-to-wheel ./mytool \
--name mytool-bin \
--version 2.0.0 \
--description "My awesome CLI tool" \
--license MIT \
--author "Jane Doe" \
--author-email "[email protected]" \
--url "https://github.com/jane/mytool" \
--readme README.md
Platform-Specific Build
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64
With Version Embedding
go-to-wheel ./mytool --version 1.2.3 --set-version-var main.version
Option Processing Flow
graph TD
A[Parse CLI Arguments] --> B[Validate go_dir exists]
B --> C{Check go.mod}
C -->|Valid| D[Set defaults]
C -->|Invalid| E[Exit with error]
D --> F[Parse platforms list]
F --> G[Build ldflags]
G --> H[Cross-compile for each platform]
H --> I[Generate Python wheel]
I --> J[Move to output-dir]
J --> K[Print summary]Default Values Behavior
| Option | Default Source | Override Mechanism |
|---|---|---|
| Package name | go_path.name (directory basename) | --name |
| Version | "0.1.0" | --version |
| Entry point | Same as package name | --entry-point |
| Platforms | DEFAULT_PLATFORMS list (all 8) | --platforms |
| Go binary | "go" from PATH | --go-binary |
| Description | "Go binary packaged as Python wheel" | --description |
| Requires Python | ">=3.10" | --requires-python |
Sources: go_to_wheel/__init__.py:175-190
Error Handling
The CLI validates inputs and exits with descriptive errors:
| Error Condition | Exit Behavior |
|---|---|
| Go directory not found | FileNotFoundError: Go directory not found: <path> |
| No go.mod file | ValueError: Not a Go module: <path> |
| Go binary not found | FileNotFoundError from subprocess |
| Compilation failure | RuntimeError: Go compilation failed for <os>/<arch> |
| No wheels built | Error: No wheels were built |
Return Codes
| Code | Meaning |
|---|---|
| 0 | Success - wheels built and written to output directory |
| 1 | Error occurred (invalid input, compilation failure, etc.) |
Sources: go_to_wheel/__init__.py:130-155
Sources: go_to_wheel/__init__.py:52-85
Usage Examples
Related topics: Quick Start Guide, Wheel Generation Process
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Quick Start Guide, Wheel Generation Process
Usage Examples
This page provides comprehensive usage examples for go-to-wheel, demonstrating how to compile Go CLI programs into distributable Python wheels. The examples progress from basic usage to advanced configurations, covering all available command-line options and common use cases.
Overview
go-to-wheel transforms Go binaries into Python packages that can be installed via pip or pipx. The tool handles cross-compilation for multiple platforms, generates proper Python wheel metadata, and creates installable packages with executable entry points. All examples assume Go is installed and available in the system PATH.
Basic Usage
The simplest way to build wheels from a Go module is to pass the module directory path:
go-to-wheel ./mytool
This command:
- Locates the Go module in
./mytool(requiresgo.modfile) - Cross-compiles the binary for all supported platforms
- Creates wheels in
./distdirectory - Uses the directory name as the package name
- Defaults to version
0.1.0
The resulting wheels follow the naming convention {name}-{version}-py3-none-{platform_tag}.whl.
Command Line Options
Option Reference Table
| Option | Description | Default Value |
|---|---|---|
--name NAME | Python package name | Directory basename |
--version VERSION | Package version | 0.1.0 |
--output-dir DIR | Directory for built wheels | ./dist |
--entry-point NAME | CLI command name | Same as package name |
--platforms PLATFORMS | Comma-separated list of targets | All supported platforms |
--go-binary PATH | Path to Go binary | go |
--description TEXT | Package description | "Go binary packaged as Python wheel" |
--license LICENSE | License identifier | None |
--author AUTHOR | Author name | None |
--author-email EMAIL | Author email | None |
--url URL | Project URL | None |
--requires-python VERSION | Python version requirement | >=3.10 |
--readme PATH | Path to README markdown file | None |
--set-version-var VAR | Go variable for --version value | None |
--ldflags FLAGS | Additional Go linker flags | None |
Sources: go_to_wheel/__init__.py:1-100
Platform Selection
Build for All Platforms
By default, go-to-wheel builds wheels for all supported platforms:
| Platform | Wheel Tag |
|---|---|
linux-amd64 | manylinux_2_17_x86_64 |
linux-arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | musllinux_1_2_x86_64 |
linux-arm64-musl | musllinux_1_2_aarch64 |
darwin-amd64 | macosx_10_9_x86_64 |
darwin-arm64 | macosx_11_0_arm64 |
windows-amd64 | win_amd64 |
windows-arm64 | win_arm64 |
Sources: README.md:1-50
Build for Specific Platforms
To build only for specific platforms, use the --platforms option with a comma-separated list:
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64
This produces wheels for only Linux amd64 and macOS ARM64, reducing build time when you don't need all platform variants.
Using a Custom Go Binary
If Go is not in your PATH or you need a specific version:
go-to-wheel ./mytool --go-binary /usr/local/go/bin/go
Package Naming
Custom Package Name
Override the default package name derived from the directory:
go-to-wheel ./mytool --name my-python-tool
This creates wheels named my-python-tool-{version}-py3-none-{platform_tag}.whl.
Custom Entry Point
The CLI command name can differ from the package name:
go-to-wheel ./mytool --name my-tool-bin --entry-point mytool
This creates a package named my-tool-bin but installs the command as mytool.
Version Management
Setting Package Version
go-to-wheel ./mytool --version 2.0.0
Embedding Version in Go Binary
To embed the version into the Go binary at compile time, define a version variable in your Go source:
var version = "dev"
func main() {
if os.Args[1] == "--version" {
fmt.Println(version)
}
}
Then use --set-version-var to pass the version via linker flags:
go-to-wheel ./mytool --version 2.0.0 --set-version-var main.version
This passes -X main.version=2.0.0 to the Go linker. The combined linker flags become -s -w -X main.version=2.0.0.
Sources: README.md:50-80
Custom Linker Flags
Using ldflags
Pass arbitrary Go linker flags with --ldflags:
go-to-wheel ./mytool --version 2.0.0 \
--ldflags "-X main.version=2.0.0 -X main.commit=abc123"
Flags are appended to the default -s -w, so the full linker invocation becomes:
-ldflags="-s -w -X main.version=2.0.0 -X main.commit=abc123"
The -s flag strips symbol table, and -w removes DWARF debugging information, reducing binary size.
Sources: spec.md:50-80
Output Management
Custom Output Directory
go-to-wheel ./mytool --output-dir ./wheels
Installing Built Wheels
After building, install wheels with pip:
pip install ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl
Or test directly with uv:
uv run --with ./dist/mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl mytool --help
Complete Examples
Basic Distribution
For internal distribution without PyPI publishing:
go-to-wheel ./mytool
PyPI-Ready Distribution
Full metadata configuration for PyPI publishing:
go-to-wheel ./mytool \
--name mytool-bin \
--version 2.0.0 \
--description "My awesome tool" \
--license MIT \
--author "Jane Doe" \
--author-email "[email protected]" \
--url "https://github.com/jane/mytool" \
--readme README.md
This generates wheels with complete metadata suitable for publishing to PyPI. The --readme option sets the long description from your README file.
Sources: README.md:80-120
Development Workflow
Typical development workflow:
# Build all wheels
go-to-wheel ./mytool --output-dir ./dist
# Test specific wheel
uv run --with ./dist/mytool-0.1.0-py3-none-manylinux_2_17_x86_64.whl mytool --help
# Install locally for testing
pip install ./dist/mytool-0.1.0-py3-none-manylinux_2_17_x86_64.whl
# Uninstall after testing
pip uninstall mytool
Build Process Flow
graph TD
A[go-to-wheel command] --> B[Validate Go directory]
B --> C[Check go.mod exists]
C --> D{For each platform}
D --> E[Cross-compile with GOOS/GOARCH]
E --> F[Create Python package structure]
F --> G[Generate __init__.py and __main__.py]
G --> H[Generate METADATA and WHEEL files]
H --> I[Calculate RECORD with SHA256]
I --> J[Zip into wheel file]
J --> K{More platforms?}
K -->|Yes| D
K -->|No| L[Move wheels to output dir]
L --> M[Print summary]The build process uses CGO_ENABLED=0 for static binaries, ensuring compatibility across different Linux distributions without libc dependencies.
Sources: spec.md:30-60
Error Handling
Common errors and solutions:
| Error | Cause | Solution |
|---|---|---|
Go directory not found | Invalid path | Verify the directory exists |
Not a Go module | Missing go.mod | Ensure directory contains go.mod |
Go compilation failed | Build error in Go code | Check Go source for errors |
No wheels were built | Platform validation failed | Verify platform names are correct |
Sources: go_to_wheel/__init__.py:150-200
Quick Reference
| Task | Command |
|---|---|
| Build all wheels | go-to-wheel ./mytool |
| Custom name | go-to-wheel ./mytool --name my-package |
| Specific version | go-to-wheel ./mytool --version 1.2.3 |
| Specific platforms | go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64 |
| With metadata | go-to-wheel ./mytool --author "Name" --license MIT --readme README.md |
| Embed version | go-to-wheel ./mytool --version 1.0 --set-version-var main.version |
Sources: go_to_wheel/__init__.py:1-100
System Architecture
Related topics: Wheel Generation Process, Supported Platforms
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Wheel Generation Process, Supported Platforms
System Architecture
Overview
go-to-wheel is a Python CLI tool that bridges the Go and Python packaging ecosystems. It takes a Go module directory, cross-compiles it for multiple target platforms, and packages each binary as a PEP 427-compliant Python wheel with executable entry points. This enables Go binaries to be distributed and installed via pip or pipx.
Sources: spec.md
Architecture Components
The system consists of four primary components working in sequence:
graph TD
A[Go Module Input] --> B[Input Validation]
B --> C[Cross-Compilation Engine]
C --> D[Wheel Builder]
D --> E[Output Wheels]
B --> B1[Verify go.mod exists]
B --> B2[Validate package name]
C --> C1[GOOS/GOARCH env vars]
C --> C2[CGO_ENABLED=0]
D --> D1[Generate METADATA]
D --> D2[Generate WHEEL]
D --> D3[Generate RECORD]
D --> D4[Create zip archive]Component Responsibilities
| Component | Purpose | Key Functions |
|---|---|---|
| CLI Parser | Parse command-line arguments | argparse, argument validation |
| Input Validator | Verify Go module structure | Path existence, go.mod detection |
| Cross-Compiler | Build binaries for target platforms | subprocess.run(), env var management |
| Wheel Builder | Create PEP 427 compliant wheels | zipfile, metadata generation |
Sources: go_to_wheel/__init__.py:1-50
Platform Mapping System
The platform mapping system translates human-readable platform names into Go build environment variables and Python wheel tags.
graph LR
P1["linux-amd64"] --> PM1["PLATFORM_MAPPINGS"]
P2["darwin-arm64"] --> PM1
P3["windows-amd64"] --> PM1
PM1 --> G["GOOS/GOARCH"]
PM1 --> W["Wheel Tag"]
G --> G1["linux/amd64"]
W --> W1["manylinux_2_17_x86_64"]Platform Mapping Table
| Platform Name | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
windows-arm64 | windows | arm64 | win_arm64 |
Sources: go_to_wheel/__init__.py:23-32
Build Process Workflow
flowchart TD
START[Start build_wheels] --> V1{Go directory exists?}
V1 -->|No| ERROR1[FileNotFoundError]
V1 -->|Yes| V2{go.mod exists?}
V2 -->|No| ERROR2[ValueError]
V2 -->|Yes| PARSE[Parse arguments]
PARSE --> SETUP[Set defaults<br/>name, entry_point,<br/>platforms]
SETUP --> FOR_LOOP[For each platform]
FOR_LOOP --> CROSS[cross_compile_go]
CROSS --> BUILD[Build wheel structure]
BUILD --> GEN_INIT[Generate __init__.py]
BUILD --> GEN_MAIN[Generate __main__.py]
BUILD --> GEN_META[Generate METADATA]
BUILD --> GEN_WHEEL[Generate WHEEL]
BUILD --> GEN_RECORD[Generate RECORD]
BUILD --> GEN_ENTRY[Generate entry_points.txt]
GEN_INIT --> ZIP[Create wheel.zip]
GEN_MAIN --> ZIP
GEN_META --> ZIP
GEN_WHEEL --> ZIP
GEN_RECORD --> ZIP
GEN_ENTRY --> ZIP
ZIP --> NEXT{More platforms?}
NEXT -->|Yes| FOR_LOOP
NEXT -->|No| OUTPUT[Move wheels to output_dir]
OUTPUT --> DONE[Return wheel paths]Cross-Compilation Details
The cross-compilation step uses Go's built-in cross-compilation support:
GOOS={goos} GOARCH={goarch} CGO_ENABLED=0 go build \
-ldflags="-s -w" \
-o {output_path} \
{go_module_path}
Key compilation flags:
CGO_ENABLED=0: Disables C bindings for static binaries-ldflags="-s -w": Strips debug info and symbol tables.exeextension added automatically on Windows
Sources: spec.md
Wheel Structure
Each generated wheel follows the PEP 427 standard structure:
{package_name}-{version}-py3-none-{platform_tag}.whl
├── {package_name}/
│ ├── __init__.py
│ ├── __main__.py
│ └── bin/
│ └── {binary_name}[.exe]
├── {package_name}-{version}.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── entry_points.txt
Sources: spec.md
Generated File Contents
#### __init__.py
The wrapper module that locates and executes the bundled binary:
"""Go binary packaged as Python wheel."""
import os
import stat
import subprocess
import sys
__version__ = "{version}"
def get_binary_path():
"""Return the path to the bundled binary."""
binary = os.path.join(os.path.dirname(__file__), "bin", "{binary_name}")
# Ensure binary is executable on Unix
if sys.platform != "win32":
current_mode = os.stat(binary).st_mode
if not (current_mode & stat.S_IXUSR):
os.chmod(binary, current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
return binary
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
os.execvp(binary, [binary] + sys.argv[1:])
Sources: go_to_wheel/__init__.py:75-100
#### METADATA (PEP 566)
Metadata-Version: 2.1
Name: {package_name}
Version: {version}
Summary: {description}
License: {license}
Author: {author}
Author-email: {author_email}
Home-page: {url}
Requires-Python: {requires_python}
#### WHEEL (PEP 427)
Wheel-Version: 1.0
Generator: go-to-wheel {go_to_wheel_version}
Root-Is-Purelib: false
Tag: py3-none-{platform_tag}
#### RECORD
CSV format with columns: path,hash,size
#### entry_points.txt
[console_scripts]
{entry_point} = {package_name}:main
Sources: spec.md
Command-Line Interface
Options Table
| Option | Description | Default |
|---|---|---|
--name NAME | Python package name | Directory basename |
--version VERSION | Package version | 0.1.0 |
--output-dir DIR | Directory for built wheels | ./dist |
--entry-point NAME | CLI command name | Same as package name |
--platforms PLATFORMS | Comma-separated list of targets | All 8 platforms |
--go-binary PATH | Path to Go binary | go |
--description TEXT | Package description | "Go binary packaged as Python wheel" |
--license LICENSE | License identifier | None |
--author AUTHOR | Author name | None |
--author-email EMAIL | Author email | None |
--url URL | Project URL | None |
--requires-python VERSION | Python version requirement | >=3.10 |
--readme PATH | Path to README markdown file | None |
--set-version-var VAR | Go variable for version via -X | None |
--ldflags FLAGS | Additional Go linker flags | None |
Sources: go_to_wheel/__init__.py:50-130
Data Flow Diagram
flowchart LR
subgraph Input
A[Go Module Directory]
B[CLI Arguments]
end
subgraph Processing
C[Input Validation]
D[Cross-Compilation]
E[Metadata Generation]
F[Zip Packaging]
end
subgraph Output
G[Python Wheel Files]
H[dist/ Directory]
end
A --> C
B --> C
C --> D
D --> E
E --> F
F --> G
G --> HKey Function Signatures
`build_wheels()`
def build_wheels(
go_dir: str,
*,
name: str | None = None,
version: str = "0.1.0",
output_dir: str = "./dist",
entry_point: str | None = None,
platforms: list[str] | None = None,
go_binary: str = "go",
description: str = "Go binary packaged as Python wheel",
requires_python: str = ">=3.10",
author: str | None = None,
author_email: str | None = None,
license_: str | None = None,
url: str | None = None,
readme: str | None = None,
ldflags: str | None = None,
set_version_var: str | None = None,
) -> list[str]:
"""Build Python wheels from a Go module."""
Sources: go_to_wheel/__init__.py:150-200
`cross_compile_go()`
def cross_compile_go(
go_dir: Path,
output_path: Path,
goos: str,
goarch: str,
go_binary: str = "go",
ldflags: str | None = None,
) -> None:
"""Cross-compile Go binary for target platform."""
Sources: go_to_wheel/__init__.py:65-92
Dependency Architecture
graph TD
A[go-to-wheel] --> B[Python Standard Library]
B --> B1[argparse]
B --> B2[zipfile]
B --> B3[subprocess]
B --> B4[pathlib]
B --> B5[hashlib]
B --> B6[csv]
A --> C[External Dependency]
C --> C1[Go Toolchain]Python Dependencies
The tool uses only Python standard library modules, requiring no external Python dependencies:
argparse: CLI argument parsingzipfile: Wheel archive creationsubprocess: Go compilation invocationpathlib: Path manipulationhashlib: SHA256 hashing for RECORDcsv: RECORD file generationtempfile: Temporary build directoryshutil: File operations
External Dependencies
- Go >= 1.16: Required for
go buildand cross-compilation
Sources: spec.md
Version Information
| Item | Value |
|---|---|
| Project Version | 0.1.0 |
| Python Requirement | >=3.10 |
| Go Requirement | >=1.16 |
| License | Apache 2.0 |
Sources: go_to_wheel/__init__.py:10
Installation Methods
graph TD
A[User] --> B{Installation Method}
B --> C[pip install]
B --> D[pipx install]
C --> E[System Python]
D --> F[Isolated environment<br/>with PATH access]
E --> G[Requires Go in PATH]
F --> GSources: README.md
Sources: spec.md
Supported Platforms
Related topics: System Architecture, Wheel Generation Process
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture, Wheel Generation Process
Supported Platforms
go-to-wheel provides comprehensive cross-platform support for distributing Go CLI binaries as Python wheels. The tool automatically generates wheels for all supported platforms with correct PEP 427 tags, enabling seamless installation via pip or pipx.
Platform Architecture
go-to-wheel maps Go's GOOS/GOARCH environment variables to Python wheel platform tags. The mapping system allows for precise targeting of specific architectures while maintaining compatibility with the Python packaging ecosystem.
graph TD
A[Go Source Code] --> B[go-to-wheel build]
B --> C{Platform Selection}
C -->|Default| D[All 8 Platforms]
C -->|Custom| E[User-specified subset]
D --> F[Cross-compile with GOOS/GOARCH]
E --> F
F --> G[Generate wheel with platform tag]
G --> H[Installable via pip/pipx]Sources: go_to_wheel/__init__.py:9-27
Default Supported Platforms
The following 8 platform targets are built by default when no --platforms flag is specified:
| Platform Identifier | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
windows-arm64 | windows | arm64 | win_arm64 |
Sources: go_to_wheel/__init__.py:9-27
Platform Selection Options
Building All Platforms (Default)
Without specifying platforms, all 8 targets are built:
go-to-wheel ./mytool
Building Specific Platforms
Use the --platforms flag with a comma-separated list:
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64
Only Linux amd64 and macOS ARM64 wheels will be generated.
Sources: README.md
Linux Variants
glibc-based (manylinux)
The linux-amd64 and linux-arm64 targets produce binaries linked against glibc, using the manylinux_2_17 container tag. These wheels are compatible with most modern Linux distributions including:
- Ubuntu 18.04+
- Debian 10+
- Fedora 30+
- RHEL/CentOS 8+
- Amazon Linux 2
musl-based (musllinux)
The linux-amd64-musl and linux-arm64-musl targets produce static binaries linked against musl libc. These wheels target:
- Alpine Linux (all versions)
- OpenWrt
- Other musl-based distributions
Musl builds are compiled with CGO_ENABLED=0 to ensure fully static linking, eliminating any libc dependency issues.
Sources: spec.md
macOS Support
Universal2 Consideration
While go-to-wheel does not natively generate darwin-universal2 wheels that combine both architectures, the tool can build both darwin-amd64 and darwin-arm64 separately. Users can combine them using Apple's lipo tool if a single universal binary is required:
lipo -create mytool-darwin-amd64 mytool-darwin-arm64 -output mytool-darwin-universal
Sources: spec.md
macOS Version Compatibility
| Wheel Tag | Minimum macOS Version | Architecture |
|---|---|---|
macosx_10_9_x86_64 | macOS 10.9 (Mavericks) | Intel |
macosx_11_0_arm64 | macOS 11.0 (Big Sur) | Apple Silicon |
The x86_64 tag maintains backwards compatibility to macOS 10.9, while the ARM64 tag starts from macOS 11.0 due to Apple Silicon hardware requirements.
Windows Support
Windows builds are cross-compiled from Linux/macOS hosts using CGO_ENABLED=0. The tool automatically appends the .exe extension to the binary within the wheel.
| Wheel Tag | Architecture | Notes |
|---|---|---|
win_amd64 | x86_64 | 64-bit Windows |
win_arm64 | ARM64 | Windows on ARM devices |
Build Process
The platform-specific build workflow demonstrates how go-to-wheel handles cross-compilation:
graph LR
A[Source Host] -->|GOOS=linux GOARCH=amd64| B[Linux amd64 binary]
A -->|GOOS=linux GOARCH=arm64| C[Linux arm64 binary]
A -->|GOOS=darwin GOARCH=amd64| D[macOS amd64 binary]
A -->|GOOS=darwin GOARCH=arm64| E[macOS arm64 binary]
A -->|GOOS=windows GOARCH=amd64| F[Windows amd64 binary]
B --> G[Wheel: manylinux_2_17_x86_64]
C --> H[Wheel: manylinux_2_17_aarch64]
D --> I[Wheel: macosx_10_9_x86_64]
E --> J[Wheel: macosx_11_0_arm64]
F --> K[Wheel: win_amd64]
style A fill:#f9f,color:#000
style G fill:#9f9,color:#000
style H fill:#9f9,color:#000
style I fill:#9f9,color:#000
style J fill:#9f9,color:#000
style K fill:#9f9,color:#000Each build uses the following environment configuration:
env["GOOS"] = goos
env["GOARCH"] = goarch
env["CGO_ENABLED"] = "0"
Sources: go_to_wheel/__init__.py:129-136
Wheel Installation Compatibility
The wheel filename format ensures pip automatically selects the correct wheel for the target platform:
{package_name}-{version}-py3-none-{platform_tag}.whl
Example wheel names:
| Wheel Filename | Platform |
|---|---|
mytool-1.0.0-py3-none-manylinux_2_17_x86_64.whl | Linux x86_64 (glibc) |
mytool-1.0.0-py3-none-musllinux_1_2_aarch64.whl | Linux ARM64 (musl) |
mytool-1.0.0-py3-none-macosx_11_0_arm64.whl | macOS Apple Silicon |
mytool-1.0.0-py3-none-win_amd64.whl | Windows x86_64 |
Sources: spec.md
Requirements
Building wheels requires:
| Component | Requirement |
|---|---|
| Go | >= 1.16 (for go mod support) |
| Python | >= 3.10 |
| Python dependencies | None (stdlib only) |
The build host does not need to match the target platform due to Go's native cross-compilation support.
Platform Detection for Binary Execution
The generated __init__.py wrapper uses sys.platform to handle platform-specific behavior:
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
# On Windows, use subprocess to properly handle signals
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
# On Unix, exec replaces the process
os.execvp(binary, [binary] + sys.argv[1:])
This ensures consistent behavior across all supported platforms while respecting OS-specific process management conventions.
Sources: go_to_wheel/__init__.py:158-170
Sources: go_to_wheel/__init__.py:9-27
Wheel Generation Process
Related topics: System Architecture, Supported Platforms
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture, Supported Platforms
Wheel Generation Process
Overview
The Wheel Generation Process is the core mechanism of go-to-wheel that transforms a Go CLI program into a distributable Python wheel package. This process handles cross-compilation, binary packaging, and wheel metadata generation for multiple target platforms in a single operation.
The wheel generation workflow accepts a Go module directory, validates the input, compiles static binaries for each target platform, creates a Python package wrapper, and packages everything into properly tagged wheel files following PEP 427 and PEP 376 standards.
Sources: spec.md:1-25
Platform Configuration
Supported Platforms
The tool supports eight target platforms by default, covering major operating systems and architectures:
| Platform Key | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
windows-arm64 | windows | arm64 | win_arm64 |
Sources: go_to_wheel/__init__.py:15-29
Platform Mapping Structure
The PLATFORM_MAPPINGS dictionary maps platform keys to a tuple containing the Go operating system, Go architecture, and the corresponding Python wheel platform tag:
PLATFORM_MAPPINGS: dict[str, tuple[str, str, str]] = {
"linux-amd64": ("linux", "amd64", "manylinux_2_17_x86_64"),
# ... additional platforms
}
The DEFAULT_PLATFORMS list defines which platforms are built when no specific platform is specified:
DEFAULT_PLATFORMS = [
"linux-amd64",
"linux-arm64",
"linux-amd64-musl",
"linux-arm64-musl",
"darwin-amd64",
"darwin-arm64",
"windows-amd64",
"windows-arm64",
]
Sources: go_to_wheel/__init__.py:31-37
Build Process Architecture
High-Level Workflow
graph TD
A[Start: go-to-wheel Command] --> B[Validate Go Directory]
B --> C{Valid?}
C -->|No| D[Error: Directory not found or no go.mod]
C -->|Yes| E[Parse Options and Set Defaults]
E --> F{Platforms specified?}
F -->|Yes| G[Use Specified Platforms]
F -->|No| H[Use DEFAULT_PLATFORMS]
G --> I[For Each Platform]
H --> I
I --> J[Cross-Compile Go Binary]
J --> K[Create Package Directory Structure]
K --> L[Generate Python Wrapper Files]
L --> M[Generate Metadata Files]
M --> N[Create Wheel ZIP Archive]
N --> O{More Platforms?}
O -->|Yes| I
O -->|No| P[Move Wheels to Output Directory]
P --> Q[Print Summary]Main Entry Point
The build_wheels() function serves as the primary entry point for the wheel generation process. It accepts the following parameters:
| Parameter | Type | Default | Description | |
|---|---|---|---|---|
go_dir | str | Required | Path to Go module directory | |
name | `str \ | None` | None | Python package name (defaults to directory basename) |
version | str | "0.1.0" | Package version | |
output_dir | str | "./dist" | Directory for built wheels | |
entry_point | `str \ | None` | None | CLI command name (defaults to package name) |
platforms | `list[str] \ | None` | None | Target platforms (defaults to all) |
go_binary | str | "go" | Path to Go binary | |
description | str | "Go binary packaged as Python wheel" | Package description | |
requires_python | str | ">=3.10" | Python version requirement | |
author | `str \ | None` | None | Author name |
author_email | `str \ | None` | None | Author email |
license_ | `str \ | None` | None | License identifier |
url | `str \ | None` | None | Project URL |
readme | `str \ | None` | None | Path to README markdown file |
ldflags | `str \ | None` | None | Additional Go linker flags |
set_version_var | `str \ | None` | None | Go variable to set via -X ldflag |
Sources: go_to_wheel/__init__.py:136-168
Step-by-Step Generation Process
Step 1: Input Validation
The process begins with comprehensive validation of the input parameters:
- Directory Existence: Verify the Go directory exists using
Path.resolve() - Go Module Verification: Check for
go.modfile to confirm valid Go module - README File Check: If
--readmeis provided, verify the file exists and read its content - Default Value Assignment: Set defaults for
nameandentry_pointfrom directory name
go_path = Path(go_dir).resolve()
if not go_path.exists():
raise FileNotFoundError(f"Go directory not found: {go_dir}")
if not (go_path / "go.mod").exists():
raise ValueError(f"Not a Go module: {go_dir} (no go.mod file found)")
Sources: go_to_wheel/__init__.py:179-186
Step 2: Cross-Compilation
Each Go binary is compiled for its target platform using environment variables:
graph LR
A[Platform: linux-amd64] --> B["GOOS=linux<br/>GOARCH=amd64<br/>CGO_ENABLED=0"]
B --> C["go build<br/>-ldflags='-s -w'<br/>-o output"]
C --> D[Static Binary]
E[Platform: windows-amd64] --> F["GOOS=windows<br/>GOARCH=amd64<br/>CGO_ENABLED=0"]
F --> G["go build<br/>-ldflags='-s -w'<br/>-o output.exe"]
G --> H[Static Binary .exe]Compilation Environment Variables:
| Variable | Value | Purpose |
|---|---|---|
GOOS | Platform's OS | Target operating system |
GOARCH | Platform's architecture | Target CPU architecture |
CGO_ENABLED | 0 | Disable CGO for static binaries |
Linker Flags:
The default linker flags -s -w strip debug information and symbol tables to reduce binary size. Additional flags can be appended via the --ldflags option:
GOOS={goos} GOARCH={goarch} CGO_ENABLED=0 go build \
-ldflags="-s -w {additional_flags}" \
-o {output_path} \
{go_module_path}
Sources: spec.md:145-156
Step 3: Wheel Package Structure Creation
The wheel contains a specific directory structure following PEP 427:
{package_name}-{version}-py3-none-{platform_tag}.whl
├── {package_name}/
│ ├── __init__.py
│ ├── __main__.py
│ └── bin/
│ └── {binary_name}[.exe]
├── {package_name}-{version}.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── entry_points.txt
Sources: spec.md:40-57
Step 4: Python Wrapper Generation
The generated __init__.py provides a thin wrapper that executes the bundled binary:
"""Go binary packaged as Python wheel."""
import os
import sys
import subprocess
__version__ = "{version}"
def get_binary_path():
"""Return the path to the bundled binary."""
return os.path.join(os.path.dirname(__file__), "bin", "{binary_name}")
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
# On Windows, use subprocess to properly handle signals
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
# On Unix, exec replaces the process
os.execvp(binary, [binary] + sys.argv[1:])
The __main__.py serves as the entry point bridge:
from . import main
main()
Sources: spec.md:63-87
Step 5: Metadata File Generation
#### METADATA (PEP 566)
Generated dynamically with package metadata:
Metadata-Version: 2.1
Name: {normalized_name}
Version: {version}
Summary: {description}
License: {license}
Author: {author}
Author-email: {author_email}
Home-page: {url}
Requires-Python: {requires_python}
#### WHEEL (PEP 427)
Wheel-Version: 1.0
Generator: go-to-wheel {version}
Root-Is-Purelib: false
Tag: py3-none-{platform_tag}
#### entry_points.txt
[console_scripts]
{entry_point} = {package_name}:main
#### RECORD (PEP 376)
CSV format tracking all files with SHA256 hashes:
{package_name}/__init__.py,sha256={hash},{size}
{package_name}/__main__.py,sha256={hash},{size}
{package_name}/bin/{binary_name},sha256={hash},{size}
{package_name}-{version}.dist-info/METADATA,sha256={hash},{size}
{package_name}-{version}.dist-info/WHEEL,sha256={hash},{size}
{package_name}-{version}.dist-info/entry_points.txt,sha256={hash},{size}
{package_name}-{version}.dist-info/RECORD,,
Sources: spec.md:162-183
Step 6: Wheel Archive Creation
The wheel is created as a ZIP archive with specific handling:
with zipfile.ZipFile(wheel_path, "w", zipfile.ZIP_DEFLATED) as whl:
for file_path, content in files.items():
# Set executable permission for binary
if "/bin/" in file_path:
info = zipfile.ZipInfo(file_path)
# Set Unix permissions: rwxr-xr-x (0755)
info.external_attr = (
stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
stat.S_IROTH | stat.S_IXOTH
) << 16
whl.writestr(info, content)
else:
whl.writestr(file_path, content)
Binary files receive executable permissions (0755) via the ZIP external attributes, ensuring they can be executed after installation.
Sources: go_to_wheel/__init__.py:110-123
Wheel Naming Convention
Wheels follow PEP 427 naming with format:
{name}-{version}-py3-none-{platform_tag}.whl
Examples:
| Package | Version | Platform | Full Wheel Name |
|---|---|---|---|
myapp | 1.0.0 | linux-amd64 | myapp-1.0.0-py3-none-manylinux_2_17_x86_64.whl |
myapp | 1.0.0 | darwin-arm64 | myapp-1.0.0-py3-none-macosx_11_0_arm64.whl |
myapp | 1.0.0 | windows-amd64 | myapp-1.0.0-py3-none-win_amd64.whl |
Package names are normalized per PEP 503: hyphens are used in wheel filenames, while the import name (directory) uses underscores.
Sources: spec.md:207-216
Linker Flags Integration
Version Variable Setting
The --set-version-var option embeds the package version into a Go variable at compile time:
go-to-wheel ./mytool --version 2.0.0 --set-version-var main.version
This passes -X main.version=2.0.0 to the Go linker. The combined ldflags are constructed as:
combined_ldflags_parts: list[str] = []
if set_version_var:
combined_ldflags_parts.append(f"-X {set_version_var}={version}")
if ldflags:
combined_ldflags_parts.append(ldflags)
combined_ldflags = " ".join(combined_ldflags_parts)
The final linker invocation becomes: -ldflags="-s -w -X main.version=2.0.0"
Sources: go_to_wheel/__init__.py:196-203
Custom ldflags
Additional arbitrary linker flags can be passed with --ldflags:
go-to-wheel ./mytool --ldflags "-X main.version=2.0.0 -X main.commit=abc123"
These flags are appended to the default -s -w, so the full invocation becomes -ldflags="-s -w -X main.version=2.0.0 -X main.commit=abc123".
Sources: README.md:48-51
Error Handling
| Error Condition | Handling |
|---|---|
| Directory not found | FileNotFoundError with message |
No go.mod file | ValueError indicating invalid Go module |
| README file not found | FileNotFoundError for README path |
| Go binary not found | FileNotFoundError from subprocess |
| Build failure | Exception propagates with Go error output |
| No wheels built | Prints error and returns exit code 1 |
The CLI entry point catches FileNotFoundError and ValueError exceptions, printing them to stderr and returning exit code 1:
try:
wheels = build_wheels(...)
except (FileNotFoundError, ValueError) as e:
print(f"Error: {e}", file=sys.stderr)
return 1
Sources: go_to_wheel/__init__.py:92-95
Output Example
Successful wheel generation produces output like:
go-to-wheel v0.1.0
Building from ./myapp
✓ myapp-1.0.0-py3-none-manylinux_2_17_x86_64.whl
✓ myapp-1.0.0-py3-none-manylinux_2_17_aarch64.whl
✓ myapp-1.0.0-py3-none-musllinux_1_2_x86_64.whl
✓ myapp-1.0.0-py3-none-musllinux_1_2_aarch64.whl
✓ myapp-1.0.0-py3-none-macosx_10_9_x86_64.whl
✓ myapp-1.0.0-py3-none-macosx_11_0_arm64.whl
✓ myapp-1.0.0-py3-none-win_amd64.whl
✓ myapp-1.0.0-py3-none-win_arm64.whl
Done! Built 8 wheels in ./dist
Sources: spec.md:225-240
Related Components
| Component | Purpose |
|---|---|
build_wheels() | Main orchestrator function |
build_single_wheel() | Creates wheel for one platform |
compile_go_binary() | Executes Go cross-compilation |
generate_metadata() | Creates METADATA content |
generate_wheel_file() | Creates WHEEL file content |
generate_record() | Creates RECORD with hashes |
generate_entry_points() | Creates entry_points.txt |
These functions work together to transform a Go module into a distributable Python wheel package that can be installed via pip or pipx.
Sources: go_to_wheel/__init__.py:96-135
See Also
Sources: spec.md:1-25
Development Guide
Related topics: Installation, Configuration and Metadata
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Installation, Configuration and Metadata
Development Guide
Overview
This guide covers everything you need to know to develop, test, and contribute to go-to-wheel. The project is a Python CLI tool that compiles Go programs into Python wheels, enabling distribution of Go binaries via PyPI and pipx. Sources: README.md:1-15
The tool requires no external Python dependencies—only Python's standard library is used—making it lightweight and easy to maintain. Sources: spec.md:95-97
Prerequisites
Before setting up a development environment, ensure you have the following installed:
| Requirement | Minimum Version | Purpose |
|---|---|---|
| Python | >= 3.10 | Runtime for the tool |
| Go | >= 1.16 | Required for go build commands |
| uv | Latest | Package manager and test runner |
| Git | Any recent | Version control |
Sources: spec.md:95-100
Repository Structure
go-to-wheel/
├── go_to_wheel/
│ └── __init__.py # Main module with all logic
├── tests/
│ └── test_*.py # Test files
├── pyproject.toml # Project configuration
├── README.md # User documentation
├── spec.md # Technical specification
└── LICENSE # Apache 2.0 license
The main implementation resides in go_to_wheel/__init__.py, which contains:
- Platform mapping definitions
- CLI argument parsing
- Go cross-compilation logic
- Wheel building functions
- Metadata generation
Sources: go_to_wheel/__init__.py:1-50
Setting Up the Development Environment
1. Clone the Repository
git clone https://github.com/simonw/go-to-wheel
cd go-to-wheel
Sources: README.md:55-58
2. Install Dependencies
The project uses uv for dependency management. Install all development dependencies:
uv sync
This automatically installs the project and its test dependencies based on pyproject.toml.
3. Verify Installation
Confirm the tool is accessible:
uv run go-to-wheel --version
Running Tests
The project uses pytest for testing. Execute the full test suite with:
uv run pytest
Sources: README.md:59-60
Test Structure
Tests are located in the tests/ directory and follow the naming pattern test_*.py. Each test file typically corresponds to a major component:
| Test File | Coverage Area |
|---|---|
test_platforms.py | Platform mapping and validation |
test_build.py | Wheel building logic |
test_cli.py | Command-line argument parsing |
Running Specific Tests
Run tests matching a pattern:
uv run pytest -k "test_name_pattern"
Run with verbose output:
uv run pytest -v
Code Architecture
Core Components
graph TD
A[CLI Entry Point] --> B[Argument Parser]
B --> C[build_wheels Function]
C --> D[Platform Validation]
C --> E[Go Cross-Compilation]
E --> F[Wheel Assembly]
F --> G[Metadata Generation]
F --> H[ZIP Creation]
G --> H
H --> I[Output Wheels]Platform Mapping System
The tool defines platform mappings that translate human-readable platform names to Go environment variables and wheel tags:
PLATFORM_MAPPINGS: dict[str, tuple[str, str, str]] = {
"linux-amd64": ("linux", "amd64", "manylinux_2_17_x86_64"),
"linux-arm64": ("linux", "arm64", "manylinux_2_17_aarch64"),
"linux-amd64-musl": ("linux", "amd64", "musllinux_1_2_x86_64"),
"linux-arm64-musl": ("linux", "arm64", "musllinux_1_2_aarch64"),
"darwin-amd64": ("darwin", "amd64", "macosx_10_9_x86_64"),
"darwin-arm64": ("darwin", "arm64", "macosx_11_0_arm64"),
"windows-amd64": ("windows", "amd64", "win_amd64"),
"windows-arm64": ("windows", "arm64", "win_arm64"),
}
Sources: go_to_wheel/__init__.py:19-27
Default Platforms
graph LR
A[Default Platforms] --> B[Linux amd64]
A --> C[Linux arm64]
A --> D[Linux amd64-musl]
A --> E[Linux arm64-musl]
A --> F[macOS amd64]
A --> G[macOS arm64]
A --> H[Windows amd64]
A --> I[Windows arm64]The DEFAULT_PLATFORMS list defines which platforms are built when none are specified:
DEFAULT_PLATFORMS = [
"linux-amd64",
"linux-arm64",
"linux-amd64-musl",
"linux-arm64-musl",
"darwin-amd64",
"darwin-arm64",
"windows-amd64",
"windows-arm64",
]
Sources: go_to_wheel/__init__.py:29-37
Build Workflow
sequenceDiagram
participant User
participant CLI
participant Builder
participant Go
participant WheelBuilder
User->>CLI: go-to-wheel ./myapp
CLI->>Builder: build_wheels(go_dir)
Builder->>Go: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build
Go-->>Builder: Binary for linux/amd64
Builder->>Go: (repeat for each platform)
Go-->>Builder: Binaries for all platforms
Builder->>WheelBuilder: Create wheel structure
WheelBuilder->>WheelBuilder: Generate __init__.py
WheelBuilder->>WheelBuilder: Generate METADATA
WheelBuilder->>WheelBuilder: Create RECORD with hashes
WheelBuilder->>Builder: Wheel file
Builder->>User: List of wheel pathsBuilding Wheels Locally
Local Installation
Install the package in development mode:
uv pip install -e .
Building Wheels for Testing
Create test wheels for a sample Go module:
go-to-wheel ./path/to/go-module --name my-test-package
This creates wheels in ./dist for all default platforms. Sources: README.md:31-37
Custom Platform Selection
Build only specific platforms to speed up iteration:
go-to-wheel ./myapp --platforms linux-amd64,darwin-arm64
CLI Argument Reference
The tool accepts the following arguments for development and testing:
| Argument | Type | Default | Purpose |
|---|---|---|---|
--name | string | Directory name | Python package name |
--version | string | 0.1.0 | Package version |
--output-dir | string | ./dist | Output directory |
--platforms | string | All platforms | Target platforms |
--go-binary | string | go | Go binary path |
--ldflags | string | None | Additional linker flags |
Sources: go_to_wheel/__init__.py:75-100
Cross-Compilation Process
The build process uses Go's cross-compilation with specific environment settings:
def compile_go_binary(
go_dir: str,
goos: str,
goarch: str,
output_path: str,
go_binary: str,
ldflags: str | None = None,
) -> None:
env = os.environ.copy()
env["GOOS"] = goos
env["GOARCH"] = goarch
env["CGO_ENABLED"] = "0" # Static binaries
ldflags_value = "-s -w" # Strip debug info
if ldflags:
ldflags_value += " " + ldflags
cmd = [go_binary, "build", f"-ldflags={ldflags_value}", "-o", output_path, "."]
Key points:
CGO_ENABLED=0ensures static binaries with no libc dependency-ldflags="-s -w"strips debug information to reduce binary size- Windows builds automatically receive
.exeextension
Sources: go_to_wheel/__init__.py:120-140
Wheel Structure Generation
Each wheel contains a Python wrapper that executes the bundled binary:
graph TD
subgraph Wheel Contents
A[Package Directory] --> B[__init__.py]
A --> C[__main__.py]
A --> D[bin/]
D --> E[Go Binary]
A --> F[.dist-info/]
F --> G[METADATA]
F --> H[WHEEL]
F --> I[RECORD]
F --> J[entry_points.txt]
endThe __init__.py includes:
def get_binary_path():
"""Return the path to the bundled binary."""
return os.path.join(os.path.dirname(__file__), "bin", "{binary_name}")
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
os.execvp(binary, [binary] + sys.argv[1:])
Sources: spec.md:58-75
Version Information
The project version is defined in the main module:
__version__ = "0.1.0"
Sources: go_to_wheel/__init__.py:12
To check the installed version during development:
uv run python -c "import go_to_wheel; print(go_to_wheel.__version__)"
Debugging
Enable Verbose Output
The CLI outputs progress during builds:
go-to-wheel v0.1.0
Building from ./myapp
linux-amd64... done
darwin-arm64... done
Common Build Failures
| Issue | Cause | Solution |
|---|---|---|
go.mod not found | Directory is not a Go module | Run go mod init first |
Go not found | Go not in PATH | Install Go or use --go-binary |
| Build timeout | Complex Go project | Increase timeout or skip platforms |
Inspecting Generated Wheels
List contents of a wheel:
unzip -l myapp-1.0.0-py3-none-manylinux_2_17_x86_64.whl
Contributing
Development Workflow
- Fork the repository on GitHub
- Clone your fork locally
- Create a feature branch:
git checkout -b feature/my-feature - Make changes and add tests
- Run the test suite:
uv run pytest - Commit your changes with clear messages
- Push to your fork and submit a pull request
Code Style
- Follow PEP 8 for Python code
- Use type hints for function parameters and return values
- Add docstrings to public functions
- Keep functions focused and small
Testing Guidelines
- All new features should include tests
- Ensure existing tests pass before submitting
- Use descriptive test names:
test_platform_mapping_linux_amd64
Future Development Considerations
The specification outlines potential future features not yet implemented:
| Feature | Status | Description |
|---|---|---|
| Configuration file | Not started | Support pyproject.toml or go-to-wheel.toml |
| Auto version detection | Not started | Parse from VERSION file or git tags |
| Multiple entry points | Not started | Support Go modules with multiple binaries |
| Universal2 macOS | Not started | Combine arm64 and x86_64 with lipo |
| Build caching | Not started | Skip unchanged platforms |
| PyPI publishing | Not started | Add go-to-wheel publish command |
Sources: spec.md:80-93
Related Documentation
- User README - Installation and usage instructions
- Technical Specification - Detailed system design
- maturin - Rust equivalent that inspired this project
- pip-binary-factory - Template for pre-built binaries
Sources: spec.md:95-100
Configuration and Metadata
Related topics: CLI Options Reference, Development Guide
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: CLI Options Reference, Development Guide
Configuration and Metadata
This page documents the configuration system and metadata handling in go-to-wheel, a tool that compiles Go CLI programs into Python wheels.
Overview
go-to-wheel provides extensive configuration options for customizing wheel builds. Configuration is passed through command-line arguments and is used to generate proper Python wheel metadata according to PEP 427 and PEP 566 standards.
The tool supports two layers of configuration:
- Build Configuration - Controls how the Go binary is compiled (platforms, Go binary path, ldflags)
- Package Metadata - Defines Python package metadata (name, version, author, license, etc.)
Sources: go_to_wheel/__init__.py:1-50
Platform Configuration
Supported Platforms
go-to-wheel supports cross-compilation for 8 different platform configurations:
| Platform Identifier | GOOS | GOARCH | Wheel Tag |
|---|---|---|---|
linux-amd64 | linux | amd64 | manylinux_2_17_x86_64 |
linux-arm64 | linux | arm64 | manylinux_2_17_aarch64 |
linux-amd64-musl | linux | amd64 | musllinux_1_2_x86_64 |
linux-arm64-musl | linux | arm64 | musllinux_1_2_aarch64 |
darwin-amd64 | darwin | amd64 | macosx_10_9_x86_64 |
darwin-arm64 | darwin | arm64 | macosx_11_0_arm64 |
windows-amd64 | windows | amd64 | win_amd64 |
windows-arm64 | windows | arm64 | win_arm64 |
Sources: go_to_wheel/__init__.py:22-30
Platform Selection
By default, all 8 platforms are built. Users can specify a subset using the --platforms option:
go-to-wheel ./mytool --platforms linux-amd64,darwin-arm64
Platform identifiers are defined in the PLATFORM_MAPPINGS dictionary and can be parsed from comma-separated input.
Sources: go_to_wheel/__init__.py:86-90
Build Workflow
graph TD
A[Parse --platforms argument] --> B{Platforms specified?}
B -->|No| C[Use DEFAULT_PLATFORMS]
B -->|Yes| D[Split by comma]
D --> E[Validate each platform]
E --> F[For each platform]
F --> G[Set GOOS/GOARCH env]
G --> H[Run go build with CGO_ENABLED=0]
H --> I[Create wheel structure]
I --> J[Generate METADATA, WHEEL, RECORD]
J --> K[Zip into .whl file]
K --> FCommand-Line Options
Full Option Reference
| Option | Type | Default | Description |
|---|---|---|---|
--name | string | Directory basename | Python package name |
--version | string | 0.1.0 | Package version |
--output-dir | string | ./dist | Directory for built wheels |
--entry-point | string | Same as --name | CLI command name |
--platforms | string | All 8 platforms | Comma-separated platform list |
--go-binary | string | go | Path to Go binary |
--description | string | "Go binary packaged as Python wheel" | Package description |
--license | string | None | SPDX license identifier |
--author | string | None | Author name |
--author-email | string | None | Author email |
--url | string | None | Project URL |
--requires-python | string | >=3.10 | Python version requirement |
--readme | string | None | Path to README markdown file |
--ldflags | string | None | Additional Go linker flags |
--set-version-var | string | None | Go variable for version embedding |
Sources: go_to_wheel/__init__.py:52-115
Build Configuration Options
These options control the Go compilation process:
def build_wheels(
go_dir: str,
*,
name: str | None = None,
version: str = "0.1.0",
output_dir: str = "./dist",
entry_point: str | None = None,
platforms: list[str] | None = None,
go_binary: str = "go",
description: str = "Go binary packaged as Python wheel",
# ... metadata options ...
ldflags: str | None = None,
set_version_var: str | None = None,
) -> list[str]:
Sources: go_to_wheel/__init__.py:178-220
Linker Flags Configuration
The --ldflags and --set-version-var options control Go linker flags:
Default flags: -s -w (strips debug info and symbol tables)
Additional flags can be appended using --ldflags:
go-to-wheel ./mytool --ldflags "-X main.commit=abc123"
Version embedding via --set-version-var:
go-to-wheel ./mytool --version 2.0.0 --set-version-var main.version
This passes -X main.version=2.0.0 to the Go linker.
Sources: README.md:30-50
The linker flags are combined in this order:
-s -w(default strip flags)-X {set_version_var}={version}(if--set-version-varis set)- User-provided
--ldflags(appended last)
Sources: go_to_wheel/__init__.py:255-265
Metadata Generation
METADATA File (PEP 566)
Generated by generate_metadata():
def generate_metadata(
name: str,
version: str,
description: str,
*,
author: str | None = None,
author_email: str | None = None,
license_: str | None = None,
url: str | None = None,
requires_python: str = ">=3.10",
readme_content: str | None = None,
) -> str:
"""Generate METADATA file content."""
lines = [
"Metadata-Version: 2.1",
f"Name: {name}",
f"Version: {version}",
f"Summary: {description}",
]
if author:
lines.append(f"Author: {author}")
if author_email:
lines.append(f"Author-email: {author_email}")
if license_:
lines.append(f"License: {license_}")
if url:
lines.append(f"Home-page: {url}")
lines.append(f"Requires-Python: {requires_python}")
if readme_content:
lines.append("Description-Content-Type: text/markdown")
lines.append("")
lines.append(readme_content)
return "\n".join(lines) + "\n"
Sources: go_to_wheel/__init__.py:280-315
WHEEL File (PEP 427)
Generated by generate_wheel_metadata():
Wheel-Version: 1.0
Generator: go-to-wheel {version}
Root-Is-Purelib: false
Tag: py3-none-{platform_tag}
Key points:
Root-Is-Purelib: falseindicates platform-specific binarypy3-noneindicates Python 3+ requirement{platform_tag}varies by target (e.g.,manylinux_2_17_x86_64)
Sources: go_to_wheel/__init__.py:317-325
entry_points.txt
Generated for console script entry points:
[console_scripts]
{entry_point} = {import_name}:main
The import_name is derived from the package name by replacing hyphens with underscores.
Sources: go_to_wheel/__init__.py:327-332
RECORD File
Generated by generate_record() using SHA256 hashes:
def generate_record(files: dict[str, bytes]) -> str:
"""Generate RECORD file content."""
output = io.StringIO()
writer = csv.writer(output)
for path, content in files.items():
if path.endswith("RECORD"):
writer.writerow([path, "", ""])
else:
hash_value = base64.urlsafe_b64encode(
hashlib.sha256(content).digest()
).rstrip(b"=").decode("ascii")
writer.writerow([path, f"sha256={hash_value}", len(content)])
return output.getvalue()
Sources: go_to_wheel/__init__.py:334-348
Package Structure
The generated wheel follows this structure:
{package_name}-{version}-py3-none-{platform_tag}.whl
├── {import_name}/
│ ├── __init__.py
│ ├── __main__.py
│ └── bin/
│ └── {binary_name}[.exe]
├── {import_name}-{version}.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── entry_points.txt
Sources: spec.md:60-80
__init__.py Template
"""Go binary packaged as Python wheel."""
import os
import sys
import subprocess
__version__ = "{version}"
def get_binary_path():
"""Return the path to the bundled binary."""
return os.path.join(os.path.dirname(__file__), "bin", "{binary_name}")
def main():
"""Execute the bundled binary."""
binary = get_binary_path()
if sys.platform == "win32":
sys.exit(subprocess.call([binary] + sys.argv[1:]))
else:
os.execvp(binary, [binary] + sys.argv[1:])
Sources: spec.md:82-100
Metadata Flow
graph LR
A[CLI Arguments] --> B[ArgumentParser]
B --> C[build_wheels function]
C --> D[Input Validation]
D --> E[README Processing]
E --> F[Platform Loop]
F --> G[Go Compilation]
G --> H[Wheel Assembly]
H --> I[Metadata Generation]
I --> J[ZIP Creation]
J --> K[Output Directory]
C -->|name, version| L[METADATA]
C -->|platform_tag| M[WHEEL]
C -->|entry_point| N[entry_points.txt]
H --> O[RECORD with hashes]Input Validation
The build_wheels() function validates inputs:
| Check | Behavior on Failure |
|---|---|
| Go directory exists | Raises FileNotFoundError |
go.mod file present | Raises ValueError |
| README file exists (if provided) | Raises FileNotFoundError |
| Package name valid (PEP 503) | Raises ValueError |
Sources: go_to_wheel/__init__.py:225-235
Configuration Precedence
When multiple configuration sources conflict, the following precedence applies:
- Command-line arguments (highest priority)
- Default values (lowest priority)
For --ldflags, user-provided flags are appended after default flags, allowing overrides.
Environment Variables Used
During build, these environment variables are set:
| Variable | Value | Purpose |
|---|---|---|
GOOS | Platform GOOS | Cross-compilation target |
GOARCH | Platform GOARCH | Cross-compilation target |
CGO_ENABLED | 0 | Static binary compilation |
Sources: go_to_wheel/__init__.py:270-280
Example: Full Metadata Configuration
go-to-wheel ./mytool \
--name mytool-bin \
--version 2.0.0 \
--description "My awesome tool" \
--license MIT \
--author "Jane Doe" \
--author-email "[email protected]" \
--url "https://github.com/jane/mytool" \
--readme README.md
This produces a wheel with complete PyPI-compatible metadata.
Sources: README.md:55-65
Sources: go_to_wheel/__init__.py:1-50
Doramagic Pitfall Log
Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.
The project should not be treated as fully validated until this signal is reviewed.
Users cannot judge support quality until recent activity, releases, and issue response are checked.
The project may affect permissions, credentials, data exposure, or host boundaries.
The project may affect permissions, credentials, data exposure, or host boundaries.
Doramagic Pitfall Log
Doramagic extracted 6 source-linked risk signals. Review them before installing or handing real data to the project.
1. Capability assumption: README/documentation is current enough for a first validation pass.
- Severity: medium
- Finding: README/documentation is current enough for a first validation pass.
- User impact: The project should not be treated as fully validated until this signal is reviewed.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: capability.assumptions | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | README/documentation is current enough for a first validation pass.
2. Maintenance risk: Maintainer activity is unknown
- Severity: medium
- Finding: Maintenance risk is backed by a source signal: Maintainer activity is unknown. Treat it as a review item until the current version is checked.
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | last_activity_observed missing
3. Security or permission risk: no_demo
- Severity: medium
- Finding: no_demo
- User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: downstream_validation.risk_items | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | no_demo; severity=medium
4. Security or permission risk: no_demo
- Severity: medium
- Finding: no_demo
- User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: risks.scoring_risks | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | no_demo; severity=medium
5. Maintenance risk: issue_or_pr_quality=unknown
- Severity: low
- Finding: issue_or_pr_quality=unknown。
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | issue_or_pr_quality=unknown
6. Maintenance risk: release_recency=unknown
- Severity: low
- Finding: release_recency=unknown。
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | hn_item:48109677 | https://news.ycombinator.com/item?id=48109677 | release_recency=unknown
Source: Doramagic discovery, validation, and Project Pack records
Community Discussion Evidence
These external discussion links are review inputs, not standalone proof that the project is production-ready.
Count of project-level external discussion links exposed on this manual page.
Open the linked issues or discussions before treating the pack as ready for your environment.
Community Discussion Evidence
Doramagic exposes project-level community discussion separately from official documentation. Review these links before using go-to-wheel with real data or production workflows.
- README/documentation is current enough for a first validation pass. - GitHub / issue
Source: Project Pack community evidence and pitfall evidence