5

我是计算机工程专业的学生,​​我还有另一个 C 程序要实现。到目前为止,我总是从我以前的项目中创建一个新的 Makefile,每次都需要时间来设置。是否有一个我可以使用的 Makefile 画布功能如此强大,以至于我需要提供的只是包含 main 函数的 c 文件的名称?

我的想法是,对于每个新项目,我都会创建一个包含该项目的文件夹Makefile、一个 bin 文件夹和一个 src 文件夹。然后,我会将一些变量编辑到 Makefile(包含main()函数的 C 文件),然后make会自动构建所有内容,同时考虑到依赖关系。

你知道这样的Makefile存在吗?

[通过 Alexandre C. 编辑]:对于仅使用标准库并在标准 unix os 上运行的此类项目,Automake/Autoconf 太过分了。对于我们需要实现的项目,依赖关系(对于要链接的文件)总是可以从“.h”包含中推导出来的,而且通常涉及的文件很少。

4

7 回答 7

3

最接近神奇和完美Makefile的是使用可移植 C 程序和库的事实标准:Automake

作为从这个页面提取的示例,这是您将添加到 Makefile.am 的示例:

bin_PROGRAMS = zardoz
zardoz_SOURCES = main.c head.c float.c vortex9.c gun.c
zardoz_LDADD = $(LIBOBJS)

我们在这里添加的 make 目标的等价物称为zardoz. 源在下指定zardoz_SOURCES,需要链接的任何其他库都在下指定zardoz_LDADD。您不需要指定main函数所在的位置,因为链接器会在链接阶段自动找到它。如果它不存在,则链接将简单地失败。

于 2012-10-10T14:26:18.063 回答
1


    PROGRAM = test-program
    CFLAGS = -c -Wall

    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/*.c))
    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/more-sources*.c))

    all: $(OBJECTS)
        gcc -o ./bin/$(PROGRAM) $(OBJECTS)

    %.o: %.c %.h
        gcc $(CFLAGS) $< -o $@


像这样的东西可以解决问题。我显然没有对此进行测试。您可以选择基于每个文件覆盖默认编译。

于 2012-10-10T14:39:33.840 回答
1

您可以轻松编写自己的宏“包”来执行此操作。例如,将此文件创建为样板,调用它program.mk并将其放在树中的中心位置:

lang.c.objs   = $(patsubst %.c,%.o,$(1))
lang.c.link   = $(CC) $(CFLAGS) $(LDFLAGS) -o $(1) $(2)
lang.c++.objs = $(patsubst %.cpp,%.o,$(1))
lang.c++.link = $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(1) $(2)

define make-program
  program.$(1).lang    ?= c
  program.$(1).objects ?= $$(call lang.$$(program.$(1).lang).objs,$$(program.$(1).sources))
  $$(program.$(1).name): $$(program.$(1).objects) $$(program.$(1).extra-deps)
      $$(call lang.$$(program.$(1).lang).link,$$@,$$^ $$(program.$(1).ldlibs))
  CLEANABLE    += $$(program.$(1).name)
  ALL_PROGRAMS += $$(program.$(1).name)
endef

# If the user didn't specify a list of programs, build them all
ifndef PROGRAMS
  PROGRAMS = $(foreach p,$(filter program.%.name,$(.VARIABLES)),\
    $(patsubst program.%.name,%,$(p)))
endif

# Generate the rule to build each program
$(foreach p,$(PROGRAMS),$(eval $(call make-program,$(p))))

.PHONY: all clean
all: $(ALL_PROGRAMS)

clean: ; rm -f $(CLEANABLE)

.DEFAULT_GOAL := all

现在,在您要构建程序的每个目录中,您的 makefile 可以是:

program.p.name    = Program
program.p.sources = Program1.c Program2.c

include path/to/program.mk

类似的library.mk可以用于库。这种方法非常强大并且很容易扩展。

于 2012-10-10T16:47:57.557 回答
0

我也推荐autotools,你最终会学会欣赏它,它绝对值得,你可以找一个关于如何使用它的教程,或者,如果你不介意看一本小书,我建议阅读autobook,如果你只想要一个简单的 Makefile 模板,我用这个:

BIN     = <binary name>
OBJ     = <object files *.o>
LIB     = <libraries>
CFLAGS  = -O2 -Wall -I. 
LDFLAGS = <ld flags>

CC = gcc
LD = ld
RM = rm -f 

all:: $(BIN)

$(BIN): $(OBJ)
    $(CC) $(LDFLAGS) $(OBJ) $(LIB) -o $(BIN)

clean:
    $(RM) $(BIN) *.o

%.o: %.c %.h
    $(CC) $(CFLAGS) -c $<
于 2012-10-10T14:39:13.867 回答
0

./configure您可以像大多数开源项目一样,通过著名的命令使用 autoconf 自动生成 Makefile 。参考这个链接:配置文件模板生成makefile

于 2012-10-10T15:07:40.187 回答
0

如果您对标准 UNIX 环境感兴趣,为什么不简单地坚持使用 POSIXmake提供的功能呢?

# Lean and mean.
.POSIX:

CFLAGS = -DNDEBUG -O
LDFLAGS = -s

TARGETS = abc def

all: $(TARGETS)

clean:
    rm -f $(TARGETS)

# Some dependencies.
abc: def.h

# Some special rules.
def: def.c
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ def.c -l m

对于大多数小型项目,您不需要比默认设置更复杂的东西;也许您应该专注于项目本身,而不是浪费时间编写复杂的 makefile 逻辑。

POSIX 的一个限制make是没有模式规则,并且后缀规则不能处理目录;因此,在不同目录中传播源文件将需要每个文件的单个规则,或者递归制作。就个人而言,对于小型项目,我不会费心将源文件移动到其他地方。坚持使用 POSIXmake还将提供跨其他与 POSIX 兼容的构建系统(make例如 BSD)的可移植性。

关于自动依赖计算,请阅读gcc -M. 它生成包含依赖规则的makefile。然后你可以include在你的makefile中使用它们。但是,在我看来,更好的做法是在单个头文件中跟踪所有重要的依赖关系,并通过后缀规则使每个文件都依赖于该头文件。这并不麻烦,而且您仍然可以节省一些便携性。

总而言之,我的建议是避免过度担心并发症。他们很少有回报。特别是,我从来没有喜欢过autotools,我肯定永远不会喜欢。UNIX 的一切都是为了简单。不必要地使事情复杂化是违反 UNIX 精神的。:)

于 2012-10-16T06:04:39.453 回答
0

我想出了以下解决方案:

# Author Matthieu Sieben (http://matthieusieben.com)
# Version 23/10/2012
#
# This Makefile is licensed under the Creative Commons Attribution
# Partage dans les Mêmes Conditions 2.0 Belgique License.
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.0/be/.
# Use at your own risk.
#
# Use Makefile.inc to write you own rules or to overwrite the default values defined here.

SRCDIR  = ./src
BINDIR  = ./bin

SHELL   = /bin/bash
CC      = /usr/bin/gcc
LIBS    =
CFLAGS  = -Wall -Wextra -Werror
LDFLAGS =

.PHONY: default all clean distclean
.SUFFIXES: .c .o

# make "all" the default target
default: all

-include Makefile.inc
-include Makefile.d

CFLAGS += -I$(SRCDIR)
ifeq ($(DEBUG), 1)
    CFLAGS += -DDEBUG=1 -ggdb
else
    CFLAGS += -DDEBUG=0 -O2
    LDFLAGS += -O2
endif

%.o: %.c
    @echo "  Compiling `basename $<`";
    @$(CC) $(CFLAGS) -o $@ -c $<;

clean:
    @echo "Cleaning compiled files";
    @find $(SRCDIR) -name "*.o" -exec rm {} \;
    @[ -e Makefile.d ] && rm Makefile.d;

distclean: clean
    @echo "Removing executables";
    @find $(BINDIR) -type f -exec rm {} \;

Makefile.d: $(shell find $(SRCDIR) -type f -name "*.c")
    @echo "Building dependencies";
    @for file in `find $(SRCDIR) -name "*.c" -print`; do\
        $(CC) $(CFLAGS) -MM -MT $${file/%.c/.o} $$file | tr -d "\n\\" >> Makefile.d.tmp;\
        echo -e "\n" >> Makefile.d.tmp;\
    done;\
    for file in `find $(SRCDIR) -name "*.c" -exec grep -Hs "main(" {} \; | cut -f1 -d':'`; do\
        execname=`basename $$file .c`;\
        objs="$${file/%.c/.o}";\
        for header in `$(CC) $(CFLAGS) -MM $$file | tr " " "\n" | grep ".h$$" | sort | uniq | tr "\n" " "`; do\
            if [ -f $${header/%.h/.c} ]; then\
                objs+=" $${header/%.h/.o}";\
            fi;\
            for obj in `grep "^$${header/%.h/.o}" Makefile.d.tmp | tr " " "\n" | grep ".h$$" | tr "\n" " "`; do\
                if [ -f $${obj/%.h/.c} ]; then\
                    objs+=" $${obj/%.h/.o}";\
                fi;\
            done;\
        done;\
        objs=`echo -n "$$objs"  | tr " " "\n" | sort | uniq | tr "\n" " "`;\
        echo "all: $$execname" >> Makefile.d.tmp;\
        echo "$$execname: $(BINDIR)/$$execname" >> Makefile.d.tmp;\
        echo "$(BINDIR)/$$execname: $$objs" >> Makefile.d.tmp;\
        echo "  @echo \"Linking $$execname\";" >> Makefile.d.tmp;\
        echo "  @\$$(CC) \$$(LDFLAGS) -o \$$@ $$objs \$$(LIBS);" >> Makefile.d.tmp;\
        echo >> Makefile.d.tmp;\
    done;\
    mv Makefile.d.tmp $@;\

它不是很健壮,因此请随时提供改进。下面是它的工作原理。这里的想法是,对于 .c 中的每个文件$(SRCDIR),查找那些包含 main 函数 ( grep "main(" src/*.c) 的文件。然后,为每个本地包含#include "myfile.h"添加myfile.o到该可执行文件的对象列表中,然后在 Makefile.d 中写入上面的行

编辑 这个新版本的脚本支持 bin 文件链接的依赖项。

于 2012-10-23T15:50:21.003 回答