go编译Cloudreve源码-【Golang源码分析】Golang如何实现bootstrap-dist介绍(二)

前言

根据《Golang如何实现Bootstrap(一)》的相关指导,我们知道go1.3的go编译需要go_bootstrap,但是生成go_bootstrap需要dist工具来生成。 所以本期主要关注dist工具。

1.dist工具简介

其实dist工具是属于go的一个启动工具go编译Cloudreve源码,负责创建C程序(如Go编译器)和go工具的初始启动副本。 它还可以用作包罗万象的 shell 脚本来替代以前完成的零散工作。 该工具可以通过“go tool dist”命令来操作。 该工具在不同系统下对应于pkg/tool/下的目录。

图1-1-1 dist工具简介

那么我们来看看dist作业的操作,如图1-1-1所示。 可以看到dist作业有6个操作,包括复制安装信息、编译go_boostrap、清理编译文件、查看go env、安装并复制go工具、查看go版本。

通过《【Golang源码分析】Golang如何实现Bootstrapping(一)》的理解,我们知道dist是用C源码编写的。 linux下是在make.bash中通过gcc编译的,命令如下:

#gcc -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist '-DGOROOT_FINAL="/mnt"' cmd/dist/buf.c cmd/dist/build.c cmd/dist/buildgc.c cmd/dist/buildruntime.c cmd/dist/goc2c.c cmd/dist/main.c cmd/dist/unix.c cmd/dist/windows.c

2.dist文件介绍

所有学习的关键就是先看官方文档是怎么说的,然后学习能力强的可以看源码,加深对学习的理解。

在查看dist目录之前,先看一下它对应的文档:

文档说:Dist 本身是用极其简单的 C 语言编写的。与 C 库(甚至标准 C 库)的所有交互都仅限于单个系统特定文件(plan9.c、unix.c、windows.c),以提高可移植性。 其他需要功能的文件应通过可移植层公开。 函数在可移植层中以 x 前缀开头,否则使用与现有函数相同的名称,或者与现有函数混淆。 例如,xprintf 是一个可移植的 printf。

到目前为止,dist 中最常见的数据类型是字符串和字符串。 但是, dist 使用两个命名数据结构 Buf 和 Vec 来代替 char 和 char* 数据结构,它们保存它们指向的所有数据。 Buf操作是以b开头的函数; Vec 操作是一个以 v 开头的函数。任何函数在堆栈上声明 Buf 或 Vecs 的基本方式应该是

void myfunc(void)
{
    Buf b1, b2;
    Vec v1;
    
    binit(&b1);
    binit(&b2);
    vinit(&v1);
    
    ... main code ...
    bprintf(&b1, "hello, world");
    vadd(&v1, bstr(&b1));  // v1 takes a copy of its argument
    bprintf(&b2, "another string");
    vadd(&v1, bstr(&b2));  // v1 now has two strings
    
    bfree(&b1);
    bfree(&b2);
    vfree(&v1);
}

binit/vinit 调用打算使用缓冲区或向量,从而初始化数据结构,并且 bfree/vfree 调用释放它们仍然保留的任何视频内存。 使用这个习惯用法给我们提供了词法作用域的赋值。

看完文档中的一些基本介绍后,可以看一下源代码文件对应的dist的函数。

图2-1-1 dist对应源码

对应的源代码如图2-1-1所示。 dist源码对应8个c文件和2个头文件,所以我们来分析一下每个c文件的用途。

3.dist源码分析

在研究源码之前,可以先看一下go_boostrap是如何编译的。 根据《【Golang源码分析】Golang如何实现Bootstrapping(一)》,我们知道go_boostrap是通过以下命令编译的:

#/mnt/pkg/tool/linux_amd64/dist boostrap -a -v

图3-1-1 执行dist命令

执行dist命令后,可以在编译boostrap时在lib、cmd、pkg中看到相应的问题。 接下来使用gdb来了解一下dist编译boostrap的过程。

3.1 带参数调试dist

图3-1-2 调试区域

在调试dist时,最好使用src/cmd/dist/dist编译的dist文件。 由于执行dist boostrap后“/mnt/pkg/tool/linux_amd64/dist”中的文件会被清除,所以编译时去掉“-O2”。 用gdb调试可以输入:

#gdb -c /mnt/src/cmd/dist/dist  

进入终端后,再次输入:

(gdb)set args bootstrap -a -v

这样就可以带参数调试dist了,如图3-1-2所示。

