2
0
mirror of synced 2025-02-24 23:38:22 +00:00
mobile/cmd/gomobile/bind_iosapp.go
Elias Naur 5bfa0d14b7 cmd/gomobile,cmd/gobind: allow per-platform bindings again
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>
2018-04-17 15:00:54 +00:00

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
`))