diff --git a/README.md b/README.md index bb30320..170e9e1 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,18 @@ To [install python modules](https://packaging.python.org/tutorials/packaging-pro python3 -m pip install --upgrade setuptools wheel ``` +### Windows + +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. + +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. + +The standard python install does not create a `python3.exe` which gopy looks for -- follow instructions here: +https://stackoverflow.com/questions/39910730/python3-is-not-recognized-as-an-internal-or-external-command-operable-program/41492852 +(just make a copy of python.exe to python3.exe in the relevant installed location). + +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). + ## Community The `go-python` community can be reached out at [go-python@googlegroups.com](mailto:go-python@googlegroups.com) or via the web forum: [go-python group](https://groups.google.com/forum/#!forum/go-python). diff --git a/bind/gen.go b/bind/gen.go index f0b41ee..d8c8393 100644 --- a/bind/gen.go +++ b/bind/gen.go @@ -33,6 +33,9 @@ const ( ModePkg = "pkg" ) +// set this to true if OS is windows +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 @@ -332,7 +335,8 @@ def Init(): ` - // 3 = gencmd, 4 = vm, 5 = libext 6 = extraGccArgs + // 3 = gencmd, 4 = vm, 5 = libext 6 = extraGccArgs, 7 = CFLAGS, 8 = LDLFAGS, + // 9 = windows special declspec hack MakefileTemplate = `# Makefile for python interface for package %[1]s. # File is generated by gopy. Do not edit. # %[2]s @@ -366,6 +370,7 @@ build: $(PYTHON) build.py # build the _%[1]s$(LIBEXT) library that contains the cgo and CPython wrappers # generated %[1]s.py python wrapper imports this c-code package + %[9]s $(GCC) %[1]s.c %[6]s %[1]s_go$(LIBEXT) -o _%[1]s$(LIBEXT) $(CFLAGS) $(LDFLAGS) -fPIC --shared -w ` @@ -695,7 +700,12 @@ 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) } else { - g.makefile.Printf(MakefileTemplate, g.cfg.Name, g.cfg.Cmd, gencmd, g.cfg.VM, g.libext, g.extraGccArgs, pycfg.CFlags, pycfg.LdFlags) + 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) } } diff --git a/bind/utils.go b/bind/utils.go index 41f2992..a78fe4f 100644 --- a/bind/utils.go +++ b/bind/utils.go @@ -110,6 +110,7 @@ version=sys.version_info.major if "GOPY_INCLUDE" in os.environ and "GOPY_LIBDIR" in os.environ and "GOPY_PYLIB" in os.environ: print(json.dumps({ "version": version, + "minor": sys.version_info.minor, "incdir": os.environ["GOPY_INCLUDE"], "libdir": os.environ["GOPY_LIBDIR"], "libpy": os.environ["GOPY_PYLIB"], @@ -121,6 +122,7 @@ if "GOPY_INCLUDE" in os.environ and "GOPY_LIBDIR" in os.environ and "GOPY_PYLIB" else: print(json.dumps({ "version": sys.version_info.major, + "minor": sys.version_info.minor, "incdir": ds.get_python_inc(), "libdir": ds.get_config_var("LIBDIR"), "libpy": ds.get_config_var("LIBRARY"), @@ -149,6 +151,7 @@ else: var raw struct { Version int `json:"version"` + Minor int `json:"minor"` IncDir string `json:"incdir"` LibDir string `json:"libdir"` LibPy string `json:"libpy"` @@ -164,6 +167,20 @@ else: raw.IncDir = filepath.ToSlash(raw.IncDir) raw.LibDir = filepath.ToSlash(raw.LibDir) + // on windows these can be empty -- use include dir which is usu good + if raw.LibDir == "" && raw.IncDir != "" { + raw.LibDir = raw.IncDir + if strings.HasSuffix(raw.LibDir, "include") { + raw.LibDir = raw.LibDir[:len(raw.LibDir)-len("include")] + "libs" + } + fmt.Printf("no LibDir -- copy from IncDir: %s\n", raw.LibDir) + } + + if raw.LibPy == "" { + raw.LibPy = fmt.Sprintf("python%d%d", raw.Version, raw.Minor) + fmt.Printf("no LibPy -- set to: %s\n", raw.LibPy) + } + if strings.HasSuffix(raw.LibPy, ".a") { raw.LibPy = raw.LibPy[:len(raw.LibPy)-len(".a")] } diff --git a/cmd_build.go b/cmd_build.go index 320e791..f23df8c 100644 --- a/cmd_build.go +++ b/cmd_build.go @@ -197,6 +197,16 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error { return err } + if bind.WindowsOS { + fmt.Printf("Doing windows sed hack to fix declspec for PyInit\n") + cmd = exec.Command("sed", "-i", "s/ PyInit_/ __declspec(dllexport) PyInit_/g", cfg.Name+".c") + cmdout, err = cmd.CombinedOutput() + if err != nil { + fmt.Printf("cmd had error: %v output:\no%v\n", err, string(cmdout)) + return err + } + } + cflags := strings.Fields(strings.TrimSpace(pycfg.CFlags)) cflags = append(cflags, "-fPIC", "-Ofast") if include, exists := os.LookupEnv("GOPY_INCLUDE"); exists { diff --git a/main_test.go b/main_test.go index 8837ab8..db1061a 100644 --- a/main_test.go +++ b/main_test.go @@ -937,7 +937,7 @@ func writeGoMod(t *testing.T, pkgDir, tstDir string) { module dummy require github.com/go-python/gopy v0.0.0 -replace github.com/go-python/gopy => "%s" +replace github.com/go-python/gopy => %s ` contents := fmt.Sprintf(template, pkgDir) if err := ioutil.WriteFile(filepath.Join(tstDir, "go.mod"), []byte(contents), 0666); err != nil { diff --git a/main_windows.go b/main_windows.go index f844afc..9800b09 100644 --- a/main_windows.go +++ b/main_windows.go @@ -2,11 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build windows // +build windows package main +import "github.com/go-python/gopy/bind" + const ( - libExt = ".dll" + libExt = ".pyd" extraGccArgs = "" ) + +func init() { + bind.WindowsOS = true +} diff --git a/main_windows_test.go b/main_windows_test.go index dc6e45d..07aac46 100644 --- a/main_windows_test.go +++ b/main_windows_test.go @@ -1,7 +1,7 @@ // Copyright 2015 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. - +//go:build windows // +build windows package main @@ -10,30 +10,22 @@ import ( "log" "os" "os/exec" - "path" "strings" ) func init() { + + testEnvironment = os.Environ() + var ( - py2 = "python2" - py3 = "python3" - pypy2 = "pypy" - pypy3 = "pypy3" + // py2 = "python2" + py3 = "python3" + // pypy2 = "pypy" + // pypy3 = "pypy3" ) - if os.Getenv("GOPY_APPVEYOR_CI") == "1" { - log.Printf("Running in appveyor CI") - var ( - cpy2dir = os.Getenv("CPYTHON2DIR") - cpy3dir = os.Getenv("CPYTHON3DIR") - pypy2dir = os.Getenv("PYPY2DIR") - pypy3dir = os.Getenv("PYPY3DIR") - ) - py2 = path.Join(cpy2dir, "python") - py3 = path.Join(cpy3dir, "python") - pypy2 = path.Join(pypy2dir, "pypy") - pypy3 = path.Join(pypy3dir, "pypy") + if os.Getenv("GOPY_TRAVIS_CI") == "1" { + log.Printf("Running in travis CI") } var ( @@ -46,23 +38,26 @@ func init() { module string mandatory bool }{ - // {"py2", py2, "", true}, - {"py2-cffi", py2, "cffi", true}, - // {"py3", py3, "", true}, - {"py3-cffi", py3, "cffi", true}, - {"pypy2-cffi", pypy2, "cffi", false}, - {"pypy3-cffi", pypy3, "cffi", false}, + {"py3", py3, "", true}, + // {"py2", py2, "", true}, } { args := []string{"-c", ""} if be.module != "" { args[1] = "import " + be.module } log.Printf("checking testbackend: %q...", be.name) - cmd := exec.Command(be.vm, args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() + + py, err := exec.LookPath(be.vm) + if err != nil { + log.Printf("gopy: could not locate 'python' executable (err: %v)", err) + } else { + log.Printf("python executable found at: %s\n", py) + cmd := exec.Command(py, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + } if err != nil { log.Printf("disabling testbackend: %q, error: '%s'", be.name, err.Error()) testBackends[be.name] = "" @@ -79,8 +74,8 @@ func init() { if len(disabled) > 0 { log.Printf("The following test backends are not available: %s", strings.Join(disabled, ", ")) - if os.Getenv("GOPY_APPVEYOR_CI") == "1" && missing > 0 { - log.Fatalf("Not all backends available in appveyor CI") + if os.Getenv("GOPY_TRAVIS_CI") == "1" && missing > 0 { + log.Fatalf("Not all backends available in travis CI") } } }