github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/common/compiler/vyper.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 // Package compiler wraps the Solidity and Vyper compiler executables (solc; vyper). 19 package compiler 20 21 import ( 22 "bytes" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "os/exec" 27 "strconv" 28 "strings" 29 ) 30 31 // Vyper contains information about the vyper compiler. 32 type Vyper struct { 33 Path, Version, FullVersion string 34 Major, Minor, Patch int 35 } 36 37 func (s *Vyper) makeArgs() []string { 38 p := []string{ 39 "-f", "combined_json", 40 } 41 return p 42 } 43 44 // VyperVersion runs vyper and parses its version output. 45 func VyperVersion(vyper string) (*Vyper, error) { 46 if vyper == "" { 47 vyper = "vyper" 48 } 49 var out bytes.Buffer 50 cmd := exec.Command(vyper, "--version") 51 cmd.Stdout = &out 52 err := cmd.Run() 53 if err != nil { 54 return nil, err 55 } 56 matches := versionRegexp.FindStringSubmatch(out.String()) 57 if len(matches) != 4 { 58 return nil, fmt.Errorf("can't parse vyper version %q", out.String()) 59 } 60 s := &Vyper{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} 61 if s.Major, err = strconv.Atoi(matches[1]); err != nil { 62 return nil, err 63 } 64 if s.Minor, err = strconv.Atoi(matches[2]); err != nil { 65 return nil, err 66 } 67 if s.Patch, err = strconv.Atoi(matches[3]); err != nil { 68 return nil, err 69 } 70 return s, nil 71 } 72 73 // CompileVyper compiles all given Vyper source files. 74 func CompileVyper(vyper string, sourcefiles ...string) (map[string]*Contract, error) { 75 if len(sourcefiles) == 0 { 76 return nil, errors.New("vyper: no source files") 77 } 78 source, err := slurpFiles(sourcefiles) 79 if err != nil { 80 return nil, err 81 } 82 s, err := VyperVersion(vyper) 83 if err != nil { 84 return nil, err 85 } 86 args := s.makeArgs() 87 cmd := exec.Command(s.Path, append(args, sourcefiles...)...) 88 return s.run(cmd, source) 89 } 90 91 func (s *Vyper) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { 92 var stderr, stdout bytes.Buffer 93 cmd.Stderr = &stderr 94 cmd.Stdout = &stdout 95 if err := cmd.Run(); err != nil { 96 return nil, fmt.Errorf("vyper: %v\n%s", err, stderr.Bytes()) 97 } 98 99 return ParseVyperJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " ")) 100 } 101 102 // ParseVyperJSON takes the direct output of a vyper --f combined_json run and 103 // parses it into a map of string contract name to Contract structs. The 104 // provided source, language and compiler version, and compiler options are all 105 // passed through into the Contract structs. 106 // 107 // The vyper output is expected to contain ABI and source mapping. 108 // 109 // Returns an error if the JSON is malformed or missing data, or if the JSON 110 // embedded within the JSON is malformed. 111 func ParseVyperJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) { 112 var output map[string]interface{} 113 if err := json.Unmarshal(combinedJSON, &output); err != nil { 114 return nil, err 115 } 116 117 // Compilation succeeded, assemble and return the contracts. 118 contracts := make(map[string]*Contract) 119 for name, info := range output { 120 // Parse the individual compilation results. 121 if name == "version" { 122 continue 123 } 124 c := info.(map[string]interface{}) 125 126 contracts[name] = &Contract{ 127 Code: c["bytecode"].(string), 128 RuntimeCode: c["bytecode_runtime"].(string), 129 Info: ContractInfo{ 130 Source: source, 131 Language: "Vyper", 132 LanguageVersion: languageVersion, 133 CompilerVersion: compilerVersion, 134 CompilerOptions: compilerOptions, 135 SrcMap: c["source_map"], 136 SrcMapRuntime: "", 137 AbiDefinition: c["abi"], 138 UserDoc: "", 139 DeveloperDoc: "", 140 Metadata: "", 141 }, 142 } 143 } 144 return contracts, nil 145 }