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  }