forked from go-python/gopy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd_exe.go
135 lines (114 loc) · 5.02 KB
/
cmd_exe.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 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.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/go-python/gopy/bind"
"github.com/gonuts/commander"
"github.com/gonuts/flag"
)
// python packaging links:
// https://pypi.org/
// https://packaging.python.org/tutorials/packaging-projects/
// https://docs.python.org/3/tutorial/modules.html
func gopyMakeCmdExe() *commander.Command {
cmd := &commander.Command{
Run: gopyRunCmdExe,
UsageLine: "exe <go-package-name> [other-go-package...]",
Short: "generate and compile (C)Python language bindings for Go, and make a standalone python executable with all the code -- must provide suitable main function code",
Long: `
exe generates and compiles (C)Python language bindings for a Go package, including subdirectories, and generates a standalone python executable and associated module packaging suitable for distribution. if setup.py file does not yet exist in the target directory, then it along with other default packaging files are created, using arguments. Typically you create initial default versions of these files and then edit them, and after that, only regenerate the go binding files.
The primary need for an exe instead of a pkg dynamic library is when the main thread must be used for something other than running the python interpreter, such as for a GUI library where the main thread must be used for running the GUI event loop (e.g., GoGi).
When including multiple packages, list in order of increasing dependency, and use -name arg to give appropriate name.
ex:
$ gopy exe [options] <go-package-name> [other-go-package...]
$ gopy exe github.com/go-python/gopy/_examples/hi
`,
Flag: *flag.NewFlagSet("gopy-exe", flag.ExitOnError),
}
cmd.Flag.String("vm", "python", "path to python interpreter")
cmd.Flag.String("output", "", "output directory for root of package")
cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)")
cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library -- defaults to GoPyMainRun() but typically should be overriden")
cmd.Flag.Bool("symbols", true, "include symbols in output")
cmd.Flag.String("exclude", "", "comma-separated list of package names to exclude")
cmd.Flag.String("user", "", "username on https://www.pypa.io/en/latest/ for package name suffix")
cmd.Flag.String("version", "0.1.0", "semantic version number -- can use e.g., git to get this from tag and pass as argument")
cmd.Flag.String("author", "gopy", "author name")
cmd.Flag.String("email", "gopy@example.com", "author email")
cmd.Flag.String("desc", "", "short description of project (long comes from README.md)")
cmd.Flag.String("url", "https://github.com/go-python/gopy", "home page for project")
cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected")
return cmd
}
func gopyRunCmdExe(cmdr *commander.Command, args []string) error {
if len(args) == 0 {
err := fmt.Errorf("gopy: expect a fully qualified go package name as argument")
log.Println(err)
return err
}
var (
odir = cmdr.Flag.Lookup("output").Value.Get().(string)
name = cmdr.Flag.Lookup("name").Value.Get().(string)
mainstr = cmdr.Flag.Lookup("main").Value.Get().(string)
vm = cmdr.Flag.Lookup("vm").Value.Get().(string)
symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool)
exclude = cmdr.Flag.Lookup("exclude").Value.Get().(string)
user = cmdr.Flag.Lookup("user").Value.Get().(string)
version = cmdr.Flag.Lookup("version").Value.Get().(string)
author = cmdr.Flag.Lookup("author").Value.Get().(string)
email = cmdr.Flag.Lookup("email").Value.Get().(string)
desc = cmdr.Flag.Lookup("desc").Value.Get().(string)
url = cmdr.Flag.Lookup("url").Value.Get().(string)
nowarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool)
)
cmdstr := argStr()
bind.NoWarn = nowarn
if name == "" {
path := args[0]
_, name = filepath.Split(path)
}
var err error
odir, err = genOutDir(odir)
if err != nil {
return err
}
setupfn := filepath.Join(odir, "setup.py")
if _, err = os.Stat(setupfn); os.IsNotExist(err) {
err = GenPyPkgSetup(odir, name, cmdstr, user, version, author, email, desc, url, vm)
if err != nil {
return err
}
}
defex := []string{"testdata", "internal", "python", "examples", "cmd"}
excl := append(strings.Split(exclude, ","), defex...)
exmap := make(map[string]struct{})
for i := range excl {
ex := strings.TrimSpace(excl[i])
exmap[ex] = struct{}{}
}
odir = filepath.Join(odir, name) // package must be in subdir
odir, err = genOutDir(odir)
if err != nil {
return err
}
for _, path := range args {
var rootdir string
var err error
if strings.HasPrefix(path, "./") {
rootdir = path
} else {
rootdir, err = GoSrcDir(path)
}
if err != nil {
return err
}
buildPkgRecurse(odir, path, rootdir, rootdir, exmap)
}
return runBuild(bind.ModeExe, odir, name, cmdstr, vm, mainstr, symbols)
}