Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support imports in solcjs CLI #114

Closed
lukehedger opened this issue Jun 28, 2017 · 21 comments
Closed

Support imports in solcjs CLI #114

lukehedger opened this issue Jun 28, 2017 · 21 comments

Comments

@lukehedger
Copy link

Given the following directory structure:

contracts/
  Source.sol
  Imported.sol

Where Source.sol:

pragma solidity ^0.4.0;

import './Imported.sol';

contract Source {
}

Running the following command:

solcjs --optimize --bin contracts/Source.sol

Based on the docs, I would expect the compiler to resolve the import but instead it throws:

contracts/Source.sol:3:1: Error: Source "contracts/Imported.sol" not found: File not supplied initially.
import './Imported.sol';
^---------------------^

If I include Imported.sol as a source file it will compile without error:

solcjs --optimize --bin contracts/Source.sol contracts/Imported.sol

Could you clarify my understanding here or is this a bug? Thanks! 🙃

@lukehedger
Copy link
Author

Just seen this note in readme:

Note that all input files that are imported have to be supplied, the compiler will not load any additional files on its own.

Which answers my question!

What's the reason for this/any scope for improving it in the future?

@axic
Copy link
Member

axic commented Jul 10, 2017

@lukehedger someone needs to do implement it. If you are interested adding support for it, please make a PR and we're happy to help to finish and include that feature.

@lukehedger
Copy link
Author

Happy to look at adding support but would you be able to point me in the right direction to get started? Would the feature be added to the JS bindings or to the compiler itself?

@axic
Copy link
Member

axic commented Jul 18, 2017

This is a feature missing in the solcjs tool included in this repository. The solc.compile method supports a callback to read external files, see https://github.com/ethereum/solc-js#from-version-021

@axic axic changed the title Resolving imports Support imports in solcjs CLI Jul 18, 2017
@lukehedger
Copy link
Author

Just came across this tool which may be of use in the meantime: https://github.com/TiesNetwork/solidify

@mimitc123
Copy link

mimitc123 commented Dec 5, 2017

@Suhail
Copy link

Suhail commented Jan 2, 2018

I solved this by doing the below as things have been improved as of 0.2.1:

var solc = require('solc');
var fs = require('fs');

var inputs = {
    'auction.sol': fs.readFileSync('auction.sol').toString(),
};

// Assumes imported files are in the same folder/local path
function findImports(path) {
    return {
        'contents': fs.readFileSync(path).toString()
    }
}

var compiledCode = solc.compile({sources: inputs}, 1, findImports)

fs.writeFile('compiled.json', JSON.stringify(compiledCode), function(err) {
    if (err) throw err;
    console.log('Compiled & saved');
});

I then read in compiled.json later in a .js file using web3 to use it via a browser using browserify:

compiledCode =  JSON.parse(fs.readFileSync('compiled.json').toString());

//console.log(compiledCode);

abi = JSON.parse(compiledCode.contracts['auction.sol:ContractName'].interface);
bytecode = compiledCode.contracts['auction.sol:ContractName'].bytecode;

Hope this is helpful to someone else.

@arashkiani
Copy link

so writing import is just a fun activity that solc dont understand ?

@aj0strow
Copy link

It looks like if findImports(path) had a second parameter, the source file that is calling import, it would be easy to generalize this.

var fs = require('fs')
var path = require('path')

function findImports(importPath, sourcePath) {
  try {
    var filePath = path.resolve(sourcePath, importPath)
    return { contents: fs.readFileSync(filePath).toString() }
  } catch (e) {
    return { error: e.message }
  }
}

solc.compile(..., findImports)

Is there a way to receive the source path too? I tried looking in the soljson.js file but it looks like it is already compiled from somewhere else.

@flcoder
Copy link

flcoder commented Jul 29, 2018

@aj0strow 👍

@kangdao
Copy link

kangdao commented Aug 14, 2018

solcjs --abi contract.sol
Tell me how to generate bytecode

@wangjj9219
Copy link

@kangdao try it: solcjs --bin contract.sol

@uluhonolulu
Copy link

@axic I tried to include all imported contracts, but it still fails for me (Windows, version 0.5.4+commit.9549d8ff.Emscripten.clang):

> solcjs .\contracts\StorageController.sol .\contracts\Owned.sol .\contracts\SafeMath.sol .\contracts\Owned.sol --bin
.\contracts\StorageController.sol:4:1: ParserError: Source "BaseContract.sol" not found: File import callback not supported
import "./BaseContract.sol";
^--------------------------^

.\contracts\StorageController.sol:5:1: ParserError: Source "Owned.sol" not found: File import callback not supported
import "./Owned.sol";
^-------------------^

.\contracts\StorageController.sol:6:1: ParserError: Source "SafeMath.sol" not found: File import callback not supported
import "./SafeMath.sol";

@axic
Copy link
Member

axic commented Feb 28, 2019

The feature is not implemented yet. Passing multiple contracts will work unfortunately.

@fasidOnGit
Copy link

fasidOnGit commented Aug 25, 2019

Bingo! I have a fix that works!! This will simply compile all the .sol files in the directory and its sub-directories and feed it into solc.compile

//uitls.js

const fs = require('fs');
const path = require('path');
const findImports = (dir) => {
    const files = walkSync(dir);
    return (fileName) => {
        const fileNames = Object.keys(files);
        for(const key in files) {
            if(key.indexOf(fileName) > -1) {
                return { contents: files[key]};
            }
        }
        return {error: fileName + 'not Found'};
    }
};

const walkSync = (dir, filelist) => {
    var fs = fs || require('fs'),
        files = fs.readdirSync(dir);
    filelist = filelist || [];
    files.forEach(function(file) {
        if (fs.statSync(dir + '/' + file).isDirectory()) {
            console.log('Directory:', dir + '/' + file);
            filelist = walkSync(dir + '/' + file, filelist);
        }
        else {
            if(file.indexOf('.sol') > 0) {
                console.log('File: ', dir + '/' + file)
                // filelist.push({[file]: fs.readFileSync(dir + '/' + file, 'utf-8')});
                filelist[dir + '/' + file] = fs.readFileSync(dir + '/' + file, 'utf-8')
            }
        }
    });
    return filelist;
};

module.exports = {
    findImports
};

and in solc.compile just pass this as the last parameter like so

//compile.js
const {findImports} = require('./utils');
...
const input = YOUR_INPUT_JSON_TO_COMPILE;
solc.compile(JSON.stringify(input), findImports(__dirname + '/contracts'));
...
...

@akiabi
Copy link

akiabi commented Mar 22, 2020

Bingo! I have a fix that works!! This will simply compile all the .sol files in the directory and its sub-directories and feed it into solc.compile

//uitls.js

const fs = require('fs');
const path = require('path');
const findImports = (dir) => {
    const files = walkSync(dir);
    return (fileName) => {
        const fileNames = Object.keys(files);
        for(const key in files) {
            if(key.indexOf(fileName) > -1) {
                return { contents: files[key]};
            }
        }
        return {error: fileName + 'not Found'};
    }
};

const walkSync = (dir, filelist) => {
    var fs = fs || require('fs'),
        files = fs.readdirSync(dir);
    filelist = filelist || [];
    files.forEach(function(file) {
        if (fs.statSync(dir + '/' + file).isDirectory()) {
            console.log('Directory:', dir + '/' + file);
            filelist = walkSync(dir + '/' + file, filelist);
        }
        else {
            if(file.indexOf('.sol') > 0) {
                console.log('File: ', dir + '/' + file)
                // filelist.push({[file]: fs.readFileSync(dir + '/' + file, 'utf-8')});
                filelist[dir + '/' + file] = fs.readFileSync(dir + '/' + file, 'utf-8')
            }
        }
    });
    return filelist;
};

module.exports = {
    findImports
};

and in solc.compile just pass this as the last parameter like so

//compile.js
const {findImports} = require('./utils');
...
const input = YOUR_INPUT_JSON_TO_COMPILE;
solc.compile(JSON.stringify(input), findImports(__dirname + '/contracts'));
...
...

wer shld be the utilis.js and compile.js shld be added?

@fasidOnGit
Copy link

@akiabi Could be at the project root dir?

@Garito
Copy link

Garito commented Nov 13, 2020

Hi
I use the remappings option in settings to do this in python
Why is here different or am I losing something?

@bakeiro
Copy link

bakeiro commented Dec 14, 2021

Hi!

I'm getting a Invalid callback object specified all the time, seems that the second parameter needs to be an object and not a callback (solc 8.10)

does anyone knows more about this? thanks!

image

@chriseth
Copy link
Contributor

Can you come to the solidity gitter / matrix channel as described in the readme: https://github.com/ethereum/solidity#readme ?

@jonathansmirnoff
Copy link

Hi!

I'm getting a Invalid callback object specified all the time, seems that the second parameter needs to be an object and not a callback (solc 8.10)

does anyone knows more about this? thanks!

image

did you find a way to solve this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests