【问题标题】:How to write a good and efficient makefile如何编写一个好的高效的makefile
【发布时间】:2014-01-23 02:05:40
【问题描述】:

我有以下文件夹结构:

TOPDIR
|
├── a
│   ├── a.c
│   ├── a.h
│   └── a.mk
├── b
│   ├── b.c
│   ├── b.h
│   └── b.mk
├── c
│   ├── c.c
│   ├── c.h
│   └── c.mk
├── include
│   └── common.h
├── root
│    ├── main.c
│    └── root.mk
└── Makefile

每个条件

我的目标是在TOPDIR 下编写主Makefile 和子makefile,*.mk 在子文件夹中,include 文件夹包含一些常用定义。 root 文件夹包含我的主文件(main 函数位于此处)。同时在main.c中会调用a.cb.c的函数,c.c是驱动相关的,会从a.cb.c调用

问题

我写了sub-makefile like(我用了一个a.mk,其他的都一样,只有root.mk有点不同):

#MODULE will be modified for each sub folder
MODULE = a
LIB = $(MAKE_DIR)/libs/lib$(MODULE).a
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

#generate lib file from obj file
$(LIB): $(OBJS)
    @mkdir -p ../libs
    @$(AR) cr $@ $^
    @echo "    Archive    $(notdir $@)"

#compile obj file from source file
$(OBJS): $(SRCS)
    @$(CC) $(CFLAGS) -c $^
    @echo "    CC        $(OBJS)"

.PHONY: clean
clean:
    @$(RM) -f $(LIB) $(OBJS)
    @$(RM) -f *.expand
    @echo "    Remove Objects:   $(OBJS)"
    @echo "    Remove Libraries:  $(notdir $(LIB))"

我写了root.mk 喜欢:

PROG = ../prog/DEMO

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

#generate finial target file for run
$(PROG): $(SRCS)
    @mkdir -p ../prog
    @$(CC) $^ $(CFLAGS) -Wl,-Map=$(PROG).map $(LIBS) -o $@
    @echo "    Generate Program $(notdir $(PROG)) from $^"

.PHONY: clean
clean:
    @$(RM) -f $(OBJS) $(PROG)
    @$(RM) -f *.expand
    @$(RM) -rf ../prog ../libs
    @echo "    Remove Objects:   $(OBJS)"
    @echo "    Remove Libraries:  $(notdir $(PROG))"

我写了 main Makefile 喜欢:

MAKE_DIR = $(PWD)

ROOT_DIR    := $(MAKE_DIR)/root 
DRV_DIR     := $(MAKE_DIR)/driver
INCLUDE_DIR := $(MAKE_DIR)/include
DEBUG_DIR   := $(MAKE_DIR)/debug

INC_SRCH_PATH := 
INC_SRCH_PATH += -I$(ROOT_DIR)
INC_SRCH_PATH += -I$(DRV_DIR) 
INC_SRCH_PATH += -I$(INCLUDE_DIR)
INC_SRCH_PATH += -I$(DEBUG_DIR)

LIB_SRCH_PATH :=
LIB_SRCH_PATH += -L$(MAKE_DIR)/libs

CC = gcc
LD = ld

#problem happan here, if I change the sequence of LIB, 
#during the finial link, it will find some function un-referenced, 
#why can I put liba first?
LIBS := -lc -lb -la

CFLAGS :=
CFLAGS += $(INC_SRCH_PATH) $(LIB_SRCH_PATH) 
CFLAGS += -Wall -O -ggdb
CFLAGS += -DDEBUG -D_REENTRANT

LDFLAGS :=

export MAKE_DIR CC LD CFLAGS LDFLAGS LIBS LINT INC_SRCH_PATH

all:
    @$(MAKE) -C a -f a.mk
    @$(MAKE) -C b -f b.mk
    @$(MAKE) -C c -f c.mk
    @$(MAKE) -C root -f root.mk

.PHONY: clean
clean:
    @$(MAKE) -C debug -f debug.mk clean
    @$(MAKE) -C driver -f driver.mk clean
    @$(MAKE) -C mw -f mw.mk clean
    @$(MAKE) -C root -f root.mk clean

问题

  1. 在主Makefile 中,我定义了我将使用哪个LIB 文件,如果需要将其移至root.mk 更好?

  2. 在 sub-makefile 中,我没有使用 -MM 生成依赖文件,如果这导致了问题,我无法更改我的 lib* 的顺序,我也在 Makefile cmets 中进行了描述。

  3. 好像我的makefile系统无法检测到我更新了一些头文件,例如,我先编译了整个代码,然后,我修改了一个头文件,当我尝试重新编译时,没有编译任何源文件

如果:

#Automatic dependency magic:
%.d: src/%.c
   $(CC) -MM -o$@ $<

-include (MYPROG_OBJECTS:%.o=%.d)

需要添加到每个子makefile中吗?

【问题讨论】:

标签: c makefile


【解决方案1】:

这条规则肯定是错误的:

$(OBJS): $(SRCS)
        @$(CC) $(CFLAGS) -c $^
        @echo "    CC        $(OBJS)"

目标行将扩展为:

a.o b.o c.o d.o : a.c b.c c.c d.c

这是不对的。和写这个是一样的:

a.o : a.c b.c c.c d.c
        ...
b.o : a.c b.c c.c d.c
        ...
c.o : a.c b.c c.c d.c
        ...
d.o : a.c b.c c.c d.c
        ...

这意味着每当您更改任何源文件时,所有目标文件都将被重建。您应该在这里使用模式规则:

%.o : %.c
        @$(CC) $(CFLAGS) -o $@ -c $<
        @echo "    CC        $@"

一次编译一个目标文件。

就你的问题而言,我不明白问题 #1。

问题#2 和#3(如果我理解正确的话)是一回事:#3(更改头文件时不会重新编译文件)的原因是您没有声明头文件的任何先决条件。 Make 对此没有任何内置支持,因此您要么必须手动完成(将 a.o : a.c b.h c.h g.h 添加到您的 makefile 中),要么自动生成依赖项。

如果您的编译器支持这些标志,则依赖项生成通常会使用 -MM 或类似标志。

【讨论】:

  • 如果我使用%o:%c,我还需要定义$(OBJS)$(SRCS)吗?
  • 同时,lib文件生成怎么写?使用%a:%o?
  • 你需要OBJS 因为你需要$(LIB) 依赖它们。你需要SRCS,这样你就可以计算OBJS。所以是的。对于库,您可以只编写一个显式规则。您建议的模式规则在大多数情况下都无法正常工作,因为它说“从 one .o 文件构建 .a 文件”,这不是构建库的方式。您可以使用模式来做到这一点,但由于您通常只为每个 makefile 构建一个库,因此通常不值得。
  • 我只是使用了一个非常简单的文件夹树和文件,我的主要想法是将代码分成不同的功能模块,每个专用模块将位于一个子文件夹中,并且在编译过程中可以生成出来一个lib 文件,在我生成所有模块的lib 后,我将它们与我的main 函数文件链接在一起。你说这不是lib 的构建方式,那么你能否就如何组织或生成lib 文件提供更多建议?
  • 所以 cmets 不是长时间讨论此类事情的地方。你可以通过 help-make@gnu.org 邮件列表寻求帮助。然而,这不是我说的。我所说的是,当您构建一个库时,您不会(通常)从单个目标文件创建它,而是从一组目标文件创建它。模式规则通常定义了从单个输入文件创建单个输出文件的方法(例如,将源文件编译为目标文件)。因此,模式规则通常不适合创建库。
猜你喜欢
  • 2011-04-11
  • 1970-01-01
  • 2019-10-04
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 2018-11-29
  • 2011-04-04
相关资源
最近更新 更多