From b39afc674ca58efe82149d485ec4b02229f99f8e Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Sun, 20 Aug 2017 12:10:33 -0500 Subject: [PATCH] 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)) } }