forked from golang/mobile
-
Notifications
You must be signed in to change notification settings - Fork 10
/
main.go
170 lines (152 loc) · 4.79 KB
/
main.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2014 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 (
"bytes"
"flag"
"fmt"
"go/ast"
"go/types"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/sagernet/gomobile/internal/importers"
"github.com/sagernet/gomobile/internal/importers/java"
"github.com/sagernet/gomobile/internal/importers/objc"
"golang.org/x/tools/go/packages"
)
var (
lang = flag.String("lang", "", "target languages for bindings, either java, go, or objc. If empty, all languages are generated.")
outdir = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
javaPkg = flag.String("javapkg", "", "custom Java package path prefix. Valid only with -lang=java.")
libname = flag.String("libname", "gojni", "custom library name. Valid only with -lang=java.")
prefix = flag.String("prefix", "", "custom Objective-C name prefix. Valid only with -lang=objc.")
bootclasspath = flag.String("bootclasspath", "", "Java bootstrap classpath.")
classpath = flag.String("classpath", "", "Java classpath.")
tags = flag.String("tags", "", "build tags.")
)
var usage = `The Gobind tool generates Java language bindings for Go.
For usage details, see doc.go.`
func main() {
flag.Parse()
run()
os.Exit(exitStatus)
}
func run() {
var langs []string
if *lang != "" {
langs = strings.Split(*lang, ",")
} else {
langs = []string{"go", "java", "objc"}
}
// We need to give appropriate environment variables like CC or CXX so that the returned packages no longer have errors.
// However, getting such environment variables is difficult or impossible so far.
// Gomobile can obtain such environment variables in env.go, but this logic assumes some condiitons gobind doesn't assume.
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedFiles |
packages.NeedImports | packages.NeedDeps |
packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo,
BuildFlags: []string{"-tags", strings.Join(strings.Split(*tags, ","), " ")},
}
// Call Load twice to warm the cache. There is a known issue that the result of Load
// depends on build cache state. See golang/go#33687.
packages.Load(cfg, flag.Args()...)
allPkg, err := packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatal(err)
}
jrefs, err := importers.AnalyzePackages(allPkg, "Java/")
if err != nil {
log.Fatal(err)
}
orefs, err := importers.AnalyzePackages(allPkg, "ObjC/")
if err != nil {
log.Fatal(err)
}
var classes []*java.Class
if len(jrefs.Refs) > 0 {
jimp := &java.Importer{
Bootclasspath: *bootclasspath,
Classpath: *classpath,
JavaPkg: *javaPkg,
}
classes, err = jimp.Import(jrefs)
if err != nil {
log.Fatal(err)
}
}
var otypes []*objc.Named
if len(orefs.Refs) > 0 {
otypes, err = objc.Import(orefs)
if err != nil {
log.Fatal(err)
}
}
if len(classes) > 0 || len(otypes) > 0 {
srcDir := *outdir
if srcDir == "" {
srcDir, err = ioutil.TempDir(os.TempDir(), "gobind-")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(srcDir)
} else {
srcDir, err = filepath.Abs(srcDir)
if err != nil {
log.Fatal(err)
}
}
if len(classes) > 0 {
if err := genJavaPackages(srcDir, classes, jrefs.Embedders); err != nil {
log.Fatal(err)
}
}
if len(otypes) > 0 {
if err := genObjcPackages(srcDir, otypes, orefs.Embedders); err != nil {
log.Fatal(err)
}
}
// Add a new directory to GOPATH where the file for reverse bindings exist, and recreate allPkg.
// It is because the current allPkg did not solve imports for reverse bindings.
var gopath string
if out, err := exec.Command("go", "env", "GOPATH").Output(); err != nil {
log.Fatal(err)
} else {
gopath = string(bytes.TrimSpace(out))
}
if gopath != "" {
gopath = string(filepath.ListSeparator) + gopath
}
gopath = srcDir + gopath
cfg.Env = append(os.Environ(), "GOPATH="+gopath)
allPkg, err = packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatal(err)
}
}
typePkgs := make([]*types.Package, len(allPkg))
astPkgs := make([][]*ast.File, len(allPkg))
for i, pkg := range allPkg {
// Ignore pkg.Errors. pkg.Errors can exist when Cgo is used, but this should not affect the result.
// See the discussion at golang/go#36547.
typePkgs[i] = pkg.Types
astPkgs[i] = pkg.Syntax
}
for _, l := range langs {
for i, pkg := range typePkgs {
genPkg(l, pkg, astPkgs[i], typePkgs, classes, otypes, *libname)
}
// Generate the error package and support files
genPkg(l, nil, nil, typePkgs, classes, otypes, *libname)
}
}
var exitStatus = 0
func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprintln(os.Stderr)
exitStatus = 1
}