Metadata-Version: 2.4
Name: py-win-x86-64-gcc
Version: 0.1.2
Summary: Download tools from w64devkit with Python.
License-Expression: MIT
License-File: LICENSE
Author: GGN_2015
Author-email: neko@jlulug.org
Requires-Python: >=3.10
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Description-Content-Type: text/markdown

# py_win_x86_64_gcc

Use a bundled Windows x86_64 GCC toolchain from Python.

`py_win_x86_64_gcc` packages the `w64devkit` toolchain and provides small helper
functions that return executable paths such as `gcc.exe`, `g++.exe`, `make.exe`,
and `7zr.exe`. It is useful for Python scripts that need a predictable C/C++
compiler on 64-bit Windows without asking users to install MinGW or modify
`PATH` manually.

> [!IMPORTANT]
> This project simply packages `7zr.exe` and `w64devkit.exe` without any modifications.
> - `7zr.exe` is sourced from https://www.7-zip.org/download.html
> - `w64devkit.exe` is sourced from https://github.com/skeeto/w64devkit/releases
>
> This project is only compatible with **64-bit Windows systems based on the x86_64 architecture**.

## What is included

- `7zr.exe`, the standalone 7-Zip extractor.
- `w64devkit-x64-2.7.0`, including GCC, G++, GNU Make, GDB, and related tools.
- Python helpers for locating tools inside the extracted `w64devkit/bin`
  directory.

## Installation

```cmd
pip install py_win_x86_64_gcc
```

## Quick start

> [!NOTE]
> The first import or tool lookup may extract `w64devkit` from the bundled
> archive. This usually takes about ten seconds.

```python
from py_win_x86_64_gcc import get_all_tools, get_tool

# List executable files available from w64devkit/bin.
print(get_all_tools())

# Get absolute paths for specific tools.
print(get_tool("7zr"))
print(get_tool("g++"))
print(get_tool("gcc"))
```

## Compile a C++ file

```python
import subprocess

from py_win_x86_64_gcc import get_tool

gxx = get_tool("g++")
if gxx is None:
    raise RuntimeError("g++ was not found")

ret1 = subprocess.run(
    [gxx, "test_gcc.cpp", "-o", "test_gcc.exe"],
    check=True,
)
ret2 = subprocess.run(["test_gcc.exe"])
```

Tool names may be passed with or without a suffix:

```python
assert get_tool("gcc") == get_tool("gcc.exe")
assert get_tool("g++") == get_tool("g++.exe")
```

## API

### `get_tool(tool_name: str, force_unzip: bool = False) -> str | None`

Returns the absolute path to a bundled tool.

- `tool_name` may be `gcc`, `gcc.exe`, `g++`, `make`, `gdb`, `7zr`, and so on.
- `7zr` is returned from the package data directory.
- Other tools are returned from `w64devkit/bin`.
- `None` is returned when the requested tool is not available.
- Set `force_unzip=True` to remove and re-extract the bundled `w64devkit`
  directory before looking up the tool.

### `get_all_tools(force_unzip: bool = False) -> list[str]`

Returns sorted executable filenames available in `w64devkit/bin`, plus
`7zr.exe`.

### `unzip(force: bool = False, quiet: bool = False) -> bool`

Extracts the bundled `w64devkit` archive when needed and returns `True` when
the toolchain directory is ready.

- `force=True` removes the existing extracted directory and extracts again.
- `quiet=True` suppresses the `py_win_x86_64_gcc: unzip ...` message.

## First-use extraction and multiprocessing

The extracted `w64devkit` directory is intentionally excluded from the Python
package and is created on first use. The package uses a lock file during
extraction, so multiple Python processes can import the package or call
`get_tool()` at the same time. Only one process performs the extraction; the
others wait and then reuse the completed directory.

If extraction is interrupted and leaves an incomplete directory behind, the next
call will remove the incomplete directory and extract it again.

## Subprocess usage tips

Prefer passing the tool path as the first item in a subprocess argument list:

```python
import subprocess

from py_win_x86_64_gcc import get_tool

subprocess.run(
    [get_tool("gcc"), "--version"],
    check=True,
)
```

You do not need to add `w64devkit/bin` to `PATH` for this style of invocation.

Some build systems expect compiler tools to be discoverable from `PATH`. In
that case, prepend the parent directory of `gcc.exe` for the subprocess only:

```python
import os
import subprocess

from py_win_x86_64_gcc import get_tool

gcc = get_tool("gcc")
if gcc is None:
    raise RuntimeError("gcc was not found")

env = os.environ.copy()
env["PATH"] = os.path.dirname(gcc) + os.pathsep + env["PATH"]

subprocess.run(["make"], check=True, env=env)
```

## Troubleshooting

- `FileNotFoundError` for `7zr.exe` or `w64devkit-x64-2.7.0.7z.exe` usually
  means the package data files were not installed correctly.
- `get_tool(...)` returning `None` means that executable is not included in
  `w64devkit/bin` or the name is misspelled.
- This package is Windows-only and targets x86_64 systems. It is not intended
  for Linux, macOS, Windows ARM64, or 32-bit Windows.

