Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node源码粗读(2):node编译过程详解及如何在本地进行源码修改和调试 #9

Open
xtx1130 opened this issue Nov 22, 2017 · 5 comments

Comments

@xtx1130
Copy link
Owner

xtx1130 commented Nov 22, 2017

上篇文章直接单刀直入讲述了node运行的一个整体流程,导致很多人在群里或者cnode中询问如何可以在本地断点调试node。因此献上这篇文章,让大家对node编译过程和开发有个整体的认知

c++编译三部曲

$ ./configure
$ make  
$ make install

相信看到这三个命令后,大家应该是十分熟悉。没错,node编译也就是这三步。

  • 第一步:配置。软件装到哪里、什么参数、什么os、装什么东西,全都是configure来确定的
  • 第二步:编译。make会读取makefile的配置,进行编译,生成可执行文件
  • 第三步:安装。make install 会根据设定好的路径,把软件安装到系统中

接下来,我们从这三个步骤入手,来串一下node整体的编译过程。

node 编译过程

./configure

目标很明确,我们直奔configure文件,来查看node的configure究竟都做了些什么。
issue9-1
额,原来关键代码就在这里,其中import了三个模块,分别为:nodedownload(下载模块)、getmoduleversion(获取版本)、以及gyp_node。大家知道,node其实是基于gyp进行编译的那么这个gyp_node模块具体是什么呢?让我们继续扒到tools/gyp_node
issue9-2
通过gyp_node中的这段代码可以读出来,其实最后运行的是node.gyp,我们视线跳转到node.gyp,而node.gyp就是构建文件内容,即python的一个数据结构(类似一个json串)。通过筛查其中的target_name我们可以发现这个gyp_node做了有如下几件事情(只说比较重要的,有兴趣的同学可以自己翻看node.gyp):

  • 关联node可执行文件的sources
  • 定义node_js2c的输出文件为node_javascript.cc
  • node_dtrace动态跟踪框架一系列定义
  • cctest 测试相关定义

接下来视线回到gyp_node.py。下面有几行有意思的代码:

  if sys.platform != 'win32' and 'ninja' not in args:
    # Tell gyp to write the Makefiles into output_dir
    args.extend(['--generator-output', output_dir])

    # Tell make to write its output into the same dir
    args.extend(['-Goutput_dir=' + output_dir])

这几行代码会根据node.gyp中的target_name和type在./out文件夹中,生成Makefile和*.mk文件:
issue9-3
回到./configure中,有几个配置参数需要我们注意一下:

parser.add_option('--prefix',
    action='store',
    dest='prefix',
    default='/usr/local',
    help='select the install prefix [default: %default]')
parser.add_option('--debug',
    action='store_true',
    dest='debug',
    help='also build debug build')

请大家注意下这两个参数,一个是--prefix,如果不设定prefix,默认路径相当于安装到了全局,不利于我们进行调试,--debug参数在运行configure的时候也是一定要加的。加了--debug,make的才会生成相应的/out/Debug文件夹。即,如果你想在本地调试的话,需要运行的第一个步骤的代码是:

$ ./configure --preifx=your repo --debug

make

入手make的话,我们直接切入到makefile就好了。然后我们就会发现如下代码:

ifeq ($(BUILDTYPE),Release)
all: out/Makefile $(NODE_EXE)
else
all: out/Makefile $(NODE_EXE) $(NODE_G_EXE)
endif

在这里,分为了两种情况,上面为Release的情况,下面是configure --debug的情况。可以看到debug多一个步骤$(NODE_G_EXE),经过查找其实是node_g,用于debug模式用的。$(NODE_EXE)就不过多介绍了,就是node的执行文件,下面主要看一下out/Makefile。不知道大家对out/文件夹还熟悉吗?没错,就是上一步./configure生成的。我们翻到out/Makefile中看一下这个makefile做了什么。
issue9-4
这里只做一段代码的截图,不过我们已经可以发现这里面引用了所有的mk文件,之后进行编译,因为他监听了所有的mk,相当于这里监听了所有的node相关的文件,只要include的关联文件有改动,在make的时候都会造成out/Makefile的重新编译,接下来我们回到主文件./Makefile:

.PHONY: $(NODE_EXE) $(NODE_G_EXE)
$(NODE_EXE): config.gypi out/Makefile
	$(MAKE) -C out BUILDTYPE=Release V=$(V)
	if [ ! -r $@ -o ! -L $@ ]; then ln -fs out/Release/$(NODE_EXE) $@; fi

$(NODE_G_EXE): config.gypi out/Makefile
	$(MAKE) -C out BUILDTYPE=Debug V=$(V)
	if [ ! -r $@ -o ! -L $@ ]; then ln -fs out/Debug/$(NODE_EXE) $@; fi

.PHONY的目标是$(NODE_EXE) $(NODE_G_EXE),说明在每次make的时候,这两个target一定会运行,看上面的代码这两个target都关联了 out/Makefile,而out/Makefile刚才已经讲过了,他可以监听到所有的node源码文件,只要有改动,在make的时候就会重新编译。通过这一套流程,其实可以实现如下的功能:

修改node源码文件->make重新编译->node重新编译生成

如果我们在调试的时候,如果一直用的是编译之后的node,那么我们其实在修改源码之后,只需要重新make一下就好了。

make install

如果只是本地做调试用,其实到第二步就可以了,make install 相当于把可执行文件安装到第一步定义的preifx中,在这里我建议大家,在git clone 代码的时候,直接clone到你心仪的目录下。

本地调试开发node源码

本地编译

在本地开发的时候,我选择的ide是cLion,除了开始生成的cMakefile比较坑爹外,还是不错的。
首先,把node代码 clone下来:

$ git clone https://github.com/nodejs/node.git

然后,进入到node中运行:

$ ./configure --prefix=your repo --debug 

然后进行make操作:

$ make

这样,相当于已经编译完成node了。

导入项目

我们打开cLion把这个项目到cLion中,导入进来之后会自动生成一个CMakeLists.txt,我们不需要管这个文件,在进行调试的时候,通过make就可以了。CMakeLists.txt由于是自动生成的,只是简单的遍历了一遍c++基础文件,没有相应的链接库,所以即使跑也是跑不通的。

关联node

在cLion顶部菜单run->Edit Configuration中,进行一下配置:
issue9-5
一定要确保before launch中是空的,不要加build,如果加了build就会在运行前调用cmake读取CMakeLists.txt,这并不是我们所希望的。executable可执行文件,选择out/Debug/node。

断点调试

还是刚才那张图,在你的programming arguments中加上要调试的js文件地址,然后去node.cc中打断点,然后运行debug试一下,看看是否会走到你的断点中,不出意外的话,是完全ok的。

node源码修改调试

如果你想对node源码进行修改调试,认真看了上面的文章的同学应该已经想到了:在修改完代码之后,通过make进行编译,就会触发out/Makefile,从而重新生成out/Debug/node文件,注意一下make的控制台输出:

if [ ! -r node_g -o ! -L node_g ]; then ln -fs out/Debug/node node_g; fi

在这里,对out/Debug/node做了一个软链,如果是第一次编译,会建立一个软链,连接到node_g。

by小菜

@gengjiawen
Copy link

gengjiawen commented Nov 7, 2018

可以使用这个项目https://github.com/lev-kazakov/node-cmake-generator , 生成cmake文件,但是不支持CMake最新版, see https://github.com/lev-kazakov/node-cmake-generator/issues/1。

@xtx1130
Copy link
Owner Author

xtx1130 commented Nov 9, 2018

@gengjiawen 感谢你的分享,在本地开发中,每次我改完代码都是用make做的重新编译,没有在clion开启自动编译。因为我的本地代码会定期拉master然后自动编译,所以用make是最保险的方案。

@AsceticBoy
Copy link

image
image
请教下,在导入后,debug时看起来还是在用cmake build,并且报了错,不过我用的版本和您可能不太一样

@xtx1130
Copy link
Owner Author

xtx1130 commented May 9, 2019

@AsceticBoy 你看上面关联node部分

一定要确保before launch中是空的,不要加build

目测你现在的before launch中还有build脚本
image

@AsceticBoy
Copy link

@xtx1130 谢谢🙏,确实是这样

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants