我如何为 C 项目创建一个带有 SRC、 OBJ 和 BIN 子目录的 Makefile?

几个月前,我为学校布置的作业想出了以下通用的 Makefile:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------


# project name (generate executable with this name)
TARGET   = projectname


CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.


LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall


SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f


$(TARGET): obj
@$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"


obj: $(SOURCES) $(INCLUDES)
@$(CC) $(CFLAGS) $(SOURCES)
@echo "Compilation complete!"


clean:
@$(rm) $(TARGET) $(OBJECTS)
@echo "Cleanup complete!"

这将基本上编译每个 .c.h文件,以生成 .o文件和可执行 projectname都在同一个文件夹中。

现在,我想推动这一点。 如何编写 Makefile 来编译具有以下目录结构的 C 项目?

 ./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>

换句话说,我希望有一个 Makefile,它将 C 源代码从 ./src/编译成 ./obj/,然后链接所有内容以在 ./bin/中创建可执行文件。

我尝试读取不同的 Makefile,但是我就是不能让它们适用于上面的项目结构; 相反,项目由于各种错误而无法编译。当然,我可以使用完全成熟的 IDE (单片开发,Anjuta 等) ,但是我更愿意坚持使用 gEdit 和好的 ol’终端。

有没有一位大师可以给我一个可行的解决方案,或者关于如何做到这一点的清晰信息?谢谢!

我不知道

最终解决方案:

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------


# project name (generate executable with this name)
TARGET   = projectname


CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.


LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm


# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin


SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f




$(BINDIR)/$(TARGET): $(OBJECTS)
@$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
@echo "Linking complete!"


$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
@$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"


.PHONY: clean
clean:
@$(rm) $(OBJECTS)
@echo "Cleanup complete!"


.PHONY: remove
remove: clean
@$(rm) $(BINDIR)/$(TARGET)
@echo "Executable removed!"
80824 次浏览

You can add the -I flag to the compiler flags (CFLAGS) to indicate where the compiler should look for source files , and the -o flag to indicate where the binary should be left:

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin


$(TARGET): obj
@$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"

In order to drop the object files into the obj directory, use the -o option when compiling. Also, look at the $@ and $< automatic variables.

For example, consider this simple Makefile

CFLAGS= -g -Wall -O3
OBJDIR= ./obj


SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)


%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

Update>

By looking at your makefile, I realize you are using the -o flag. Good. Continue using it, but add a target directory variable to indicate where the output file should be written.

First, your $(OBJECTS) rule is problematic, because:

  1. it's kind of indiscriminate, making all sources prerequisites of every object,
  2. it often uses the wrong source (as you discovered with file1.o and file2.o)
  3. it tries to build executables instead of stopping at objects, and
  4. the name of the target (foo.o) is not what the rule will actually produce (obj/foo.o).

I suggest the following:

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)


$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"

The $(TARGET) rule has the same problem that the target name does not actually describe what the rule builds. For that reason, if you type make several times, Make will rebuild the target each time, even though there is no reason to. A small change fixes that:

$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $@ $(LFLAGS) $(OBJECTS)
@echo "Linking complete!"

Once that's all in order, you might consider more sophisticated dependency handling; if you modify one of the header files, this makefile will not know which objects/executables must be rebuilt. But that can wait for another day.

EDIT:
Sorry, I omitted part of the $(OBJECTS) rule above; I've corrected it. (I wish I could use "strike" inside a code sample.)

I have stopped writing makefiles these days, if your intention is to learn go ahead, else you have good makefile generator that comes with eclipse CDT. If you want some maintainability / multiple project support with in your build tree, have a look at the following -

https://github.com/dmoulding/boilermake I found this pretty good..!