3.2 解析dist的入口源码

在看dist源码之前,先看一下dist/main.c源码,如下:

#include "a.h"
int vflag;
char *argv0;
// cmdtab records the available commands.
static struct {
    char *name;
    void (*f)(int, char**);
} cmdtab[] = {
    {"banner", cmdbanner},       //查看编译信息函数
    {"bootstrap", cmdbootstrap}, //bootstrap函数
    {"clean", cmdclean},  //清理cmd函数
    {"env", cmdenv},   //查看go env函数
    {"install", cmdinstall}, //安装cmd函数
    {"version", cmdversion}, //查看go 版本函数
};
// The OS-specific main calls into the portable code here.
void
xmain(int argc, char **argv)
{
    int i;
    if(argc <= 1)
        usage();
    
    //根据参数命令不同的函数
    for(i=0; i<nelem(cmdtab); i++) {
        if(streq(cmdtab[i].name, argv[1])) {
            cmdtab[i].f(argc-1, argv+1);
            return;
        }
    }
    xprintf("unknown command %sn", argv[1]);
    usage();
}

根据源码可以知道,bootstrap会调用cmdbootstrap函数,而编译go_bootstrap也是在cmdbootstrap函数中。

3.3 分析cmdbootstrap函数

接下来我们看一下对应的cmdbootstrap函数的实现:

void
cmdbootstrap(int argc, char **argv)
{
    int i;
    Buf b;
    char *oldgoos, *oldgoarch, *oldgochar;
    binit(&b);
    ARGBEGIN{
    case 'a':  //接受-a参数,表示编译全部
        rebuildall = 1;
        break;
    case 'v':  //接受-v参数,打印安装信息
        vflag++;
        break;
    default:
        usage();
    }ARGEND
    if(argc > 0)
        usage();
    if(rebuildall)
        clean();   //清理安装内容信息
    goversion = findgoversion();
    setup();
    xsetenv("GOROOT", goroot);  //设置GOROOT环境变量
    xsetenv("GOROOT_FINAL", goroot_final); //设置GOROOT_FINAL环境变量
    // For the main bootstrap, building for host os/arch.
    oldgoos = goos;
    oldgoarch = goarch;
    oldgochar = gochar;
    goos = gohostos;
    goarch = gohostarch;
    gochar = gohostchar;
    xsetenv("GOARCH", goarch);
    xsetenv("GOOS", goos);
    for(i=0; i<nelem(buildorder); i++) {
        install(bprintf(&b, buildorder[i], gohostchar)); //编译并安装
        if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
            install(bprintf(&b, buildorder[i], oldgochar)); //编译并安装
    }
    goos = oldgoos;
    goarch = oldgoarch;
    gochar = oldgochar;
    xsetenv("GOARCH", goarch);
    xsetenv("GOOS", goos);
    // Build pkg/runtime for actual goos/goarch too.
    if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
        install("pkg/runtime"); 编译并安装runtime
    bfree(&b);
}

cmdbootstrap函数比较简单。 主要做一些接受参数、清理安装内容、初始化环境变量等操作。 其实关键是install函数。

3.4 分析install函数流程

图3-4-1 dist编译流程

它是编译参数的集合。 其实最后会调用runv函数进行编译,runv函数会根据不同的系统调用不同的genrun函数。 如果是unix/linux系列,会调用unix.c中的genrun,如果是windows,会在genrun函数中组装参数后,调用windows.c中的genrun。 根据不同的系统,会调用不同的执行函数。

图3-4-2 调试go源码编译

其实go源码编译会调用“/mnt/pkg/tool/linux_amd64/6g”,这个6g显然不是固定文件。 我们来调试看看。

4.调试6g

图4-1 调试6g

调试6ggo编译Cloudreve源码,设置mian函数断点。 在src/lib9/main.c中可以清楚地看到main的使用。 这块叫做Plan 9 C,然后在plan9中调用lex生成的源代码进行词法分析。

图4-2 lex

对应src/cmd/gc/lex.c,lex与yacc结合进行句型分析。 最后生成相应的可执行文件。

总结 dist工具是属于go的一个启动工具。 go_boostrap是通过dist编译的。 dist工具可以编译c和go。 go1.3使用Plan 9来编译go。 编译参数在genrun函数中组装,根据不同的系统会调用不同的执行函数。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 go编译Cloudreve源码-【Golang源码分析】Golang如何实现bootstrap-dist介绍(二) https://www.wkzy.net/game/194315.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务