Skip to content

Commit 2206061

Browse files
rjl493456442gballet
authored andcommitted
cmd/abigen: refactor command line interface (#19797)
* cmd, common: refactor abigen command line interface * cmd/abigen: address comment
1 parent cdfe9a3 commit 2206061

File tree

3 files changed

+171
-103
lines changed

3 files changed

+171
-103
lines changed

cmd/abigen/main.go

Lines changed: 161 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,129 @@ package main
1818

1919
import (
2020
"encoding/json"
21-
"flag"
2221
"fmt"
2322
"io/ioutil"
2423
"os"
2524
"strings"
2625

2726
"github.com/ethereum/go-ethereum/accounts/abi/bind"
27+
"github.com/ethereum/go-ethereum/cmd/utils"
2828
"github.com/ethereum/go-ethereum/common/compiler"
2929
"github.com/ethereum/go-ethereum/crypto"
30+
"github.com/ethereum/go-ethereum/log"
31+
"gopkg.in/urfave/cli.v1"
3032
)
3133

32-
var (
33-
abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind, - for STDIN")
34-
binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)")
35-
typFlag = flag.String("type", "", "Struct name for the binding (default = package name)")
34+
const (
35+
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
36+
{{if .Description}}{{.Description}}
37+
{{end}}{{if .Subcommands}}
38+
SUBCOMMANDS:
39+
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
40+
{{end}}{{end}}{{if .Flags}}
41+
OPTIONS:
42+
{{range $.Flags}}{{"\t"}}{{.}}
43+
{{end}}
44+
{{end}}`
45+
)
3646

37-
solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind")
38-
solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
39-
excFlag = flag.String("exc", "", "Comma separated types to exclude from binding")
47+
var (
48+
// Git SHA1 commit hash of the release (set via linker flags)
49+
gitCommit = ""
50+
gitDate = ""
4051

41-
vyFlag = flag.String("vy", "", "Path to the Ethereum contract Vyper source to build and bind")
42-
vyperFlag = flag.String("vyper", "vyper", "Vyper compiler to use if source builds are requested")
52+
app *cli.App
4353

44-
pkgFlag = flag.String("pkg", "", "Package name to generate the binding into")
45-
outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
46-
langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)")
54+
// Flags needed by abigen
55+
abiFlag = cli.StringFlag{
56+
Name: "abi",
57+
Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
58+
}
59+
binFlag = cli.StringFlag{
60+
Name: "bin",
61+
Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
62+
}
63+
typeFlag = cli.StringFlag{
64+
Name: "type",
65+
Usage: "Struct name for the binding (default = package name)",
66+
}
67+
jsonFlag = cli.StringFlag{
68+
Name: "combined-json",
69+
Usage: "Path to the combined-json file generated by compiler",
70+
}
71+
solFlag = cli.StringFlag{
72+
Name: "sol",
73+
Usage: "Path to the Ethereum contract Solidity source to build and bind",
74+
}
75+
solcFlag = cli.StringFlag{
76+
Name: "solc",
77+
Usage: "Solidity compiler to use if source builds are requested",
78+
Value: "solc",
79+
}
80+
vyFlag = cli.StringFlag{
81+
Name: "vy",
82+
Usage: "Path to the Ethereum contract Vyper source to build and bind",
83+
}
84+
vyperFlag = cli.StringFlag{
85+
Name: "vyper",
86+
Usage: "Vyper compiler to use if source builds are requested",
87+
Value: "vyper",
88+
}
89+
excFlag = cli.StringFlag{
90+
Name: "exc",
91+
Usage: "Comma separated types to exclude from binding",
92+
}
93+
pkgFlag = cli.StringFlag{
94+
Name: "pkg",
95+
Usage: "Package name to generate the binding into",
96+
}
97+
outFlag = cli.StringFlag{
98+
Name: "out",
99+
Usage: "Output file for the generated binding (default = stdout)",
100+
}
101+
langFlag = cli.StringFlag{
102+
Name: "lang",
103+
Usage: "Destination language for the bindings (go, java, objc)",
104+
Value: "go",
105+
}
47106
)
48107

49-
func main() {
50-
// Parse and ensure all needed inputs are specified
51-
flag.Parse()
108+
func init() {
109+
app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
110+
app.Flags = []cli.Flag{
111+
abiFlag,
112+
binFlag,
113+
typeFlag,
114+
jsonFlag,
115+
solFlag,
116+
solcFlag,
117+
vyFlag,
118+
vyperFlag,
119+
excFlag,
120+
pkgFlag,
121+
outFlag,
122+
langFlag,
123+
}
124+
app.Action = utils.MigrateFlags(abigen)
125+
cli.CommandHelpTemplate = commandHelperTemplate
126+
}
52127

53-
if *abiFlag == "" && *solFlag == "" && *vyFlag == "" {
54-
fmt.Printf("No contract ABI (--abi), Solidity source (--sol), or Vyper source (--vy) specified\n")
55-
os.Exit(-1)
56-
} else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && (*solFlag != "" || *vyFlag != "") {
57-
fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity (--sol) and Vyper (--vy) flags\n")
58-
os.Exit(-1)
59-
} else if *solFlag != "" && *vyFlag != "" {
60-
fmt.Printf("Solidity (--sol) and Vyper (--vy) flags are mutually exclusive\n")
61-
os.Exit(-1)
62-
}
63-
if *pkgFlag == "" {
64-
fmt.Printf("No destination package specified (--pkg)\n")
65-
os.Exit(-1)
128+
func abigen(c *cli.Context) error {
129+
utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
130+
if c.GlobalString(pkgFlag.Name) == "" {
131+
utils.Fatalf("No destination package specified (--pkg)")
66132
}
67133
var lang bind.Lang
68-
switch *langFlag {
134+
switch c.GlobalString(langFlag.Name) {
69135
case "go":
70136
lang = bind.LangGo
71137
case "java":
72138
lang = bind.LangJava
73139
case "objc":
74140
lang = bind.LangObjC
141+
utils.Fatalf("Objc binding generation is uncompleted")
75142
default:
76-
fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag)
77-
os.Exit(-1)
143+
utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
78144
}
79145
// If the entire solidity code was specified, build and bind based on that
80146
var (
@@ -84,34 +150,67 @@ func main() {
84150
sigs []map[string]string
85151
libs = make(map[string]string)
86152
)
87-
if *solFlag != "" || *vyFlag != "" || *abiFlag == "-" {
153+
if c.GlobalString(abiFlag.Name) != "" {
154+
// Load up the ABI, optional bytecode and type name from the parameters
155+
var (
156+
abi []byte
157+
err error
158+
)
159+
input := c.GlobalString(abiFlag.Name)
160+
if input == "-" {
161+
abi, err = ioutil.ReadAll(os.Stdin)
162+
} else {
163+
abi, err = ioutil.ReadFile(input)
164+
}
165+
if err != nil {
166+
utils.Fatalf("Failed to read input ABI: %v", err)
167+
}
168+
abis = append(abis, string(abi))
169+
170+
var bin []byte
171+
if binFile := c.GlobalString(binFlag.Name); binFile != "" {
172+
if bin, err = ioutil.ReadFile(binFile); err != nil {
173+
utils.Fatalf("Failed to read input bytecode: %v", err)
174+
}
175+
if strings.Contains(string(bin), "//") {
176+
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
177+
}
178+
}
179+
bins = append(bins, string(bin))
180+
181+
kind := c.GlobalString(typeFlag.Name)
182+
if kind == "" {
183+
kind = c.GlobalString(pkgFlag.Name)
184+
}
185+
types = append(types, kind)
186+
} else {
88187
// Generate the list of types to exclude from binding
89188
exclude := make(map[string]bool)
90-
for _, kind := range strings.Split(*excFlag, ",") {
189+
for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
91190
exclude[strings.ToLower(kind)] = true
92191
}
93-
94-
var contracts map[string]*compiler.Contract
95192
var err error
193+
var contracts map[string]*compiler.Contract
96194

97195
switch {
98-
case *solFlag != "":
99-
contracts, err = compiler.CompileSolidity(*solcFlag, *solFlag)
196+
case c.GlobalIsSet(solFlag.Name):
197+
contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
198+
if err != nil {
199+
utils.Fatalf("Failed to build Solidity contract: %v", err)
200+
}
201+
case c.GlobalIsSet(vyFlag.Name):
202+
contracts, err = compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
100203
if err != nil {
101-
fmt.Printf("Failed to build Solidity contract: %v\n", err)
102-
os.Exit(-1)
204+
utils.Fatalf("Failed to build Vyper contract: %v", err)
103205
}
104-
case *vyFlag != "":
105-
contracts, err = compiler.CompileVyper(*vyperFlag, *vyFlag)
206+
case c.GlobalIsSet(jsonFlag.Name):
207+
jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
106208
if err != nil {
107-
fmt.Printf("Failed to build Vyper contract: %v\n", err)
108-
os.Exit(-1)
209+
utils.Fatalf("Failed to read combined-json from compiler: %v", err)
109210
}
110-
default:
111-
contracts, err = contractsFromStdin()
211+
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
112212
if err != nil {
113-
fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err)
114-
os.Exit(-1)
213+
utils.Fatalf("Failed to read contract information from json output: %v", err)
115214
}
116215
}
117216
// Gather all non-excluded contract for binding
@@ -121,65 +220,39 @@ func main() {
121220
}
122221
abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
123222
if err != nil {
124-
fmt.Printf("Failed to parse ABIs from compiler output: %v\n", err)
125-
os.Exit(-1)
223+
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
126224
}
127225
abis = append(abis, string(abi))
128226
bins = append(bins, contract.Code)
129227
sigs = append(sigs, contract.Hashes)
130-
131228
nameParts := strings.Split(name, ":")
132229
types = append(types, nameParts[len(nameParts)-1])
133230

134231
libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
135232
libs[libPattern] = nameParts[len(nameParts)-1]
136233
}
137-
} else {
138-
// Otherwise load up the ABI, optional bytecode and type name from the parameters
139-
abi, err := ioutil.ReadFile(*abiFlag)
140-
141-
if err != nil {
142-
fmt.Printf("Failed to read input ABI: %v\n", err)
143-
os.Exit(-1)
144-
}
145-
abis = append(abis, string(abi))
146-
147-
var bin []byte
148-
if *binFlag != "" {
149-
if bin, err = ioutil.ReadFile(*binFlag); err != nil {
150-
fmt.Printf("Failed to read input bytecode: %v\n", err)
151-
os.Exit(-1)
152-
}
153-
}
154-
bins = append(bins, string(bin))
155-
156-
kind := *typFlag
157-
if kind == "" {
158-
kind = *pkgFlag
159-
}
160-
types = append(types, kind)
161234
}
162235
// Generate the contract binding
163-
code, err := bind.Bind(types, abis, bins, sigs, *pkgFlag, lang, libs)
236+
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs)
164237
if err != nil {
165-
fmt.Printf("Failed to generate ABI binding: %v\n", err)
166-
os.Exit(-1)
238+
utils.Fatalf("Failed to generate ABI binding: %v", err)
167239
}
168240
// Either flush it out to a file or display on the standard output
169-
if *outFlag == "" {
241+
if !c.GlobalIsSet(outFlag.Name) {
170242
fmt.Printf("%s\n", code)
171-
return
243+
return nil
172244
}
173-
if err := ioutil.WriteFile(*outFlag, []byte(code), 0600); err != nil {
174-
fmt.Printf("Failed to write ABI binding: %v\n", err)
175-
os.Exit(-1)
245+
if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
246+
utils.Fatalf("Failed to write ABI binding: %v", err)
176247
}
248+
return nil
177249
}
178250

179-
func contractsFromStdin() (map[string]*compiler.Contract, error) {
180-
bytes, err := ioutil.ReadAll(os.Stdin)
181-
if err != nil {
182-
return nil, err
251+
func main() {
252+
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
253+
254+
if err := app.Run(os.Args); err != nil {
255+
fmt.Fprintln(os.Stderr, err)
256+
os.Exit(1)
183257
}
184-
return compiler.ParseCombinedJSON(bytes, "", "", "", "")
185258
}

cmd/utils/flags.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) {
944944
// setIPC creates an IPC path configuration from the set command line flags,
945945
// returning an empty string if IPC was explicitly disabled, or the set path.
946946
func setIPC(ctx *cli.Context, cfg *node.Config) {
947-
checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
947+
CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
948948
switch {
949949
case ctx.GlobalBool(IPCDisabledFlag.Name):
950950
cfg.IPCPath = ""
@@ -1329,10 +1329,10 @@ func setWhitelist(ctx *cli.Context, cfg *eth.Config) {
13291329
}
13301330
}
13311331

1332-
// checkExclusive verifies that only a single instance of the provided flags was
1332+
// CheckExclusive verifies that only a single instance of the provided flags was
13331333
// set by the user. Each flag might optionally be followed by a string type to
13341334
// specialize it further.
1335-
func checkExclusive(ctx *cli.Context, args ...interface{}) {
1335+
func CheckExclusive(ctx *cli.Context, args ...interface{}) {
13361336
set := make([]string, 0, 1)
13371337
for i := 0; i < len(args); i++ {
13381338
// Make sure the next argument is a flag and skip if not set
@@ -1386,10 +1386,10 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
13861386
// SetEthConfig applies eth-related command line flags to the config.
13871387
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
13881388
// Avoid conflicting network flags
1389-
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
1390-
checkExclusive(ctx, LightServFlag, SyncModeFlag, "light")
1389+
CheckExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
1390+
CheckExclusive(ctx, LightServFlag, SyncModeFlag, "light")
13911391
// Can't use both ephemeral unlocked and external signer
1392-
checkExclusive(ctx, DeveloperFlag, ExternalSignerFlag)
1392+
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag)
13931393
var ks *keystore.KeyStore
13941394
if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
13951395
ks = keystores[0].(*keystore.KeyStore)

common/compiler/solidity.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin
142142
if err := json.Unmarshal(combinedJSON, &output); err != nil {
143143
return nil, err
144144
}
145-
146145
// Compilation succeeded, assemble and return the contracts.
147146
contracts := make(map[string]*Contract)
148147
for name, info := range output.Contracts {
@@ -151,14 +150,10 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin
151150
if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil {
152151
return nil, fmt.Errorf("solc: error reading abi definition (%v)", err)
153152
}
154-
var userdoc interface{}
155-
if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil {
156-
return nil, fmt.Errorf("solc: error reading user doc: %v", err)
157-
}
158-
var devdoc interface{}
159-
if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil {
160-
return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
161-
}
153+
var userdoc, devdoc interface{}
154+
json.Unmarshal([]byte(info.Userdoc), &userdoc)
155+
json.Unmarshal([]byte(info.Devdoc), &devdoc)
156+
162157
contracts[name] = &Contract{
163158
Code: "0x" + info.Bin,
164159
RuntimeCode: "0x" + info.BinRuntime,

0 commit comments

Comments
 (0)