github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/common/compiler/solidity.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package compiler
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"os/exec"
    26  	"path/filepath"
    27  	"regexp"
    28  	"strings"
    29  
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/crypto"
    32  	"github.com/ethereum/go-ethereum/logger"
    33  	"github.com/ethereum/go-ethereum/logger/glog"
    34  )
    35  
    36  const (
    37  	// flair           = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015"
    38  	flair           = ""
    39  	languageVersion = "0"
    40  )
    41  
    42  var (
    43  	versionRegExp = regexp.MustCompile("[0-9]+.[0-9]+.[0-9]+")
    44  	params        = []string{
    45  		"--binary",       // Request to output the contract in binary (hexadecimal).
    46  		"file",           //
    47  		"--json-abi",     // Request to output the contract's JSON ABI interface.
    48  		"file",           //
    49  		"--natspec-user", // Request to output the contract's Natspec user documentation.
    50  		"file",           //
    51  		"--natspec-dev",  // Request to output the contract's Natspec developer documentation.
    52  		"file",
    53  		"--add-std",
    54  		"1",
    55  	}
    56  )
    57  
    58  type Contract struct {
    59  	Code string       `json:"code"`
    60  	Info ContractInfo `json:"info"`
    61  }
    62  
    63  type ContractInfo struct {
    64  	Source          string      `json:"source"`
    65  	Language        string      `json:"language"`
    66  	LanguageVersion string      `json:"languageVersion"`
    67  	CompilerVersion string      `json:"compilerVersion"`
    68  	AbiDefinition   interface{} `json:"abiDefinition"`
    69  	UserDoc         interface{} `json:"userDoc"`
    70  	DeveloperDoc    interface{} `json:"developerDoc"`
    71  }
    72  
    73  type Solidity struct {
    74  	solcPath string
    75  	version  string
    76  }
    77  
    78  func New(solcPath string) (sol *Solidity, err error) {
    79  	// set default solc
    80  	if len(solcPath) == 0 {
    81  		solcPath = "solc"
    82  	}
    83  	solcPath, err = exec.LookPath(solcPath)
    84  	if err != nil {
    85  		return
    86  	}
    87  
    88  	cmd := exec.Command(solcPath, "--version")
    89  	var out bytes.Buffer
    90  	cmd.Stdout = &out
    91  	err = cmd.Run()
    92  	if err != nil {
    93  		return
    94  	}
    95  
    96  	version := versionRegExp.FindString(out.String())
    97  	sol = &Solidity{
    98  		solcPath: solcPath,
    99  		version:  version,
   100  	}
   101  	glog.V(logger.Info).Infoln(sol.Info())
   102  	return
   103  }
   104  
   105  func (sol *Solidity) Info() string {
   106  	return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair)
   107  }
   108  
   109  func (sol *Solidity) Version() string {
   110  	return sol.version
   111  }
   112  
   113  func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) {
   114  
   115  	if len(source) == 0 {
   116  		err = fmt.Errorf("empty source")
   117  		return
   118  	}
   119  
   120  	wd, err := ioutil.TempDir("", "solc")
   121  	if err != nil {
   122  		return
   123  	}
   124  	defer os.RemoveAll(wd)
   125  
   126  	in := strings.NewReader(source)
   127  	var out bytes.Buffer
   128  	// cwd set to temp dir
   129  	cmd := exec.Command(sol.solcPath, params...)
   130  	cmd.Dir = wd
   131  	cmd.Stdin = in
   132  	cmd.Stdout = &out
   133  	err = cmd.Run()
   134  	if err != nil {
   135  		err = fmt.Errorf("solc error: %v", err)
   136  		return
   137  	}
   138  
   139  	matches, _ := filepath.Glob(wd + "/*.binary")
   140  	if len(matches) < 1 {
   141  		err = fmt.Errorf("solc error: missing code output")
   142  		return
   143  	}
   144  
   145  	contracts = make(map[string]*Contract)
   146  	for _, path := range matches {
   147  		_, file := filepath.Split(path)
   148  		base := strings.Split(file, ".")[0]
   149  
   150  		codeFile := filepath.Join(wd, base+".binary")
   151  		abiDefinitionFile := filepath.Join(wd, base+".abi")
   152  		userDocFile := filepath.Join(wd, base+".docuser")
   153  		developerDocFile := filepath.Join(wd, base+".docdev")
   154  
   155  		var code, abiDefinitionJson, userDocJson, developerDocJson []byte
   156  		code, err = ioutil.ReadFile(codeFile)
   157  		if err != nil {
   158  			err = fmt.Errorf("error reading compiler output for code: %v", err)
   159  			return
   160  		}
   161  		abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile)
   162  		if err != nil {
   163  			err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err)
   164  			return
   165  		}
   166  		var abiDefinition interface{}
   167  		err = json.Unmarshal(abiDefinitionJson, &abiDefinition)
   168  
   169  		userDocJson, err = ioutil.ReadFile(userDocFile)
   170  		if err != nil {
   171  			err = fmt.Errorf("error reading compiler output for userDoc: %v", err)
   172  			return
   173  		}
   174  		var userDoc interface{}
   175  		err = json.Unmarshal(userDocJson, &userDoc)
   176  
   177  		developerDocJson, err = ioutil.ReadFile(developerDocFile)
   178  		if err != nil {
   179  			err = fmt.Errorf("error reading compiler output for developerDoc: %v", err)
   180  			return
   181  		}
   182  		var developerDoc interface{}
   183  		err = json.Unmarshal(developerDocJson, &developerDoc)
   184  
   185  		contract := &Contract{
   186  			Code: "0x" + string(code),
   187  			Info: ContractInfo{
   188  				Source:          source,
   189  				Language:        "Solidity",
   190  				LanguageVersion: languageVersion,
   191  				CompilerVersion: sol.version,
   192  				AbiDefinition:   abiDefinition,
   193  				UserDoc:         userDoc,
   194  				DeveloperDoc:    developerDoc,
   195  			},
   196  		}
   197  
   198  		contracts[base] = contract
   199  	}
   200  
   201  	return
   202  }
   203  
   204  func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
   205  	infojson, err := json.Marshal(info)
   206  	if err != nil {
   207  		return
   208  	}
   209  	contenthash = common.BytesToHash(crypto.Sha3(infojson))
   210  	err = ioutil.WriteFile(filename, infojson, 0600)
   211  	return
   212  }