00:00
要给大家来讲解一下怎么样去编写一个合约的编译脚本,呃,那大家其实已经能够看到,就是我们一开始用的编译方式是用sock这个工具对吧,在命令行里边直接去敲,然后so杠杠。Bin,杠杠,Abi,然后加上我们的合约原文件的名称,就可以编译出对应的字节码和abi的接口,那这个过程当中,其实大家就会发现我们在把它编译出来之后呢,提取里边的内容的时候,一开始我们就是直接命令行把它输出,然后把它复制粘贴过来啊,这个就非常繁琐,而且大家发现复制的时候经常就会出错,那之后呢,我们就用了一个稍微好,看起来稍微好一点的办法,我们在自己的项目里边引入一个sock的模块,我们nv n PM install so。然后我们在note命令行里边直接就可以把它require进来之后,我们就可以在命令行里边去直接做编译了,对吧,就直接可以soft.comp就可以得到一个编译之后的这样对象大家应该还有印象对吧。
01:14
那在这个过程当中,我们自然就可以想到了,那一开始我们那样手动复制粘贴很麻烦,而之后我们可以在命令行里边直接把它生成一个JS对象,那我们既然可以把它在命令行实现,那我们就可以把它提炼出来,变成我们的一个自动化的脚本,呃,之前我们所写的那些脚本,呃,关于我们WEB3使用的那些脚本,其实都是这样的一个原则,对吧?呃,我们在已经在命令行里面一条一条输过之后,然后把它提取出来,这就是我们的一个脚本,这节课我们就先来完成我们的编译命令,构建的一个编译脚本这样的一个东西啊,首先我们做一点。准备工作,大家可以打开自己的bash,我们在里边来做一些。
02:06
目录文件目录的一些创建,目录结构的创建啊,先把这个server先停掉,大家之前可能也是在同同同一个目录下面去创建了很多的这个项目文件夹,对吧?我这里给大家推荐的是就是另外再重新创建一个文件夹,然后我这里推荐的是就叫做contract work flow,因为我们这个相当于大家可以看到这都是合约的工作流里边的一些内容,对吧,一个是编译,一个是部署的,这都很重要,之后我们还会讲到测试,呃,测试的时候我们可能会用到moa这个。就是框架啊,这个是之后的事情,我们现在的话就是先建一个这样的文件夹,Di contract work flow,好,那么在里边呢,我们为了便于以后。
03:05
大家对这一个各种各样的文件做管理,我们可以怎么样呢?分门别类的建立不同的文件夹,比如说我们建立一个叫做contract的文件夹还可以,然后再加一个,我们现在不是要做这个脚本吗?脚本化吗?我们再建立一个scripts的一个文件夹,再往后呢,我们来建一个compel的文件夹,这个文件夹放什么呢?我们就准备放一些编译之后的结果,对吧?呃,就是我们的结果要输出出来放到这里,然后到时候要用的时候直接到这里去找去拿就可以了。呃,最后当然就是我们为以后留下余地,就是再建一个测试的文件夹给test,那顾名思义了,就是以后我们放一些测试文件,包括测试结果,如果想输出也也放到这个里面去,好,这就是我们的一个简单的目录结构啊。
04:08
所以我们现在在当前的项目文件夹下面,根目录下面已经建了四个子目录,然后接下来当然了,我们既然要做那个编译脚本嘛,首先我们应该把源码准备好,对吧,那这个源码的话大家随意啊,就是可以。可以用我们之前已经讲过的源码,也可以自己就是想写什么随便去写,我这里只是为了说明我们这个流程,所以说就是用的是最简单的例子,就是我们一开始第一天接触solid的时候,给大家举了一个例子,就是呃,小汽车car这个合约,我这里就作为一个例子,把它当成我们的源码了,好,我直接copy过来啊,这个很简单,大家也不用再去实现,放在我们的contracts下面,我们规范化的去把它放在这里,对吧?好,建立一个car.so。
05:10
直接copy过来好,有了这个原文件之后呢,那大家就可以想到了,有了原文件,接下来我们还得有一些编译工具,对吧?之前我们所用到的编辑编译工具就是sock,那同样我们这里也需要有sock。那大家都知道怎么装so呢?N PM into sock呃,当然我这里不知道我们现在的网络状况怎么样啊,这里现场装看起来好像不是很快,呃,那那我这里就先不装了,因为我在另外一个目录下面都已经把这些装好,对吧?大家的话就在我们已经创建的这个目录下面去完成这个n PM in store的这个过程,先把这个sock再装好。当然如果大家说我之前已经有装好的,大家如果要是已经确定sock。
06:09
里边的那些就是所有的那些安装模块,大家都知道,都在一起的话,也可以直接把它复制过来,复制到当前的一个这个,呃,Note modules下面对吧。好啊,呃,当然了,大家如果要是想要在这里做做这个包管理的话,最好是先n PM in对吧,但可以先先这样做一下,但是不要在这个contract下面啊,在根录下面可以去n PM in。好,这个因为很简单,所以大家只要敲了命令就可以,我们准备好so就可以了,之前大家都已经做过的,接下来我们就是真正要完成我们的编译脚本了。呃,那这个编辑脚本,其实大家应该想到这个东西。
07:06
其实就是我们之前已经讲过的一些东西的一个集合,对吧?好,我们准备写一个编译脚本,这个编译脚本应该是什么样子呢?呃,这个编译脚本就是。啊,大家可以看到这里是用到了这个ES6的一些规范化的写法,对吧,就是把我们的全局的一些变量直接放在cost里边,Count的定义,然后我们定义一个FS,因为我们会从这个文件里面去读取嘛,所以我们require一个FS。当然了,有FS,那肯定我们还有那个sock对吧,So,等于require,然后接下来。呃呃,这里面我们还用到另外一个啊,应该是需要有一个pass这一个模块,我们去require pass FS和pass这个都应该是note默认的模块,所以大家不需要去专门去安装啊,正常情况下大家应该都有啊好,我们先把这个需要的一些组件先先拿过来,然后呢,大家就想到了,我们肯定就是呃,需要去做我们的文件的读取,对吧?大家应该想到我们当时是会有一个提取原文件。
08:35
比方说我们叫做contract source,然后它就需要取文件从我们的file里边去读取,这个我们一般情况是用同步方式去读取的,对吧?啊跟我们今天早上那个服务器上面读取的时候,大家是要用异步的方式,这里的话我们可以用同步方式去读的,好,那这个读取的话,我们就后面是需要给这个文件的,呃,路径了,对吧,所以说这个文件路径的话,我们前面再配一下吧。
09:09
我们这里给一个文件路径的话,给一个变量就叫做contract pass。然后后面文件格式我们用UTF8。Utf。同样上面大家看我现在这些我认为都是常量对吧,所以说就都是cons的啊,大家如果想用腕也可以用腕,那么我们在上面就应该还会有一个contract contract pass,然后这个怎么定义呢?那大家就会想到这个肯定就是在我们当前目录,我们现在是执行这个脚本对吧,执行这个脚本我们默认的脚本应该放在我们前面的scripts下那个目录下面。
10:02
那我们要找原文件的话,它应该是在它的上级目录里边,然后下面又有一个contracts文件夹,然后里边才会放我们的car那个合约,对吧,所以我们在这里会把它。呃,我们去做一个pass的这样写,那这个result方法是一个什么样的东西呢?它其实就是把我们后面给定的这些路径全部拼接起来。就是拼成一个完整的路径,好,那我们怎么拼完整路径的,我们首先要用这个啊,这个大家是不是应该用过的,见过对吧?There name前面是两个下划线啊,大家不要写,写成一个这个一开始大家可能容易写错两个下划线,这个就表这是默认的一个保留变量,就是我们当前执行的目录,对吧。
11:03
那接下来它要拼接一个什么呢?我们拼接一个它的副副目录,上一级目录,然后下边会有一个contract contract对吧,应该是用的这个啊,然后我们再凭接一个最后的文件名叫car.so好,这就是我们的文件目录,好,那接下来我们就可以编译了,呃,这个编译结果也可以用cost来保存啊,但是事实上可能大家会觉得这里可能用let会好一点,就是会觉得这个我不应该是一个常量,我的原文件可能是常量,但是就是这里后面读出来的东西应该是不是常量啊,这个就大家来自己来考虑,看大家喜欢用什么样的变量,呃,对它进行这种。定义域和这种生命周期的考量啊呃,接下来我们就定义一个叫compel result吧,这就是我们要拿到这个编译结果,它的编译结果是什么呢?就用到so了,对吧,so.com。
12:12
然后它里边我们要传进去我们的contract s,好,然后我们后面给一个参数给一个一,这个一是什么意思呢?之前我们好像就是直接给S就可以了,对吧?它后面第二参数可以给一个一,这个表示要打开soap工具里边的优化器,所以我们这里可以把这个优化器打开。一个比较好的时间方式啊,好OK,那这个其实我们就已经已经完完成了,对吧?我们来pencil.lo一下,还result,好,这非常简单,就就这么简单,我们把之前我们能够想到的命令其实就已经写出来了,其实我们之前最关键的就这么两行,对吧?把这个原文件读出来,然后把它compare,就这么两行,那别的其实我们也只是就是把这个路径做了一个拼接,然后引入了一些别的拈,要处理它的拈,然后最后把它打印出来,所以说非常简单,这就是我们基本的一个编译脚本,那大家就可以想到,呃,这么一个编译脚本,这个还有必要讲啊,这完全没必要讲,这里应该是一样的吧,啊,可能就是变量有点不一样,对吧,这个基本上没有什么其他的东西,那这个东西我们先。
13:43
看一下结果啊,大家如果要是已经跟着我一起把它敲完的话,我们就一起来看一下结果好了。呃,我这里没有装上,那我还是到我自己装好的这个目录下面去啊,好到script下面去给大家看一下我这里的,我们这里我先把我之前写好的这个脚本先换一下啊。
14:16
好,那么我们现在去创建一个comp comp.JS这样一个脚本。我们把这边的复制过来。好,那么现在我们大家知道就可以去编译了,对吧?Not comp,呃,大家要确定我们这里是contract,下面是有这个是有合约的对吧?这个得得先保证好,这里有card点。
15:02
呃,我我需要把这个OK退到这里,我们可以直接scripts下面的comp,好,我们看一下这个效果怎么样。把我们之前在命令行里面敲的东西全整合在一起,对吧?啊,那这个其实就大家应该有印象,之前我们就已经看到过这个编译的结果呢,它会输出一个很大的一个对象对吧,就是包括我们当时知道,包括字节码,包括API都在里边。那这个对象里面,它到底是一个什么层级结构呢?我们再再回过头来看一眼,它的层级结构是。最外层是一个contracts,也就是表明我们所有的合约,在这个文件里面,所有的合约这一次都会去做一个编译,当然了,在我们这个简单的例子里边,只有一个car,所以下面都包在一起了。呃,那大家可以看到这个car是我们整个contracts这个这个key下面的一个value,那一个value呢,又是一个大的对象,这个对象是car这个合约。
16:21
生成的编译对象对吧,那整个的这个编译对象又是什么呢?它的K叫做。字符串啊,冒号看,注意这是它的K,然后它的值是什么呢?Value是什么呢?又是一个大对象,所以它是这样对象套对象嵌套了三层到了这里,所以大家可以看到里边就是这个car的合约对象编译结果里边的一些要素,嗯,那我们知道就是我们现在最关心的,其实大家可以看到这个ma data里边,大家这个所有的基本信息都在里面,对吧?Ma data是原数据嘛,所以基本信息都在里面,我们的编译器是零点四点二五点,呃,这样的一个版本,然后呃,Language solid,然后后面还有这个就是output是什么样的,大家可以看到这个abi,它还有一个基本的定义,对吧,把API都放在里边了,然后下面还有source,它是什么样的类型,呃。
17:19
呃,CHECK256的这种加密方式,大家可以看到UIL是什么样的,所有的这些原数据都在里边,然后下边这个op codes是我们的操作码,就是所谓的这个在EVM上执行的这些东西,大家可以看到生成操作码非常的复杂,可读性也会比较差,那像咱们真正关心的其实就是之前给大家说过的一个是bad code前面这一部分。还有一个是下边的interface,它不叫abi,叫interface。稍微注意一下。interface这里跟我们。在外面用so工具命令行编译出来的那个结果不一样,那个结果是直接就是就是不带引号的一个结果,对吧,我们是把它复制出来就可以直接付给我们的API变量,那这里的话直接读的时候,它是一个带引号的,所以它是一个字符串。
18:15
这个稍微注意一下。好,那么我们看到这样的一个编译结果呢,呃,这个怎么说呢,其实大家会发现它。呃,确实是编译完了,确实确实是我们实现这个自动化的,但是这个结果似乎可读性也不好。然后呢,我们想要的东西好像也不是那么容易的能够拿出来。所以那么我们决定要对这个编译脚本做一些改良,做一些改进,好我们看一下,就接下来我们做什么样的一个改进啊,首先我们会想我们的这个编译结果是应该能保存下来的,对吧。首先我们应该是就是把它,诶这个是。
19:07
哦,这里是用到了另外一个小工具,就是大家看到,就是我们如果要想把这个编译的结果真正保存下来的话,用note本身提供的系统提供的这个FS,这个呃,就是file system的这个组件,它可能做不了太多的事情,那我们这里想做的一个是什么呢?是是想要把输出的结果直接输出到另外一个文件里面去做这样的一个操作。那我们这里就用到了另外一个叫做FS的一个工具,那所以呃,这个大家可以试一下啊,我不知道大家本身会不会有这个,如果要是没有的话,我们就需要先NPM一下对吧?N PM install FS,呃,我这里好像是。
20:01
我可以看一下我有没有装,我记得好像是没有装,应该本身就是有的哦,我这里是装过的,所以说那大家可能还是要需要去装一下啊n PM install FS这样去装一下,好,那么我们把它装好之后呢,呃,大家就会看到我们想要去做一个什么事情啊,我们我们需要的是呃。我们需要的是去把它做一个就是做一个,就是相当于大家看这个操作是什么,就是我们会去把所有的这个contracts里边的内容拿出来,然后呢,每一个都去把它输出到这个对应的pass下面。啊,所以这一部分可能稍微有一点复杂,我们先看懂这个逻辑,大家看一下这个是怎么样去写对吧?呃,这个逻辑是什么样?大家看啊,object.key然后下面是result.contract这是代表什么呢?大家看我们这里是首先。
21:18
我们呃,他这里的result就是我们这里的呃,Comp result对吧?啊,我们在这里直接就先开始敲吧,object.kids把这个全拿出来,Comp result把这个都拿出来之后,大家就会呃点contract啊,那这个东西我们在这个刚才输出的这个结果里面,大家要来看一下。这是我们整个的刚才的那个result结果对吧,那么他点contract。Key,然后它的contracts,所以它的contracts其实是我们下面的这些所有合约的一个对象的集合。
22:08
那所以他这里取了所有的case,也就是要取出我们所有的合约名称是不是。所以大家看清楚这个操作啊,它要做的是这样一个操作,好,那接下来好,大家看到这个FO1呢,这是应该是也是ES6里面的一种,大家把它认为是for循环就可以,对吧,一个数组或者一个对象去做便利的时候,我们可以用for意识方法,然后把里边的每一个元素提出来去做对应的操作,好那么我们。点for each里边我们拿出来的就是一个name,对吧,就是file name吧,我们写的明确一点啊,不能叫file name contract name。
23:00
好,那么我们拿到了这个内容之后,再看看去做一些什么事情啊,首先大家可以看到。呃呃,这个看起来我们还是用那个比较好啊,因为后面我们还会去真正拿到就是真正的合约,合约的名字,因为我们这里拿到的是在带冒号的,对吧?大家会想到这里拿到是一个带冒号的东西,所以后面这个用的是什么呢?大家看到replace后面用到了一个,呃,就是有点像我们的那个sed,就是shell里面S的这个命令一样的一个操作,对吧?它后面这个是表示什么?以冒号打头的内容全部都替换成后面这个空格,其实也就是删掉冒号打头的冒号对吧?好,那么我们这里就在这里,其实我们应该定义let,对吧,这里写的是cost啊,大家可以根据自己的需求觉得用什么样的这种变量定义的方式,Contract contract内等于。
24:07
呃,我们这里去把name做一个做一个选择,然后name.replace。呃,这里会定义一个,大家看到这个表达式啊,就是上面这个朝上的小尖这个符号,然后后面跟冒号,其实就代表是以冒号开头这样一个一个东西,然后当然后面我们就把它替换成这个,好,这里是不是不能是字符串啊,它是就是这样的一个规定的。呃,正则表达,所以大家注意前面是一个正则表达式,后面是它的这个字符。所以是这样的一个一个操作啊,好,那么大家看到我们把这一个contract name真正提出来之后,那之后其实我们就又是确定我们要把它放在哪里,然后output Jason think,那这个是干什么的,这其实就是会把我们的这个呃,Jason的一个对象直接输出到一个文件里面去。
25:20
然后后面跟的参数是文件的路径,然后呃,我们的这个名称是什么contract内对吧。所以呃呃,后后面这个不是不是我们名称啊,就是fair pass,就是我们的路径,就包含了我们的文件名称,然后后面这个是我们的内容,就是到底是要把什么样的内容输出到里面去,好,我们把这个还是先实现一下啊。呃,我们先要去定义一个fair pass是吧,这个fair pass,那又是pass去拼接一个。
26:07
DR,那大家想这个我们输出的路径是在哪里呢?是要输出到compel的那个文件夹下面对吧?那是我们的编译结果的文件目录嘛,然后我们,哦,我们这里可能就需要这里就不是指定的名称了,那是拿出来的那个contract name会作为我们的名称,对吧?所以呃,大家注意这里的写法啊,我们直接用contract内。然后,然后我们就加上一个。应该是一个字符串对吧。再再看一眼啊好,这里就没有拼接,直接就用我们的这个变量名称去表示了,大家看这个反引号是代表什么意思呢?这个反引号是代表。
27:12
对,模板字符串,所以我们可以直接在这里用这个模板字符串的形式去把它拼接在一起,那前面用Dollar符,呃,Dollar Dollar符和这个画括号括起来的其实是模板字符串里边引入的变量,对吧?所以我们前面的这个contract name在这里的话,如果我们是car.sol这个源文件,那么其实就应该是car,对吧?前面我们去掉了冒号,这里就是car,那么我们输出的这个文件呢?就是叫car.jason是这样的一个文件,好。呃,那最后我们就是要要输出了对吧,FS点,呃,它这个应该是叫output think是这个我看一下对不对啊。
28:07
Output Jason啊,Jason think啊,因为它是要把这个一个Jason对象放到我们的这个文件目录下面来,所以这里是fair pass,后边要放什么东西呢?我们要放的是编译结果里边的内里边的东西,对吧?呃,我们看应该就是放到这里啊。好。Compel result,这个不对,我们还应该得放在compel result的contracts下面。大家注意一下它的这个层级结构contracts,好,那么contracts下面我们再去取它的name,那取出来的就是下一层的那个对象,对吧?这里的name我们认为就是冒号car这个东西,所以我们取编译结果,点contracts下边,然后再去取冒号card的内容,我们取到的就是它整个的这个编译对象,我们把它保存起来,放在叫做一个car.jason的文件里面,好,那么我们这里边就就这样来写啊,最后我们可能输出一个。
29:32
突出一个log,这个就大家随便说我们这个保存到了哪里就可以了,对吧,比方说叫saving Jason fell to,呃,那我们这里就加上我们当前的这个fair pass,好,然后就把它输出在这里就可以了,好,那大家可以看到就是我们这样的话啊,这里要注意啊,我们上面把这个做一个替换fstra对吧,因为本身在FS里边是没有我们这里面的这个output Jason这个方法的,所以FS里边有这个方法,所以我们就会用到它的这个。
30:20
好,那接下来我们再把它看一下,我们现在经过改良之后的这个脚本,他的行为是什么样的,应该在下面compare.gs。先把它都删掉吧,这有问题啊,康FS好,因为这个下面我本来就是已经生成的这个东西的,所以我们先去清空一下,然后node scripts下面的compel执行我们的脚本。
31:22
好,再来看看我们会得到一个什么样的东西。诶,大家看到我们现在上面还是输出了这个所有的东西,对吧,但是我们下面多了一句说saving Jason fair to这个目录,好,那么我们去看一下这个目录下边,呃,其实就是在我们当前目录下的compel,对吧。看到有一个看Jason啊,我们看看。他已经把我们的所有的这些东西都已经转成了一个大的这对象,其实别的都还好,关键的就是这个abi,它真正转成Jason对象了,对吧。
32:04
啊,所以这里大家可以看到我们的结果,这就不用每次都打印在控制台上一大串,那大家可以想到我们现在就不需要看这么复杂的这个输出结果了,我直接在这里可以把这条这句pencil.lo删掉了,对吧?好,这是我们改进之后的第一个版本的编译脚本啊,那大家可以看到啊,就是在这个过程当中,呃,我们编译完了之后,还是要来试一下,看看我们是不是能正常的编译通过。
我来说两句