CL 99316 changed gobind to be platform independent, so standalone bindings could be generated without having the Android and Xcode SDKs installed. However, bindings that does depend on GOOS for its exported API, in particular go source files that use Cgo now only works if the exported API is extracted to platform independent files. By switching to use the source importer, importer.For("source", nil), gobind can type check the bound packages even in the presence of Cgo. The source importer in Go 1.9 and 1.10 has problems with relative imports and imports from testdata directories (issues 23092 and 24392), but works from Go 1.10.1 on. Fixes golang/go#24856 Change-Id: Icb18dce15325b7d4e58cabc1181051bc6269fc1f Reviewed-on: https://go-review.googlesource.com/99777 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
200 lines
4.8 KiB
Go
200 lines
4.8 KiB
Go
// Copyright 2015 The Go 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"
|
|
"go/build"
|
|
"io"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
)
|
|
|
|
func goIOSBind(gobind string, pkgs []*build.Package, archs []string) error {
|
|
// Run gobind to generate the bindings
|
|
cmd := exec.Command(
|
|
gobind,
|
|
"-lang=go,objc",
|
|
"-outdir="+tmpdir,
|
|
)
|
|
cmd.Env = append(cmd.Env, "GOOS=darwin")
|
|
if len(ctx.BuildTags) > 0 {
|
|
cmd.Args = append(cmd.Args, "-tags="+strings.Join(ctx.BuildTags, ","))
|
|
}
|
|
if bindPrefix != "" {
|
|
cmd.Args = append(cmd.Args, "-prefix="+bindPrefix)
|
|
}
|
|
for _, p := range pkgs {
|
|
cmd.Args = append(cmd.Args, p.ImportPath)
|
|
}
|
|
if err := runCmd(cmd); err != nil {
|
|
return err
|
|
}
|
|
|
|
srcDir := filepath.Join(tmpdir, "src", "gobind")
|
|
gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
|
|
|
|
name := pkgs[0].Name
|
|
title := strings.Title(name)
|
|
|
|
if buildO != "" && !strings.HasSuffix(buildO, ".framework") {
|
|
return fmt.Errorf("static framework name %q missing .framework suffix", buildO)
|
|
}
|
|
if buildO == "" {
|
|
buildO = title + ".framework"
|
|
}
|
|
|
|
fileBases := make([]string, len(pkgs)+1)
|
|
for i, pkg := range pkgs {
|
|
fileBases[i] = bindPrefix + strings.Title(pkg.Name)
|
|
}
|
|
fileBases[len(fileBases)-1] = "universe"
|
|
|
|
cmd = exec.Command("xcrun", "lipo", "-create")
|
|
|
|
for _, arch := range archs {
|
|
env := darwinEnv[arch]
|
|
env = append(env, gopath)
|
|
path, err := goIOSBindArchive(name, env)
|
|
if err != nil {
|
|
return fmt.Errorf("darwin-%s: %v", arch, err)
|
|
}
|
|
cmd.Args = append(cmd.Args, "-arch", archClang(arch), path)
|
|
}
|
|
|
|
// Build static framework output directory.
|
|
if err := removeAll(buildO); err != nil {
|
|
return err
|
|
}
|
|
headers := buildO + "/Versions/A/Headers"
|
|
if err := mkdir(headers); err != nil {
|
|
return err
|
|
}
|
|
if err := symlink("A", buildO+"/Versions/Current"); err != nil {
|
|
return err
|
|
}
|
|
if err := symlink("Versions/Current/Headers", buildO+"/Headers"); err != nil {
|
|
return err
|
|
}
|
|
if err := symlink("Versions/Current/"+title, buildO+"/"+title); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd.Args = append(cmd.Args, "-o", buildO+"/Versions/A/"+title)
|
|
if err := runCmd(cmd); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Copy header file next to output archive.
|
|
headerFiles := make([]string, len(fileBases))
|
|
if len(fileBases) == 1 {
|
|
headerFiles[0] = title + ".h"
|
|
err := copyFile(
|
|
headers+"/"+title+".h",
|
|
srcDir+"/"+bindPrefix+title+".objc.h",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
for i, fileBase := range fileBases {
|
|
headerFiles[i] = fileBase + ".objc.h"
|
|
err := copyFile(
|
|
headers+"/"+fileBase+".objc.h",
|
|
srcDir+"/"+fileBase+".objc.h")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
err := copyFile(
|
|
headers+"/ref.h",
|
|
srcDir+"/ref.h")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
headerFiles = append(headerFiles, title+".h")
|
|
err = writeFile(headers+"/"+title+".h", func(w io.Writer) error {
|
|
return iosBindHeaderTmpl.Execute(w, map[string]interface{}{
|
|
"pkgs": pkgs, "title": title, "bases": fileBases,
|
|
})
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
resources := buildO + "/Versions/A/Resources"
|
|
if err := mkdir(resources); err != nil {
|
|
return err
|
|
}
|
|
if err := symlink("Versions/Current/Resources", buildO+"/Resources"); err != nil {
|
|
return err
|
|
}
|
|
err := writeFile(buildO+"/Resources/Info.plist", func(w io.Writer) error {
|
|
_, err := w.Write([]byte(iosBindInfoPlist))
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var mmVals = struct {
|
|
Module string
|
|
Headers []string
|
|
}{
|
|
Module: title,
|
|
Headers: headerFiles,
|
|
}
|
|
err = writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error {
|
|
return iosModuleMapTmpl.Execute(w, mmVals)
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return symlink("Versions/Current/Modules", buildO+"/Modules")
|
|
}
|
|
|
|
const iosBindInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
</dict>
|
|
</plist>
|
|
`
|
|
|
|
var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" {
|
|
header "ref.h"
|
|
{{range .Headers}} header "{{.}}"
|
|
{{end}}
|
|
export *
|
|
}`))
|
|
|
|
func goIOSBindArchive(name string, env []string) (string, error) {
|
|
arch := getenv(env, "GOARCH")
|
|
archive := filepath.Join(tmpdir, name+"-"+arch+".a")
|
|
err := goBuild("gobind", env, "-buildmode=c-archive", "-o", archive)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return archive, nil
|
|
}
|
|
|
|
var iosBindHeaderTmpl = template.Must(template.New("ios.h").Parse(`
|
|
// Objective-C API for talking to the following Go packages
|
|
//
|
|
{{range .pkgs}}// {{.ImportPath}}
|
|
{{end}}//
|
|
// File is generated by gomobile bind. Do not edit.
|
|
#ifndef __{{.title}}_FRAMEWORK_H__
|
|
#define __{{.title}}_FRAMEWORK_H__
|
|
|
|
{{range .bases}}#include "{{.}}.objc.h"
|
|
{{end}}
|
|
#endif
|
|
`))
|