|
|
@@ -19,6 +19,7 @@ package compiler
|
|
|
import (
|
|
|
"bytes"
|
|
|
"encoding/json"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"io/ioutil"
|
|
|
"os"
|
|
|
@@ -110,95 +111,82 @@ func (sol *Solidity) Version() string {
|
|
|
return sol.version
|
|
|
}
|
|
|
|
|
|
-func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) {
|
|
|
-
|
|
|
+// Compile builds and returns all the contracts contained within a source string.
|
|
|
+func (sol *Solidity) Compile(source string) (map[string]*Contract, error) {
|
|
|
+ // Short circuit if no source code was specified
|
|
|
if len(source) == 0 {
|
|
|
- err = fmt.Errorf("empty source")
|
|
|
- return
|
|
|
+ return nil, errors.New("solc: empty source string")
|
|
|
}
|
|
|
-
|
|
|
+ // Create a safe place to dump compilation output
|
|
|
wd, err := ioutil.TempDir("", "solc")
|
|
|
if err != nil {
|
|
|
- return
|
|
|
+ return nil, fmt.Errorf("solc: failed to create temporary build folder: %v", err)
|
|
|
}
|
|
|
defer os.RemoveAll(wd)
|
|
|
|
|
|
- in := strings.NewReader(source)
|
|
|
- var out bytes.Buffer
|
|
|
- // cwd set to temp dir
|
|
|
+ // Assemble the compiler command, change to the temp folder and capture any errors
|
|
|
+ stderr := new(bytes.Buffer)
|
|
|
+
|
|
|
cmd := exec.Command(sol.solcPath, params...)
|
|
|
cmd.Dir = wd
|
|
|
- cmd.Stdin = in
|
|
|
- cmd.Stdout = &out
|
|
|
- err = cmd.Run()
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("solc error: %v", err)
|
|
|
- return
|
|
|
- }
|
|
|
+ cmd.Stdin = strings.NewReader(source)
|
|
|
+ cmd.Stderr = stderr
|
|
|
|
|
|
+ if err := cmd.Run(); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: %v\n%s", err, string(stderr.Bytes()))
|
|
|
+ }
|
|
|
+ // Sanity check that something was actually built
|
|
|
matches, _ := filepath.Glob(wd + "/*.binary")
|
|
|
if len(matches) < 1 {
|
|
|
- err = fmt.Errorf("solc error: missing code output")
|
|
|
- return
|
|
|
+ return nil, fmt.Errorf("solc: no build results found")
|
|
|
}
|
|
|
-
|
|
|
- contracts = make(map[string]*Contract)
|
|
|
+ // Compilation succeeded, assemble and return the contracts
|
|
|
+ contracts := make(map[string]*Contract)
|
|
|
for _, path := range matches {
|
|
|
_, file := filepath.Split(path)
|
|
|
base := strings.Split(file, ".")[0]
|
|
|
|
|
|
- codeFile := filepath.Join(wd, base+".binary")
|
|
|
- abiDefinitionFile := filepath.Join(wd, base+".abi")
|
|
|
- userDocFile := filepath.Join(wd, base+".docuser")
|
|
|
- developerDocFile := filepath.Join(wd, base+".docdev")
|
|
|
-
|
|
|
- var code, abiDefinitionJson, userDocJson, developerDocJson []byte
|
|
|
- code, err = ioutil.ReadFile(codeFile)
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("error reading compiler output for code: %v", err)
|
|
|
- return
|
|
|
+ // Parse the individual compilation results (code binary, ABI definitions, user and dev docs)
|
|
|
+ var binary []byte
|
|
|
+ if binary, err = ioutil.ReadFile(filepath.Join(wd, base+".binary")); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error reading compiler output for code: %v", err)
|
|
|
}
|
|
|
- abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile)
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err)
|
|
|
- return
|
|
|
- }
|
|
|
- var abiDefinition interface{}
|
|
|
- err = json.Unmarshal(abiDefinitionJson, &abiDefinition)
|
|
|
|
|
|
- userDocJson, err = ioutil.ReadFile(userDocFile)
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("error reading compiler output for userDoc: %v", err)
|
|
|
- return
|
|
|
+ var abi interface{}
|
|
|
+ if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".abi")); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error reading abi definition: %v", err)
|
|
|
+ } else if err = json.Unmarshal(blob, &abi); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error parsing abi definition: %v", err)
|
|
|
}
|
|
|
- var userDoc interface{}
|
|
|
- err = json.Unmarshal(userDocJson, &userDoc)
|
|
|
|
|
|
- developerDocJson, err = ioutil.ReadFile(developerDocFile)
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("error reading compiler output for developerDoc: %v", err)
|
|
|
- return
|
|
|
+ var userdoc interface{}
|
|
|
+ if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docuser")); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error reading user doc: %v", err)
|
|
|
+ } else if err = json.Unmarshal(blob, &userdoc); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error parsing user doc: %v", err)
|
|
|
}
|
|
|
- var developerDoc interface{}
|
|
|
- err = json.Unmarshal(developerDocJson, &developerDoc)
|
|
|
|
|
|
- contract := &Contract{
|
|
|
- Code: "0x" + string(code),
|
|
|
+ var devdoc interface{}
|
|
|
+ if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docdev")); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
|
|
|
+ } else if err = json.Unmarshal(blob, &devdoc); err != nil {
|
|
|
+ return nil, fmt.Errorf("solc: error parsing dev doc: %v", err)
|
|
|
+ }
|
|
|
+ // Assemble the final contract
|
|
|
+ contracts[base] = &Contract{
|
|
|
+ Code: "0x" + string(binary),
|
|
|
Info: ContractInfo{
|
|
|
Source: source,
|
|
|
Language: "Solidity",
|
|
|
LanguageVersion: languageVersion,
|
|
|
CompilerVersion: sol.version,
|
|
|
- AbiDefinition: abiDefinition,
|
|
|
- UserDoc: userDoc,
|
|
|
- DeveloperDoc: developerDoc,
|
|
|
+ AbiDefinition: abi,
|
|
|
+ UserDoc: userdoc,
|
|
|
+ DeveloperDoc: devdoc,
|
|
|
},
|
|
|
}
|
|
|
-
|
|
|
- contracts[base] = contract
|
|
|
}
|
|
|
-
|
|
|
- return
|
|
|
+ return contracts, nil
|
|
|
}
|
|
|
|
|
|
func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
|