mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
common/compiler: complete reworking of compilers section
Signed-off-by: RJ Catalano <rj@monax.io>
This commit is contained in:
parent
a38afe2815
commit
82e0dd79f4
4 changed files with 543 additions and 238 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"`
|
||||
//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 {
|
||||
// (Optional) what to get in the output, can be any combination of [abi, bin, userdoc, devdoc, metadata]
|
||||
// abi: application binary interface. Necessary for interaction with contracts.
|
||||
// bin: binary bytecode. Necessary for creating and deploying and interacting with contracts.
|
||||
// userdoc: natspec for users.
|
||||
// devdoc: natspec for devs.
|
||||
// metadata: contract metadata.
|
||||
CombinedOutput []string
|
||||
// (Optional) Direct string of library address mappings.
|
||||
// Syntax: <libraryName>:<address>,<libraryName>:<address>
|
||||
// Address is interpreted as a hex string optionally prefixed by 0x.
|
||||
Libraries map[string]common.Address
|
||||
// (Optional) Remappings, see https://solidity.readthedocs.io/en/latest/layout-of-source-files.html#use-in-actual-compilers
|
||||
// Syntax: <remoteName>=<localName>
|
||||
Remappings []string
|
||||
// (Optional) if true, enable standard library contracts
|
||||
StdLib bool
|
||||
// (Optional) if true, optimizes solidity code
|
||||
Optimize bool
|
||||
CombinedJson []string
|
||||
ToLink []string
|
||||
Version string
|
||||
// (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
|
||||
}
|
||||
Version string
|
||||
func (s *Solidity) defaultFlagOpts() (f SolcFlagOpts) {
|
||||
f = SolcFlagOpts{
|
||||
CombinedOutput: []string{"bin", "abi", "userdoc", "devdoc"},
|
||||
StdLib: true,
|
||||
Optimize: true,
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
return contracts, nil
|
||||
if err = json.Unmarshal(stdout.Bytes(), &output); err != nil {
|
||||
return SolcReturn{}, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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;
|
||||
}
|
||||
`
|
||||
librarySource = `
|
||||
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)
|
||||
}
|
||||
if len(contracts) != 1 {
|
||||
t.Errorf("one contract expected, got %d", len(contracts))
|
||||
}
|
||||
c, ok := contracts["test"]
|
||||
if !ok {
|
||||
c, ok = contracts["<stdin>:test"]
|
||||
if !ok {
|
||||
t.Fatal("info for contract 'test' not present in result")
|
||||
}
|
||||
}
|
||||
if c.Code == "" {
|
||||
t.Error("empty code")
|
||||
}
|
||||
if c.Info.Source != testSource {
|
||||
t.Error("wrong source")
|
||||
}
|
||||
if c.Info.CompilerVersion == "" {
|
||||
t.Error("empty version")
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.WriteString(solFile)
|
||||
flags := SolcFlagOpts{
|
||||
CombinedOutput: []string{"bin", "abi"},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue