├── solc ├── utils │ ├── __init__.py │ ├── filesystem.py │ ├── types.py │ └── string.py ├── __init__.py ├── exceptions.py ├── wrapper.py ├── main.py └── install.py ├── pytest.ini ├── requirements-dev.txt ├── MANIFEST.in ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── tests ├── core │ ├── utility │ │ ├── test_solc_version.py │ │ ├── test_is_executable_available.py │ │ └── test_solc_supports_standard_json.py │ ├── compilation │ │ ├── test_compile_empty.py │ │ ├── test_compile_from_source_code.py │ │ ├── test_compiler_from_source_file.py │ │ ├── test_compiler_remappings.py │ │ └── test_compile_standard.py │ ├── linking │ │ └── test_library_linking.py │ └── wrapper │ │ └── test_solc_wrapper.py ├── installation │ └── test_solc_installation.py └── conftest.py ├── .bumpversion.cfg ├── tox.ini ├── .gitignore ├── LICENSE ├── Makefile ├── CONTRIBUTING.md ├── setup.py ├── CHANGELOG ├── .travis.yml ├── README.md └── scripts └── install_solc.py /solc/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts= -v --showlocals 3 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pytest>=2.9.2 2 | tox>=2.3.1 3 | semantic_version>=2.6.0 4 | bumpversion==0.5.3 5 | twine==1.12.1 6 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include requirements.txt 4 | 5 | recursive-exclude * __pycache__ 6 | recursive-exclude * *.py[co] 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What was wrong? 2 | 3 | 4 | 5 | ### How was it fixed? 6 | 7 | 8 | 9 | #### Cute Animal Picture 10 | 11 | > put a cute animal picture here. 12 | -------------------------------------------------------------------------------- /tests/core/utility/test_solc_version.py: -------------------------------------------------------------------------------- 1 | from solc import get_solc_version 2 | 3 | import semantic_version 4 | 5 | 6 | def test_get_solc_version(): 7 | version = get_solc_version() 8 | 9 | assert isinstance(version, semantic_version.Version) 10 | -------------------------------------------------------------------------------- /tests/core/utility/test_is_executable_available.py: -------------------------------------------------------------------------------- 1 | from solc.utils.filesystem import ( 2 | is_executable_available, 3 | ) 4 | 5 | 6 | def test_ls_is_available(): 7 | assert is_executable_available('ls') is True 8 | 9 | 10 | def test_for_unavailable_executable(): 11 | assert is_executable_available('there_should_not_be_an_executable_by_this_name') is False 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * `py-solc` Version: x.x.x 2 | * `solc` Version: x.x.x 3 | * Python Version: x.x.x 4 | * OS: osx/linux/win 5 | 6 | 7 | ### What was wrong? 8 | 9 | Please include information like: 10 | 11 | * full output of the error you received 12 | * what command you ran 13 | * the code that caused the failure 14 | 15 | #### Cute Animal Picture 16 | 17 | > put a cute animal picture here. 18 | -------------------------------------------------------------------------------- /tests/core/utility/test_solc_supports_standard_json.py: -------------------------------------------------------------------------------- 1 | from solc import get_solc_version 2 | from solc.main import solc_supports_standard_json_interface 3 | 4 | import semantic_version 5 | 6 | 7 | def test_solc_supports_standard_json_interface(): 8 | version = get_solc_version() 9 | 10 | if version in semantic_version.Spec("<0.4.11"): 11 | assert not solc_supports_standard_json_interface() 12 | else: 13 | assert solc_supports_standard_json_interface() 14 | -------------------------------------------------------------------------------- /solc/utils/filesystem.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def is_executable_available(program): 5 | def is_exe(fpath): 6 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 7 | 8 | fpath = os.path.dirname(program) 9 | if fpath: 10 | if is_exe(program): 11 | return True 12 | else: 13 | for path in os.environ["PATH"].split(os.pathsep): 14 | path = path.strip('"') 15 | exe_file = os.path.join(path, program) 16 | if is_exe(exe_file): 17 | return True 18 | 19 | return False 20 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 3.2.0 3 | commit = True 4 | tag = True 5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[^.]*)\.(?P\d+))? 6 | serialize = 7 | {major}.{minor}.{patch}-{stage}.{devnum} 8 | {major}.{minor}.{patch} 9 | 10 | [bumpversion:part:stage] 11 | optional_value = stable 12 | first_value = stable 13 | values = 14 | alpha 15 | beta 16 | stable 17 | 18 | [bumpversion:part:devnum] 19 | 20 | [bumpversion:file:setup.py] 21 | search = version='{current_version}', 22 | replace = version='{new_version}', 23 | 24 | -------------------------------------------------------------------------------- /tests/core/compilation/test_compile_empty.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from solc import ( 6 | compile_files, 7 | compile_standard, 8 | ) 9 | 10 | from solc.exceptions import ContractsNotFound 11 | 12 | 13 | def test_compile_empty_folder(): 14 | """Execute compile on a folder without contracts.""" 15 | 16 | with pytest.raises(ContractsNotFound): 17 | compile_files([]) 18 | 19 | 20 | @pytest.mark.requires_standard_json 21 | def test_compile_standard_empty_sources(): 22 | with pytest.raises(ContractsNotFound): 23 | compile_standard({'language': 'Solidity', 'sources': {}}) 24 | -------------------------------------------------------------------------------- /solc/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import sys 4 | import warnings 5 | 6 | from .main import ( # noqa: F401 7 | get_solc_version_string, 8 | get_solc_version, 9 | compile_files, 10 | compile_source, 11 | compile_standard, 12 | link_code, 13 | ) 14 | from .install import ( # noqa: F401 15 | install_solc, 16 | ) 17 | 18 | if sys.version_info.major < 3: 19 | warnings.simplefilter('always', DeprecationWarning) 20 | warnings.warn(DeprecationWarning( 21 | "The `py-solc` library is dropping support for Python 2. Upgrade to Python 3." 22 | )) 23 | warnings.resetwarnings() 24 | -------------------------------------------------------------------------------- /tests/core/compilation/test_compile_from_source_code.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from solc import compile_source 4 | 5 | pytestmark = pytest.mark.usefixtures('supported_solc_version') 6 | 7 | 8 | def test_source_code_compilation(FOO_SOURCE, is_new_key_format): 9 | output = compile_source(FOO_SOURCE, optimize=True) 10 | assert output 11 | 12 | if is_new_key_format: 13 | contact_key = ':Foo' 14 | else: 15 | contact_key = 'Foo' 16 | 17 | assert contact_key in output 18 | 19 | foo_contract_data = output[contact_key] 20 | assert 'bin' in foo_contract_data 21 | assert 'bin-runtime' in foo_contract_data 22 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist= 3 | py{34,35,36}-{core,installation} 4 | flake8 5 | 6 | [flake8] 7 | max-line-length= 100 8 | exclude= tests/* 9 | 10 | [testenv] 11 | commands= 12 | core: py.test {posargs:tests/core} 13 | installation: py.test {posargs:-s tests/installation} 14 | passenv = 15 | SOLC_BINARY 16 | TRAVIS_BUILD_DIR 17 | SOLC_RUN_INSTALL_TESTS 18 | setenv = 19 | py{34,35,36}-installation: SOLC_RUN_INSTALL_TESTS=enabled 20 | deps = 21 | -r{toxinidir}/requirements-dev.txt 22 | basepython = 23 | py34: python3.4 24 | py35: python3.5 25 | py36: python3.6 26 | 27 | [testenv:flake8] 28 | basepython=python 29 | deps=flake8 30 | commands=flake8 {toxinidir}/solc 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # Binaries 4 | bin 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Packages 10 | *.egg 11 | *.egg-info 12 | dist 13 | build 14 | eggs 15 | parts 16 | var 17 | sdist 18 | develop-eggs 19 | .installed.cfg 20 | lib 21 | lib64 22 | 23 | # Installer logs 24 | pip-log.txt 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .tox 29 | nosetests.xml 30 | 31 | # Translations 32 | *.mo 33 | 34 | # Mr Developer 35 | .mr.developer.cfg 36 | .project 37 | .pydevproject 38 | 39 | # Complexity 40 | output/*.html 41 | output/*/index.html 42 | 43 | # Sphinx 44 | docs/_build 45 | 46 | # Blockchain 47 | **/chains/*/nodes/* 48 | **/chains/*/nodekey 49 | **/chains/*/dapp/* 50 | **/chains/*/chaindata/* 51 | 52 | # Known Contracts 53 | **/known_contracts.json 54 | 55 | # py.test cache 56 | .cache 57 | 58 | # npm 59 | node_modules 60 | -------------------------------------------------------------------------------- /tests/core/compilation/test_compiler_from_source_file.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import os 4 | 5 | from semantic_version import Spec 6 | from solc import ( 7 | get_solc_version, 8 | compile_files, 9 | ) 10 | 11 | pytestmark = pytest.mark.usefixtures('supported_solc_version') 12 | 13 | 14 | def test_source_files_compilation(FOO_SOURCE, is_new_key_format, contracts_dir): 15 | source_file_path = os.path.join(contracts_dir, 'Foo.sol') 16 | with open(source_file_path, 'w') as source_file: 17 | source_file.write(FOO_SOURCE) 18 | 19 | output = compile_files([source_file_path], optimize=True) 20 | 21 | assert output 22 | 23 | if is_new_key_format: 24 | contract_key = '{0}:Foo'.format(os.path.abspath(source_file_path)) 25 | else: 26 | contract_key = 'Foo' 27 | 28 | assert contract_key in output 29 | 30 | foo_contract_data = output[contract_key] 31 | assert 'bin' in foo_contract_data 32 | assert 'bin-runtime' in foo_contract_data 33 | -------------------------------------------------------------------------------- /tests/core/compilation/test_compiler_remappings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from semantic_version import Spec 4 | from solc import ( 5 | get_solc_version, 6 | compile_files, 7 | ) 8 | 9 | 10 | def test_import_remapping(contracts_dir, is_new_key_format, BAR_SOURCE, BAZ_SOURCE): 11 | solc_version = get_solc_version() 12 | 13 | source_file_path = os.path.join(contracts_dir, 'Bar.sol') 14 | with open(source_file_path, 'w') as source_file: 15 | source_file.write(BAR_SOURCE) 16 | 17 | source_file_path = os.path.join(contracts_dir, 'Baz.sol') 18 | with open(source_file_path, 'w') as source_file: 19 | source_file.write(BAZ_SOURCE) 20 | 21 | output = compile_files([source_file_path], import_remappings=["contracts={}".format(contracts_dir)]) 22 | 23 | assert output 24 | 25 | if is_new_key_format: 26 | contact_key = '{0}:Baz'.format(os.path.abspath(source_file_path)) 27 | else: 28 | contact_key = 'Baz' 29 | 30 | assert contact_key in output 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Piper Merriam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc clean-build 2 | 3 | help: 4 | @echo "clean-build - remove build artifacts" 5 | @echo "clean-pyc - remove Python file artifacts" 6 | @echo "lint - check style with flake8" 7 | @echo "test - run tests quickly with the default Python" 8 | @echo "testall - run tests on every Python version with tox" 9 | @echo "release - package and upload a release" 10 | @echo "sdist - package" 11 | 12 | clean: clean-build clean-pyc 13 | 14 | clean-build: 15 | rm -fr build/ 16 | rm -fr dist/ 17 | rm -fr *.egg-info 18 | 19 | clean-pyc: 20 | find . -name '*.pyc' -exec rm -f {} + 21 | find . -name '*.pyo' -exec rm -f {} + 22 | find . -name '*~' -exec rm -f {} + 23 | 24 | lint: 25 | flake8 solc 26 | 27 | test: 28 | py.test tests 29 | 30 | test-all: 31 | tox 32 | 33 | release: clean 34 | CURRENT_SIGN_SETTING=$(git config commit.gpgSign) 35 | git config commit.gpgSign true 36 | bumpversion $(bump) 37 | git push upstream && git push upstream --tags 38 | python setup.py sdist bdist_wheel 39 | twine upload dist/* 40 | git config commit.gpgSign "$(CURRENT_SIGN_SETTING)" 41 | 42 | sdist: clean 43 | python setup.py sdist bdist_wheel 44 | ls -l dist 45 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | To start development for Populus you should begin by cloning the repo. 4 | 5 | ```bash 6 | $ git clone git@github.com/pipermerriam/py-solc.git 7 | ``` 8 | 9 | 10 | # Cute Animal Pictures 11 | 12 | All pull requests need to have a cute animal picture. This is a very important 13 | part of the development process. 14 | 15 | 16 | # Pull Requests 17 | 18 | In general, pull requests are welcome. Please try to adhere to the following. 19 | 20 | - code should conform to PEP8 and as well as the linting done by flake8 21 | - include tests. 22 | - include any relevant documentation updates. 23 | 24 | It's a good idea to make pull requests early on. A pull request represents the 25 | start of a discussion, and doesn't necessarily need to be the final, finished 26 | submission. 27 | 28 | GitHub's documentation for working on pull requests is [available here](https://help.github.com/articles/about-pull-requests/). 29 | 30 | Always run the tests before submitting pull requests, and ideally run `tox` in 31 | order to check that your modifications don't break anything. 32 | 33 | Once you've made a pull request take a look at the travis build status in the 34 | GitHub interface and make sure the tests are runnning as you'd expect. 35 | -------------------------------------------------------------------------------- /solc/utils/types.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numbers 3 | import collections 4 | 5 | 6 | if sys.version_info.major == 2: 7 | integer_types = (int, long) # noqa: F821 8 | bytes_types = (bytes, bytearray) 9 | text_types = (unicode,) # noqa: F821 10 | string_types = (basestring, bytearray) # noqa: F821 11 | else: 12 | integer_types = (int,) 13 | bytes_types = (bytes, bytearray) 14 | text_types = (str,) 15 | string_types = (bytes, str, bytearray) 16 | 17 | 18 | def is_integer(value): 19 | return isinstance(value, integer_types) and not isinstance(value, bool) 20 | 21 | 22 | def is_bytes(value): 23 | return isinstance(value, bytes_types) 24 | 25 | 26 | def is_text(value): 27 | return isinstance(value, text_types) 28 | 29 | 30 | def is_string(value): 31 | return isinstance(value, string_types) 32 | 33 | 34 | def is_boolean(value): 35 | return isinstance(value, bool) 36 | 37 | 38 | def is_dict(obj): 39 | return isinstance(obj, collections.Mapping) 40 | 41 | 42 | def is_list_like(obj): 43 | return not is_string(obj) and isinstance(obj, collections.Sequence) 44 | 45 | 46 | def is_null(obj): 47 | return obj is None 48 | 49 | 50 | def is_number(obj): 51 | return isinstance(obj, numbers.Number) 52 | -------------------------------------------------------------------------------- /solc/exceptions.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | from .utils.string import force_text 4 | 5 | 6 | def force_text_maybe(value, encoding='iso-8859-1'): 7 | if value is not None: 8 | return force_text(value) 9 | 10 | 11 | DEFAULT_MESSAGE = "An error occurred during execution" 12 | 13 | 14 | class SolcError(Exception): 15 | message = DEFAULT_MESSAGE 16 | 17 | def __init__(self, command, return_code, stdin_data, stdout_data, stderr_data, message=None): 18 | if message is not None: 19 | self.message = message 20 | self.command = command 21 | self.return_code = return_code 22 | self.stdin_data = force_text_maybe(stdin_data, 'utf8') 23 | self.stderr_data = force_text_maybe(stderr_data, 'utf8') 24 | self.stdout_data = force_text_maybe(stdout_data, 'utf8') 25 | 26 | def __str__(self): 27 | return textwrap.dedent((""" 28 | {s.message} 29 | > command: `{command}` 30 | > return code: `{s.return_code}` 31 | > stderr: 32 | {s.stdout_data} 33 | > stdout: 34 | {s.stderr_data} 35 | """).format( 36 | s=self, 37 | command=' '.join(self.command), 38 | )).strip() 39 | 40 | 41 | class ContractsNotFound(SolcError): 42 | message = "No contracts found during compilation" 43 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from setuptools import ( 4 | setup, 5 | find_packages, 6 | ) 7 | 8 | 9 | setup( 10 | name='py-solc', 11 | # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility. 12 | version='3.2.0', 13 | description="""Python wrapper around the solc binary""", 14 | long_description_markdown_filename='README.md', 15 | author='Piper Merriam', 16 | author_email='pipermerriam@gmail.com', 17 | url='https://github.com/ethereum/py-solc', 18 | include_package_data=True, 19 | py_modules=['solc'], 20 | setup_requires=['setuptools-markdown'], 21 | python_requires='>=3.4, <4', 22 | install_requires=[ 23 | "semantic_version>=2.6.0", 24 | ], 25 | license="MIT", 26 | zip_safe=False, 27 | keywords='ethereum solidity solc', 28 | packages=find_packages(exclude=["tests", "tests.*"]), 29 | classifiers=[ 30 | 'Intended Audience :: Developers', 31 | 'License :: OSI Approved :: MIT License', 32 | 'Natural Language :: English', 33 | 'Programming Language :: Python :: 2', 34 | 'Programming Language :: Python :: 2.7', 35 | 'Programming Language :: Python :: 3', 36 | 'Programming Language :: Python :: 3.4', 37 | 'Programming Language :: Python :: 3.5', 38 | ], 39 | ) 40 | -------------------------------------------------------------------------------- /tests/installation/test_solc_installation.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | import semantic_version 6 | 7 | from solc import ( 8 | get_solc_version, 9 | ) 10 | from solc.install import ( 11 | INSTALL_FUNCTIONS, 12 | get_platform, 13 | install_solc, 14 | get_executable_path, 15 | get_extract_path, 16 | ) 17 | 18 | 19 | INSTALLATION_TEST_PARAMS = tuple( 20 | (platform, version) 21 | for platform, platform_install_functions in INSTALL_FUNCTIONS.items() 22 | for version in platform_install_functions.keys() 23 | ) 24 | 25 | 26 | @pytest.mark.skipif( 27 | 'SOLC_RUN_INSTALL_TESTS' not in os.environ, 28 | reason=( 29 | "Installation tests will not run unless `SOLC_RUN_INSTALL_TESTS` " 30 | "environment variable is set" 31 | ), 32 | ) 33 | @pytest.mark.parametrize( 34 | "platform,version", 35 | INSTALLATION_TEST_PARAMS, 36 | ) 37 | def test_solc_installation_as_function_call(monkeypatch, tmpdir, platform, version): 38 | if get_platform() != platform: 39 | pytest.skip("Wront platform for install script") 40 | 41 | base_install_path = str(tmpdir.mkdir("temporary-dir")) 42 | monkeypatch.setenv('SOLC_BASE_INSTALL_PATH', base_install_path) 43 | 44 | # sanity check that it's not already installed. 45 | executable_path = get_executable_path(version) 46 | assert not os.path.exists(executable_path) 47 | 48 | install_solc(identifier=version, platform=platform) 49 | 50 | assert os.path.exists(executable_path) 51 | monkeypatch.setenv('SOLC_BINARY', executable_path) 52 | 53 | extract_path = get_extract_path(version) 54 | if os.path.exists(extract_path): 55 | contains_so_file = any( 56 | os.path.basename(path).partition(os.path.extsep)[2] == 'so' 57 | for path 58 | in os.listdir(extract_path) 59 | ) 60 | if contains_so_file: 61 | monkeypatch.setenv('LD_LIBRARY_PATH', extract_path) 62 | 63 | actual_version = get_solc_version() 64 | expected_version = semantic_version.Spec(version.lstrip('v')) 65 | 66 | assert actual_version in expected_version 67 | -------------------------------------------------------------------------------- /solc/utils/string.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import codecs 3 | 4 | from .types import ( 5 | is_bytes, 6 | is_text, 7 | is_string, 8 | is_dict, 9 | is_list_like, 10 | ) 11 | 12 | 13 | def force_bytes(value, encoding='iso-8859-1'): 14 | if is_bytes(value): 15 | return bytes(value) 16 | elif is_text(value): 17 | return codecs.encode(value, encoding) 18 | else: 19 | raise TypeError("Unsupported type: {0}".format(type(value))) 20 | 21 | 22 | def force_text(value, encoding='iso-8859-1'): 23 | if is_text(value): 24 | return value 25 | elif is_bytes(value): 26 | return codecs.decode(value, encoding) 27 | else: 28 | raise TypeError("Unsupported type: {0}".format(type(value))) 29 | 30 | 31 | def force_obj_to_bytes(obj): 32 | if is_string(obj): 33 | return force_bytes(obj) 34 | elif is_dict(obj): 35 | return { 36 | k: force_obj_to_bytes(v) for k, v in obj.items() 37 | } 38 | elif is_list_like(obj): 39 | return type(obj)(force_obj_to_bytes(v) for v in obj) 40 | else: 41 | return obj 42 | 43 | 44 | def force_obj_to_text(obj): 45 | if is_string(obj): 46 | return force_text(obj) 47 | elif is_dict(obj): 48 | return { 49 | k: force_obj_to_text(v) for k, v in obj.items() 50 | } 51 | elif is_list_like(obj): 52 | return type(obj)(force_obj_to_text(v) for v in obj) 53 | else: 54 | return obj 55 | 56 | 57 | def coerce_args_to_bytes(fn): 58 | @functools.wraps(fn) 59 | def inner(*args, **kwargs): 60 | bytes_args = force_obj_to_bytes(args) 61 | bytes_kwargs = force_obj_to_bytes(kwargs) 62 | return fn(*bytes_args, **bytes_kwargs) 63 | return inner 64 | 65 | 66 | def coerce_args_to_text(fn): 67 | @functools.wraps(fn) 68 | def inner(*args, **kwargs): 69 | text_args = force_obj_to_text(args) 70 | text_kwargs = force_obj_to_text(kwargs) 71 | return fn(*text_args, **text_kwargs) 72 | return inner 73 | 74 | 75 | def coerce_return_to_bytes(fn): 76 | @functools.wraps(fn) 77 | def inner(*args, **kwargs): 78 | return force_obj_to_bytes(fn(*args, **kwargs)) 79 | return inner 80 | 81 | 82 | def coerce_return_to_text(fn): 83 | @functools.wraps(fn) 84 | def inner(*args, **kwargs): 85 | return force_obj_to_text(fn(*args, **kwargs)) 86 | return inner 87 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 3.0.0 2 | ----- 3 | 4 | - Add support for python 3.6 5 | - Drop support for python 2.7 6 | 7 | 2.2.0 8 | ----- 9 | 10 | - Support for `solc==0.4.20` 11 | - Support for `solc==0.4.21` 12 | - Support for `solc==0.4.22` 13 | - Support for `solc==0.4.23` 14 | - Support for `solc==0.4.24` 15 | 16 | 2.1.0 17 | ----- 18 | 19 | - Support for `solc==0.4.18` 20 | - Support for `solc==0.4.19` 21 | 22 | 2.0.0 23 | ----- 24 | 25 | - Remove gevent support 26 | 27 | 1.4.0 28 | ----- 29 | 30 | - Support for `solc==0.4.17` 31 | 32 | 1.3.0 33 | ----- 34 | 35 | - Support for `solc==0.4.14` 36 | - Support for `solc==0.4.15` 37 | - Support for `solc==0.4.16` 38 | 39 | 1.2.2 40 | ----- 41 | 42 | - Bugfix for solc installation for certain platform. 43 | 44 | 1.2.1 45 | ----- 46 | 47 | - Add support for `solc==0.4.13` 48 | 49 | 1.2.0 50 | ----- 51 | 52 | - Add support for `solc==0.4.12` 53 | - Experimental `solc` installation feature. Install solc using the `solc.install_solc` function or `python -m solc.install v0.4.12` 54 | 55 | 1.1.0 56 | ----- 57 | 58 | - Add support for `solc==0.4.11` 59 | - Add support for `--standard-json` command line argument via `compile_standard` function. 60 | - Add support for `--allow-paths` command line argument via `allow_paths` kwarg. 61 | 62 | 1.0.1 63 | ----- 64 | 65 | - Expands test suite to include solidity `0.4.7`, `0.4.8` and `0.4.9` 66 | 67 | 1.0.0 68 | ----- 69 | 70 | - Remove contract data modification from library 71 | 72 | 0.9.1 73 | ----- 74 | 75 | - Add `devdoc` and `userdoc` to compiler output object. 76 | 77 | 0.9.0 78 | ----- 79 | 80 | - Change from `async` terminology to `compat` 81 | - Change env variables to `TESTRPC_THREADING_BACKEND` and `THREADING_BACKEND` 82 | 83 | 0.8.0 84 | ----- 85 | 86 | - Remove hard gevent dependency 87 | 88 | 0.7.0 89 | ----- 90 | 91 | - Drop support for 0.3.6 92 | - Add support for 0.4.6 93 | - Add ability to specify import remappings 94 | 95 | 0.6.0 96 | ----- 97 | 98 | - Support for solc 0.4.1 and 0.4.2 99 | 100 | 0.5.0 101 | ----- 102 | 103 | - Add ability to set solc binary path with env variable `SOLC_BINARY` 104 | 105 | 0.4.0 106 | ----- 107 | 108 | - Fix bug where wrapper didn't use the provided alternative solc binary path. 109 | - Fix issue where compile result solc version wouldn't match actual compiler 110 | version if custom compiler path was used. 111 | - Use `gevent` 112 | 113 | 0.3.0 114 | ----- 115 | 116 | - Graceful failure mode when no contracts are found in the compiler output. 117 | 118 | 0.2.0 119 | ----- 120 | 121 | - Add `link_code` function. 122 | 123 | 0.1.0 124 | ----- 125 | 126 | - Initial Release 127 | -------------------------------------------------------------------------------- /tests/core/linking/test_library_linking.py: -------------------------------------------------------------------------------- 1 | from solc import link_code 2 | 3 | CODE = "6060604052610199806100126000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806344fd4fa01461004f57806358de5f041461005e578063e7f09e051461006d5761004d565b005b61005c600480505061007c565b005b61006b60048050506100db565b005b61007a600480505061013a565b005b73__TestB_________________________________630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b565b73__TestC_________________________________630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b565b73__TestA_________________________________630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b56" 4 | 5 | TEST_A_ADDRESS = "0xd3cda913deb6f67967b99d67acdfa1712c293601" 6 | TEST_B_ADDRESS = "0x304a554a310c7e546dfe434669c62820b7d83490" 7 | TEST_C_ADDRESS = "0xbb9bc244d798123fde783fcc1c72d3bb8c189413" 8 | 9 | LINKED_CODE = "6060604052610199806100126000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806344fd4fa01461004f57806358de5f041461005e578063e7f09e051461006d5761004d565b005b61005c600480505061007c565b005b61006b60048050506100db565b005b61007a600480505061013a565b005b73304a554a310c7e546dfe434669c62820b7d83490630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b565b73bb9bc244d798123fde783fcc1c72d3bb8c189413630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b565b73d3cda913deb6f67967b99d67acdfa1712c293601630c55699c604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303818660325a03f415610002575050505b56" 10 | 11 | 12 | def test_partial_code_linking(): 13 | output = link_code(CODE, {'TestA': TEST_A_ADDRESS}) 14 | assert '__TestA__' not in output 15 | assert '__TestB__' in output 16 | assert '__TestC__' in output 17 | assert TEST_A_ADDRESS[2:] in output 18 | 19 | 20 | def test_full_code_linking(): 21 | output = link_code(CODE, { 22 | 'TestA': TEST_A_ADDRESS, 23 | 'TestB': TEST_B_ADDRESS, 24 | 'TestC': TEST_C_ADDRESS, 25 | }) 26 | assert '__TestA__' not in output 27 | assert '__TestB__' not in output 28 | assert '__TestC__' not in output 29 | assert TEST_A_ADDRESS[2:] in output 30 | assert TEST_B_ADDRESS[2:] in output 31 | assert TEST_C_ADDRESS[2:] in output 32 | assert output == LINKED_CODE 33 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import textwrap 4 | 5 | from semantic_version import Spec 6 | 7 | from solc import get_solc_version 8 | from solc.main import solc_supports_standard_json_interface 9 | 10 | 11 | @pytest.fixture() 12 | def contracts_dir(tmpdir): 13 | return str(tmpdir.mkdir("contracts")) 14 | 15 | 16 | @pytest.fixture(scope="session") 17 | def solc_version(): 18 | return get_solc_version() 19 | 20 | 21 | @pytest.fixture() 22 | def supported_solc_version(solc_version): 23 | if solc_version not in Spec('>=0.4.1,<=0.4.25,!=0.4.10,!=0.4.3,!=0.4.4,!=0.4.5'): 24 | raise AssertionError("Unsupported compiler version: {0}".format(solc_version)) 25 | 26 | return solc_version 27 | 28 | 29 | @pytest.fixture() 30 | def is_new_key_format(): 31 | return get_solc_version() in Spec('>=0.4.9') 32 | 33 | 34 | @pytest.fixture() 35 | def FOO_SOURCE(supported_solc_version, solc_version): 36 | if solc_version in Spec('<0.4.17'): 37 | return textwrap.dedent('''\ 38 | pragma solidity ^0.4.0; 39 | 40 | contract Foo { 41 | function Foo() {} 42 | 43 | function return13() public returns (uint) { 44 | return 13; 45 | } 46 | } 47 | ''') 48 | else: 49 | return textwrap.dedent('''\ 50 | pragma solidity ^0.4.17; 51 | 52 | contract Foo { 53 | function Foo() public {} 54 | 55 | function return13() public pure returns (uint) { 56 | return 13; 57 | } 58 | } 59 | ''') 60 | 61 | 62 | @pytest.fixture() 63 | def BAR_SOURCE(supported_solc_version): 64 | return textwrap.dedent('''\ 65 | pragma solidity ^0.4.0; 66 | 67 | contract Bar { 68 | function Bar() public {} 69 | } 70 | ''') 71 | 72 | 73 | @pytest.fixture() 74 | def BAZ_SOURCE(supported_solc_version): 75 | return textwrap.dedent('''\ 76 | pragma solidity ^0.4.0; 77 | 78 | import "contracts/Bar.sol"; 79 | 80 | contract Baz is Bar { 81 | function Baz() public {} 82 | 83 | function get_funky() public returns (string) { 84 | return "funky"; 85 | } 86 | } 87 | ''') 88 | 89 | 90 | @pytest.fixture() 91 | def INVALID_SOURCE(supported_solc_version): 92 | return textwrap.dedent('''\ 93 | pragma solidity ^0.4.0; 94 | contract Foo { 95 | ''') 96 | 97 | 98 | def pytest_runtest_setup(item): 99 | if (item.get_marker('requires_standard_json') is not None and 100 | not solc_supports_standard_json_interface()): 101 | pytest.skip('requires `--standard-json` support') 102 | -------------------------------------------------------------------------------- /tests/core/wrapper/test_solc_wrapper.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import pytest 4 | 5 | import json 6 | import os 7 | 8 | from solc import get_solc_version 9 | from solc.wrapper import ( 10 | solc_wrapper, 11 | ) 12 | 13 | 14 | def is_benign(err): 15 | return not err or err in ( 16 | 'Warning: This is a pre-release compiler version, please do not use it in production.\n', 17 | ) 18 | 19 | 20 | def test_help(): 21 | output, err, _, _ = solc_wrapper(help=True, success_return_code=1) 22 | assert output 23 | assert 'Solidity' in output 24 | assert is_benign(err) 25 | 26 | 27 | def test_version(): 28 | output, err, _, _ = solc_wrapper(version=True) 29 | assert output 30 | assert 'Version' in output 31 | assert is_benign(err) 32 | 33 | 34 | def test_providing_stdin(FOO_SOURCE): 35 | output, err, _, _ = solc_wrapper(stdin=FOO_SOURCE, bin=True) 36 | assert output 37 | assert 'Foo' in output 38 | assert is_benign(err) 39 | 40 | 41 | def test_providing_single_source_file(contracts_dir, FOO_SOURCE): 42 | source_file_path = os.path.join(contracts_dir, 'Foo.sol') 43 | with open(source_file_path, 'w') as source_file: 44 | source_file.write(FOO_SOURCE) 45 | 46 | output, err, _, _ = solc_wrapper(source_files=[source_file_path], bin=True) 47 | assert output 48 | assert 'Foo' in output 49 | assert is_benign(err) 50 | 51 | 52 | def test_providing_multiple_source_files(contracts_dir, FOO_SOURCE, BAR_SOURCE): 53 | source_file_a_path = os.path.join(contracts_dir, 'Foo.sol') 54 | source_file_b_path = os.path.join(contracts_dir, 'Bar.sol') 55 | 56 | with open(source_file_a_path, 'w') as source_file: 57 | source_file.write(FOO_SOURCE) 58 | with open(source_file_b_path, 'w') as source_file: 59 | source_file.write(BAR_SOURCE) 60 | 61 | output, err, _, _ = solc_wrapper(source_files=[source_file_a_path, source_file_b_path], bin=True) 62 | assert output 63 | assert 'Foo' in output 64 | assert 'Bar' in output 65 | assert is_benign(err) 66 | 67 | 68 | @pytest.mark.requires_standard_json 69 | def test_providing_standard_json_input(FOO_SOURCE, BAR_SOURCE): 70 | stdin = json.dumps({ 71 | "language": "Solidity", 72 | "sources": { 73 | "Foo.sol": { 74 | "content": FOO_SOURCE 75 | }, 76 | "Bar.sol": { 77 | "content": BAR_SOURCE 78 | } 79 | }, 80 | "settings": 81 | { 82 | "outputSelection": { 83 | "*": { 84 | "*": [ "abi", "evm.bytecode.link_references", "evm.bytecode.object", "devdoc", "metadata", "userdoc" ] 85 | } 86 | } 87 | } 88 | }) 89 | 90 | output, err, _, _ = solc_wrapper(stdin=stdin, standard_json=True) 91 | output = json.loads(output) 92 | assert output 93 | assert 'Foo.sol' in output['contracts'] 94 | assert 'Bar.sol' in output['contracts'] 95 | assert is_benign(err) 96 | -------------------------------------------------------------------------------- /tests/core/compilation/test_compile_standard.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from solc import ( 6 | compile_standard, 7 | ) 8 | from solc.exceptions import SolcError 9 | 10 | pytestmark = pytest.mark.requires_standard_json 11 | 12 | 13 | def contract_in_output_map(contract_name, contract_map): 14 | try: 15 | return int(contract_map 16 | ['contracts/{}.sol'.format(contract_name)] 17 | [contract_name] 18 | ['evm']['bytecode']['object'], 16) is not None 19 | except: 20 | return False 21 | 22 | 23 | def test_compile_standard(FOO_SOURCE): 24 | result = compile_standard({ 25 | 'language': 'Solidity', 26 | 'sources': { 27 | 'contracts/Foo.sol': { 28 | 'content': FOO_SOURCE, 29 | }, 30 | }, 31 | 'outputSelection': { 32 | "*": {"*": ["evm.bytecode.object"]}, 33 | }, 34 | }) 35 | 36 | assert isinstance(result, dict) 37 | assert 'contracts' in result 38 | assert contract_in_output_map('Foo', result['contracts']) 39 | 40 | 41 | def test_compile_standard_invalid_source(INVALID_SOURCE): 42 | with pytest.raises(SolcError): 43 | compile_standard({ 44 | 'language': 'Solidity', 45 | 'sources': { 46 | 'contracts/Foo.sol': { 47 | 'content': INVALID_SOURCE, 48 | }, 49 | }, 50 | 'outputSelection': { 51 | "*": {"*": ["evm.bytecode.object"]}, 52 | }, 53 | }) 54 | 55 | 56 | def test_compile_standard_with_dependency(BAR_SOURCE, BAZ_SOURCE): 57 | result = compile_standard({ 58 | 'language': 'Solidity', 59 | 'sources': { 60 | 'contracts/Bar.sol': { 61 | 'content': BAR_SOURCE, 62 | }, 63 | 'contracts/Baz.sol': { 64 | 'content': BAZ_SOURCE, 65 | }, 66 | }, 67 | 'outputSelection': { 68 | "*": {"*": ["evm.bytecode.object"]}, 69 | }, 70 | }) 71 | 72 | assert isinstance(result, dict) 73 | assert 'contracts' in result 74 | assert contract_in_output_map('Bar', result['contracts']) 75 | assert contract_in_output_map('Baz', result['contracts']) 76 | 77 | 78 | def test_compile_standard_with_file_paths(FOO_SOURCE, is_new_key_format, contracts_dir): 79 | source_file_path = os.path.join(contracts_dir, 'Foo.sol') 80 | with open(source_file_path, 'w') as source_file: 81 | source_file.write(FOO_SOURCE) 82 | 83 | result = compile_standard({ 84 | 'language': 'Solidity', 85 | 'sources': { 86 | 'contracts/Foo.sol': { 87 | 'urls': [source_file_path], 88 | }, 89 | }, 90 | 'outputSelection': { 91 | "*": {"*": ["evm.bytecode.object"]}, 92 | }, 93 | }, allow_paths=contracts_dir) 94 | 95 | assert isinstance(result, dict) 96 | assert 'contracts' in result 97 | assert contract_in_output_map('Foo', result['contracts']) 98 | -------------------------------------------------------------------------------- /solc/wrapper.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | import subprocess 5 | 6 | from .exceptions import ( 7 | SolcError, 8 | ) 9 | from .utils.string import ( 10 | force_bytes, 11 | coerce_return_to_text, 12 | ) 13 | 14 | 15 | def get_solc_binary_path(): 16 | return os.environ.get('SOLC_BINARY', 'solc') 17 | 18 | 19 | @coerce_return_to_text 20 | def solc_wrapper(solc_binary=None, 21 | stdin=None, 22 | help=None, 23 | version=None, 24 | add_std=None, 25 | combined_json=None, 26 | optimize=None, 27 | optimize_runs=None, 28 | libraries=None, 29 | output_dir=None, 30 | gas=None, 31 | assemble=None, 32 | link=None, 33 | source_files=None, 34 | import_remappings=None, 35 | ast=None, 36 | ast_json=None, 37 | asm=None, 38 | asm_json=None, 39 | opcodes=None, 40 | bin=None, 41 | bin_runtime=None, 42 | clone_bin=None, 43 | abi=None, 44 | interface=None, 45 | hashes=None, 46 | userdoc=None, 47 | devdoc=None, 48 | formal=None, 49 | allow_paths=None, 50 | standard_json=None, 51 | success_return_code=0, 52 | evm_version=None): 53 | if solc_binary is None: 54 | solc_binary = get_solc_binary_path() 55 | 56 | command = [solc_binary] 57 | 58 | if help: 59 | command.append('--help') 60 | 61 | if version: 62 | command.append('--version') 63 | 64 | if add_std: 65 | command.append('--add-std') 66 | 67 | if optimize: 68 | command.append('--optimize') 69 | 70 | if optimize_runs is not None: 71 | command.extend(('--optimize-runs', str(optimize_runs))) 72 | 73 | if link: 74 | command.append('--link') 75 | 76 | if libraries is not None: 77 | command.extend(('--libraries', libraries)) 78 | 79 | if output_dir is not None: 80 | command.extend(('--output-dir', output_dir)) 81 | 82 | if combined_json: 83 | command.extend(('--combined-json', combined_json)) 84 | 85 | if gas: 86 | command.append('--gas') 87 | 88 | if allow_paths: 89 | command.extend(('--allow-paths', allow_paths)) 90 | 91 | if standard_json: 92 | command.append('--standard-json') 93 | 94 | if assemble: 95 | command.append('--assemble') 96 | 97 | if import_remappings is not None: 98 | command.extend(import_remappings) 99 | 100 | if source_files is not None: 101 | command.extend(source_files) 102 | 103 | # 104 | # Output configuration 105 | # 106 | if ast: 107 | command.append('--ast') 108 | 109 | if ast_json: 110 | command.append('--ast-json') 111 | 112 | if asm: 113 | command.append('--asm') 114 | 115 | if asm_json: 116 | command.append('--asm-json') 117 | 118 | if opcodes: 119 | command.append('--opcodes') 120 | 121 | if bin: 122 | command.append('--bin') 123 | 124 | if bin_runtime: 125 | command.append('--bin-runtime') 126 | 127 | if clone_bin: 128 | command.append('--clone-bin') 129 | 130 | if abi: 131 | command.append('--abi') 132 | 133 | if interface: 134 | command.append('--interface') 135 | 136 | if hashes: 137 | command.append('--hashes') 138 | 139 | if userdoc: 140 | command.append('--userdoc') 141 | 142 | if devdoc: 143 | command.append('--devdoc') 144 | 145 | if formal: 146 | command.append('--formal') 147 | 148 | if stdin is not None: 149 | # solc seems to expects utf-8 from stdin: 150 | # see Scanner class in Solidity source 151 | stdin = force_bytes(stdin, 'utf8') 152 | 153 | if evm_version: 154 | command.extend(('--evm-version', evm_version)) 155 | 156 | proc = subprocess.Popen(command, 157 | stdin=subprocess.PIPE, 158 | stdout=subprocess.PIPE, 159 | stderr=subprocess.PIPE) 160 | 161 | stdoutdata, stderrdata = proc.communicate(stdin) 162 | 163 | if proc.returncode != success_return_code: 164 | raise SolcError( 165 | command=command, 166 | return_code=proc.returncode, 167 | stdin_data=stdin, 168 | stdout_data=stdoutdata, 169 | stderr_data=stderrdata, 170 | ) 171 | 172 | return stdoutdata, stderrdata, command, proc 173 | -------------------------------------------------------------------------------- /solc/main.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import functools 4 | import json 5 | import re 6 | 7 | from .exceptions import ( 8 | SolcError, 9 | ContractsNotFound, 10 | ) 11 | 12 | from .utils.filesystem import ( 13 | is_executable_available, 14 | ) 15 | from .wrapper import ( 16 | get_solc_binary_path, 17 | solc_wrapper, 18 | ) 19 | 20 | import semantic_version 21 | 22 | 23 | VERSION_DEV_DATE_MANGLER_RE = re.compile(r'(\d{4})\.0?(\d{1,2})\.0?(\d{1,2})') 24 | strip_zeroes_from_month_and_day = functools.partial(VERSION_DEV_DATE_MANGLER_RE.sub, 25 | r'\g<1>.\g<2>.\g<3>') 26 | 27 | 28 | def is_solc_available(): 29 | solc_binary = get_solc_binary_path() 30 | return is_executable_available(solc_binary) 31 | 32 | 33 | def get_solc_version_string(**kwargs): 34 | kwargs['version'] = True 35 | stdoutdata, stderrdata, command, proc = solc_wrapper(**kwargs) 36 | _, _, version_string = stdoutdata.partition('\n') 37 | if not version_string or not version_string.startswith('Version: '): 38 | raise SolcError( 39 | command=command, 40 | return_code=proc.returncode, 41 | stdin_data=None, 42 | stdout_data=stdoutdata, 43 | stderr_data=stderrdata, 44 | message="Unable to extract version string from command output", 45 | ) 46 | return version_string 47 | 48 | 49 | def get_solc_version(**kwargs): 50 | # semantic_version as of 2017-5-5 expects only one + to be used in string 51 | return semantic_version.Version( 52 | strip_zeroes_from_month_and_day( 53 | get_solc_version_string(**kwargs) 54 | [len('Version: '):] 55 | .replace('++', 'pp'))) 56 | 57 | 58 | def solc_supports_standard_json_interface(**kwargs): 59 | return get_solc_version() in semantic_version.Spec('>=0.4.11') 60 | 61 | 62 | def _parse_compiler_output(stdoutdata): 63 | output = json.loads(stdoutdata) 64 | 65 | if "contracts" not in output: 66 | return {} 67 | 68 | contracts = output['contracts'] 69 | sources = output['sources'] 70 | 71 | for source, data in contracts.items(): 72 | data['abi'] = json.loads(data['abi']) 73 | data['ast'] = sources[source.split(':')[0]]['AST'] 74 | 75 | return contracts 76 | 77 | 78 | ALL_OUTPUT_VALUES = ( 79 | "abi", 80 | "asm", 81 | "ast", 82 | "bin", 83 | "bin-runtime", 84 | "clone-bin", 85 | "devdoc", 86 | "interface", 87 | "opcodes", 88 | "userdoc", 89 | ) 90 | 91 | 92 | def compile_source(source, 93 | allow_empty=False, 94 | output_values=ALL_OUTPUT_VALUES, 95 | **kwargs): 96 | if 'stdin' in kwargs: 97 | raise ValueError( 98 | "The `stdin` keyword is not allowed in the `compile_source` function" 99 | ) 100 | if 'combined_json' in kwargs: 101 | raise ValueError( 102 | "The `combined_json` keyword is not allowed in the `compile_source` function" 103 | ) 104 | 105 | combined_json = ','.join(output_values) 106 | compiler_kwargs = dict(stdin=source, combined_json=combined_json, **kwargs) 107 | 108 | stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs) 109 | 110 | contracts = _parse_compiler_output(stdoutdata) 111 | 112 | if not contracts and not allow_empty: 113 | raise ContractsNotFound( 114 | command=command, 115 | return_code=proc.returncode, 116 | stdin_data=source, 117 | stdout_data=stdoutdata, 118 | stderr_data=stderrdata, 119 | ) 120 | return contracts 121 | 122 | 123 | def compile_files(source_files, 124 | allow_empty=False, 125 | output_values=ALL_OUTPUT_VALUES, 126 | **kwargs): 127 | if 'combined_json' in kwargs: 128 | raise ValueError( 129 | "The `combined_json` keyword is not allowed in the `compile_files` function" 130 | ) 131 | 132 | combined_json = ','.join(output_values) 133 | compiler_kwargs = dict(source_files=source_files, combined_json=combined_json, **kwargs) 134 | 135 | stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs) 136 | 137 | contracts = _parse_compiler_output(stdoutdata) 138 | 139 | if not contracts and not allow_empty: 140 | raise ContractsNotFound( 141 | command=command, 142 | return_code=proc.returncode, 143 | stdin_data=None, 144 | stdout_data=stdoutdata, 145 | stderr_data=stderrdata, 146 | ) 147 | return contracts 148 | 149 | 150 | def compile_standard(input_data, allow_empty=False, **kwargs): 151 | if not input_data.get('sources') and not allow_empty: 152 | raise ContractsNotFound( 153 | command=None, 154 | return_code=None, 155 | stdin_data=json.dumps(input_data, sort_keys=True, indent=2), 156 | stdout_data=None, 157 | stderr_data=None, 158 | ) 159 | 160 | stdoutdata, stderrdata, command, proc = solc_wrapper( 161 | stdin=json.dumps(input_data), 162 | standard_json=True, 163 | **kwargs 164 | ) 165 | 166 | compiler_output = json.loads(stdoutdata) 167 | if 'errors' in compiler_output: 168 | has_errors = any(error['severity'] == 'error' for error in compiler_output['errors']) 169 | if has_errors: 170 | error_message = "\n".join(tuple( 171 | error['formattedMessage'] 172 | for error in compiler_output['errors'] 173 | if error['severity'] == 'error' 174 | )) 175 | raise SolcError( 176 | command, 177 | proc.returncode, 178 | json.dumps(input_data), 179 | stdoutdata, 180 | stderrdata, 181 | message=error_message, 182 | ) 183 | return compiler_output 184 | 185 | 186 | def link_code(unlinked_data, libraries): 187 | libraries_arg = ','.join(( 188 | ':'.join((lib_name, lib_address)) 189 | for lib_name, lib_address in libraries.items() 190 | )) 191 | stdoutdata, stderrdata, _, _ = solc_wrapper( 192 | stdin=unlinked_data, 193 | link=True, 194 | libraries=libraries_arg, 195 | ) 196 | 197 | return stdoutdata.strip() 198 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.5" 4 | os: 5 | - linux 6 | dist: trusty 7 | sudo: required 8 | env: 9 | global: 10 | - SOLC_BASE_INSTALL_PATH=$TRAVIS_BUILD_DIR 11 | matrix: 12 | include: 13 | # lint 14 | - python: "3.5" 15 | env: TOX_POSARGS="-e flake8" 16 | # installation 17 | - python: "3.4" 18 | env: TOX_POSARGS="-e py34-installation" 19 | - python: "3.5" 20 | env: TOX_POSARGS="-e py35-installation" 21 | - python: "3.6" 22 | env: TOX_POSARGS="-e py36-installation" 23 | # solc 0.4.1 24 | - python: "3.4" 25 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.1 26 | - python: "3.5" 27 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.1 28 | - python: "3.6" 29 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.1 30 | # solc 0.4.2 31 | - python: "3.4" 32 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.2 33 | - python: "3.5" 34 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.2 35 | - python: "3.6" 36 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.2 37 | # solc 0.4.6 38 | - python: "3.4" 39 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.6 40 | - python: "3.5" 41 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.6 42 | - python: "3.6" 43 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.6 44 | # solc 0.4.7 45 | - python: "3.4" 46 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.7 47 | - python: "3.5" 48 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.7 49 | - python: "3.6" 50 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.7 51 | # solc 0.4.8 52 | - python: "3.4" 53 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.8 54 | - python: "3.5" 55 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.8 56 | - python: "3.6" 57 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.8 58 | # solc 0.4.9 59 | - python: "3.4" 60 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.9 61 | - python: "3.5" 62 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.9 63 | - python: "3.6" 64 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.9 65 | # solc 0.4.11 66 | - python: "3.4" 67 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.11 68 | - python: "3.5" 69 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.11 70 | - python: "3.6" 71 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.11 72 | # solc 0.4.12 73 | - python: "3.4" 74 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.12 75 | - python: "3.5" 76 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.12 77 | - python: "3.6" 78 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.12 79 | # solc 0.4.13 80 | - python: "3.4" 81 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.13 82 | - python: "3.5" 83 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.13 84 | - python: "3.6" 85 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.13 86 | # solc 0.4.14 87 | - python: "3.4" 88 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.14 89 | - python: "3.5" 90 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.14 91 | - python: "3.6" 92 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.14 93 | # solc 0.4.15 94 | - python: "3.4" 95 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.15 96 | - python: "3.5" 97 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.15 98 | - python: "3.6" 99 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.15 100 | # solc 0.4.16 101 | - python: "3.4" 102 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.16 103 | - python: "3.5" 104 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.16 105 | - python: "3.6" 106 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.16 107 | # solc 0.4.17 108 | - python: "3.4" 109 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.17 110 | - python: "3.5" 111 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.17 112 | - python: "3.6" 113 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.17 114 | # solc 0.4.18 115 | - python: "3.4" 116 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.18 117 | - python: "3.5" 118 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.18 119 | - python: "3.6" 120 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.18 121 | # solc 0.4.19 122 | - python: "3.4" 123 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.19 124 | - python: "3.5" 125 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.19 126 | - python: "3.6" 127 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.19 128 | # solc 0.4.20 129 | - python: "3.4" 130 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.20 131 | - python: "3.5" 132 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.20 133 | - python: "3.6" 134 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.20 135 | # solc 0.4.21 136 | - python: "3.4" 137 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.21 138 | - python: "3.5" 139 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.21 140 | - python: "3.6" 141 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.21 142 | # solc 0.4.22 143 | - python: "3.4" 144 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.22 145 | - python: "3.5" 146 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.22 147 | - python: "3.6" 148 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.22 149 | # solc 0.4.23 150 | - python: "3.4" 151 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.23 152 | - python: "3.5" 153 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.23 154 | - python: "3.6" 155 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.23 156 | # solc 0.4.24 157 | - python: "3.4" 158 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.24 159 | - python: "3.5" 160 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.24 161 | - python: "3.6" 162 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.24 163 | # solc 0.4.25 164 | - python: "3.4" 165 | env: TOX_POSARGS="-e py34" SOLC_VERSION=v0.4.25 166 | - python: "3.5" 167 | env: TOX_POSARGS="-e py35" SOLC_VERSION=v0.4.25 168 | - python: "3.6" 169 | env: TOX_POSARGS="-e py36" SOLC_VERSION=v0.4.25 170 | 171 | cache: 172 | - pip: true 173 | before_install: 174 | install: 175 | - travis_retry pip install setuptools --upgrade 176 | - travis_retry pip install tox 177 | - pip --version 178 | - pip install -e . # install py-solc globally in order to use the solc installation scripts. 179 | before_script: 180 | - python --version 181 | - if [ -n "$SOLC_VERSION" ]; then python -m solc.install $SOLC_VERSION; fi 182 | - if [ -n "$SOLC_VERSION" ]; then export SOLC_BINARY="$SOLC_BASE_INSTALL_PATH/solc-$SOLC_VERSION/bin/solc"; fi 183 | - if [ -n "$SOLC_VERSION" ]; then export LD_LIBRARY_PATH="$SOLC_BASE_INSTALL_PATH/solc-$SOLC_VERSION/bin"; fi 184 | - env 185 | - if [ -n "$SOLC_BINARY" ]; then $SOLC_BINARY --version; fi 186 | script: 187 | - tox $TOX_POSARGS 188 | after_script: 189 | - cat .tox/$TOX_POSARGS/log/*.log 190 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # py-solc 2 | 3 | [![Build Status](https://travis-ci.org/ethereum/py-solc.png)](https://travis-ci.org/ethereum/py-solc) 4 | [![PyPi version](https://img.shields.io/pypi/v/py-solc.svg)](https://pypi.python.org/pypi/py-solc) 5 | 6 | 7 | Python wrapper around the `solc` Solidity compiler. 8 | 9 | 10 | ## Dependency 11 | 12 | This library requires the `solc` executable to be present. 13 | 14 | Only versions `>=0.4.2` are supported and tested though this library may work 15 | with other versions. 16 | 17 | [solc installation instructions](http://solidity.readthedocs.io/en/latest/installing-solidity.html) 18 | 19 | 20 | ## Quickstart 21 | 22 | Installation 23 | 24 | ```sh 25 | pip install py-solc 26 | ``` 27 | 28 | ## Development 29 | 30 | Clone the repository and then run: 31 | 32 | ```sh 33 | pip install -e . -r requirements-dev.txt 34 | ``` 35 | 36 | 37 | ### Running the tests 38 | 39 | You can run the tests with: 40 | 41 | ```sh 42 | py.test tests 43 | ``` 44 | 45 | Or you can install `tox` to run the full test suite. 46 | 47 | 48 | ### Releasing 49 | 50 | Pandoc is required for transforming the markdown README to the proper format to 51 | render correctly on pypi. 52 | 53 | For Debian-like systems: 54 | 55 | ``` 56 | apt install pandoc 57 | ``` 58 | 59 | Or on OSX: 60 | 61 | ```sh 62 | brew install pandoc 63 | ``` 64 | 65 | To release a new version: 66 | 67 | ```sh 68 | bumpversion $$VERSION_PART_TO_BUMP$$ 69 | git push && git push --tags 70 | make release 71 | ``` 72 | 73 | 74 | #### How to bumpversion 75 | 76 | The version format for this repo is `{major}.{minor}.{patch}` for stable, and 77 | `{major}.{minor}.{patch}-{stage}.{devnum}` for unstable (`stage` can be alpha or beta). 78 | 79 | To issue the next version in line, use bumpversion and specify which part to bump, 80 | like `bumpversion minor` or `bumpversion devnum`. 81 | 82 | If you are in a beta version, `bumpversion stage` will switch to a stable. 83 | 84 | To issue an unstable version when the current version is stable, specify the 85 | new version explicitly, like `bumpversion --new-version 4.0.0-alpha.1 devnum` 86 | 87 | 88 | 89 | 90 | ## Standard JSON Compilation 91 | 92 | Use the `solc.compile_standard` function to make use the [standard-json] compilation feature. 93 | 94 | [Solidity Documentation for Standard JSON input and ouptup format](http://solidity.readthedocs.io/en/develop/using-the-compiler.html#compiler-input-and-output-json-description) 95 | 96 | ``` 97 | >>> from solc import compile_standard 98 | >>> compile_standard({ 99 | ... 'language': 'Solidity', 100 | ... 'sources': {'Foo.sol': 'content': "...."}, 101 | ... }) 102 | { 103 | 'contracts': {...}, 104 | 'sources': {...}, 105 | 'errors': {...}, 106 | } 107 | >>> compile_standard({ 108 | ... 'language': 'Solidity', 109 | ... 'sources': {'Foo.sol': 'urls': ["/path/to/my/sources/Foo.sol"]}, 110 | ... }, allow_paths="/path/to/my/sources") 111 | { 112 | 'contracts': {...}, 113 | 'sources': {...}, 114 | 'errors': {...}, 115 | } 116 | ``` 117 | 118 | 119 | ## Legacy Combined JSON compilation 120 | 121 | 122 | ```python 123 | >>> from solc import compile_source, compile_files, link_code 124 | >>> compile_source("contract Foo { function Foo() {} }") 125 | { 126 | 'Foo': { 127 | 'abi': [{'inputs': [], 'type': 'constructor'}], 128 | 'code': '0x60606040525b5b600a8060126000396000f360606040526008565b00', 129 | 'code_runtime': '0x60606040526008565b00', 130 | 'source': None, 131 | 'meta': { 132 | 'compilerVersion': '0.3.5-9da08ac3', 133 | 'language': 'Solidity', 134 | 'languageVersion': '0', 135 | }, 136 | }, 137 | } 138 | >>> compile_files(["/path/to/Foo.sol", "/path/to/Bar.sol"]) 139 | { 140 | 'Foo': { 141 | 'abi': [{'inputs': [], 'type': 'constructor'}], 142 | 'code': '0x60606040525b5b600a8060126000396000f360606040526008565b00', 143 | 'code_runtime': '0x60606040526008565b00', 144 | 'source': None, 145 | 'meta': { 146 | 'compilerVersion': '0.3.5-9da08ac3', 147 | 'language': 'Solidity', 148 | 'languageVersion': '0', 149 | }, 150 | }, 151 | 'Bar': { 152 | 'abi': [{'inputs': [], 'type': 'constructor'}], 153 | 'code': '0x60606040525b5b600a8060126000396000f360606040526008565b00', 154 | 'code_runtime': '0x60606040526008565b00', 155 | 'source': None, 156 | 'meta': { 157 | 'compilerVersion': '0.3.5-9da08ac3', 158 | 'language': 'Solidity', 159 | 'languageVersion': '0', 160 | }, 161 | }, 162 | } 163 | >>> unlinked_code = "606060405260768060106000396000f3606060405260e060020a6000350463e7f09e058114601a575b005b60187f0c55699c00000000000000000000000000000000000000000000000000000000606090815273__TestA_________________________________90630c55699c906064906000906004818660325a03f41560025750505056" 164 | >>> link_code(unlinked_code, {'TestA': '0xd3cda913deb6f67967b99d67acdfa1712c293601'}) 165 | ... "606060405260768060106000396000f3606060405260e060020a6000350463e7f09e058114601a575b005b60187f0c55699c00000000000000000000000000000000000000000000000000000000606090815273d3cda913deb6f67967b99d67acdfa1712c29360190630c55699c906064906000906004818660325a03f41560025750505056" 166 | ``` 167 | 168 | ## Setting the path to the `solc` binary 169 | 170 | You can use the environment variable `SOLC_BINARY` to set the path to your solc binary. 171 | 172 | 173 | ## Installing the `solc` binary 174 | 175 | > This feature is experimental and subject to breaking changes. 176 | 177 | Any of the following versions of `solc` can be installed using `py-solc` on the 178 | listed platforms. 179 | 180 | * `v0.4.1` (linux) 181 | * `v0.4.2` (linux) 182 | * `v0.4.6` (linux) 183 | * `v0.4.7` (linux) 184 | * `v0.4.8` (linux/osx) 185 | * `v0.4.9` (linux) 186 | * `v0.4.11` (linux/osx) 187 | * `v0.4.12` (linux/osx) 188 | * `v0.4.13` (linux/osx) 189 | * `v0.4.14` (linux/osx) 190 | * `v0.4.15` (linux/osx) 191 | * `v0.4.16` (linux/osx) 192 | * `v0.4.17` (linux/osx) 193 | * `v0.4.18` (linux/osx) 194 | * `v0.4.19` (linux/osx) 195 | * `v0.4.20` (linux/osx) 196 | * `v0.4.21` (linux/osx) 197 | * `v0.4.22` (linux/osx) 198 | * `v0.4.23` (linux/osx) 199 | * `v0.4.24` (linux/osx) 200 | * `v0.4.25` (linux/osx) 201 | 202 | Installation can be done via the command line: 203 | 204 | ```bash 205 | $ python -m solc.install v0.4.25 206 | ``` 207 | 208 | Or from python using the `install_solc` function. 209 | 210 | ```python 211 | >>> from solc import install_solc 212 | >>> install_solc('v0.4.25') 213 | ``` 214 | 215 | The installed binary can be found under your home directory. The `v0.4.25` 216 | binary would be located at `$HOME/.py-solc/solc-v0.4.25/bin/solc`. Older linux 217 | installs will also require that you set the environment variable 218 | `LD_LIBRARY_PATH=$HOME/.py-solc/solc-v0.4.25/bin` 219 | 220 | 221 | ## Import path remappings 222 | 223 | `solc` provides path aliasing allow you to have more reusable project configurations. 224 | 225 | You can use this like: 226 | 227 | ``` 228 | from solc import compile_source, compile_files, link_code 229 | 230 | compile_files([source_file_path], import_remappings=["zeppeling=/my-zeppelin-checkout-folder"]) 231 | ``` 232 | 233 | [More information about solc import aliasing](http://solidity.readthedocs.io/en/develop/layout-of-source-files.html#paths) 234 | -------------------------------------------------------------------------------- /scripts/install_solc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Compile solidity. 3 | 4 | - if [ -n "$SOLC_VERSION" ]; then export SOLC_BINARY="$TRAVIS_BUILD_DIR/solc-versions/solc-$SOLC_VERSION/solc"; fi 5 | - if [ -n "$SOLC_VERSION" ]; then export LD_LIBRARY_PATH="$TRAVIS_BUILD_DIR/solc-versions/solc-$SOLC_VERSION"; fi 6 | - if [ -n "$SOLC_VERSION" ]; then sudo apt-get install -y tree unzip; fi 7 | 8 | mkdir -p solc-versions/solc-$SOLC_VERSION 9 | cd solc-versions/solc-$SOLC_VERSION 10 | git clone --recurse-submodules --branch v$SOLC_VERSION --depth 50 https://github.com/ethereum/solidity.git 11 | ./solidity/scripts/install_deps.sh 12 | """ 13 | import functools 14 | import os 15 | import stat 16 | import subprocess 17 | import sys 18 | 19 | import zipfile 20 | 21 | 22 | V0_4_1 = 'v0.4.1' 23 | V0_4_2 = 'v0.4.2' 24 | V0_4_6 = 'v0.4.6' 25 | V0_4_7 = 'v0.4.7' 26 | V0_4_8 = 'v0.4.8' 27 | V0_4_9 = 'v0.4.9' 28 | V0_4_11 = 'v0.4.11' 29 | V0_4_12 = 'v0.4.12' 30 | 31 | 32 | LINUX = 'linux' 33 | OSX = 'darwin' 34 | WINDOWS = 'win32' 35 | 36 | 37 | BASE_INSTALL_PATH = os.path.join( 38 | os.path.dirname(os.path.dirname(__file__)), 39 | 'bin', 40 | ) 41 | 42 | 43 | INSTALL_PATH_TEMPLATE = os.path.join( 44 | BASE_INSTALL_PATH, 45 | "solc-{0}", 46 | ) 47 | 48 | 49 | def get_platform(): 50 | if sys.platform.startswith('linux'): 51 | return LINUX 52 | elif sys.platform == OSX: 53 | return OSX 54 | elif sys.platform == WINDOWS: 55 | return WINDOWS 56 | else: 57 | raise KeyError("Unknown platform: {0}".format(sys.platform)) 58 | 59 | 60 | def is_executable_available(program): 61 | def is_exe(fpath): 62 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 63 | 64 | fpath = os.path.dirname(program) 65 | if fpath: 66 | if is_exe(program): 67 | return True 68 | else: 69 | for path in os.environ["PATH"].split(os.pathsep): 70 | path = path.strip('"') 71 | exe_file = os.path.join(path, program) 72 | if is_exe(exe_file): 73 | return True 74 | 75 | return False 76 | 77 | 78 | def ensure_path_exists(dir_path): 79 | """ 80 | Make sure that a path exists 81 | """ 82 | if not os.path.exists(dir_path): 83 | os.makedirs(dir_path) 84 | return True 85 | return False 86 | 87 | 88 | def ensure_parent_dir_exists(path): 89 | ensure_path_exists(os.path.dirname(path)) 90 | 91 | 92 | def check_subprocess_call(command, message=None, stderr=subprocess.STDOUT, **proc_kwargs): 93 | if message: 94 | print(message) 95 | print("Executing: {0}".format(" ".join(command))) 96 | 97 | return subprocess.check_call( 98 | command, 99 | stderr=subprocess.STDOUT, 100 | **proc_kwargs 101 | ) 102 | 103 | 104 | def check_subprocess_output(command, message=None, stderr=subprocess.STDOUT, **proc_kwargs): 105 | if message: 106 | print(message) 107 | print("Executing: {0}".format(" ".join(command))) 108 | 109 | return subprocess.check_output( 110 | command, 111 | stderr=subprocess.STDOUT, 112 | **proc_kwargs 113 | ) 114 | 115 | 116 | def chmod_plus_x(executable_path): 117 | current_st = os.stat(executable_path) 118 | os.chmod(executable_path, current_st.st_mode | stat.S_IEXEC) 119 | 120 | 121 | is_git_available = is_executable_available('git') 122 | 123 | 124 | SOLIDITY_GIT_URI = "https://github.com/ethereum/solidity.git" 125 | 126 | 127 | def get_source_path(identifier): 128 | return os.path.join( 129 | INSTALL_PATH_TEMPLATE.format(identifier), 130 | 'source', 131 | ) 132 | 133 | 134 | def clone_solidity_repository(identifier): 135 | destination = get_source_path(identifier) 136 | ensure_parent_dir_exists(destination) 137 | command = [ 138 | "git", "clone", 139 | "--recurse-submodules", 140 | "--branch", identifier, 141 | "--depth", "10", 142 | SOLIDITY_GIT_URI, 143 | destination, 144 | ] 145 | 146 | return check_subprocess_call( 147 | command, 148 | message="Checking out solidity repository @ {0}".format(identifier), 149 | ) 150 | 151 | 152 | def install_dependencies(identifier): 153 | source_path = get_source_path(identifier) 154 | install_deps_script_path = os.path.join(source_path, 'scripts', 'install_deps.sh') 155 | 156 | return check_subprocess_call( 157 | command=["sh", install_deps_script_path], 158 | message="Running dependency installation script `install_deps.sh` @ {0}".format( 159 | install_deps_script_path, 160 | ), 161 | ) 162 | 163 | 164 | def get_release_zipfile_path(identifier): 165 | return os.path.join( 166 | INSTALL_PATH_TEMPLATE.format(identifier), 167 | 'release.zip', 168 | ) 169 | 170 | 171 | def get_static_linux_binary_path(identifier): 172 | extract_path = get_extract_path(identifier) 173 | return os.path.join( 174 | extract_path, 175 | 'solc', 176 | ) 177 | 178 | 179 | def get_extract_path(identifier): 180 | return os.path.join( 181 | INSTALL_PATH_TEMPLATE.format(identifier), 182 | 'bin', 183 | ) 184 | 185 | 186 | def get_ubuntu_executable_path(identifier): 187 | extract_path = get_extract_path(identifier) 188 | return os.path.join( 189 | extract_path, 190 | 'solc', 191 | ) 192 | 193 | 194 | DOWNLOAD_UBUNTU_RELEASE_URI_TEMPLATE = "https://github.com/ethereum/solidity/releases/download/{0}/solidity-ubuntu-trusty.zip" 195 | 196 | 197 | def download_ubuntu_release(identifier): 198 | download_uri = DOWNLOAD_UBUNTU_RELEASE_URI_TEMPLATE.format(identifier) 199 | release_zipfile_path = get_release_zipfile_path(identifier) 200 | 201 | ensure_parent_dir_exists(release_zipfile_path) 202 | 203 | command = [ 204 | "wget", download_uri, 205 | '-c', # resume previously incomplete download. 206 | '-O', release_zipfile_path, 207 | ] 208 | 209 | return check_subprocess_call( 210 | command, 211 | message="Downloading ubuntu release from {0}".format(download_uri), 212 | ) 213 | 214 | 215 | DOWNLOAD_STATIC_RELEASE_URI_TEMPLATE = "https://github.com/ethereum/solidity/releases/download/{0}/solc-static-linux" 216 | 217 | 218 | def download_static_release(identifier): 219 | download_uri = DOWNLOAD_STATIC_RELEASE_URI_TEMPLATE.format(identifier) 220 | static_binary_path = get_static_linux_binary_path(identifier) 221 | 222 | ensure_parent_dir_exists(static_binary_path) 223 | 224 | command = [ 225 | "wget", download_uri, 226 | '-c', # resume previously incomplete download. 227 | '-O', static_binary_path, 228 | ] 229 | 230 | return check_subprocess_call( 231 | command, 232 | message="Downloading static linux binary from {0}".format(download_uri), 233 | ) 234 | 235 | 236 | def extract_release(identifier): 237 | release_zipfile_path = get_release_zipfile_path(identifier) 238 | 239 | extract_path = get_extract_path(identifier) 240 | ensure_path_exists(extract_path) 241 | 242 | print("Extracting zipfile: {0} -> {1}".format(release_zipfile_path, extract_path)) 243 | 244 | with zipfile.ZipFile(release_zipfile_path) as zipfile_file: 245 | zipfile_file.extractall(extract_path) 246 | 247 | executable_path = get_ubuntu_executable_path(identifier) 248 | 249 | print("Making `solc` binary executable: `chmod +x {0}`".format(executable_path)) 250 | chmod_plus_x(executable_path) 251 | 252 | 253 | def install_solc_dependencies(identifier): 254 | source_git_dir = os.path.join( 255 | get_source_path(identifier), 256 | '.git', 257 | ) 258 | if not os.path.exists(source_git_dir): 259 | clone_solidity_repository(identifier) 260 | 261 | install_dependencies(identifier) 262 | 263 | 264 | def install_solc_from_ubuntu_release_zip(identifier): 265 | download_ubuntu_release(identifier) 266 | extract_release(identifier) 267 | 268 | extract_path = get_extract_path(identifier) 269 | executable_path = get_ubuntu_executable_path(identifier) 270 | assert os.path.exists(executable_path), "Executable not found @".format(executable_path) 271 | 272 | check_version_command = [executable_path, '--version'] 273 | 274 | check_subprocess_output( 275 | check_version_command, 276 | message="Checking installed executable version @ {0}".format(executable_path), 277 | env={'LD_LIBRARY_PATH': extract_path}, 278 | ) 279 | 280 | print("solc successfully installed at: {0}".format(executable_path)) 281 | 282 | 283 | def install_solc_from_static_linux(identifier): 284 | download_static_release(identifier) 285 | 286 | executable_path = get_static_linux_binary_path(identifier) 287 | chmod_plus_x(executable_path) 288 | 289 | check_version_command = [executable_path, '--version'] 290 | 291 | check_subprocess_output( 292 | check_version_command, 293 | message="Checking installed executable version @ {0}".format(executable_path), 294 | ) 295 | 296 | print("solc successfully installed at: {0}".format(executable_path)) 297 | 298 | 299 | def install_from_ubuntu_release(identifier): 300 | install_solc_dependencies(identifier) 301 | install_solc_from_ubuntu_release_zip(identifier) 302 | 303 | 304 | install_v0_4_1 = functools.partial(install_from_ubuntu_release, V0_4_1) 305 | install_v0_4_2 = functools.partial(install_from_ubuntu_release, V0_4_2) 306 | install_v0_4_6 = functools.partial(install_from_ubuntu_release, V0_4_6) 307 | install_v0_4_7 = functools.partial(install_from_ubuntu_release, V0_4_7) 308 | install_v0_4_8 = functools.partial(install_from_ubuntu_release, V0_4_8) 309 | install_v0_4_9 = functools.partial(install_from_ubuntu_release, V0_4_9) 310 | 311 | 312 | def install_from_static_linux(identifier): 313 | install_solc_from_static_linux(identifier) 314 | 315 | 316 | install_v0_4_11 = functools.partial(install_solc_from_static_linux, V0_4_11) 317 | install_v0_4_12 = functools.partial(install_solc_from_static_linux, V0_4_12) 318 | 319 | 320 | INSTALL_FUNCTIONS = { 321 | LINUX: { 322 | V0_4_1: install_v0_4_1, 323 | V0_4_2: install_v0_4_2, 324 | V0_4_6: install_v0_4_6, 325 | V0_4_7: install_v0_4_7, 326 | V0_4_8: install_v0_4_8, 327 | V0_4_9: install_v0_4_9, 328 | V0_4_11: install_v0_4_11, 329 | V0_4_12: install_v0_4_12, 330 | }, 331 | } 332 | 333 | 334 | def install_solc(platform, identifier): 335 | if platform not in INSTALL_FUNCTIONS: 336 | raise ValueError( 337 | "Installation of solidity is not supported on your platform ({0}). " 338 | "Supported platforms are: {1}".format( 339 | platform, 340 | ', '.join(sorted(INSTALL_FUNCTIONS.keys())), 341 | ) 342 | ) 343 | elif identifier not in INSTALL_FUNCTIONS[platform]: 344 | raise ValueError( 345 | "Installation of solidity=={0} is not supported. Must be one of {1}".format( 346 | identifier, 347 | ', '.join(sorted(INSTALL_FUNCTIONS[platform].keys())), 348 | ) 349 | ) 350 | 351 | install_fn = INSTALL_FUNCTIONS[platform][identifier] 352 | install_fn() 353 | 354 | 355 | if __name__ == "__main__": 356 | try: 357 | identifier = sys.argv[1] 358 | except IndexError: 359 | print("Invocation error. Should be invoked as `./install_solc.py `") 360 | sys.exit(1) 361 | 362 | install_solc(get_platform(), identifier) 363 | -------------------------------------------------------------------------------- /solc/install.py: -------------------------------------------------------------------------------- 1 | """ 2 | Install solc 3 | """ 4 | import functools 5 | import os 6 | import stat 7 | import subprocess 8 | import sys 9 | import contextlib 10 | 11 | import zipfile 12 | 13 | 14 | V0_4_1 = 'v0.4.1' 15 | V0_4_2 = 'v0.4.2' 16 | V0_4_6 = 'v0.4.6' 17 | V0_4_7 = 'v0.4.7' 18 | V0_4_8 = 'v0.4.8' 19 | V0_4_9 = 'v0.4.9' 20 | V0_4_11 = 'v0.4.11' 21 | V0_4_12 = 'v0.4.12' 22 | V0_4_13 = 'v0.4.13' 23 | V0_4_14 = 'v0.4.14' 24 | V0_4_15 = 'v0.4.15' 25 | V0_4_16 = 'v0.4.16' 26 | V0_4_17 = 'v0.4.17' 27 | V0_4_18 = 'v0.4.18' 28 | V0_4_19 = 'v0.4.19' 29 | V0_4_20 = 'v0.4.20' 30 | V0_4_21 = 'v0.4.21' 31 | V0_4_22 = 'v0.4.22' 32 | V0_4_23 = 'v0.4.23' 33 | V0_4_24 = 'v0.4.24' 34 | V0_4_25 = 'v0.4.25' 35 | 36 | 37 | LINUX = 'linux' 38 | OSX = 'darwin' 39 | WINDOWS = 'win32' 40 | 41 | 42 | # 43 | # System utilities. 44 | # 45 | @contextlib.contextmanager 46 | def chdir(path): 47 | original_path = os.getcwd() 48 | try: 49 | os.chdir(path) 50 | yield 51 | finally: 52 | os.chdir(original_path) 53 | 54 | 55 | def get_platform(): 56 | if sys.platform.startswith('linux'): 57 | return LINUX 58 | elif sys.platform == OSX: 59 | return OSX 60 | elif sys.platform == WINDOWS: 61 | return WINDOWS 62 | else: 63 | raise KeyError("Unknown platform: {0}".format(sys.platform)) 64 | 65 | 66 | def is_executable_available(program): 67 | def is_exe(fpath): 68 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 69 | 70 | fpath = os.path.dirname(program) 71 | if fpath: 72 | if is_exe(program): 73 | return True 74 | else: 75 | for path in os.environ["PATH"].split(os.pathsep): 76 | path = path.strip('"') 77 | exe_file = os.path.join(path, program) 78 | if is_exe(exe_file): 79 | return True 80 | 81 | return False 82 | 83 | 84 | def ensure_path_exists(dir_path): 85 | """ 86 | Make sure that a path exists 87 | """ 88 | if not os.path.exists(dir_path): 89 | os.makedirs(dir_path) 90 | return True 91 | return False 92 | 93 | 94 | def ensure_parent_dir_exists(path): 95 | ensure_path_exists(os.path.dirname(path)) 96 | 97 | 98 | def check_subprocess_call(command, message=None, stderr=subprocess.STDOUT, **proc_kwargs): 99 | if message: 100 | print(message) 101 | print("Executing: {0}".format(" ".join(command))) 102 | 103 | return subprocess.check_call( 104 | command, 105 | stderr=subprocess.STDOUT, 106 | **proc_kwargs 107 | ) 108 | 109 | 110 | def check_subprocess_output(command, message=None, stderr=subprocess.STDOUT, **proc_kwargs): 111 | if message: 112 | print(message) 113 | print("Executing: {0}".format(" ".join(command))) 114 | 115 | return subprocess.check_output( 116 | command, 117 | stderr=subprocess.STDOUT, 118 | **proc_kwargs 119 | ) 120 | 121 | 122 | def chmod_plus_x(executable_path): 123 | current_st = os.stat(executable_path) 124 | os.chmod(executable_path, current_st.st_mode | stat.S_IEXEC) 125 | 126 | 127 | SOLIDITY_GIT_URI = "https://github.com/ethereum/solidity.git" 128 | 129 | 130 | def is_git_repository(path): 131 | git_dir = os.path.join( 132 | path, 133 | '.git', 134 | ) 135 | return os.path.exists(git_dir) 136 | 137 | 138 | # 139 | # Installation filesystem path utilities 140 | # 141 | def get_base_install_path(identifier): 142 | if 'SOLC_BASE_INSTALL_PATH' in os.environ: 143 | return os.path.join( 144 | os.environ['SOLC_BASE_INSTALL_PATH'], 145 | 'solc-{0}'.format(identifier), 146 | ) 147 | else: 148 | return os.path.expanduser(os.path.join( 149 | '~', 150 | '.py-solc', 151 | 'solc-{0}'.format(identifier), 152 | )) 153 | 154 | 155 | def get_repository_path(identifier): 156 | return os.path.join( 157 | get_base_install_path(identifier), 158 | 'source', 159 | ) 160 | 161 | 162 | def get_release_zipfile_path(identifier): 163 | return os.path.join( 164 | get_base_install_path(identifier), 165 | 'release.zip', 166 | ) 167 | 168 | 169 | def get_extract_path(identifier): 170 | return os.path.join( 171 | get_base_install_path(identifier), 172 | 'bin', 173 | ) 174 | 175 | 176 | def get_executable_path(identifier): 177 | extract_path = get_extract_path(identifier) 178 | return os.path.join( 179 | extract_path, 180 | 'solc', 181 | ) 182 | 183 | 184 | def get_build_dir(identifier): 185 | repository_path = get_repository_path(identifier) 186 | return os.path.join( 187 | repository_path, 188 | 'build', 189 | ) 190 | 191 | 192 | def get_built_executable_path(identifier): 193 | build_dir = get_build_dir(identifier) 194 | return os.path.join( 195 | build_dir, 196 | 'solc', 197 | 'solc', 198 | ) 199 | 200 | 201 | # 202 | # Installation primitives. 203 | # 204 | def clone_solidity_repository(identifier): 205 | if not is_executable_available('git'): 206 | raise OSError("The `git` is required but was not found") 207 | 208 | repository_path = get_repository_path(identifier) 209 | ensure_parent_dir_exists(repository_path) 210 | command = [ 211 | "git", "clone", 212 | "--recurse-submodules", 213 | "--branch", identifier, 214 | "--depth", "10", 215 | SOLIDITY_GIT_URI, 216 | repository_path, 217 | ] 218 | 219 | return check_subprocess_call( 220 | command, 221 | message="Checking out solidity repository @ {0}".format(identifier), 222 | ) 223 | 224 | 225 | def initialize_repository_submodules(identifier): 226 | if not is_executable_available('git'): 227 | raise OSError("The `git` is required but was not found") 228 | 229 | repository_path = get_repository_path(identifier) 230 | command = [ 231 | "git", "submodule", "update", "--init", "--recursive", 232 | ] 233 | check_subprocess_call( 234 | command, 235 | "Initializing repository submodules @ {0}".format(repository_path), 236 | ) 237 | 238 | 239 | DOWNLOAD_UBUNTU_RELEASE_URI_TEMPLATE = "https://github.com/ethereum/solidity/releases/download/{0}/solidity-ubuntu-trusty.zip" # noqa: E501 240 | 241 | 242 | def download_ubuntu_release(identifier): 243 | download_uri = DOWNLOAD_UBUNTU_RELEASE_URI_TEMPLATE.format(identifier) 244 | release_zipfile_path = get_release_zipfile_path(identifier) 245 | 246 | ensure_parent_dir_exists(release_zipfile_path) 247 | 248 | command = [ 249 | "wget", download_uri, 250 | '-c', # resume previously incomplete download. 251 | '-O', release_zipfile_path, 252 | ] 253 | 254 | return check_subprocess_call( 255 | command, 256 | message="Downloading ubuntu release from {0}".format(download_uri), 257 | ) 258 | 259 | 260 | DOWNLOAD_STATIC_RELEASE_URI_TEMPLATE = "https://github.com/ethereum/solidity/releases/download/{0}/solc-static-linux" # noqa: E501 261 | 262 | 263 | def download_static_release(identifier): 264 | download_uri = DOWNLOAD_STATIC_RELEASE_URI_TEMPLATE.format(identifier) 265 | static_binary_path = get_executable_path(identifier) 266 | 267 | ensure_parent_dir_exists(static_binary_path) 268 | 269 | command = [ 270 | "wget", download_uri, 271 | '-c', # resume previously incomplete download. 272 | '-O', static_binary_path, 273 | ] 274 | 275 | return check_subprocess_call( 276 | command, 277 | message="Downloading static linux binary from {0}".format(download_uri), 278 | ) 279 | 280 | 281 | def extract_release(identifier): 282 | release_zipfile_path = get_release_zipfile_path(identifier) 283 | 284 | extract_path = get_extract_path(identifier) 285 | ensure_path_exists(extract_path) 286 | 287 | print("Extracting zipfile: {0} -> {1}".format(release_zipfile_path, extract_path)) 288 | 289 | with zipfile.ZipFile(release_zipfile_path) as zipfile_file: 290 | zipfile_file.extractall(extract_path) 291 | 292 | executable_path = get_executable_path(identifier) 293 | 294 | print("Making `solc` binary executable: `chmod +x {0}`".format(executable_path)) 295 | chmod_plus_x(executable_path) 296 | 297 | 298 | def install_solc_dependencies(identifier): 299 | repository_path = get_repository_path(identifier) 300 | if not is_git_repository(repository_path): 301 | raise OSError("Git repository not found @ {0}".format(repository_path)) 302 | 303 | with chdir(repository_path): 304 | install_deps_script_path = os.path.join(repository_path, 'scripts', 'install_deps.sh') 305 | 306 | return check_subprocess_call( 307 | command=["sh", install_deps_script_path], 308 | message="Running dependency installation script `install_deps.sh` @ {0}".format( 309 | install_deps_script_path, 310 | ), 311 | ) 312 | 313 | 314 | def install_solc_from_ubuntu_release_zip(identifier): 315 | download_ubuntu_release(identifier) 316 | extract_release(identifier) 317 | 318 | extract_path = get_extract_path(identifier) 319 | executable_path = get_executable_path(identifier) 320 | assert os.path.exists(executable_path), "Executable not found @".format(executable_path) 321 | 322 | check_version_command = [executable_path, '--version'] 323 | 324 | check_subprocess_output( 325 | check_version_command, 326 | message="Checking installed executable version @ {0}".format(executable_path), 327 | env={'LD_LIBRARY_PATH': extract_path}, 328 | ) 329 | 330 | print("solc successfully installed at: {0}".format(executable_path)) 331 | 332 | 333 | def install_solc_from_static_linux(identifier): 334 | download_static_release(identifier) 335 | 336 | executable_path = get_executable_path(identifier) 337 | chmod_plus_x(executable_path) 338 | 339 | check_version_command = [executable_path, '--version'] 340 | 341 | check_subprocess_output( 342 | check_version_command, 343 | message="Checking installed executable version @ {0}".format(executable_path), 344 | ) 345 | 346 | print("solc successfully installed at: {0}".format(executable_path)) 347 | 348 | 349 | def build_solc_from_source(identifier): 350 | if not is_git_repository(get_repository_path(identifier)): 351 | clone_solidity_repository(identifier) 352 | 353 | build_dir = get_build_dir(identifier) 354 | ensure_path_exists(build_dir) 355 | 356 | with chdir(build_dir): 357 | cmake_command = ["cmake", ".."] 358 | check_subprocess_call( 359 | cmake_command, 360 | message="Running cmake build command", 361 | ) 362 | make_command = ["make"] 363 | check_subprocess_call( 364 | make_command, 365 | message="Running make command", 366 | ) 367 | 368 | built_executable_path = get_built_executable_path(identifier) 369 | chmod_plus_x(built_executable_path) 370 | 371 | executable_path = get_executable_path(identifier) 372 | ensure_parent_dir_exists(executable_path) 373 | os.symlink(built_executable_path, executable_path) 374 | chmod_plus_x(executable_path) 375 | 376 | 377 | def install_from_ubuntu_release(identifier): 378 | if not is_git_repository(get_repository_path(identifier)): 379 | clone_solidity_repository(identifier) 380 | install_solc_dependencies(identifier) 381 | install_solc_from_ubuntu_release_zip(identifier) 382 | 383 | executable_path = get_executable_path(identifier) 384 | print("Succesfully installed solc @ `{0}`".format(executable_path)) 385 | 386 | 387 | install_v0_4_1_linux = functools.partial(install_from_ubuntu_release, V0_4_1) 388 | install_v0_4_2_linux = functools.partial(install_from_ubuntu_release, V0_4_2) 389 | install_v0_4_6_linux = functools.partial(install_from_ubuntu_release, V0_4_6) 390 | install_v0_4_7_linux = functools.partial(install_from_ubuntu_release, V0_4_7) 391 | install_v0_4_8_linux = functools.partial(install_from_ubuntu_release, V0_4_8) 392 | install_v0_4_9_linux = functools.partial(install_from_ubuntu_release, V0_4_9) 393 | 394 | 395 | def install_from_static_linux(identifier): 396 | install_solc_from_static_linux(identifier) 397 | 398 | executable_path = get_executable_path(identifier) 399 | print("Succesfully installed solc @ `{0}`".format(executable_path)) 400 | 401 | 402 | install_v0_4_11_linux = functools.partial(install_solc_from_static_linux, V0_4_11) 403 | install_v0_4_12_linux = functools.partial(install_solc_from_static_linux, V0_4_12) 404 | install_v0_4_13_linux = functools.partial(install_solc_from_static_linux, V0_4_13) 405 | install_v0_4_14_linux = functools.partial(install_solc_from_static_linux, V0_4_14) 406 | install_v0_4_15_linux = functools.partial(install_solc_from_static_linux, V0_4_15) 407 | install_v0_4_16_linux = functools.partial(install_solc_from_static_linux, V0_4_16) 408 | install_v0_4_17_linux = functools.partial(install_solc_from_static_linux, V0_4_17) 409 | install_v0_4_18_linux = functools.partial(install_solc_from_static_linux, V0_4_18) 410 | install_v0_4_19_linux = functools.partial(install_solc_from_static_linux, V0_4_19) 411 | install_v0_4_20_linux = functools.partial(install_solc_from_static_linux, V0_4_20) 412 | install_v0_4_21_linux = functools.partial(install_solc_from_static_linux, V0_4_21) 413 | install_v0_4_22_linux = functools.partial(install_solc_from_static_linux, V0_4_22) 414 | install_v0_4_23_linux = functools.partial(install_solc_from_static_linux, V0_4_23) 415 | install_v0_4_24_linux = functools.partial(install_solc_from_static_linux, V0_4_24) 416 | install_v0_4_25_linux = functools.partial(install_solc_from_static_linux, V0_4_25) 417 | 418 | 419 | def install_from_source(identifier): 420 | if not is_git_repository(get_repository_path(identifier)): 421 | clone_solidity_repository(identifier) 422 | install_solc_dependencies(identifier) 423 | build_solc_from_source(identifier) 424 | 425 | executable_path = get_executable_path(identifier) 426 | print("Succesfully installed solc @ `{0}`".format(executable_path)) 427 | 428 | 429 | install_v0_4_8_osx = functools.partial(install_from_source, V0_4_8) 430 | install_v0_4_11_osx = functools.partial(install_from_source, V0_4_11) 431 | install_v0_4_12_osx = functools.partial(install_from_source, V0_4_12) 432 | install_v0_4_13_osx = functools.partial(install_from_source, V0_4_13) 433 | install_v0_4_14_osx = functools.partial(install_from_source, V0_4_14) 434 | install_v0_4_15_osx = functools.partial(install_from_source, V0_4_15) 435 | install_v0_4_16_osx = functools.partial(install_from_source, V0_4_16) 436 | install_v0_4_17_osx = functools.partial(install_from_source, V0_4_17) 437 | install_v0_4_18_osx = functools.partial(install_from_source, V0_4_18) 438 | install_v0_4_19_osx = functools.partial(install_from_source, V0_4_19) 439 | install_v0_4_20_osx = functools.partial(install_from_source, V0_4_20) 440 | install_v0_4_21_osx = functools.partial(install_from_source, V0_4_21) 441 | install_v0_4_22_osx = functools.partial(install_from_source, V0_4_22) 442 | install_v0_4_23_osx = functools.partial(install_from_source, V0_4_23) 443 | install_v0_4_24_osx = functools.partial(install_from_source, V0_4_24) 444 | install_v0_4_25_osx = functools.partial(install_from_source, V0_4_25) 445 | 446 | 447 | INSTALL_FUNCTIONS = { 448 | LINUX: { 449 | V0_4_1: install_v0_4_1_linux, 450 | V0_4_2: install_v0_4_2_linux, 451 | V0_4_6: install_v0_4_6_linux, 452 | V0_4_7: install_v0_4_7_linux, 453 | V0_4_8: install_v0_4_8_linux, 454 | V0_4_9: install_v0_4_9_linux, 455 | V0_4_11: install_v0_4_11_linux, 456 | V0_4_12: install_v0_4_12_linux, 457 | V0_4_13: install_v0_4_13_linux, 458 | V0_4_14: install_v0_4_14_linux, 459 | V0_4_15: install_v0_4_15_linux, 460 | V0_4_16: install_v0_4_16_linux, 461 | V0_4_17: install_v0_4_17_linux, 462 | V0_4_18: install_v0_4_18_linux, 463 | V0_4_19: install_v0_4_19_linux, 464 | V0_4_20: install_v0_4_20_linux, 465 | V0_4_21: install_v0_4_21_linux, 466 | V0_4_22: install_v0_4_22_linux, 467 | V0_4_23: install_v0_4_23_linux, 468 | V0_4_24: install_v0_4_24_linux, 469 | V0_4_25: install_v0_4_25_linux, 470 | }, 471 | OSX: { 472 | V0_4_8: install_v0_4_8_osx, 473 | V0_4_11: install_v0_4_11_osx, 474 | V0_4_12: install_v0_4_12_osx, 475 | V0_4_13: install_v0_4_13_osx, 476 | V0_4_14: install_v0_4_14_osx, 477 | V0_4_15: install_v0_4_15_osx, 478 | V0_4_16: install_v0_4_16_osx, 479 | V0_4_17: install_v0_4_17_osx, 480 | V0_4_18: install_v0_4_18_osx, 481 | V0_4_19: install_v0_4_19_osx, 482 | V0_4_20: install_v0_4_20_osx, 483 | V0_4_21: install_v0_4_21_osx, 484 | V0_4_22: install_v0_4_22_osx, 485 | V0_4_23: install_v0_4_23_osx, 486 | V0_4_24: install_v0_4_24_osx, 487 | V0_4_25: install_v0_4_25_osx, 488 | } 489 | } 490 | 491 | 492 | def install_solc(identifier, platform=None): 493 | if platform is None: 494 | platform = get_platform() 495 | 496 | if platform not in INSTALL_FUNCTIONS: 497 | raise ValueError( 498 | "Installation of solidity is not supported on your platform ({0}). " 499 | "Supported platforms are: {1}".format( 500 | platform, 501 | ', '.join(sorted(INSTALL_FUNCTIONS.keys())), 502 | ) 503 | ) 504 | elif identifier not in INSTALL_FUNCTIONS[platform]: 505 | raise ValueError( 506 | "Installation of solidity=={0} is not supported. Must be one of {1}".format( 507 | identifier, 508 | ', '.join(sorted(INSTALL_FUNCTIONS[platform].keys())), 509 | ) 510 | ) 511 | 512 | install_fn = INSTALL_FUNCTIONS[platform][identifier] 513 | install_fn() 514 | 515 | 516 | if __name__ == "__main__": 517 | try: 518 | identifier = sys.argv[1] 519 | except IndexError: 520 | print("Invocation error. Should be invoked as `./install_solc.py `") 521 | sys.exit(1) 522 | 523 | install_solc(identifier) 524 | --------------------------------------------------------------------------------