使用 EOSIO.CDT 工具链

之前的文章中介绍了不用eos仓库里面的eosiocpp而使用k85提供的SDK来开发合约的方法:使用 WasmSDK 工具链。

可惜好景不长,文章发布没几天,k85就将WasmSDK工程给改名了。新的工程叫EOSIO.CDT。新工程其实是老工程
的重命名,以前的提交记录和issue都在(还可以找到我之前提交的issue记录),因此前面的文章稍微改吧改吧,我们就可以使用新的工具开发合约了。

新工具叫:EOSIO.CDT (Contract Development Toolkit) 其实名字上来说也更直观一点。如果用过eclipse写C++一定知道eclipse的C++插件也叫CDT(C++ Development Toolkit)。 所以啊,这个名字比以前的还是牛X一写,赶紧来学着用吧。

至于为什么要用这个新的工具,前面的文章其实已经说明了。
在官方EOS1.2.0的release note中提到:

使用 EOSIO.CDT 工具链_第1张图片
releasenote_121.png

在EOS 1.3以后将会废除EOS仓库里面的eosiocpp程序,也就是EOS仓库里将不会
出现"contracts/eosiolib"、 "contracts/libc++"、" contracts/musl"等合约 依赖的库文件。也没有了eosiocpp等工具

所以在1.3还没有到来之前,赶紧一起来学习CDT的使用。

编译安装

找到你自己喜欢位置,然后创建目录:

mkdir -p github.com/EOSIO/ 
cd github.com/EOSIO/

然后拉取EOSIO.CDT的仓库代码:

git clone --recursive https://github.com/EOSIO/eosio.cdt
cd eosio.cdt

因为 CDT 依赖了llvm项目,所以安装过程中需要clone llvm,整个库比较大(约1个G)。如果中途失败,可以用命令:

git submodule update --recursive

重试几次。

拉取完成后,就可以执行 :

sh build.sh

进行安装了。

这个脚本会根据不同的操作系统,执行:

scripts/
├── eosio_build_amazon.sh
├── eosio_build_centos.sh
├── eosio_build_darwin.sh
├── eosio_build_dep
├── eosio_build_fedora.sh
└── eosio_build_ubuntu.sh    

下对应平台的sh文件。

这里的构建脚本会先检查相关工具是否安装,如果没有安装则进行下载安装。因为这里下载的路径是写死的,如果发现某个文件下载不了,或者下载过慢。
可以手动次改其下载地址。

比如这里发现cmake下载太慢,所以改成国内的镜像。

// STATUS=$(curl -LO -w '%{http_code}' --connect-timeout 30 https://cmake.org/files/v3.10/cmake-3.10.2.tar.gz) 
STATUS=$(curl -LO -w '%{http_code}' --connect-timeout 30 http://distfiles.macports.org/cmake/cmake-3.10.2.tar.gz)

这里注释了原来的官网源地址,替换成了镜像地址。如果有其他的包下载不下来,进行类似的替换即可。

脚本执行完后,会得到编译成功的界面:

使用 EOSIO.CDT 工具链_第2张图片
wasmsdk_build_succ.png

还是和之前WASM的界面一样。然后到 执行:

sh install.sh

会发现在目录/usr/local/eosio.cdt/ 下按照好了SDK相关组件。

SDK组成

我们来看下编译安装好的CDT有哪些东西。

首先看/usr/local/eosio.cdt/

/usr/local/eosio.cdt/
├── bin
├── eosio.imports
├── include
├── lib
└── scripts

包含这么几个目录,其中:

  • bin: 构建工具二进制文件,比如eosiocpp
  • eosio.imports: 空文件
  • include: 编译依赖的头文件,比如eosiolib、boost、libc 等
  • lib: 编译依赖的库文件。比如libc++.a、libeosio.a。还有cmake 的模块文件EosioWasmToolchain.cmake。

这里的EosioWasmToolchain.cmake是我们后面需要用到的,其中定义了编译构建工具位置、SDK头文件的位置,依赖的SDK库文件的位置。

从上面的SDK目录我们可以看到,要编写合约。首先要对eosiolib有所了解,这边介绍的eosiolib系列文章也主要是关注这个库。同时还需要了解
libc/libcxx也就是标准C函数和C++的STL函数(PS:eosio并不支持完整的C标准库函数,以及STL,比如在stdlib.h里面找不到rand函数。这也是
为什么SDK里面有单独的头文件目录,而不能用系统自带的标准库文件目录)以及boost工具。

bin目录下是工具文件:

ls  /usr/local/eosio.cdt/bin
LLVMEosioApply.dylib    eosio-abigen        eosio-pp        llvm-nm         llvm-readelf        wasm-ld
clang           eosio-cc        llc         llvm-objcopy        llvm-readobj        wasm2wat
clang++         eosio-cpp       lld         llvm-objdump        llvm-strip      wat2wasm
clang-7         eosio-ld        llvm-ar         llvm-ranlib     op

这里对比原来的/usr/local/eosio/bin 会发现多了很多工具,比如:

  • wasm2wat : 将wasm文件转为wast文件
  • wat2wasm : 将wast文件转为wasm文件

这些webassembly相关的其实都是webassembly官方工具wabt 。除此之外,还有llvm打头的llvm工具
eosio打头的编译工具。

这里eosio-cpp替代了原来的eosio-cpp. eosio-cpp -help 一下会发现和原来已经大大不同了。新的CDT版本已经支持abi的生成,主要通过工具"eosio-abigen"

所以如果,没有使用cmake来构建工程编译规则,也可以手动调用eosio-cpp来实现。注意的是新的eosio-cpp已经不支持abi生产,也就是-g选项。需要使用
"eosio-abigen"工具。比如:

eosio-abigen hello.cpp --output=hello.abi

HelloWorld

编译好了工具,我们可以用官方的例子测试下。

去到"github.com/EOSIO/eosio.cdt/examples/hello"目录下。

修改CMakeList.txt中的"append the path to the module to include"后面增加:

list(APPEND CMAKE_MODULE_PATH "/usr/local/eosio.cdt/lib/cmake/")

将之前的库里面的cmake moudle加载上。这里还要手动修改,原因是没有设置EOSIO_CDT_ROOT变量。也可以通过设置这个变量就不用修改了。

然后执行:

cmake CMakeList.txt

cmake CMakeLists.txt
-- Setting up Eosio Wasm Toolchain
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/apollo/Repos/github.com/EOSIO/eosio.cdt/examples/hello

接着执行:

make

[ 50%] Building CXX object CMakeFiles/hello.dir/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

表示构建成功。并生成了 wasm文件 hello。

CMakeLists.txt

如果做过Android的JNI开发或者其他的嵌入式开发的话,会知道。toolchain其实是一套工具,我们只要按照规则写好makefile或者叫Android.mk就可以了。
make会自动根据规则去执行 跨平台编译,什么是跨平台编译,其实就是编译一个另一个架构平台上执行的机器码。比如在x86的windows编译手机arm cpu上可
执行的so。

这里我们的CDT SDK提供的工具链也是用类似的方式。我们只要写好CMakeLists.txt文件就可以了。剩下就交给cmake和make了。

类似Android.mk Android定义了协议模块工具和使用规则。这里我们上面提到的"EosioWasmToolchain.cmake"就是 SDK定义的规则。我们按照模板文件
github.com/EOSIO/eosio.cdt/examples/template下的CMakeLists.txt来写就好了:

cmake_minimum_required(VERSION 3.5)
project(hello_example VERSION 1.0.0)

# append the path to the module to include
list(APPEND CMAKE_MODULE_PATH "/usr/local/eosio.cdt/lib/cmake/")

#include the toolchain cmake
include(EosioWasmToolchain)

上面的我都不用改,只要将 "project" 里面的工程名改成我们的就可以了。

在CDT的example有个template的目录,里面和我上面的区别主要用了"EOSIO_CDT_ROOT"来索引cdt的cmake module。
这里我跟倾向于自己指定一个工程一个配置,否则后续不同的CDT版本,编译时需要使用不同的变量。

在上面之后加上我们要构建二进制规则:

add_executable( lol lol.cpp )
target_include_directories( lol PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ )

第一行将lol.cpp编译到lol文件中。第二行设置include文件目录为 "./" 。

如果熟悉cmake的话,这一切都是小儿科,如果不熟悉话,照着抄就可以了。

ABI

在上面的例子中都没有涉及到abi的生成。在原来的eosio-cpp工具中,我们只需要使用:

eosio-cpp -g name.abi name.hpp

就可以了。在新的CDT工具中更简单(否则也没人用新的了)。

只需要在CMakeList.txt里面增加一句:

set(CMAKE_CXX_FLAGS " -abigen ${CMAKE_CXX_FLAGS} ")

就可以自动生成ABI文件了。 这里一样以官方的example为例,进入到"github.com/eos/eosio.cdt/examples/abigen_test/"目录下。
然后运行:

cmake CMakeList.txt
make

Scanning dependencies of target test.wasm
[ 50%] Building CXX object CMakeFiles/test.wasm.dir/test.cpp.o
[100%] Linking CXX executable test.wasm
[100%] Built target test.wasm

此时和上面一样,在当前目录下就可以看到生成的test.wasm文件了。但是却没有abi文件。WHAT? 不是加了选项了么?

表急,当前的abi文件生成在CMakeFiles目录下面:

ls CMakeFiles/*.abi
CMakeFiles/test.abi

这里是因为cmake不是目标文件都是先存在在一个缓存目录中,也就是这里的CMakeFiles。已经给k85提了issue

当然,如果不习惯用cmake,也可以自己用eosio-abigen来生成:

eosio-abigen test.cpp --output=test.abi

这样组合使用eosio-cpp和eosio_abigen也可以达到原来直接写eosio-cpp的效果。不需要使用cmake了。

Action & Table

如果直接将原来的合约代码拿过来上CDT编译(或者是eosio-abigen)。会发现生成的abi文件是类似这样的空文件:

{ "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Sat Sep 15 20:38:08 2018", "version": "eosio::abi/1.0", "structs": [], "types": [], "actions": [], "tables": [], "ricardian_clauses": [],

这是因为在新的CDT中抛弃了原来的注释辅助的方式,而采用了C11/GNU扩展的辅助信息模式。

/// @abi table pooldb i64
    struct pooldb{
    ...
    }

/// @abi action
void clear();

上面这种老格式不再兼容,会生成空的abi文件。

新的格式有两种:

C11的属性格式

[[eosio::action]]
void testa( account_name n ) {
    // do something
}

struct [[eosio::table]] testtable {
    uint64_t owner;
    /* all other fields */
};

这种就和模板的语法有点类似,在函数前面或者"struct"后面增加"eosio::action"或者"eosio::table"的属性修饰。

GNU的扩展格式

__attribute__((eosio_action)) 
void testa( account_name n ){
    // do something
}


struct __attribute__((eosio_table)) testtable {
    uint64_t owner;
    /* all other fields */
};

这里一样,只是把中括号改成了"attribute(("。

相比于原来的注释类型,其实改动并不是很大。

总结

EOSIO.CDT是EOS提供的一套用来开发智能合约的SDK和编译的工具链。并且在v1.3后,原来的编译合约的方式将会被抛弃,所以我们需要早日熟悉起来这一套。

新的CDT相比原来的eosio-cpp,通过提供一个cmake的module使得可以直接通过CMaekFiles.txt 构建工程生成.wasm和.abi文件。同时也可以通过分解的
"eosio-cpp"和"eosio-abigen"命令分别编译生成wasm文件和abi文件。
工具来构建合约。

总结下新CDT优点:

  1. 不用拉下eos一大堆的代码了。
  2. 扩展了eosio-cpp。把webassembly的wabt暴露出来
  3. 提供cmake module简化cmake
  4. 支持生成WASM-elf文件,还有eosio-cc, eosio-ld, eosio-pp等可以观察生成的中间过程。
  5. 新的abi声明使用语言特性比注释更健全,不会出现以前注释写错导致action/table没生成的问题

你可能感兴趣的:(使用 EOSIO.CDT 工具链)