Skip to content

Commit d0c78b6

Browse files
committed
finally got windows working -- fixed issues with library path by grabbing include dir which seems more reliable. adds an extra sed to fix generated .c file to do stupid windows __declspec(dllexport).. argh.
1 parent c428d23 commit d0c78b6

File tree

7 files changed

+86
-35
lines changed

7 files changed

+86
-35
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ To [install python modules](https://packaging.python.org/tutorials/packaging-pro
3333
python3 -m pip install --upgrade setuptools wheel
3434
```
3535

36+
### Windows
37+
38+
As of version 0.4.0, windows is now better supported, and is passing tests (on at least one developers machine). You may still need to set some environment variables depending on your python installation, but a vanilla standard install is working.
39+
40+
Install Python from the main Python distribution: https://www.python.org/downloads/windows/ -- *do not under any circumstances install from the Microsoft Store app!* while that is very convenient, it creates symbolic links to access the python executables, which is incompatible with go exec.Command to run it, despite too many hours of trying to get around that.
41+
42+
The standard python install does not create a `python3.exe` which gopy looks for -- follow instructions here:
43+
https://stackoverflow.com/questions/39910730/python3-is-not-recognized-as-an-internal-or-external-command-operable-program/41492852
44+
(just make a copy of python.exe to python3.exe in the relevant installed location).
45+
46+
If you get a bunch of errors during linking in the build process, set `LIBDIR` or `GOPY_LIBDIR` to path to python libraries, and `LIBRARY` or `GOPY_PYLIB` to name of python library (e.g., python39 for 3.9).
47+
3648
## Community
3749

3850
The `go-python` community can be reached out at [[email protected]](mailto:[email protected]) or via the web forum: [go-python group](https://groups.google.com/forum/#!forum/go-python).

bind/gen.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ const (
3333
ModePkg = "pkg"
3434
)
3535

36+
// set this to true if OS is windows
37+
var WindowsOS = false
38+
3639
// for all preambles: 1 = name of package (outname), 2 = cmdstr
3740

3841
// 3 = libcfg, 4 = GoHandle, 5 = CGoHandle, 6 = all imports, 7 = mainstr, 8 = exe pre C, 9 = exe pre go
@@ -332,7 +335,8 @@ def Init():
332335
333336
`
334337

335-
// 3 = gencmd, 4 = vm, 5 = libext 6 = extraGccArgs
338+
// 3 = gencmd, 4 = vm, 5 = libext 6 = extraGccArgs, 7 = CFLAGS, 8 = LDLFAGS,
339+
// 9 = windows special declspec hack
336340
MakefileTemplate = `# Makefile for python interface for package %[1]s.
337341
# File is generated by gopy. Do not edit.
338342
# %[2]s
@@ -366,6 +370,7 @@ build:
366370
$(PYTHON) build.py
367371
# build the _%[1]s$(LIBEXT) library that contains the cgo and CPython wrappers
368372
# generated %[1]s.py python wrapper imports this c-code package
373+
%[9]s
369374
$(GCC) %[1]s.c %[6]s %[1]s_go$(LIBEXT) -o _%[1]s$(LIBEXT) $(CFLAGS) $(LDFLAGS) -fPIC --shared -w
370375
371376
`
@@ -695,7 +700,12 @@ func (g *pyGen) genMakefile() {
695700
if g.mode == ModeExe {
696701
g.makefile.Printf(MakefileExeTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, pycfg.CFlags, pycfg.LdFlags)
697702
} else {
698-
g.makefile.Printf(MakefileTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, g.extraGccArgs, pycfg.CFlags, pycfg.LdFlags)
703+
winhack := ""
704+
if WindowsOS {
705+
winhack = fmt.Sprintf(`# windows-only sed hack here to fix pybindgen declaration of PyInit
706+
sed -i "s/ PyInit_/ __declspec(dllexport) PyInit_/g" %s.c`, g.cfg.Name)
707+
}
708+
g.makefile.Printf(MakefileTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, g.extraGccArgs, pycfg.CFlags, pycfg.LdFlags, winhack)
699709
}
700710
}
701711

bind/utils.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ version=sys.version_info.major
110110
if "GOPY_INCLUDE" in os.environ and "GOPY_LIBDIR" in os.environ and "GOPY_PYLIB" in os.environ:
111111
print(json.dumps({
112112
"version": version,
113+
"minor": sys.version_info.minor,
113114
"incdir": os.environ["GOPY_INCLUDE"],
114115
"libdir": os.environ["GOPY_LIBDIR"],
115116
"libpy": os.environ["GOPY_PYLIB"],
@@ -121,6 +122,7 @@ if "GOPY_INCLUDE" in os.environ and "GOPY_LIBDIR" in os.environ and "GOPY_PYLIB"
121122
else:
122123
print(json.dumps({
123124
"version": sys.version_info.major,
125+
"minor": sys.version_info.minor,
124126
"incdir": ds.get_python_inc(),
125127
"libdir": ds.get_config_var("LIBDIR"),
126128
"libpy": ds.get_config_var("LIBRARY"),
@@ -149,6 +151,7 @@ else:
149151

150152
var raw struct {
151153
Version int `json:"version"`
154+
Minor int `json:"minor"`
152155
IncDir string `json:"incdir"`
153156
LibDir string `json:"libdir"`
154157
LibPy string `json:"libpy"`
@@ -164,6 +167,20 @@ else:
164167
raw.IncDir = filepath.ToSlash(raw.IncDir)
165168
raw.LibDir = filepath.ToSlash(raw.LibDir)
166169

170+
// on windows these can be empty -- use include dir which is usu good
171+
if raw.LibDir == "" && raw.IncDir != "" {
172+
raw.LibDir = raw.IncDir
173+
if strings.HasSuffix(raw.LibDir, "include") {
174+
raw.LibDir = raw.LibDir[:len(raw.LibDir)-len("include")] + "libs"
175+
}
176+
fmt.Printf("no LibDir -- copy from IncDir: %s\n", raw.LibDir)
177+
}
178+
179+
if raw.LibPy == "" {
180+
raw.LibPy = fmt.Sprintf("python%d%d", raw.Version, raw.Minor)
181+
fmt.Printf("no LibPy -- set to: %s\n", raw.LibPy)
182+
}
183+
167184
if strings.HasSuffix(raw.LibPy, ".a") {
168185
raw.LibPy = raw.LibPy[:len(raw.LibPy)-len(".a")]
169186
}

cmd_build.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error {
197197
return err
198198
}
199199

200+
if bind.WindowsOS {
201+
fmt.Printf("Doing windows sed hack to fix declspec for PyInit\n")
202+
cmd = exec.Command("sed", "-i", "s/ PyInit_/ __declspec(dllexport) PyInit_/g", cfg.Name+".c")
203+
cmdout, err = cmd.CombinedOutput()
204+
if err != nil {
205+
fmt.Printf("cmd had error: %v output:\no%v\n", err, string(cmdout))
206+
return err
207+
}
208+
}
209+
200210
cflags := strings.Fields(strings.TrimSpace(pycfg.CFlags))
201211
cflags = append(cflags, "-fPIC", "-Ofast")
202212
if include, exists := os.LookupEnv("GOPY_INCLUDE"); exists {

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ func writeGoMod(t *testing.T, pkgDir, tstDir string) {
937937
module dummy
938938
939939
require github.com/go-python/gopy v0.0.0
940-
replace github.com/go-python/gopy => "%s"
940+
replace github.com/go-python/gopy => %s
941941
`
942942
contents := fmt.Sprintf(template, pkgDir)
943943
if err := ioutil.WriteFile(filepath.Join(tstDir, "go.mod"), []byte(contents), 0666); err != nil {

main_windows.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
//go:build windows
56
// +build windows
67

78
package main
89

10+
import "github.com/go-python/gopy/bind"
11+
912
const (
10-
libExt = ".dll"
13+
libExt = ".pyd"
1114
extraGccArgs = ""
1215
)
16+
17+
func init() {
18+
bind.WindowsOS = true
19+
}

main_windows_test.go

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2015 The go-python Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
4-
4+
//go:build windows
55
// +build windows
66

77
package main
@@ -10,30 +10,22 @@ import (
1010
"log"
1111
"os"
1212
"os/exec"
13-
"path"
1413
"strings"
1514
)
1615

1716
func init() {
17+
18+
testEnvironment = os.Environ()
19+
1820
var (
19-
py2 = "python2"
20-
py3 = "python3"
21-
pypy2 = "pypy"
22-
pypy3 = "pypy3"
21+
// py2 = "python2"
22+
py3 = "python3"
23+
// pypy2 = "pypy"
24+
// pypy3 = "pypy3"
2325
)
2426

25-
if os.Getenv("GOPY_APPVEYOR_CI") == "1" {
26-
log.Printf("Running in appveyor CI")
27-
var (
28-
cpy2dir = os.Getenv("CPYTHON2DIR")
29-
cpy3dir = os.Getenv("CPYTHON3DIR")
30-
pypy2dir = os.Getenv("PYPY2DIR")
31-
pypy3dir = os.Getenv("PYPY3DIR")
32-
)
33-
py2 = path.Join(cpy2dir, "python")
34-
py3 = path.Join(cpy3dir, "python")
35-
pypy2 = path.Join(pypy2dir, "pypy")
36-
pypy3 = path.Join(pypy3dir, "pypy")
27+
if os.Getenv("GOPY_TRAVIS_CI") == "1" {
28+
log.Printf("Running in travis CI")
3729
}
3830

3931
var (
@@ -46,23 +38,26 @@ func init() {
4638
module string
4739
mandatory bool
4840
}{
49-
// {"py2", py2, "", true},
50-
{"py2-cffi", py2, "cffi", true},
51-
// {"py3", py3, "", true},
52-
{"py3-cffi", py3, "cffi", true},
53-
{"pypy2-cffi", pypy2, "cffi", false},
54-
{"pypy3-cffi", pypy3, "cffi", false},
41+
{"py3", py3, "", true},
42+
// {"py2", py2, "", true},
5543
} {
5644
args := []string{"-c", ""}
5745
if be.module != "" {
5846
args[1] = "import " + be.module
5947
}
6048
log.Printf("checking testbackend: %q...", be.name)
61-
cmd := exec.Command(be.vm, args...)
62-
cmd.Stdin = os.Stdin
63-
cmd.Stdout = os.Stdout
64-
cmd.Stderr = os.Stderr
65-
err := cmd.Run()
49+
50+
py, err := exec.LookPath(be.vm)
51+
if err != nil {
52+
log.Printf("gopy: could not locate 'python' executable (err: %v)", err)
53+
} else {
54+
log.Printf("python executable found at: %s\n", py)
55+
cmd := exec.Command(py, args...)
56+
cmd.Stdin = os.Stdin
57+
cmd.Stdout = os.Stdout
58+
cmd.Stderr = os.Stderr
59+
err = cmd.Run()
60+
}
6661
if err != nil {
6762
log.Printf("disabling testbackend: %q, error: '%s'", be.name, err.Error())
6863
testBackends[be.name] = ""
@@ -79,8 +74,8 @@ func init() {
7974
if len(disabled) > 0 {
8075
log.Printf("The following test backends are not available: %s",
8176
strings.Join(disabled, ", "))
82-
if os.Getenv("GOPY_APPVEYOR_CI") == "1" && missing > 0 {
83-
log.Fatalf("Not all backends available in appveyor CI")
77+
if os.Getenv("GOPY_TRAVIS_CI") == "1" && missing > 0 {
78+
log.Fatalf("Not all backends available in travis CI")
8479
}
8580
}
8681
}

0 commit comments

Comments
 (0)