common/compiler: complete reworking of compilers section

Signed-off-by: RJ Catalano <rj@monax.io>
This commit is contained in:
RJ Catalano 2017-08-16 14:43:58 -05:00
parent a38afe2815
commit 82e0dd79f4
No known key found for this signature in database
GPG key ID: D4AB109D9B5D6386
4 changed files with 543 additions and 238 deletions

View file

@ -1,6 +1,51 @@
package compiler package compiler
import (
"fmt"
"os/exec"
)
// An interface to denote a compiler. It implements two function.
// Compile takes in a slice of files and a series of functions made
// to represent flag options of the compiler and returns a Compile Return or an error.
// The function version() is instantiated upon the creation of the compiler to
// fill the compiler's struct with version details. It fails if there is an error in the parsing.
type Compiler interface { type Compiler interface {
PrepareCommand(files ...string) error Compile(files []string, flags FlagOpts) (Return, error)
Compile(flags ...func() string) (string, error) version() error
}
// Practicing inheritance, this struct gives us access to all types of returns.
// This is written to be extendable to other compilers.
type Return struct {
Error error
SolcReturn
//Enter your return struct here...e.g.
//SerpentReturn
//BambooReturn
//ViperReturn
}
// Practicing inheritance, this struct allows us to easily create a simple
// interface for interacting with our potentially various compilers.
type FlagOpts struct {
SolcFlagOpts
//Enter your FlagOpts struct here...
}
func InitCompiler(compilerName string) (Compiler, error) {
if _, err := exec.LookPath(compilerName); err != nil {
return nil, fmt.Errorf("compiler: could not find %v in PATH", compilerName)
}
switch compilerName {
case "solc":
s := &Solidity{}
if err := s.version(); err != nil {
return nil, err
} else {
return s, nil
}
default:
return nil, fmt.Errorf("compiler: currently does not support %v for compilation", compilerName)
}
} }

View file

@ -1,17 +0,0 @@
package compiler
func addStandardAbiAndBin() string {
return "abi,bin,"
}
func addDevDoc() string {
return "devdoc"
}
func addUserDoc() string {
return "userdoc"
}
func addMetadata() string {
return "metadata"
}

View file

