diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml new file mode 100644 index 0000000..d57e889 --- /dev/null +++ b/.github/workflows/build_release.yml @@ -0,0 +1,138 @@ +name: Build Release + +on: + push: + branches: + - "main" + +env: + BUILD_TYPE: Release + + +jobs: + create_release: + name: "Create Release" + runs-on: "ubuntu-latest" + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + + steps: + - name: Create release + id: create_release + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: true + title: "Development Build" + + build_emscripten: + name: Build for Emscripten + needs: create_release + runs-on: ubuntu-latest + + steps: + - name: Remove unused software to free up space + run: | + # These are quick to delete and large enough to make a difference + rm -rf /usr/share/swift/ # 1.3 GB in 80 subdirs + rm -rf /usr/local/lib/android/sdk/build-tools/ # 2.1 GB in 450 subdirs + rm -rf /usr/share/dotnet/shared/ # 5.3 GB in 350 subdirs + rm -rf /usr/local/lib/android/sdk/ndk/ # 7.6 GB in 1500 subdirs + + - uses: actions/checkout@v2 + + - name: Update Submodule + run: git submodule update --init --recursive + + - name: Build + run: bash ${{github.workspace}}/scripts/build_emscripten.sh + + - name: Upload Release Assets + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: soljson.js + asset_path: ${{github.workspace}}/upload/soljson.js + asset_content_type: application/octet-stream + + build_linux: + name: Build for Linux + needs: create_release + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update Submodule + run: git submodule update --init --recursive + + - name: Install Dependencies + run: bash ${{github.workspace}}/scripts/install_deps.sh + + - name: Build + run: bash ${{github.workspace}}/build.sh + + - name: Compress Artifact + run: | + cd ${{github.workspace}}/build/solppc + tar -cvzf solppc_linux.tar.gz ./solppc + + - name: Upload Release Assets + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: solppc_linux.tar.gz + asset_path: ${{github.workspace}}/build/solppc/solppc_linux.tar.gz + asset_content_type: application/octet-stream + + build-macos: + name: Build for MacOS + needs: create_release + runs-on: macos-11 + + steps: + - uses: actions/checkout@v2 + + - name: Update Submodule + run: git submodule update --init --recursive + + - name: Install Dependencies + run: | + bash ${{github.workspace}}/scripts/osx_install_dependencies.sh + + - name: Build + run: | + mkdir -p build/ + echo -n > prerelease.txt + cd build/ + cmake .. -DSOLC_LINK_STATIC=1 -DCMAKE_BUILD_TYPE=Release + make -j3 + + - name: Compress Artifact + run: | + cd ${{github.workspace}}/build/solppc + tar -cvzf solppc_darwin.tar.gz ./solppc + + - name: Upload Release Assets + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + asset_name: solppc_darwin.tar.gz + asset_path: ${{github.workspace}}/build/solppc/solppc_darwin.tar.gz + asset_content_type: application/octet-stream + + + + + + + + + diff --git a/.gitignore b/.gitignore index 0ae8334..0a9bb46 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ docs/utils/*.pyc deps/install deps/cache cmake-build-*/ +upload/ +soljson.js # vim stuff [._]*.sw[a-p] diff --git a/CMakeLists.txt b/CMakeLists.txt index 4afa022..8da44a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,5 +66,12 @@ add_subdirectory(libyul) add_subdirectory(libevmasm) add_subdirectory(libsmtutil) add_subdirectory(libsolidity) -add_subdirectory(solppc) -add_subdirectory(test) +add_subdirectory(libsolc) + +if (NOT EMSCRIPTEN) + add_subdirectory(solppc) +endif() + +if (TESTS AND NOT EMSCRIPTEN) + add_subdirectory(test) +endif() diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt new file mode 100644 index 0000000..f387122 --- /dev/null +++ b/libsolc/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(AFTER ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/solidity) + +if (EMSCRIPTEN) + # Specify which functions to export in soljson.js. + # Note that additional Emscripten-generated methods needed by solc-js are + # defined to be exported in cmake/EthCompilerSettings.cmake. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\",\"_solidity_alloc\",\"_solidity_free\",\"_solidity_reset\"]'") + add_executable(soljson libsolc.cpp libsolc.h) + target_link_libraries(soljson PRIVATE solidity) +else() + add_library(libsolc libsolc.cpp libsolc.h) + set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc) + target_link_libraries(libsolc PRIVATE solidity) +endif() diff --git a/libsolc/libsolc.cpp b/libsolc/libsolc.cpp new file mode 100644 index 0000000..f87f075 --- /dev/null +++ b/libsolc/libsolc.cpp @@ -0,0 +1,162 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Christian + * @date 2014 + * Public compiler API. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "license.h" + +using namespace std; +using namespace solidity; +using namespace solidity::util; + +using solidity::frontend::ReadCallback; +using solidity::frontend::StandardCompiler; + +namespace +{ + +// The strings in this list must not be resized after they have been added here (via solidity_alloc()), because +// this may potentially change the pointer that was passed to the caller from solidity_alloc(). +static list solidityAllocations; + +/// Find the equivalent to @p _data in the list of allocations of solidity_alloc(), +/// removes it from the list and returns its value. +/// +/// If any invalid argument is being passed, it is considered a programming error +/// on the caller-side and hence, will call abort() then. +string takeOverAllocation(char const* _data) +{ + for (auto iter = begin(solidityAllocations); iter != end(solidityAllocations); ++iter) + if (iter->data() == _data) + { + string chunk = move(*iter); + solidityAllocations.erase(iter); + return chunk; + } + + abort(); +} + +/// Resizes a std::string to the proper length based on the occurrence of a zero terminator. +void truncateCString(string& _data) +{ + size_t pos = _data.find('\0'); + if (pos != string::npos) + _data.resize(pos); +} + +ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback, void* _readContext) +{ + ReadCallback::Callback readCallback; + if (_readCallback) + { + readCallback = [=](string const& _kind, string const& _data) + { + char* contents_c = nullptr; + char* error_c = nullptr; + _readCallback(_readContext, _kind.data(), _data.data(), &contents_c, &error_c); + ReadCallback::Result result; + result.success = true; + if (!contents_c && !error_c) + { + result.success = false; + result.responseOrErrorMessage = "Callback not supported."; + } + if (contents_c) + { + result.success = true; + result.responseOrErrorMessage = takeOverAllocation(contents_c); + } + if (error_c) + { + result.success = false; + result.responseOrErrorMessage = takeOverAllocation(error_c); + } + truncateCString(result.responseOrErrorMessage); + return result; + }; + } + return readCallback; +} + +string compile(string _input, CStyleReadFileCallback _readCallback, void* _readContext) +{ + StandardCompiler compiler(wrapReadCallback(_readCallback, _readContext)); + return compiler.compile(move(_input)); +} + +} + +extern "C" +{ +extern char const* solidity_license() noexcept +{ + static string fullLicenseText = otherLicenses + licenseText; + return fullLicenseText.c_str(); +} + +extern char const* solidity_version() noexcept +{ + return frontend::VersionString.c_str(); +} + +extern char* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback, void* _readContext) noexcept +{ + return solidityAllocations.emplace_back(compile(_input, _readCallback, _readContext)).data(); +} + +extern char* solidity_alloc(size_t _size) noexcept +{ + try + { + return solidityAllocations.emplace_back(_size, '\0').data(); + } + catch (...) + { + // most likely a std::bad_alloc(), if at all. + return nullptr; + } +} + +extern void solidity_free(char* _data) noexcept +{ + takeOverAllocation(_data); +} + +extern void solidity_reset() noexcept +{ + // This is called right before each compilation, but not at the end, so additional memory + // can be freed here. + yul::YulStringRepository::reset(); + solidityAllocations.clear(); +} +} diff --git a/libsolc/libsolc.h b/libsolc/libsolc.h new file mode 100644 index 0000000..c5b3b3d --- /dev/null +++ b/libsolc/libsolc.h @@ -0,0 +1,101 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Christian + * @date 2014 + * Public compiler API. + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +#define SOLC_NOEXCEPT noexcept +#else +#define SOLC_NOEXCEPT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/// Callback used to retrieve additional source files or data. +/// +/// @param _context The readContext passed to solidity_compile. Can be NULL. +/// @param _kind The kind of callback (a string). +/// @param _data The data for the callback (a string). +/// @param o_contents A pointer to the contents of the file, if found. Allocated via solidity_alloc(). +/// @param o_error A pointer to an error message, if there is one. +/// +/// The file (as well as error) contents that is to be allocated by the callback +/// implementor must use the solidity_alloc() API to allocate its underlying +/// storage. Ownership is then transferred to the compiler which will take care +/// of the deallocation. +/// +/// If the callback is not supported, *o_contents and *o_error must be set to NULL. +typedef void (*CStyleReadFileCallback)(void* _context, char const* _kind, char const* _data, char** o_contents, char** o_error); + +/// Returns the complete license document. +/// +/// The pointer returned must NOT be freed by the caller. +char const* solidity_license() SOLC_NOEXCEPT; + +/// Returns the compiler version. +/// +/// The pointer returned must NOT be freed by the caller. +char const* solidity_version() SOLC_NOEXCEPT; + +/// Allocates a chunk of memory of @p _size bytes. +/// +/// Use this function inside callbacks to allocate data that is to be passed to +/// the compiler. You may use solidity_free() or solidity_reset() to free this +/// memory again but it is not required as the compiler takes ownership for any +/// data passed to it via callbacks. +/// +/// This function will return NULL if the requested memory region could not be allocated. +char* solidity_alloc(size_t _size) SOLC_NOEXCEPT; + +/// Explicitly frees the memory (@p _data) that was being allocated with solidity_alloc() +/// or returned by a call to solidity_compile(). +/// +/// Important, this call will abort() in case of any invalid argument being passed to this call. +void solidity_free(char* _data) SOLC_NOEXCEPT; + +/// Takes a "Standard Input JSON" and an optional callback (can be set to null). Returns +/// a "Standard Output JSON". Both are to be UTF-8 encoded. +/// +/// @param _input The input JSON to process. +/// @param _readCallback The optional callback pointer. Can be NULL, but if not NULL, +/// it can be called by the compiler to request additional input. +/// Please see the documentation of the type for details. +/// @param _readContext An optional context pointer passed to _readCallback. Can be NULL. +/// +/// @returns A pointer to the result. The pointer returned must be freed by the caller using solidity_free() or solidity_reset(). +char* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback, void* _readContext) SOLC_NOEXCEPT; + +/// Frees up any allocated memory. +/// +/// NOTE: the pointer returned by solidity_compile as well as any other pointer retrieved via solidity_alloc() +/// is invalid after calling this! +void solidity_reset() SOLC_NOEXCEPT; + +#ifdef __cplusplus +} +#endif diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 8a5d246..c65513c 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -4146,7 +4146,7 @@ string YulUtilFunctions::panicFunction(util::PanicCode _code) )") ("functionName", functionName) ("selector", util::selectorFromSignature("Panic(uint256)").str()) - ("code", toCompactHexWithPrefix(_code)) + ("code", toCompactHexWithPrefix(static_cast(_code))) .render(); }); } diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh new file mode 100755 index 0000000..406ac60 --- /dev/null +++ b/scripts/build_emscripten.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# Bash script for building Solidity for emscripten. +# +# The documentation for solidity is hosted at: +# +# https://docs.soliditylang.org +# +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -e + +if test -z "$1"; then + BUILD_DIR="emscripten_build" +else + BUILD_DIR="$1" +fi + +# solbuildpackpusher/solidity-buildpack-deps:emscripten-4 +docker run -v $(pwd):/root/project -w /root/project \ + solbuildpackpusher/solidity-buildpack-deps@sha256:434719d8104cab47712dd1f56f255994d04eb65b802c0d382790071c1a0c074b \ + ./scripts/ci/build_emscripten.sh $BUILD_DIR diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh new file mode 100755 index 0000000..36133fe --- /dev/null +++ b/scripts/ci/build_emscripten.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# This script builds the solidity binary using Emscripten. +# Emscripten is a way to compile C/C++ to JavaScript. +# +# http://kripken.github.io/emscripten-site/ +# +# First run install_dep.sh OUTSIDE of docker and then +# run this script inside a docker image trzeci/emscripten +# +# The documentation for solidity is hosted at: +# +# https://docs.soliditylang.org +# +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +set -ev + +if test -z "$1"; then + BUILD_DIR="emscripten_build" +else + BUILD_DIR="$1" +fi + +WORKSPACE=/root/project + +cd $WORKSPACE + +# shellcheck disable=SC2166 +if [[ "$CIRCLE_BRANCH" = release || -n "$CIRCLE_TAG" || -n "$FORCE_RELEASE" || "$(git tag --points-at HEAD 2>/dev/null)" == v* ]] +then + echo -n >prerelease.txt +else + # Use last commit date rather than build date to avoid ending up with builds for + # different platforms having different version strings (and therefore producing different bytecode) + # if the CI is triggered just before midnight. + TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="ci.%cd" >prerelease.txt +fi +if [ -n "$CIRCLE_SHA1" ] +then + echo -n "$CIRCLE_SHA1" >commit_hash.txt +fi + +mkdir -p $BUILD_DIR +cd $BUILD_DIR +emcmake cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBoost_USE_STATIC_LIBS=1 \ + -DBoost_USE_STATIC_RUNTIME=1 \ + -DTESTS=0 \ + .. +make -j3 solidity_BuildInfo.h soljson + +cd .. +mkdir -p upload +cp $BUILD_DIR/libsolc/soljson.js upload/ +cp $BUILD_DIR/libsolc/soljson.js ./ + +OUTPUT_SIZE=`ls -la soljson.js` + +echo "Emscripten output size: $OUTPUT_SIZE"