From a38afe28157a05b981d6790c54f89c238576dcf5 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Tue, 15 Aug 2017 11:26:13 -0500 Subject: [PATCH 01/11] common/compiler,cmd/abigen: update solidity compiler with library linking options Signed-off-by: RJ Catalano --- cmd/abigen/main.go | 7 +++-- common/compiler/compiler.go | 6 ++++ common/compiler/flags.go | 17 ++++++++++ common/compiler/solidity.go | 43 ++++++++++++++++++++++++++ common/compiler/solidity_test.go | 53 ++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 common/compiler/compiler.go create mode 100644 common/compiler/flags.go diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 3a1ae6f4c3..f63b3fa7a7 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -29,9 +29,10 @@ import ( ) var ( - abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") - binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") - typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") + abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") + binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") + typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") + linkFlag = flag.String("link", "", "Library flag linker for name to addresses in the code") solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") diff --git a/common/compiler/compiler.go b/common/compiler/compiler.go new file mode 100644 index 0000000000..e95c45ae9e --- /dev/null +++ b/common/compiler/compiler.go @@ -0,0 +1,6 @@ +package compiler + +type Compiler interface { + PrepareCommand(files ...string) error + Compile(flags ...func() string) (string, error) +} diff --git a/common/compiler/flags.go b/common/compiler/flags.go new file mode 100644 index 0000000000..3c587120f4 --- /dev/null +++ b/common/compiler/flags.go @@ -0,0 +1,17 @@ +package compiler + +func addStandardAbiAndBin() string { + return "abi,bin," +} + +func addDevDoc() string { + return "devdoc" +} + +func addUserDoc() string { + return "userdoc" +} + +func addMetadata() string { + return "metadata" +} diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index abb8039896..6e69136834 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io/ioutil" + "os" "os/exec" "regexp" "strconv" @@ -52,6 +53,15 @@ type ContractInfo struct { type Solidity struct { Path, Version, FullVersion string Major, Minor, Patch int + Files []string + FlagOpts SolcFlagOpts +} + +type SolcFlagOpts struct { + Optimize bool + CombinedJson []string + ToLink []string + Version string } // --combined-output format @@ -136,6 +146,35 @@ func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, 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 +} + +/*func (s *Solidity) Compile(flags ...func() string) (string, error) { + var command []string + for _, flag := range flags { + command := append(command, flag()) + } + if len(s.FlagOpts.ToLink) > 0 { + command := append(command, s.linkLibraries()) + } + + finalCommand := strings.Join(command, "") + + exec.Command("solc", finalCommand) + +}*/ + func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) { var stderr, stdout bytes.Buffer cmd.Stderr = &stderr @@ -182,6 +221,10 @@ func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, erro return contracts, nil } +func (s *Solidity) linkLibraries() string { + return "--libraries " + strings.Join(s.FlagOpts.ToLink, ",") +} + func slurpFiles(files []string) (string, error) { var concat bytes.Buffer for _, file := range files { diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 0da3bb337e..d248720e0d 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -18,6 +18,7 @@ package compiler import ( "os/exec" + "strings" "testing" ) @@ -30,6 +31,40 @@ contract test { } } ` + librarySource = ` +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]; + } +} + +contract C { + Set.Data knownValues; + function register(uint value) { + require(Set.insert(knownValues, value)); + } +}` ) func skipWithoutSolc(t *testing.T) { @@ -75,3 +110,21 @@ func TestCompileError(t *testing.T) { } t.Logf("error: %v", err) } + +func TestCompilerLinking(t *testing.T) { + skipWithoutSolc(t) + + solc, err := SolidityVersion("") + if err != nil { + t.Fatalf("%v", err) + } + + solc.FlagOpts.ToLink = append(solc.FlagOpts.ToLink, "Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a") + + linkedLibraries := solc.linkLibraries() + + testingCase1 := "--libraries Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a" + if strings.Compare(linkedLibraries, testingCase1) != 0 { + t.Errorf("expected %v, got %v", linkedLibraries, testingCase1) + } +} From 82e0dd79f43a5ad8aa5d3d52db0ce061b7e83dcd Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Wed, 16 Aug 2017 14:43:58 -0500 Subject: [PATCH 02/11] common/compiler: complete reworking of compilers section Signed-off-by: RJ Catalano --- common/compiler/compiler.go | 49 ++++- common/compiler/flags.go | 17 -- common/compiler/solidity.go | 348 ++++++++++++++++------------- common/compiler/solidity_test.go | 367 +++++++++++++++++++++++++------ 4 files changed, 543 insertions(+), 238 deletions(-) delete mode 100644 common/compiler/flags.go diff --git a/common/compiler/compiler.go b/common/compiler/compiler.go index e95c45ae9e..8b85f00274 100644 --- a/common/compiler/compiler.go +++ b/common/compiler/compiler.go @@ -1,6 +1,51 @@ 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 { - PrepareCommand(files ...string) error - Compile(flags ...func() string) (string, error) + Compile(files []string, flags FlagOpts) (Return, 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) + } } diff --git a/common/compiler/flags.go b/common/compiler/flags.go deleted file mode 100644 index 3c587120f4..0000000000 --- a/common/compiler/flags.go +++ /dev/null @@ -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" -} diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 6e69136834..7170db8d0a 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -22,217 +22,253 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "os" "os/exec" + "path" + "reflect" "regexp" "strconv" "strings" + + "github.com/ethereum/go-ethereum/common" ) var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) -type Contract struct { - Code string `json:"code"` - Info ContractInfo `json:"info"` +//The following represents solidity outputs from the compiler that we're interested in +type SolcReturn struct { + Warning string + Version string `json:"version"` + Contracts map[string]SolcItems `json:"contracts"` } -type ContractInfo struct { - Source string `json:"source"` - Language string `json:"language"` - LanguageVersion string `json:"languageVersion"` - CompilerVersion string `json:"compilerVersion"` - CompilerOptions string `json:"compilerOptions"` - AbiDefinition interface{} `json:"abiDefinition"` - UserDoc interface{} `json:"userDoc"` - DeveloperDoc interface{} `json:"developerDoc"` - Metadata string `json:"metadata"` +//The key return items to enable unmarshalling from the returns from the compiler +type SolcItems struct { + Bin string `json:"bin"` + Abi string `json:"abi"` + DevDoc string `json:"devdoc"` + UserDoc string `json:"userdoc"` + 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. type Solidity struct { Path, Version, FullVersion string Major, Minor, Patch int - Files []string - FlagOpts SolcFlagOpts } +//This is a template to define our inputs for the compiler flags type SolcFlagOpts struct { - Optimize bool - CombinedJson []string - ToLink []string - Version string + // (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: :
,:
+ // 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: = + Remappings []string + // (Optional) if true, enable standard library contracts + StdLib bool + // (Optional) if true, optimizes solidity code + Optimize bool + // (Optional) the number of optimization runs to run on solidity + OptimizeRuns uint64 + // (Optional) For anything else we may have missed, if filled will default override other flags. + Exec string } -// --combined-output format -type solcOutput struct { - Contracts map[string]struct { - Bin, Abi, Devdoc, Userdoc, Metadata string +func (s *Solidity) defaultFlagOpts() (f SolcFlagOpts) { + f = SolcFlagOpts{ + CombinedOutput: []string{"bin", "abi", "userdoc", "devdoc"}, + StdLib: true, + Optimize: true, } - Version string -} -func (s *Solidity) makeArgs() []string { - p := []string{ - "--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 { + f.CombinedOutput = append(f.CombinedOutput, "metadata") } - if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { - p[1] += ",metadata" - } - return p + + return } // SolidityVersion runs solc and parses its version output. -func SolidityVersion(solc string) (*Solidity, error) { - if solc == "" { - solc = "solc" - } +func (s *Solidity) version() error { var out bytes.Buffer - cmd := exec.Command(solc, "--version") + cmd := exec.Command("solc", "--version") cmd.Stdout = &out err := cmd.Run() if err != nil { - return nil, err + return err } matches := versionRegexp.FindStringSubmatch(out.String()) 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 { - return nil, err + return err } 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 { - return nil, 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 err } return nil } -/*func (s *Solidity) Compile(flags ...func() string) (string, error) { - var command []string - for _, flag := range flags { - command := append(command, flag()) - } - if len(s.FlagOpts.ToLink) > 0 { - command := append(command, s.linkLibraries()) +// Compiles a series of files using the solidity compiler +func (s *Solidity) Compile(files []string, flags FlagOpts) (Return, error) { + + if reflect.DeepEqual(flags.SolcFlagOpts, (SolcFlagOpts{})) { + flags.SolcFlagOpts = s.defaultFlagOpts() } - 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 + + cmd := exec.Command("solc", flagsAndFiles...) cmd.Stderr = &stderr cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) - } - var output solcOutput - if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { - return nil, err + + if err = cmd.Run(); err != nil { + return SolcReturn{}, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) } - // Compilation succeeded, assemble and return the contracts. - contracts := make(map[string]*Contract) - 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, - }, - } + if err = json.Unmarshal(stdout.Bytes(), &output); err != nil { + return SolcReturn{}, err } - return contracts, nil -} -func (s *Solidity) linkLibraries() string { - 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 + return } diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index d248720e0d..e34d085f3e 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -17,21 +17,38 @@ package compiler import ( - "os/exec" + "io/ioutil" + "os" + "path/filepath" "strings" "testing" + + "github.com/ethereum/go-ethereum/common" ) -const ( - testSource = ` -contract test { - /// @notice Will multiply ` + "`a`" + ` by 7. - function multiply(uint a) returns(uint d) { - return a * 7; - } -} -` - librarySource = ` +const solFile = `pragma solidity >= 0.0.0; + contract main { + uint a; + function f() { + a = 1; + } + }` + +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 { struct Data { mapping(uint => bool) flags; } function insert(Data storage self, uint value) @@ -62,69 +79,293 @@ library Set { contract C { Set.Data knownValues; function register(uint value) { - require(Set.insert(knownValues, value)); + if (!Set.insert(knownValues, value)) + throw; } }` -) -func skipWithoutSolc(t *testing.T) { - if _, err := exec.LookPath("solc"); err != nil { +const solFile1 = `pragma solidity >=0.0.0; + +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) } -} - -func TestCompiler(t *testing.T) { - skipWithoutSolc(t) - - contracts, err := CompileSolidityString("", testSource) + solc = solc.(*Solidity) + file, err := os.Create("simpleContract.sol") + defer os.Remove("simpleContract.sol") if err != nil { - t.Fatalf("error compiling source. result %v: %v", contracts, err) + t.Fatal(err) } - if len(contracts) != 1 { - t.Errorf("one contract expected, got %d", len(contracts)) + file.WriteString(solFile) + flags := SolcFlagOpts{ + CombinedOutput: []string{"bin", "abi"}, } - c, ok := contracts["test"] - if !ok { - c, ok = contracts[":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) { - 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("") + solReturn, err := solc.Compile([]string{"simpleContract.sol"}, FlagOpts{SolcFlagOpts: flags}) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - solc.FlagOpts.ToLink = append(solc.FlagOpts.ToLink, "Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a") - - linkedLibraries := solc.linkLibraries() - - testingCase1 := "--libraries Set:0x692a70d2e424a56d2c6c27aa97d1a86395877b3a" - if strings.Compare(linkedLibraries, testingCase1) != 0 { - t.Errorf("expected %v, got %v", linkedLibraries, testingCase1) + 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) + } +} + +func TestSolcCompilerError(t *testing.T) { + 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) } } From b39afc674ca58efe82149d485ec4b02229f99f8e Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 12:10:33 -0500 Subject: [PATCH 03/11] common/compiler: all tests passing for new go common compiler setup Signed-off-by: RJ Catalano --- common/compiler/compiler.go | 43 ++--- common/compiler/solidity.go | 214 +++++++++------------ common/compiler/solidity_test.go | 307 +++++++++++++------------------ 3 files changed, 227 insertions(+), 337 deletions(-) diff --git a/common/compiler/compiler.go b/common/compiler/compiler.go index 8b85f00274..96276ccde0 100644 --- a/common/compiler/compiler.go +++ b/common/compiler/compiler.go @@ -1,24 +1,17 @@ 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. +// An interface to denote a version specific compiler. // 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 { - Compile(files []string, flags FlagOpts) (Return, error) + Compile(flags FlagOpts, files ...string) (Return, error) version() error } -// Practicing inheritance, this struct gives us access to all types of returns. +// This struct via embedding gives us access to all types of returns. // This is written to be extendable to other compilers. type Return struct { - Error error + Typ CompilerType SolcReturn //Enter your return struct here...e.g. //SerpentReturn @@ -26,26 +19,18 @@ type Return struct { //ViperReturn } -// Practicing inheritance, this struct allows us to easily create a simple -// interface for interacting with our potentially various compilers. +// 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) - } -} +type CompilerType byte + +const ( + Solc CompilerType = iota + // Serpent + // Bamboo + // Viper +) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index 7170db8d0a..d829a92b2e 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -22,9 +22,7 @@ import ( "encoding/json" "errors" "fmt" - "os" "os/exec" - "path" "reflect" "regexp" "strconv" @@ -35,6 +33,22 @@ import ( var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) +// Initialize a versioned Solc compiler. +func InitSolc(command string) (Compiler, error) { + if command == "" { + command = "solc" + } + if _, err := exec.LookPath(command); err != nil { + return nil, fmt.Errorf("compiler: could not find %v in PATH", command) + } + s := &Solidity{NamedCmd: command} + if err := s.version(); err != nil { + return nil, err + } + return s, nil + +} + //The following represents solidity outputs from the compiler that we're interested in type SolcReturn struct { Warning string @@ -51,42 +65,15 @@ type SolcItems struct { 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. type Solidity struct { - Path, Version, FullVersion string - Major, Minor, Patch int + NamedCmd, Path, Version, FullVersion string + Major, Minor, Patch int } //This is a template to define our inputs for the compiler flags type SolcFlagOpts struct { - // (Optional) what to get in the output, can be any combination of [abi, bin, userdoc, devdoc, metadata] + // (Required) 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. @@ -110,24 +97,10 @@ type SolcFlagOpts struct { Exec string } -func (s *Solidity) defaultFlagOpts() (f SolcFlagOpts) { - f = SolcFlagOpts{ - CombinedOutput: []string{"bin", "abi", "userdoc", "devdoc"}, - StdLib: true, - Optimize: true, - } - - if s.Major >= 0 && s.Minor >= 4 && s.Patch > 6 { - f.CombinedOutput = append(f.CombinedOutput, "metadata") - } - - return -} - // SolidityVersion runs solc and parses its version output. func (s *Solidity) version() error { var out bytes.Buffer - cmd := exec.Command("solc", "--version") + cmd := exec.Command(s.NamedCmd, "--version") cmd.Stdout = &out err := cmd.Run() if err != nil { @@ -135,7 +108,7 @@ func (s *Solidity) version() error { } matches := versionRegexp.FindStringSubmatch(out.String()) if len(matches) != 4 { - return fmt.Errorf("can't parse solc version %q", out.String()) + return fmt.Errorf("%v: can't parse version %q", s.NamedCmd, out.String()) } s = &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]} if s.Major, err = strconv.Atoi(matches[1]); err != nil { @@ -151,62 +124,49 @@ func (s *Solidity) version() error { } // Compiles a series of files using the solidity compiler -func (s *Solidity) Compile(files []string, flags FlagOpts) (Return, error) { +func (s *Solidity) Compile(flags FlagOpts, files ...string) (Return, error) { if reflect.DeepEqual(flags.SolcFlagOpts, (SolcFlagOpts{})) { - flags.SolcFlagOpts = s.defaultFlagOpts() + flags.defaultSolcFlagOpts(s) } - //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 - } - - // 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 - } - } - - // 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 + return s.execute(flags.assembleSolcCommand(files...)...) } -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 +func (s *Solidity) Link(libs map[string]common.Address, binary string) (string, error) { + var refinedBinary bytes.Buffer + var stderr bytes.Buffer + + buf := bytes.NewBufferString(binary) + linkCmd := exec.Command("solc", "--link", "--libraries", stringifyLibs(libs)) + + linkCmd.Stdin = buf + linkCmd.Stderr = &stderr + linkCmd.Stdout = &refinedBinary + + linkCmd.Start() + linkCmd.Wait() + + if stderr.String() != "" { + return "", errors.New(stderr.String()) } + return refinedBinary.String(), nil +} + +func stringifyLibs(libs map[string]common.Address) string { + var combinedLibs []string + for x, y := range libs { + combinedLibs = append(combinedLibs, x+":"+y.String()) + } + return strings.Join(combinedLibs, ",") +} + +func (f SolcFlagOpts) assembleSolcCommand(files ...string) (command []string) { + 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, " ")) @@ -215,9 +175,7 @@ func (f SolcFlagOpts) assembleSolcCommand(binary bool, files ...string) (command 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, ",")) + command = append(command, []string{"--libraries", stringifyLibs(f.Libraries)}...) } if f.Optimize { command = append(command, "--optimize") @@ -232,43 +190,39 @@ func (f SolcFlagOpts) assembleSolcCommand(binary bool, files ...string) (command 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 - - cmd := exec.Command("solc", flagsAndFiles...) - cmd.Stderr = &stderr - cmd.Stdout = &stdout - - if err = cmd.Run(); err != nil { - return SolcReturn{}, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes()) +func (f *SolcFlagOpts) defaultSolcFlagOpts(s *Solidity) { + f = &SolcFlagOpts{ + CombinedOutput: []string{"bin", "abi", "userdoc", "devdoc"}, + StdLib: true, + Optimize: true, } - if err = json.Unmarshal(stdout.Bytes(), &output); err != nil { - return SolcReturn{}, err + if s.Major >= 0 && s.Minor >= 4 && s.Patch > 6 { + f.CombinedOutput = append(f.CombinedOutput, "metadata") } return } + +func (s *Solidity) execute(flagsAndFiles ...string) (Return, error) { + var stderr, stdout bytes.Buffer + var output SolcReturn + + cmd := exec.Command(s.NamedCmd, flagsAndFiles...) + cmd.Stderr = &stderr + cmd.Stdout = &stdout + + if err := cmd.Run(); err != nil { + return Return{}, fmt.Errorf("%v: %v\n%s", s.NamedCmd, err, stderr.Bytes()) + } + + buf := stdout.Bytes() + + if err := json.Unmarshal(buf, &output); err != nil { + return Return{}, err + } + + output.Warning = string(stderr.Bytes()) + + return Return{Solc, output}, nil +} diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index e34d085f3e..795b38d0ca 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -26,13 +26,10 @@ import ( "github.com/ethereum/go-ethereum/common" ) -const solFile = `pragma solidity >= 0.0.0; +const regularSolFile = `pragma solidity >= 0.0.0; contract main { - uint a; - function f() { - a = 1; - } - }` + + }` const faultySol = `pragma solidity >= 0.0.0; contract main { @@ -85,6 +82,17 @@ contract C { }` const solFile1 = `pragma solidity >=0.0.0; +import "/somedir/set.sol"; + +contract C { + Set.Data knownValues; + function register(uint value) { + if (!Set.insert(knownValues, value)) + throw; + } +}` + +const solFile2 = `pragma solidity >=0.0.0; library Set { struct Data { mapping(uint => bool) flags; } @@ -113,259 +121,202 @@ library Set { } }` -const solFile2 = `pragma solidity >=0.0.0; -import "set.sol"; +func writeToTempFile(tmpfile *os.File, content []byte) error { -contract C { - Set.Data knownValues; - function register(uint value) { - if (!Set.insert(knownValues, value)) - throw; - } -}` + if _, err := tmpfile.Write(content); err != nil { + return err + } + if err := tmpfile.Close(); err != nil { + return err + } + return nil +} func TestSolcCompilerNormal(t *testing.T) { - solc, err := InitCompiler("solc") + + solc, err := InitSolc("solc") if err != nil { - t.Skip(err) + t.Fatalf("Could not initialize solc: %v", err) } - solc = solc.(*Solidity) - file, err := os.Create("simpleContract.sol") - defer os.Remove("simpleContract.sol") + + content := []byte(regularSolFile) + tmpfile, err := ioutil.TempFile("", "simpleContract.sol") if err != nil { t.Fatal(err) } - file.WriteString(solFile) + defer os.Remove(tmpfile.Name()) // clean up + + err = writeToTempFile(tmpfile, content) + if err != nil { + t.Fatal(err) + } + flags := SolcFlagOpts{ CombinedOutput: []string{"bin", "abi"}, } - solReturn, err := solc.Compile([]string{"simpleContract.sol"}, FlagOpts{SolcFlagOpts: flags}) + solReturn, err := solc.Compile(FlagOpts{flags}, tmpfile.Name()) if err != nil { - t.Fatal(err) + t.Errorf("Expected no errors: %v", err) } - 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) + if solReturn.Warning != "" || len(solReturn.Contracts) != 1 { + t.Fatalf("Expected no warnings and expected 1 contract item. Got %v for warnings, and %v for contract items", solReturn.Warning, len(solReturn.Contracts)) } } func TestSolcCompilerError(t *testing.T) { - solc, err := InitCompiler("solc") + solc, err := InitSolc("solc") if err != nil { - t.Skip(err) + t.Fatalf("Could not initialize solc: %v", err) } - solc = solc.(*Solidity) - file, err := os.Create("faultyContract.sol") - defer os.Remove("faultyContract.sol") + content := []byte(faultySol) + tmpfile, err := ioutil.TempFile("", "faultyContract.sol") if err != nil { t.Fatal(err) } - file.WriteString(faultySol) + defer os.Remove(tmpfile.Name()) // clean up + + err = writeToTempFile(tmpfile, content) + if err != nil { + t.Fatal(err) + } + 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 { + _, err = solc.Compile(FlagOpts{SolcFlagOpts: flags}, tmpfile.Name()) + + if err == nil { t.Fatal("Expected an error, got nil.") + } else if !strings.Contains(err.Error(), "solc") { + t.Fatal("Expected error to come directly from compiler, got err from elsewhere: %v", err) } } func TestSolcCompilerWarning(t *testing.T) { - solc, err := InitCompiler("solc") + + solc, err := InitSolc("solc") if err != nil { - t.Skip(err) + t.Fatalf("Could not initialize solc: %v", err) } - solc = solc.(*Solidity) - file, err := os.Create("simpleContract.sol") - defer os.Remove("simpleContract.sol") + content := []byte(solNoPragma) + tmpfile, err := ioutil.TempFile("", "warningContract.sol") if err != nil { t.Fatal(err) } - file.WriteString(solNoPragma) + defer os.Remove(tmpfile.Name()) // clean up + + err = writeToTempFile(tmpfile, content) + if err != nil { + t.Fatal(err) + } + flags := SolcFlagOpts{ CombinedOutput: []string{"bin", "abi"}, } - solReturn, err := solc.Compile([]string{"simpleContract.sol"}, FlagOpts{SolcFlagOpts: flags}) + solReturn, err := solc.Compile(FlagOpts{SolcFlagOpts: flags}, tmpfile.Name()) if err != nil { - t.Fatal(err) + t.Error(err) } if solReturn.Warning == "" { - t.Fatal("Expected a warning.") + t.Error("Expected a warning, got none.") } } func TestLinkingBinaries(t *testing.T) { - solc, err := InitCompiler("solc") + + solc, err := InitSolc("solc") if err != nil { - t.Skip(err) + t.Fatalf("Could not initialize solc: %v", err) } - solc = solc.(*Solidity) - file, err := os.Create("simpleLibrary.sol") - defer os.Remove("simpleLibrary.sol") + content := []byte(simplyLibrarySol) + tmpfile, err := ioutil.TempFile("", "libraryContracts.sol") if err != nil { t.Fatal(err) } - file.WriteString(simplyLibrarySol) + defer os.Remove(tmpfile.Name()) // clean up + + err = writeToTempFile(tmpfile, content) + if err != nil { + t.Fatal(err) + } + flags := SolcFlagOpts{ CombinedOutput: []string{"bin"}, } - solReturn, err := solc.Compile([]string{"simpleLibrary.sol"}, FlagOpts{SolcFlagOpts: flags}) + solReturn, err := solc.Compile(FlagOpts{SolcFlagOpts: flags}, tmpfile.Name()) 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) + if solReturn.Warning != "" || len(solReturn.Contracts) != 2 { + t.Fatalf("Expected no errors or warnings and expected contract items. Got %v for warnings, and %v for contract items", solReturn.Warning, solReturn.Contracts) } - binFile.WriteString(solReturn.Contracts["simpleLibrary.sol:C"].Bin) - _, err = solc.(*Solidity).Compile([]string{"./C.bin"}, FlagOpts{SolcFlagOpts: flags}) + output, err := solc.(*Solidity).Link(map[string]common.Address{"Set": common.StringToAddress("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a")}, solReturn.Contracts["C"].Bin) if err != nil { - t.Fatal(err) + t.Error(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) + if strings.Contains(output, "_") { + t.Errorf("Expected binaries to link, but they did not") } } 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") + solc, err := InitSolc("solc") + if err != nil { + t.Fatalf("Could not initialize solc: %v", err) + } + + tmpfile1, err := ioutil.TempFile("", "C.sol") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile1.Name()) // clean up + + err = writeToTempFile(tmpfile1, []byte(solFile1)) + if err != nil { + t.Fatal(err) + } + + dir, err := ioutil.TempDir("", "tempDir") + if err != nil { + t.Fatal(err) + } + + defer os.RemoveAll(dir) // clean up + + tmpfn := filepath.Join(dir, "set.sol") + if err := ioutil.WriteFile(tmpfn, []byte(solFile2), 0666); err != nil { + t.Fatal(err) + } + + tmpfile2, err := ioutil.TempFile("", "main.sol") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile2.Name()) // clean up + + err = writeToTempFile(tmpfile2, []byte(regularSolFile)) if err != nil { t.Fatal(err) } - c.WriteString(solFile2) flags := SolcFlagOpts{ CombinedOutput: []string{"bin", "abi"}, - Remappings: []string{`set.sol=./tempDir/set.sol`}, + Remappings: []string{`/somedir/=` + dir + "/"}, } - solReturn, err := solc.Compile([]string{"C.sol"}, FlagOpts{SolcFlagOpts: flags}) + solReturn, err := solc.Compile(FlagOpts{SolcFlagOpts: flags}, tmpfile1.Name(), tmpfile2.Name()) 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) + if solReturn.Warning != "" || len(solReturn.Contracts) != 3 { + t.Fatalf("Expected no warnings and expected %v contract items. Got %v for warnings, and %v for contract items", 3, solReturn.Warning, len(solReturn.Contracts)) } } From 573181c8ba1fe15d0afdd9249d887b14b558f65b Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 13:04:44 -0500 Subject: [PATCH 04/11] bind: not sure if I'm keeping these commits. Waiting for advice Signed-off-by: RJ Catalano --- accounts/abi/bind/bind.go | 20 ++++++++++++++++++++ cmd/abigen/main.go | 32 +++++++++++++++----------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 73e95e02a1..762008d9bf 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -41,6 +41,26 @@ const ( LangObjC ) +type BindOpts struct { + Abi string + Bin string + Typ string + + Link string + SolFiles string + RemapPaths string + Compiler string + Exec string + + Lang Lang + Pkg string + OutputFile string +} + +func CompileAndBind(files ...string) + +func BindIndividualFiles(abis string, bytecodes string, pkg string, lang Lang) + // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index f63b3fa7a7..fd4ac7f1e3 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -29,14 +29,15 @@ import ( ) var ( - abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") - binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") - typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") - linkFlag = flag.String("link", "", "Library flag linker for name to addresses in the code") + abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind") + binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") + typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") - solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") - solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") - excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") + linkFlag = flag.String("link", "", "Library flag linker for name to addresses in the code") + solFlag = flag.String("sol", "", "Comma separated path(s) to Solidity source(s) to build and bind") + pathsFlag = flag.String("paths", "", "Comma separated path(s) in the form of solidity remappings (advanced only)") + solcFlag = flag.String("compiler", "solc", "Compiler to use if source builds are requested (default = solc)") + execFlag = flag.String("exec", "", "Execute compiler with user generated command, advanced only, use commas to represent spaces in one string") pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") @@ -53,6 +54,9 @@ func main() { } else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" { fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n") os.Exit(-1) + } else if (*binFlag == "" || *solFlag == "") && *linkFlag != "" { + fmt.Printf("No contract bytecode created through (--bin) or (--sol) options to use with (--link) option\n") + os.Exit(-1) } if *pkgFlag == "" { fmt.Printf("No destination package specified (--pkg)\n") @@ -77,21 +81,15 @@ func main() { types []string ) if *solFlag != "" { - // Generate the list of types to exclude from binding - exclude := make(map[string]bool) - for _, kind := range strings.Split(*excFlag, ",") { - exclude[strings.ToLower(kind)] = true - } - contracts, err := compiler.CompileSolidity(*solcFlag, *solFlag) + solc, err := compiler.InitSolc(*solcFlag) if err != nil { - fmt.Printf("Failed to build Solidity contract: %v\n", err) + fmt.Printf("Failed to initialize Solidity compiler: %v\n", err) os.Exit(-1) } + + solc.Compile(compiler.FlagOpts{}, strings.Split(*solFlag, ",")) // Gather all non-excluded contract for binding for name, contract := range contracts { - if exclude[strings.ToLower(name)] { - continue - } abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse abis = append(abis, string(abi)) bins = append(bins, contract.Code) From d3baeb21ae067867438eccd14d96229921139825 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 14:45:16 -0500 Subject: [PATCH 05/11] bind,abigen: decide to play this a bit more conservatively Signed-off-by: RJ Catalano --- accounts/abi/bind/bind.go | 20 -------------------- cmd/abigen/main.go | 33 ++++++++++++++++++--------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 762008d9bf..73e95e02a1 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -41,26 +41,6 @@ const ( LangObjC ) -type BindOpts struct { - Abi string - Bin string - Typ string - - Link string - SolFiles string - RemapPaths string - Compiler string - Exec string - - Lang Lang - Pkg string - OutputFile string -} - -func CompileAndBind(files ...string) - -func BindIndividualFiles(abis string, bytecodes string, pkg string, lang Lang) - // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant // to be used as is in client code, but rather as an intermediate struct which // enforces compile time type safety and naming convention opposed to having to diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index fd4ac7f1e3..65294a023c 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -33,11 +33,9 @@ var ( binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") - linkFlag = flag.String("link", "", "Library flag linker for name to addresses in the code") - solFlag = flag.String("sol", "", "Comma separated path(s) to Solidity source(s) to build and bind") - pathsFlag = flag.String("paths", "", "Comma separated path(s) in the form of solidity remappings (advanced only)") - solcFlag = flag.String("compiler", "solc", "Compiler to use if source builds are requested (default = solc)") - execFlag = flag.String("exec", "", "Execute compiler with user generated command, advanced only, use commas to represent spaces in one string") + solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") + solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested") + excFlag = flag.String("exc", "", "Comma separated types to exclude from binding") pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") @@ -54,9 +52,6 @@ func main() { } else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" { fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n") os.Exit(-1) - } else if (*binFlag == "" || *solFlag == "") && *linkFlag != "" { - fmt.Printf("No contract bytecode created through (--bin) or (--sol) options to use with (--link) option\n") - os.Exit(-1) } if *pkgFlag == "" { fmt.Printf("No destination package specified (--pkg)\n") @@ -81,18 +76,26 @@ func main() { types []string ) if *solFlag != "" { + // Generate the list of types to exclude from binding + exclude := make(map[string]bool) + for _, kind := range strings.Split(*excFlag, ",") { + exclude[strings.ToLower(kind)] = true + } solc, err := compiler.InitSolc(*solcFlag) + + solReturn, err := solc.Compile(compiler.SolcFlagOpts{}, *solFlag) if err != nil { - fmt.Printf("Failed to initialize Solidity compiler: %v\n", err) + fmt.Printf("Failed to build Solidity contract: %v\n", err) os.Exit(-1) } - - solc.Compile(compiler.FlagOpts{}, strings.Split(*solFlag, ",")) // Gather all non-excluded contract for binding - for name, contract := range contracts { - abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse - abis = append(abis, string(abi)) - bins = append(bins, contract.Code) + for name, contract := range solReturn.Contracts { + if exclude[strings.ToLower(name)] { + continue + } + + abis = append(abis, contract.Abi) + bins = append(bins, contract.Bin) nameParts := strings.Split(name, ":") types = append(types, nameParts[len(nameParts)-1]) From 2dec80b1ada1ac64bf9262a0973451876e23a826 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 14:51:22 -0500 Subject: [PATCH 06/11] abigen: fix compilation issue Signed-off-by: RJ Catalano --- cmd/abigen/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 65294a023c..e751fe7680 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -17,7 +17,6 @@ package main import ( - "encoding/json" "flag" "fmt" "io/ioutil" @@ -83,7 +82,7 @@ func main() { } solc, err := compiler.InitSolc(*solcFlag) - solReturn, err := solc.Compile(compiler.SolcFlagOpts{}, *solFlag) + solReturn, err := solc.Compile(compiler.FlagOpts{}, *solFlag) if err != nil { fmt.Printf("Failed to build Solidity contract: %v\n", err) os.Exit(-1) From 9a6990c15eab06c84cd713f975517b4c391ff383 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 15:04:04 -0500 Subject: [PATCH 07/11] abigen: adding this tidbit in here for safety assurance Signed-off-by: RJ Catalano --- cmd/abigen/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index e751fe7680..6bc6b516f7 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -83,7 +83,7 @@ func main() { solc, err := compiler.InitSolc(*solcFlag) solReturn, err := solc.Compile(compiler.FlagOpts{}, *solFlag) - if err != nil { + if err != nil || solReturn.Typ != compiler.Solc { fmt.Printf("Failed to build Solidity contract: %v\n", err) os.Exit(-1) } From 163e8d868e1288aef04f5c5dc27afda261ad36f8 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 15:25:22 -0500 Subject: [PATCH 08/11] common/compiler: fix for go vet issues Signed-off-by: RJ Catalano --- common/compiler/solidity_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 795b38d0ca..bfd3fb242a 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -191,7 +191,7 @@ func TestSolcCompilerError(t *testing.T) { if err == nil { t.Fatal("Expected an error, got nil.") } else if !strings.Contains(err.Error(), "solc") { - t.Fatal("Expected error to come directly from compiler, got err from elsewhere: %v", err) + t.Fatalf("Expected error to come directly from compiler, got err from elsewhere: %v", err) } } From 10bce71bedae9836977afd37847e6fbef783cea3 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 18:58:10 -0500 Subject: [PATCH 09/11] compiler: readd skipWithoutSolc Signed-off-by: RJ Catalano --- common/compiler/solidity_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index bfd3fb242a..46296dcde9 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -121,6 +121,12 @@ library Set { } }` +func skipWithoutSolc(t *testing.T) { + if _, err := exec.LookPath("solc"); err != nil { + t.Skip(err) + } +} + func writeToTempFile(tmpfile *os.File, content []byte) error { if _, err := tmpfile.Write(content); err != nil { @@ -134,6 +140,8 @@ func writeToTempFile(tmpfile *os.File, content []byte) error { func TestSolcCompilerNormal(t *testing.T) { + skipWithoutSolc(t) + solc, err := InitSolc("solc") if err != nil { t.Fatalf("Could not initialize solc: %v", err) @@ -166,6 +174,9 @@ func TestSolcCompilerNormal(t *testing.T) { } func TestSolcCompilerError(t *testing.T) { + + skipWithoutSolc(t) + solc, err := InitSolc("solc") if err != nil { t.Fatalf("Could not initialize solc: %v", err) @@ -197,6 +208,8 @@ func TestSolcCompilerError(t *testing.T) { func TestSolcCompilerWarning(t *testing.T) { + skipWithoutSolc(t) + solc, err := InitSolc("solc") if err != nil { t.Fatalf("Could not initialize solc: %v", err) @@ -228,6 +241,8 @@ func TestSolcCompilerWarning(t *testing.T) { func TestLinkingBinaries(t *testing.T) { + skipWithoutSolc(t) + solc, err := InitSolc("solc") if err != nil { t.Fatalf("Could not initialize solc: %v", err) @@ -268,6 +283,8 @@ func TestLinkingBinaries(t *testing.T) { func TestRemappings(t *testing.T) { + skipWithoutSolc(t) + solc, err := InitSolc("solc") if err != nil { t.Fatalf("Could not initialize solc: %v", err) From a036cb9c643cc33712fa2829e8b0d3b1819756f1 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 20:59:26 -0500 Subject: [PATCH 10/11] common/compiler: add forgotten exec package to imports Signed-off-by: RJ Catalano --- common/compiler/solidity_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/compiler/solidity_test.go b/common/compiler/solidity_test.go index 46296dcde9..5691a757f4 100644 --- a/common/compiler/solidity_test.go +++ b/common/compiler/solidity_test.go @@ -19,6 +19,7 @@ package compiler import ( "io/ioutil" "os" + "os/exec" "path/filepath" "strings" "testing" From 8cb50d0b85282e47cbdf6f8a88efa827027d7251 Mon Sep 17 00:00:00 2001 From: VoR0220 Date: Thu, 16 Nov 2017 15:35:36 -0600 Subject: [PATCH 11/11] common/compiler: delete abstraction layer Signed-off-by: VoR0220 --- common/compiler/compiler.go | 36 ------------------------------------ common/compiler/solidity.go | 8 ++++---- 2 files changed, 4 insertions(+), 40 deletions(-) delete mode 100644 common/compiler/compiler.go diff --git a/common/compiler/compiler.go b/common/compiler/compiler.go deleted file mode 100644 index 96276ccde0..0000000000 --- a/common/compiler/compiler.go +++ /dev/null @@ -1,36 +0,0 @@ -package compiler - -// An interface to denote a version specific compiler. -// 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 { - Compile(flags FlagOpts, files ...string) (Return, error) - version() error -} - -// This struct via embedding gives us access to all types of returns. -// This is written to be extendable to other compilers. -type Return struct { - Typ CompilerType - SolcReturn - //Enter your return struct here...e.g. - //SerpentReturn - //BambooReturn - //ViperReturn -} - -// 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... -} - -type CompilerType byte - -const ( - Solc CompilerType = iota - // Serpent - // Bamboo - // Viper -) diff --git a/common/compiler/solidity.go b/common/compiler/solidity.go index d829a92b2e..dc56d187e2 100644 --- a/common/compiler/solidity.go +++ b/common/compiler/solidity.go @@ -34,7 +34,7 @@ import ( var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`) // Initialize a versioned Solc compiler. -func InitSolc(command string) (Compiler, error) { +func InitSolc(command string) (*Solidity, error) { if command == "" { command = "solc" } @@ -124,7 +124,7 @@ func (s *Solidity) version() error { } // Compiles a series of files using the solidity compiler -func (s *Solidity) Compile(flags FlagOpts, files ...string) (Return, error) { +func (s *Solidity) Compile(flags SolcFlagOpts, files ...string) (Return, error) { if reflect.DeepEqual(flags.SolcFlagOpts, (SolcFlagOpts{})) { flags.defaultSolcFlagOpts(s) @@ -204,7 +204,7 @@ func (f *SolcFlagOpts) defaultSolcFlagOpts(s *Solidity) { return } -func (s *Solidity) execute(flagsAndFiles ...string) (Return, error) { +func (s *Solidity) execute(flagsAndFiles ...string) (SolcReturn, error) { var stderr, stdout bytes.Buffer var output SolcReturn @@ -224,5 +224,5 @@ func (s *Solidity) execute(flagsAndFiles ...string) (Return, error) { output.Warning = string(stderr.Bytes()) - return Return{Solc, output}, nil + return output, nil }