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  }