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 }