@@ -316,6 +316,8 @@ build:
316316 # use pybindgen to build the %[1]s.c file which are the CPython wrappers to cgo wrappers..
317317 # note: pip install pybindgen to get pybindgen if this fails
318318 $(PYTHON) build.py
319+ # patch storage leaks in pybindgen output
320+ go run patch-leaks.go %[1]s.c
319321 # build the _%[1]s$(LIBEXT) library that contains the cgo and CPython wrappers
320322 # generated %[1]s.py python wrapper imports this c-code package
321323 $(GCC) %[1]s.c %[6]s %[1]s_go$(LIBEXT) -o _%[1]s$(LIBEXT) $(CFLAGS) $(LDFLAGS) -fPIC --shared -w
@@ -357,11 +359,91 @@ build:
357359 # use pybindgen to build the %[1]s.c file which are the CPython wrappers to cgo wrappers..
358360 # note: pip install pybindgen to get pybindgen if this fails
359361 $(PYTHON) build.py
362+ # patch storage leaks in pybindgen output
363+ go run patch-leaks.go %[1]s.c
360364 # build the executable
361365 - rm %[1]s_go$(LIBEXT)
362366 $(GOBUILD) -o py%[1]s
363367
364368`
369+
370+ patchLeaksPreamble = `// patch-leaks.go for post-processing %[1]s.
371+ // File is generated by gopy. Do not edit.
372+ // %[2]s
373+ // +build ignore
374+
375+ package main
376+
377+ import (
378+ "bufio"
379+ "bytes"
380+ "io/ioutil"
381+ "log"
382+ "os"
383+ "strings"
384+ )
385+
386+ const (
387+ cStringLine = " py_retval = Py_BuildValue((char *) \"s\", retval);"
388+ )
389+ var cstringFunctions = []string{
390+ `
391+
392+ patchLeaksPostamble = `
393+ }
394+
395+ func isCString(line string, names []string) bool {
396+ for _, cfn := range names {
397+ if strings.HasPrefix(line, cfn) {
398+ return true
399+ }
400+ }
401+ return false
402+ }
403+
404+ func patchCString(line string, out *bytes.Buffer) bool {
405+ out.WriteString(line)
406+ out.Write([]byte{'\n'})
407+ switch line {
408+ case "}":
409+ return false
410+ case cStringLine:
411+ out.WriteString(" free(retval);\n")
412+ return false
413+ }
414+ return true
415+ }
416+
417+ func main() {
418+ file := os.Args[1]
419+ buf, err := ioutil.ReadFile(file)
420+ if err != nil {
421+ log.Fatal(err)
422+ }
423+ sc := bufio.NewScanner(bytes.NewBuffer(buf))
424+ obuf := &bytes.Buffer{}
425+ var cstring bool
426+ for sc.Scan() {
427+ line := sc.Text()
428+ if cstring {
429+ cstring = patchCString(line, obuf)
430+ continue
431+ }
432+ cstring = isCString(line, cstringFunctions)
433+ obuf.WriteString(line)
434+ obuf.Write([]byte{'\n'})
435+ }
436+
437+ if err := sc.Err(); err != nil {
438+ log.Fatal(err)
439+ }
440+
441+ err = ioutil.WriteFile(file, obuf.Bytes(), 0644)
442+ if err != nil {
443+ log.Fatal(err)
444+ }
445+ }
446+ `
365447)
366448
367449// thePyGen is the current pyGen which is needed in symbols to lookup
@@ -398,6 +480,7 @@ func GenPyBind(mode BuildMode, odir, outname, cmdstr, vm, mainstr, libext, extra
398480
399481type pyGen struct {
400482 gofile * printer
483+ leakfile * printer
401484 pybuild * printer
402485 pywrap * printer
403486 makefile * printer
@@ -445,9 +528,11 @@ func (g *pyGen) genPackageMap() {
445528
446529func (g * pyGen ) genPre () {
447530 g .gofile = & printer {buf : new (bytes.Buffer ), indentEach : []byte ("\t " )}
531+ g .leakfile = & printer {buf : new (bytes.Buffer ), indentEach : []byte ("\t " )}
448532 g .pybuild = & printer {buf : new (bytes.Buffer ), indentEach : []byte ("\t " )}
449533 g .makefile = & printer {buf : new (bytes.Buffer ), indentEach : []byte ("\t " )}
450534 g .genGoPreamble ()
535+ g .genLeaksPreamble ()
451536 g .genPyBuildPreamble ()
452537 g .genMakefile ()
453538 oinit , err := os .Create (filepath .Join (g .odir , "__init__.py" ))
@@ -468,8 +553,10 @@ func (g *pyGen) genPrintOut(outfn string, pr *printer) {
468553func (g * pyGen ) genOut () {
469554 g .pybuild .Printf ("\n mod.generate(open('%v.c', 'w'))\n \n " , g .outname )
470555 g .gofile .Printf ("\n \n " )
556+ g .genLeaksPostamble ()
471557 g .makefile .Printf ("\n \n " )
472558 g .genPrintOut (g .outname + ".go" , g .gofile )
559+ g .genPrintOut ("patch-leaks.go" , g .leakfile )
473560 g .genPrintOut ("build.py" , g .pybuild )
474561 g .genPrintOut ("Makefile" , g .makefile )
475562}
@@ -537,6 +624,14 @@ func (g *pyGen) genGoPreamble() {
537624 g .gofile .Printf ("\n // --- generated code for package: %[1]s below: ---\n \n " , g .outname )
538625}
539626
627+ func (g * pyGen ) genLeaksPreamble () {
628+ g .leakfile .Printf (patchLeaksPreamble , g .outname , g .cmdstr )
629+ }
630+
631+ func (g * pyGen ) genLeaksPostamble () {
632+ g .leakfile .Printf (patchLeaksPostamble )
633+ }
634+
540635func (g * pyGen ) genPyBuildPreamble () {
541636 g .pybuild .Printf (PyBuildPreamble , g .outname , g .cmdstr )
542637}
0 commit comments