@ -22,217 +22,253 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum/go-ethereum/common"
) )
var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`)
type Contract struct { //The following represents solidity outputs from the compiler that we're interested in
Code string `json:"code"` type SolcReturn struct {
Info ContractInfo `json:"info"` Warning string
Version string `json:"version"`
Contracts map[string]SolcItems `json:"contracts"`
} }
type ContractInfo struct { //The key return items to enable unmarshalling from the returns from the compiler
Source string `json:"source"` type SolcItems struct {
Language string `json:"language"` Bin string `json:"bin"`
LanguageVersion string `json:"languageVersion"` Abi string `json:"abi"`
CompilerVersion string `json:"compilerVersion"` DevDoc string `json:"devdoc"`
CompilerOptions string `json:"compilerOptions"` UserDoc string `json:"userdoc"`
AbiDefinition interface{} `json:"abiDefinition"`
UserDoc interface{} `json:"userDoc"`
DeveloperDoc interface{} `json:"developerDoc"`
Metadata string `json:"metadata"` Metadata string `json:"metadata"`
} }
// Custom UnmarshalJSON is needed for the sake of capturing the warnings that can pop up
// in the compiler while still maintaining the results of the compilation.
func (ret *SolcReturn) UnmarshalJSON(data []byte) (err error) {
trimmedOutput := bytes.TrimSpace(data)
jsonBeginsCertainly := bytes.Index(trimmedOutput, []byte(`{"contracts":`))
if jsonBeginsCertainly > 0 {
ret.Warning = string(trimmedOutput[:jsonBeginsCertainly])
trimmedOutput = trimmedOutput[jsonBeginsCertainly:]
}
err = json.Unmarshal(trimmedOutput, &ret)
return
}
func (ret SolcReturn) blend(other SolcReturn) (SolcReturn, error) {
for str, items := range other.Contracts {
if _, taken := ret.Contracts[str]; taken {
return SolcReturn{}, fmt.Errorf("solc: there was an issue in blending bin and sol files, please try them separately")
} else {
ret.Contracts[str] = items
}
}
return ret, nil
}
// Solidity contains information about the solidity compiler. // Solidity contains information about the solidity compiler.
type Solidity struct { type Solidity struct {
Path, Version, FullVersion string Path, Version, FullVersion string
Major, Minor, Patch int Major, Minor, Patch int
Files []string
FlagOpts SolcFlagOpts
} }
//This is a template to define our inputs for the compiler flags
type SolcFlagOpts struct { type SolcFlagOpts struct {
// (Optional) what to get in the output, can be any combination of [abi, bin, userdoc, devdoc, metadata]
// abi: application binary interface. Necessary for interaction with contracts.
// bin: binary bytecode. Necessary for creating and deploying and interacting with contracts.
// userdoc: natspec for users.
// devdoc: natspec for devs.
// metadata: contract metadata.
CombinedOutput []string
// (Optional) Direct string of library address mappings.
// Syntax: <libraryName>:<address>,<libraryName>:<address>
// Address is interpreted as a hex string optionally prefixed by 0x.
Libraries map[string]common.Address
// (Optional) Remappings, see https://solidity.readthedocs.io/en/latest/layout-of-source-files.html#use-in-actual-compilers
// Syntax: <remoteName>=<localName>
Remappings []string
// (Optional) if true, enable standard library contracts
StdLib bool
// (Optional) if true, optimizes solidity code
Optimize bool Optimize bool
CombinedJson []string // (Optional) the number of optimization runs to run on solidity
ToLink []string OptimizeRuns uint64
Version string // (Optional) For anything else we may have missed, if filled will default override other flags.
Exec string
} }
// --combined-output format func (s *Solidity) defaultFlagOpts() (f SolcFlagOpts) {
type solcOutput struct { f = SolcFlagOpts{
Contracts map[string]struct { CombinedOutput: []string{"bin", "abi", "userdoc", "devdoc"},
Bin, Abi, Devdoc, Userdoc, Metadata string StdLib: true,
Optimize: true,
} }
Version string
}
func (s *Solidity) makeArgs() []string { if s.Major >= 0 && s.Minor >= 4 && s.Patch > 6 {
p := []string{ f.CombinedOutput = append(f.CombinedOutput, "metadata")
"--combined-json", "bin,abi,userdoc,devdoc",
"--add-std", // include standard lib contracts
"--optimize", // code optimizer switched on
} }
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {
p[1] += ",metadata" return
}
return p
} }
// SolidityVersion runs solc and parses its version output. // SolidityVersion runs solc and parses its version output.
func SolidityVersion(solc string) (*Solidity, error) { func (s *Solidity) version() error {
if solc == "" {
solc = "solc"
}
var out bytes.Buffer var out bytes.Buffer
cmd := exec.Command(solc, "--version") cmd := exec.Command("solc", "--version")
cmd.Stdout = &out cmd.Stdout = &out
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return nil, err return err
} }
matches := versionRegexp.FindStringSubmatch(out.String()) matches := versionRegexp.FindStringSubmatch(out.String())
if len(matches) != 4 { if len(matches) != 4 {
return nil, fmt.Errorf("can't parse solc version %q", out.String()) return fmt.Errorf("can't parse solc version %q", out.String())
} }
s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} s = &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]}
if s.Major, err = strconv.Atoi(matches[1]); err != nil { if s.Major, err = strconv.Atoi(matches[1]); err != nil {
return nil, err return err
} }
if s.Minor, err = strconv.Atoi(matches[2]); err != nil { if s.Minor, err = strconv.Atoi(matches[2]); err != nil {
return nil, err return err
} }
if s.Patch, err = strconv.Atoi(matches[3]); err != nil { if s.Patch, err = strconv.Atoi(matches[3]); err != nil {
return nil, err return err
}
return s, nil
}
// CompileSolidityString builds and returns all the contracts contained within a source string.
func CompileSolidityString(solc, source string) (map[string]*Contract, error) {
if len(source) == 0 {
return nil, errors.New("solc: empty source string")
}
s, err := SolidityVersion(solc)
if err != nil {
return nil, err
}
args := append(s.makeArgs(), "--")
cmd := exec.Command(s.Path, append(args, "-")...)
cmd.Stdin = strings.NewReader(source)
return s.run(cmd, source)
}
// CompileSolidity compiles all given Solidity source files.
func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) {
if len(sourcefiles) == 0 {
return nil, errors.New("solc: no source files")
}
source, err := slurpFiles(sourcefiles)
if err != nil {
return nil, err
}
s, err := SolidityVersion(solc)
if err != nil {
return nil, err
}
args := append(s.makeArgs(), "--")
cmd := exec.Command(s.Path, append(args, sourcefiles...)...)
return s.run(cmd, source)
}
func (s *Solidity) GetFiles(sourcefiles ...string) error {
if len(sourcefiles) == 0 {
return errors.New("solc: no source files")
}
for _, file := range sourcefiles {
if _, err := os.Stat(file); os.IsNotExist(err) {
return fmt.Errorf("solc: could not find file %v", file)
}
s.Files = append(s.Files, file)
} }
return nil return nil
} }
/*func (s *Solidity) Compile(flags ...func() string) (string, error) { // Compiles a series of files using the solidity compiler
var command []string func (s *Solidity) Compile(files []string, flags FlagOpts) (Return, error) {
for _, flag := range flags {
command := append(command, flag()) if reflect.DeepEqual(flags.SolcFlagOpts, (SolcFlagOpts{})) {
} flags.SolcFlagOpts = s.defaultFlagOpts()
if len(s.FlagOpts.ToLink) > 0 {
command := append(command, s.linkLibraries())
} }
finalCommand := strings.Join(command, "") //check files for .bin extension for linking addresses
//separate .sol and .bin files
//link .bins separately
solFiles, binFiles, err := s.sortAndValidateFiles(files)
if err != nil {
return Return{}, err
}
exec.Command("solc", finalCommand) // assemble commands and execute
var binResults SolcReturn
if len(binFiles) > 0 {
solcExecute := flags.assembleSolcCommand(true, binFiles...)
binResults, err = s.executeSolc(solcExecute...)
if err != nil {
return Return{}, err
}
}
}*/ var solResults SolcReturn
if len(solFiles) > 0 {
solcExecute := flags.assembleSolcCommand(false, solFiles...)
solResults, err = s.executeSolc(solcExecute...)
if err != nil {
return Return{}, err
}
}
func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { // blend the two results, even if one of them is empty (more efficient this way)
ret, err := solResults.blend(binResults)
return Return{SolcReturn: ret}, err
}
func (f SolcFlagOpts) assembleSolcCommand(binary bool, files ...string) (command []string) {
stringifyLibs := func(libs map[string]common.Address) []string {
var combinedLibs []string
for x, y := range f.Libraries {
combinedLibs = append(combinedLibs, x+":"+y.String())
}
return combinedLibs
}
switch {
case f.Exec != "":
command = append(command, f.Exec)
case binary:
if len(f.Libraries) > 0 {
combinedLibs := stringifyLibs(f.Libraries)
command = append(command, "--link --libraries")
command = append(command, strings.Join(combinedLibs, ","))
}
default:
if len(f.Remappings) > 0 {
command = append(command, strings.Join(f.Remappings, " "))
}
if len(f.CombinedOutput) > 0 {
command = append(command, "--combined-json", strings.Join(f.CombinedOutput, ","))
}
if len(f.Libraries) > 0 {
combinedLibs := stringifyLibs(f.Libraries)
command = append(command, "--link --libraries")
command = append(command, strings.Join(combinedLibs, ","))
}
if f.Optimize {
command = append(command, "--optimize")
}
if f.StdLib {
command = append(command, "--std-lib")
}
if f.OptimizeRuns != 0 {
command = append(command, "--optimize-runs", strconv.FormatUint(f.OptimizeRuns, 10))
}
}
return append(command, files...)
}
// A utility function to sort .sol and .bin files into separate slices
func (s *Solidity) sortAndValidateFiles(files []string) ([]string, []string, error) {
var solFiles []string
var binFiles []string
if len(files) == 0 {
return nil, nil, errors.New("solc: no source files")
}
for _, file := range files {
if _, err := os.Stat(file); os.IsNotExist(err) {
return nil, nil, fmt.Errorf("solc: could not find file %v", file)
}
switch path.Ext(file) {
case ".sol":
solFiles = append(solFiles, file)
case ".bin":
binFiles = append(binFiles, file)
default:
return nil, nil, fmt.Errorf("solc: unexpected file extension found during compilation: %v", file)
}
}
return solFiles, binFiles, nil
}
func (s *Solidity) executeSolc(flagsAndFiles ...string) (output SolcReturn, err error) {
var stderr, stdout bytes.Buffer var stderr, stdout bytes.Buffer
cmd := exec.Command("solc", flagsAndFiles...)
cmd.Stderr = &stderr cmd.Stderr = &stderr
cmd.Stdout = &stdout cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) if err = cmd.Run(); err != nil {
} return SolcReturn{}, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes())
var output solcOutput
if err := json.Unmarshal(stdout.Bytes(), &output); err != nil {
return nil, err
} }
// Compilation succeeded, assemble and return the contracts. if err = json.Unmarshal(stdout.Bytes(), &output); err != nil {
contracts := make(map[string]*Contract) return SolcReturn{}, err
for name, info := range output.Contracts {
// Parse the individual compilation results.
var abi interface{}
if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil {
return nil, fmt.Errorf("solc: error reading abi definition (%v)", err)
} }
var userdoc interface{}
if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil {
return nil, fmt.Errorf("solc: error reading user doc: %v", err)
}
var devdoc interface{}
if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil {
return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
}
contracts[name] = &Contract{
Code: "0x" + info.Bin,
Info: ContractInfo{
Source: source,
Language: "Solidity",
LanguageVersion: s.Version,
CompilerVersion: s.Version,
CompilerOptions: strings.Join(s.makeArgs(), " "),
AbiDefinition: abi,
UserDoc: userdoc,
DeveloperDoc: devdoc,
Metadata: info.Metadata,
},
}
}
return contracts, nil
}
func (s *Solidity) linkLibraries() string { return
return "--libraries " + strings.Join(s.FlagOpts.ToLink, ",")
}
func slurpFiles(files []string) (string, error) {
var concat bytes.Buffer
for _, file := range files {
content, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}
concat.Write(content)
}
return concat.String(), nil
} }

View file

@ -17,21 +17,38 @@
package compiler package compiler
import ( import (
"os/exec" "io/ioutil"
"os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
) )
const ( const solFile = `pragma solidity >= 0.0.0;
testSource = ` contract main {
contract test { uint a;
/// @notice Will multiply ` + "`a`" + ` by 7. function f() {
function multiply(uint a) returns(uint d) { a = 1;
return a * 7;
} }
} }`
`
librarySource = ` const faultySol = `pragma solidity >= 0.0.0;
contract main {
uint a;
function f() {
a = 1;
}
`
const solNoPragma = `contract main {
uint a;
function f() {
a = 1;
}
}`
const simplyLibrarySol = `pragma solidity >=0.0.0;
library Set { library Set {
struct Data { mapping(uint => bool) flags; } struct Data { mapping(uint => bool) flags; }
function insert(Data storage self, uint value) function insert(Data storage self, uint value)
@ -62,69 +79,293 @@ library Set {
contract C { contract C {
Set.Data knownValues; Set.Data knownValues;
function register(uint value) { function register(uint value) {
require(Set.insert(knownValues, value)); if (!Set.insert(knownValues, value))
throw;
} }
}` }`
)
func skipWithoutSolc(t *testing.T) { const solFile1 = `pragma solidity >=0.0.0;
if _, err := exec.LookPath("solc"); err != nil {
library Set {
struct Data { mapping(uint => bool) flags; }
function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
returns (bool)
{
return self.flags[value];
}
}`
const solFile2 = `pragma solidity >=0.0.0;
import "set.sol";
contract C {
Set.Data knownValues;
function register(uint value) {
if (!Set.insert(knownValues, value))
throw;
}
}`
func TestSolcCompilerNormal(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err) t.Skip(err)
} }
} solc = solc.(*Solidity)
file, err := os.Create("simpleContract.sol")
func TestCompiler(t *testing.T) { defer os.Remove("simpleContract.sol")
skipWithoutSolc(t)
contracts, err := CompileSolidityString("", testSource)
if err != nil { if err != nil {
t.Fatalf("error compiling source. result %v: %v", contracts, err) t.Fatal(err)
} }
if len(contracts) != 1 { file.WriteString(solFile)
t.Errorf("one contract expected, got %d", len(contracts)) flags := SolcFlagOpts{
CombinedOutput: []string{"bin", "abi"},
} }
c, ok := contracts["test"]
if !ok {
c, ok = contracts["<stdin>:test"]
if !ok {
t.Fatal("info for contract 'test' not present in result")
}
}
if c.Code == "" {
t.Error("empty code")
}
if c.Info.Source != testSource {
t.Error("wrong source")
}
if c.Info.CompilerVersion == "" {
t.Error("empty version")
}
}
func TestCompileError(t *testing.T) { solReturn, err := solc.Compile([]string{"simpleContract.sol"}, FlagOpts{SolcFlagOpts: flags})
skipWithoutSolc(t)
contracts, err := CompileSolidityString("", testSource[4:])
if err == nil {
t.Errorf("error expected compiling source. got none. result %v", contracts)
}
t.Logf("error: %v", err)
}
func TestCompilerLinking(t *testing.T) {
skipWithoutSolc(t)
solc, err := SolidityVersion("")
if err != nil { if err != nil {
t.Fatalf("%v", err) t.Fatal(err)
} }
solc.FlagOpts.ToLink = append(solc.FlagOpts.ToLink, "Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a") if solReturn.Error != nil || solReturn.Warning != "" || len(solReturn.Contracts) != 1 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
linkedLibraries := solc.linkLibraries() }
}
testingCase1 := "--libraries Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a"
if strings.Compare(linkedLibraries, testingCase1) != 0 { func TestSolcCompilerError(t *testing.T) {
t.Errorf("expected %v, got %v", linkedLibraries, testingCase1) solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
file, err := os.Create("faultyContract.sol")
defer os.Remove("faultyContract.sol")
if err != nil {
t.Fatal(err)
}
file.WriteString(faultySol)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin", "abi"},
}
solReturn, err := solc.Compile([]string{"faultyContract.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Error == nil {
t.Fatal("Expected an error, got nil.")
}
}
func TestSolcCompilerWarning(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
file, err := os.Create("simpleContract.sol")
defer os.Remove("simpleContract.sol")
if err != nil {
t.Fatal(err)
}
file.WriteString(solNoPragma)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin", "abi"},
}
solReturn, err := solc.Compile([]string{"simpleContract.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Warning == "" {
t.Fatal("Expected a warning.")
}
}
func TestLinkingBinaries(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
file, err := os.Create("simpleLibrary.sol")
defer os.Remove("simpleLibrary.sol")
if err != nil {
t.Fatal(err)
}
file.WriteString(simplyLibrarySol)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin"},
}
solReturn, err := solc.Compile([]string{"simpleLibrary.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Error != nil || solReturn.Warning != "" || len(solReturn.Contracts) != 2 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
}
// note: When solc upgrades to 0.4.10, will need to add "simpleLibrary.sol:" to beginning of this string
flags.Libraries = map[string]common.Address{"simpleLibrary.sol:Set": common.StringToAddress("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a")}
binFile, err := os.Create("C.bin")
defer os.Remove("C.bin")
if err != nil {
t.Fatal(err)
}
binFile.WriteString(solReturn.Contracts["simpleLibrary.sol:C"].Bin)
_, err = solc.(*Solidity).Compile([]string{"./C.bin"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
output, err := ioutil.ReadFile("C.bin")
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(output), "_") {
t.Fatal("Expected binaries to link, but they did not")
}
}
func TestLinkingBinariesAndNormalCompileMixed(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
file, err := os.Create("simpleLibrary.sol")
defer os.Remove("simpleLibrary.sol")
if err != nil {
t.Fatal(err)
}
file.WriteString(simplyLibrarySol)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin"},
}
solReturn, err := solc.Compile([]string{"simpleLibrary.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Error != nil || solReturn.Warning != "" || len(solReturn.Contracts) != 2 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
}
// note: When solc upgrades to 0.4.10, will need to add "simpleLibrary.sol:" to beginning of this string
flags.Libraries = map[string]common.Address{"simpleLibrary.sol:Set": common.StringToAddress("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a")}
binFile, err := os.Create("C.bin")
defer os.Remove("C.bin")
if err != nil {
t.Fatal(err)
}
binFile.WriteString(solReturn.Contracts["simpleLibrary.sol:C"].Bin)
solOutput, err := solc.Compile([]string{"./C.bin", "simpleLibrary.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
binOutput, err := ioutil.ReadFile("C.bin")
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(binOutput), "_") {
t.Fatal("Expected binaries to link, but they did not")
}
if solOutput.Error != nil || solOutput.Warning != "" || len(solOutput.Contracts) != 2 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
}
}
func TestMultipleFilesCompiling(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
set, err := os.Create("set.sol")
defer os.Remove("set.sol")
if err != nil {
t.Fatal(err)
}
set.WriteString(solFile1)
c, err := os.Create("C.sol")
defer os.Remove("C.sol")
if err != nil {
t.Fatal(err)
}
c.WriteString(solFile2)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin", "abi"},
}
solReturn, err := solc.Compile([]string{"C.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Error != nil || solReturn.Warning != "" || len(solReturn.Contracts) != 2 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
}
}
func TestRemappings(t *testing.T) {
solc, err := InitCompiler("solc")
if err != nil {
t.Skip(err)
}
solc = solc.(*Solidity)
if err := os.MkdirAll("."+string(filepath.Separator)+"tempDir", 0777); err != nil {
t.Fatal(err)
}
defer os.RemoveAll("." + string(filepath.Separator) + "tempDir")
os.Chdir("tempDir")
set, err := os.Create("set.sol")
if err != nil {
t.Fatal(err)
}
os.Chdir("..")
set.WriteString(solFile1)
c, err := os.Create("C.sol")
defer os.Remove("C.sol")
if err != nil {
t.Fatal(err)
}
c.WriteString(solFile2)
flags := SolcFlagOpts{
CombinedOutput: []string{"bin", "abi"},
Remappings: []string{`set.sol=./tempDir/set.sol`},
}
solReturn, err := solc.Compile([]string{"C.sol"}, FlagOpts{SolcFlagOpts: flags})
if err != nil {
t.Fatal(err)
}
if solReturn.Error != nil || solReturn.Warning != "" || len(solReturn.Contracts) != 2 {
t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for errors, %v for warnings, and %v for contract items", solReturn.Error, solReturn.Warning, solReturn.Contracts)
} }
} }