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