|
5 | 5 | package bind |
6 | 6 |
|
7 | 7 | import ( |
8 | | - "bufio" |
9 | 8 | "bytes" |
| 9 | + "encoding/json" |
10 | 10 | "fmt" |
11 | 11 | "go/types" |
12 | | - "io" |
| 12 | + "os" |
13 | 13 | "os/exec" |
14 | 14 | "regexp" |
15 | | - "sort" |
16 | 15 | "strconv" |
17 | 16 | "strings" |
| 17 | + |
| 18 | + "github.com/pkg/errors" |
18 | 19 | ) |
19 | 20 |
|
20 | 21 | func isErrorType(typ types.Type) bool { |
@@ -83,68 +84,77 @@ func isConstructor(sig *types.Signature) bool { |
83 | 84 | return false |
84 | 85 | } |
85 | 86 |
|
86 | | -// getPkgConfig returns the name of the pkg-config python's pc file |
87 | | -func getPkgConfig(vers int) (string, error) { |
88 | | - bin, err := exec.LookPath("pkg-config") |
| 87 | +type pyconfig struct { |
| 88 | + version int |
| 89 | + cflags string |
| 90 | + ldflags string |
| 91 | +} |
| 92 | + |
| 93 | +// getPythonConfig returns the needed python configuration for the given |
| 94 | +// python VM (python, python2, python3, pypy, etc...) |
| 95 | +func getPythonConfig(vm string) (pyconfig, error) { |
| 96 | + code := `import sys |
| 97 | +import distutils.sysconfig as ds |
| 98 | +import json |
| 99 | +print(json.dumps({ |
| 100 | + "version": sys.version_info.major, |
| 101 | + "incdir": ds.get_python_inc(), |
| 102 | + "libdir": ds.get_config_var("LIBDIR"), |
| 103 | + "libpy": ds.get_config_var("LIBRARY"), |
| 104 | + "shlibs": ds.get_config_var("SHLIBS"), |
| 105 | + "syslibs": ds.get_config_var("SYSLIBS"), |
| 106 | + "shlinks": ds.get_config_var("LINKFORSHARED"), |
| 107 | +})) |
| 108 | +` |
| 109 | + |
| 110 | + var cfg pyconfig |
| 111 | + bin, err := exec.LookPath(vm) |
89 | 112 | if err != nil { |
90 | | - return "", fmt.Errorf( |
91 | | - "gopy: could not locate 'pkg-config' executable (err: %v)", |
92 | | - err, |
93 | | - ) |
| 113 | + return cfg, errors.Wrapf(err, "could not locate python vm %q", vm) |
94 | 114 | } |
95 | 115 |
|
96 | | - out, err := exec.Command(bin, "--list-all").Output() |
| 116 | + buf := new(bytes.Buffer) |
| 117 | + cmd := exec.Command(bin, "-c", code) |
| 118 | + cmd.Stdin = os.Stdin |
| 119 | + cmd.Stdout = buf |
| 120 | + cmd.Stderr = os.Stderr |
| 121 | + err = cmd.Run() |
97 | 122 | if err != nil { |
98 | | - return "", fmt.Errorf( |
99 | | - "gopy: error retrieving the list of packages known to pkg-config (err: %v)", |
100 | | - err, |
101 | | - ) |
| 123 | + return cfg, errors.Wrap(err, "could not run python-config script") |
102 | 124 | } |
103 | 125 |
|
104 | | - pkgs := []string{} |
105 | | - re := regexp.MustCompile(fmt.Sprintf(`^python(\s|-|\.|)%d.*?`, vers)) |
106 | | - s := bufio.NewScanner(bytes.NewReader(out)) |
107 | | - for s.Scan() { |
108 | | - err = s.Err() |
109 | | - if err != nil { |
110 | | - if err == io.EOF { |
111 | | - err = nil |
112 | | - } |
113 | | - break |
114 | | - } |
115 | | - |
116 | | - line := s.Bytes() |
117 | | - if !bytes.HasPrefix(line, []byte("python")) { |
118 | | - continue |
119 | | - } |
120 | | - |
121 | | - if !re.Match(line) { |
122 | | - continue |
123 | | - } |
124 | | - |
125 | | - pkg := bytes.Split(line, []byte(" ")) |
126 | | - pkgs = append(pkgs, string(pkg[0])) |
| 126 | + var raw struct { |
| 127 | + Version int `json:"version"` |
| 128 | + IncDir string `json:"incdir"` |
| 129 | + LibDir string `json:"libdir"` |
| 130 | + LibPy string `json:"libpy"` |
| 131 | + ShLibs string `json:"shlibs"` |
| 132 | + SysLibs string `json:"syslibs"` |
127 | 133 | } |
128 | | - |
| 134 | + err = json.NewDecoder(buf).Decode(&raw) |
129 | 135 | if err != nil { |
130 | | - return "", fmt.Errorf( |
131 | | - "gopy: error scanning pkg-config output (err: %v)", |
132 | | - err, |
133 | | - ) |
| 136 | + return cfg, errors.Wrapf(err, "could not decode JSON script output") |
134 | 137 | } |
135 | 138 |
|
136 | | - if len(pkgs) <= 0 { |
137 | | - return "", fmt.Errorf( |
138 | | - "gopy: could not find pkg-config file (no python.pc installed?)", |
139 | | - ) |
| 139 | + if strings.HasSuffix(raw.LibPy, ".a") { |
| 140 | + raw.LibPy = raw.LibPy[:len(raw.LibPy)-len(".a")] |
| 141 | + } |
| 142 | + if strings.HasPrefix(raw.LibPy, "lib") { |
| 143 | + raw.LibPy = raw.LibPy[len("lib"):] |
140 | 144 | } |
141 | 145 |
|
142 | | - sort.Strings(pkgs) |
143 | | - |
144 | | - // FIXME(sbinet): make sure we take the latest version? |
145 | | - pkgcfg := pkgs[0] |
146 | | - |
147 | | - return pkgcfg, nil |
| 146 | + cfg.version = raw.Version |
| 147 | + cfg.cflags = strings.Join([]string{ |
| 148 | + "-I" + raw.IncDir, |
| 149 | + }, " ") |
| 150 | + cfg.ldflags = strings.Join([]string{ |
| 151 | + "-L" + raw.LibDir, |
| 152 | + "-l" + raw.LibPy, |
| 153 | + raw.ShLibs, |
| 154 | + raw.SysLibs, |
| 155 | + }, " ") |
| 156 | + |
| 157 | + return cfg, nil |
148 | 158 | } |
149 | 159 |
|
150 | 160 | func getGoVersion(version string) (int64, int64, error) { |
|
0 commit comments