这里下载https://ftp.gnu.org/gnu/make/make-4.4.tar.gz进行研读。
目标
研读的初始目的,是想看看make打印的构建命令在哪执行的。
构建make
make项目本身的编译,可以用./configure && make
来完成。
代码挖呀挖
make中target
抽象为struct file
,并用链来组织,
struct file
{
const char *name;
const char *hname; /* Hashed filename */
const char *vpath; /* VPATH/vpath pathname */
struct dep *deps; /* all dependencies, including duplicates */
struct commands *cmds; /* Commands to execute for this target. */
const char *stem; /* Implicit stem, if an implicit
rule has been used */
struct dep *also_make; /* Targets that are made by making this. */
struct file *prev; /* Previous entry for same file name;
used when there are multiple double-colon
entries for the same file. */
struct file *last; /* Last entry for the same file name. */
/* File that this file was renamed to. After any time that a
file could be renamed, call 'check_renamed' (below). */
struct file *renamed;
/* List of variable sets used for this file. */
struct variable_set_list *variables;
/* Pattern-specific variable reference for this target, or null if there
isn't one. Also see the pat_searched flag, below. */
struct variable_set_list *pat_variables;
/* Immediate dependent that caused this target to be remade,
or nil if there isn't one. */
struct file *parent;
/* For a double-colon entry, this is the first double-colon entry for
the same file. Otherwise this is null. */
struct file *double_colon;
FILE_TIMESTAMP last_mtime; /* File's modtime, if already known. */
FILE_TIMESTAMP mtime_before_update; /* File's modtime before any updating
has been performed. */
unsigned int considered; /* equal to 'considered' if file has been
considered on current scan of goal chain */
int command_flags; /* Flags OR'd in for cmds; see commands.h. */
enum update_status /* Status of the last attempt to update. */
{
us_success = 0, /* Successfully updated. Must be 0! */
us_none, /* No attempt to update has been made. */
us_question, /* Needs to be updated (-q is is set). */
us_failed /* Update failed. */
} update_status ENUM_BITFIELD (2);
enum cmd_state /* State of commands. ORDER IS IMPORTANT! */
{
cs_not_started = 0, /* Not yet started. Must be 0! */
cs_deps_running, /* Dep commands running. */
cs_running, /* Commands running. */
cs_finished /* Commands finished. */
} command_state ENUM_BITFIELD (2);
unsigned int builtin:1; /* True if the file is a builtin rule. */
unsigned int precious:1; /* Non-0 means don't delete file on quit */
unsigned int loaded:1; /* True if the file is a loaded object. */
unsigned int unloaded:1; /* True if this loaded object was unloaded. */
unsigned int low_resolution_time:1; /* Nonzero if this file's time stamp
has only one-second resolution. */
unsigned int tried_implicit:1; /* Nonzero if have searched
for implicit rule for making
this file; don't search again. */
unsigned int updating:1; /* Nonzero while updating deps of this file */
unsigned int updated:1; /* Nonzero if this file has been remade. */
unsigned int is_target:1; /* Nonzero if file is described as target. */
unsigned int cmd_target:1; /* Nonzero if file was given on cmd line. */
unsigned int phony:1; /* Nonzero if this is a phony file
i.e., a prerequisite of .PHONY. */
unsigned int intermediate:1;/* Nonzero if this is an intermediate file. */
unsigned int is_explicit:1; /* Nonzero if explicitly mentioned. */
unsigned int secondary:1; /* Nonzero means remove_intermediates should
not delete it. */
unsigned int notintermediate:1; /* Nonzero means a file is a prereq to
.NOTINTERMEDIATE. */
unsigned int dontcare:1; /* Nonzero if no complaint is to be made if
this target cannot be remade. */
unsigned int ignore_vpath:1;/* Nonzero if we threw out VPATH name. */
unsigned int pat_searched:1;/* Nonzero if we already searched for
pattern-specific variables. */
unsigned int no_diag:1; /* True if the file failed to update and no
diagnostics has been issued (dontcare). */
unsigned int was_shuffled:1; /* Did we already shuffle 'deps'? used when
--shuffle passes through the graph. */
unsigned int snapped:1; /* True if the deps of this file have been
secondary expanded. */
};
update_file
update_file_1
两个函数递归调用来完成target更新。
最终当检测到target的依赖有更新的话,就调用的是remake_file
函数,来真实执行命令。
remake_file
函数如下,
static void
remake_file (struct file *file)
{
if (file->cmds == 0)
{
if (file->phony)
/* Phony target. Pretend it succeeded. */
file->update_status = us_success;
else if (file->is_target)
/* This is a nonexistent target file we cannot make.
Pretend it was successfully remade. */
file->update_status = us_success;
else
{
/* This is a dependency file we cannot remake. Fail. */
if (!rebuilding_makefiles || !file->dontcare)
complain (file);
file->update_status = us_failed;
}
}
else
{
chop_commands (file->cmds); //将命令切分成多条
/* The normal case: start some commands. */
if (!touch_flag || file->cmds->any_recurse)
{
execute_file_commands (file);
return;
}
/* This tells notice_finished_file it is ok to touch the file. */
file->update_status = us_success;
}
/* This does the touching under -t. */
notice_finished_file (file);
}
struct commands
{
floc fileinfo; /* Where commands were defined. */
char *commands; /* Commands text. */
char **command_lines; /* Commands chopped up into lines. */
unsigned char *lines_flags; /* One set of flag bits for each line. */
unsigned short ncommand_lines;/* Number of command lines. */
char recipe_prefix; /* Recipe prefix for this command set. */
unsigned int any_recurse:1; /* Nonzero if any 'lines_flags' elt has */
/* the COMMANDS_RECURSE bit set. */
};
最后找到打印命令在src/job.c
的start_job_cpmmand
的1359行,
/* Print the command if appropriate. */
if (just_print_flag || ISDB (DB_PRINT)
|| (!(flags & COMMANDS_SILENT) && !run_silent))
OS (message, 0, "%s\n", p);
看代码的过程学到一些实用的技巧,
-
make -n
不编译,只打印编译的构建过程,而不真正去编译。这个可以用来收集编译过程。执行过程中,被执行的命令往往都有 -
make命令中每个target下面多行命令默认是放在不同shell中执行的,如下
a.out
的target是三条名字,第一条对第二条不影响,所以ls
得到的文件列表不是home目录下的。有一种one_shell
mode可以将同一个target下的所有命令用,详情参看https://www.gnu.org/software/make/manual/html_node/One-Shell.html
.a.out: a.c cd ~ ls gcc a.c