Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: goreleaser

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Verify tag matches version.go
run: |
TAG="${GITHUB_REF#refs/tags/}"
VERS=$(grep -oP 'Version\s*=\s*"\K[^"]+' version.go)
if [ "$TAG" != "$VERS" ]; then
echo "::error::pushed tag $TAG does not match version.go Version=$VERS"
exit 1
fi

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# IDE files
.idea/
*.iml

# goreleaser build output
/dist/
45 changes: 45 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
version: 2
project_name: gopy

before:
hooks:
- go mod tidy

builds:
- id: gopy
main: .
binary: gopy
env:
- CGO_ENABLED=0
goos: [linux, darwin, windows]
goarch: [amd64, arm64]
ldflags:
- -s -w

archives:
- id: gopy
formats: [tar.gz]
format_overrides:
- goos: windows
formats: [zip]
name_template: >-
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}

checksum:
name_template: "checksums.txt"

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^ci:"
- "^Merge pull request"

release:
github:
owner: go-python
name: gopy
prerelease: auto
name_template: "{{ .Tag }}"
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,7 @@ release:
git commit -am "$(VERS) release"
git tag -a $(VERS) -m "$(VERS) release"
git push
# pushing the tag triggers .github/workflows/release.yml, which builds and
# publishes the GitHub Release via goreleaser
git push origin --tags

3 changes: 3 additions & 0 deletions bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type BindCfg struct {
PkgPrefix string
// rename Go exported symbols to python PEP snake_case
RenameCase bool
// gopy version string embedded in this binary, stamped into generated
// file headers so output can be traced back to the release that produced it
Version string
}

// ErrorList is a list of errors
Expand Down
35 changes: 18 additions & 17 deletions bind/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ var WindowsOS = false

// for all preambles: 1 = name of package (outname), 2 = cmdstr

// 3 = libcfg, 4 = GoHandle, 5 = CGoHandle, 6 = all imports, 7 = mainstr, 8 = exe pre C, 9 = exe pre go
// 3 = libcfg, 4 = GoHandle, 5 = CGoHandle, 6 = all imports, 7 = mainstr, 8 = exe pre C, 9 = exe pre go, 10 = gopy version
const (
goPreamble = `/*
cgo stubs for package %[1]s.
File is generated by gopy. Do not edit.
File is generated by gopy version %[10]s. Do not edit.
%[2]s
*/

Expand Down Expand Up @@ -257,8 +257,9 @@ func GoPyMainRun() {

`

// 3 = gopy version
PyBuildPreamble = `# python build stubs for package %[1]s
# File is generated by gopy. Do not edit.
# File is generated by gopy version %[3]s. Do not edit.
# %[2]s

from pybindgen import retval, param, Function, Module
Expand Down Expand Up @@ -311,11 +312,11 @@ mod.add_function('_gopy_clear_go_tls', None, [])
// appended to imports in py wrap preamble as key for adding at end
importHereKeyString = "%%%%%%<<<<<<ADDIMPORTSHERE>>>>>>>%%%%%%%"

// 3 = specific package name, 4 = spec pkg path, 5 = doc, 6 = imports
// 3 = specific package name, 4 = spec pkg path, 5 = doc, 6 = imports, 8 = gopy version
PyWrapPreamble = `%[5]s
# python wrapper for package %[4]s within overall package %[1]s
# This is what you import to use the package.
# File is generated by gopy. Do not edit.
# File is generated by gopy version %[8]s. Do not edit.
# %[2]s

# the following is required to enable dlopen to open the _go.so file
Expand Down Expand Up @@ -373,11 +374,11 @@ except Exception:
`

// exe version of preamble -- doesn't need complex code to load _ module
// 3 = specific package name, 4 = spec pkg path, 5 = doc, 6 = imports
// 3 = specific package name, 4 = spec pkg path, 5 = doc, 6 = imports, 8 = gopy version
PyWrapExePreamble = `%[5]s
# python wrapper for package %[4]s within standalone executable package %[1]s
# This is what you import to use the package.
# File is generated by gopy. Do not edit.
# File is generated by gopy version %[8]s. Do not edit.
# %[2]s

import collections
Expand Down Expand Up @@ -425,9 +426,9 @@ def Init():
`

// 3 = gencmd, 4 = vm, 5 = libext 6 = extraGccArgs, 7 = CFLAGS, 8 = LDLFAGS,
// 9 = windows special declspec hack
// 9 = windows special declspec hack, 10 = gopy version
MakefileTemplate = `# Makefile for python interface for package %[1]s.
# File is generated by gopy. Do not edit.
# File is generated by gopy version %[10]s. Do not edit.
# %[2]s

GOCMD=go
Expand Down Expand Up @@ -464,9 +465,9 @@ build:

`

// exe version of template: 3 = gencmd, 4 = vm, 5 = libext
// exe version of template: 3 = gencmd, 4 = vm, 5 = libext, 8 = gopy version
MakefileExeTemplate = `# Makefile for python interface for standalone executable package %[1]s.
# File is generated by gopy. Do not edit.
# File is generated by gopy version %[8]s. Do not edit.
# %[2]s

GOCMD=go
Expand Down Expand Up @@ -715,12 +716,12 @@ func (g *pyGen) genGoPreamble() {
exeprego = goExePreambleGo
}
g.gofile.Printf(goPreamble, g.cfg.Name, g.cfg.Cmd, libcfg, GoHandle, CGoHandle,
pkgimport, g.cfg.Main, exeprec, exeprego)
pkgimport, g.cfg.Main, exeprec, exeprego, g.cfg.Version)
g.gofile.Printf("\n// --- generated code for package: %[1]s below: ---\n\n", g.cfg.Name)
}

func (g *pyGen) genPyBuildPreamble() {
g.pybuild.Printf(PyBuildPreamble, g.cfg.Name, g.cfg.Cmd)
g.pybuild.Printf(PyBuildPreamble, g.cfg.Name, g.cfg.Cmd, g.cfg.Version)
}

func (g *pyGen) genPyWrapPreamble() {
Expand Down Expand Up @@ -778,9 +779,9 @@ func (g *pyGen) genPyWrapPreamble() {
impstr += importHereKeyString

if g.mode == ModeExe {
g.pywrap.Printf(PyWrapExePreamble, g.cfg.Name, g.cfg.Cmd, n, pkgimport, pkgDoc, impgenstr, impstr)
g.pywrap.Printf(PyWrapExePreamble, g.cfg.Name, g.cfg.Cmd, n, pkgimport, pkgDoc, impgenstr, impstr, g.cfg.Version)
} else {
g.pywrap.Printf(PyWrapPreamble, g.cfg.Name, g.cfg.Cmd, n, pkgimport, pkgDoc, impgenstr, impstr)
g.pywrap.Printf(PyWrapPreamble, g.cfg.Name, g.cfg.Cmd, n, pkgimport, pkgDoc, impgenstr, impstr, g.cfg.Version)
}
}

Expand Down Expand Up @@ -810,14 +811,14 @@ func (g *pyGen) genMakefile() {
}

if g.mode == ModeExe {
g.makefile.Printf(MakefileExeTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, pycfg.CFlags, pycfg.LdFlags)
g.makefile.Printf(MakefileExeTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, pycfg.CFlags, pycfg.LdFlags, g.cfg.Version)
} else {
winhack := ""
if WindowsOS {
winhack = fmt.Sprintf(`# windows-only sed hack here to fix pybindgen declaration of PyInit
sed -i "s/ PyInit_/ __declspec(dllexport) PyInit_/g" %s.c`, g.cfg.Name)
}
g.makefile.Printf(MakefileTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, g.extraGccArgs, pycfg.CFlags, pycfg.LdFlags, winhack)
g.makefile.Printf(MakefileTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, g.extraGccArgs, pycfg.CFlags, pycfg.LdFlags, winhack, g.cfg.Version)
}
}

Expand Down
32 changes: 32 additions & 0 deletions cmd_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2026 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"fmt"

"github.com/gonuts/commander"
"github.com/gonuts/flag"
)

func gopyMakeCmdVersion() *commander.Command {
return &commander.Command{
Run: gopyRunCmdVersion,
UsageLine: "version",
Short: "print gopy version information",
Long: `
version prints the gopy version, git commit, and build date embedded in this binary at release time.

ex:
$ gopy version
`,
Flag: *flag.NewFlagSet("gopy-version", flag.ExitOnError),
}
}

func gopyRunCmdVersion(cmdr *commander.Command, args []string) error {
fmt.Printf("gopy version %s (commit %s, built %s)\n", Version, GitCommit, VersionDate)
return nil
}
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type BuildCfg struct {
func NewBuildCfg() *BuildCfg {
var cfg BuildCfg
cfg.Cmd = argStr()
cfg.Version = Version
return &cfg
}

Expand All @@ -49,6 +50,7 @@ func run(args []string) error {
gopyMakeCmdBuild(),
gopyMakeCmdPkg(),
gopyMakeCmdExe(),
gopyMakeCmdVersion(),
},
Flag: *flag.NewFlagSet("gopy", flag.ExitOnError),
}
Expand Down
41 changes: 41 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,47 @@ ignoring python incompatible function: .func github.com/go-python/gopy/_examples
}
}

func TestVersion(t *testing.T) {
cmd := exec.Command("go", "run", ".", "version")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("could not run %v: %+v\n%s", strings.Join(cmd.Args, " "), err, out)
}
got := strings.TrimSpace(string(out))
want := fmt.Sprintf("gopy version %s (commit %s, built %s)", Version, GitCommit, VersionDate)
if got != want {
t.Fatalf("gopy version = %q, want %q", got, want)
}
}

func TestGenHeaderHasVersion(t *testing.T) {
pyvm := testBackends["py3"]
workdir, err := os.MkdirTemp("", "gopy-")
if err != nil {
t.Fatalf("could not create workdir: %v\n", err)
}
defer os.RemoveAll(workdir)

curPkgPath := reflect.TypeOf(pkg{}).PkgPath()
fpath := filepath.Join(curPkgPath, "_examples/hi")
cmd := exec.Command("go", "run", ".", "gen", "-vm="+pyvm, "-output="+workdir, fpath)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("could not run %v: %+v\n%s", strings.Join(cmd.Args, " "), err, out)
}

want := fmt.Sprintf("generated by gopy version %s.", Version)
for _, fname := range []string{"hi.go", "build.py", "Makefile", "hi.py"} {
b, err := os.ReadFile(filepath.Join(workdir, fname))
if err != nil {
t.Fatalf("could not read generated %s: %v", fname, err)
}
if !strings.Contains(string(b), want) {
t.Errorf("%s does not contain %q:\n%s", fname, want, string(b))
}
}
}

func TestHi(t *testing.T) {
// t.Parallel()
path := "_examples/hi"
Expand Down
Loading