【问题标题】:Makefile with two targets and two languages具有两个目标和两种语言的 Makefile
【发布时间】:2015-06-18 11:26:10
【问题描述】:

我使用 C 和 C++,感谢 this answer 我能够编译和运行我的项目。现在我正在写一个makefile来打发时间。但是事情变得复杂了:

项目结构

  • 项目
    • 制作文件
  • 客户端
    • bin
      • c
      • cc
      • java
  • 服务器
    • ...
# Directories

# CLIENT
CLIENT_BIN_DIR      = /client/bin/
CLIENT_SRC_C_DIR    = /client/src/c/
CLIENT_SRC_CC_DIR   = /client/src/cc/
CLIENT_SRC_JAVA_DIR = /client/src/java/
# SECC
SERVER_BIN_DIR      = /server/bin/
SERVER_SRC_C_DIR    = /server/src/c/
SERVER_SRC_CC_DIR   = /server/src/cc/
SERVER_SRC_JAVA_DIR = /server/src/java/

# Files

# CLIENT
CLIENT_BIN          = ${CLIENT_BIN_DIR}client_app
CLIENT_SRC_C        = ${wildcard ${CLIENT_SRC_C_DIR}*.c}
CLIENT_SRC_CC       = ${wildcard ${CLIENT_SRC_CC_DIR}*.cc}
CLIENT_SRC_JAVA     = ${wildcard ${CLIENT_SRC_JAVA_DIR}*.java}

# SERVER
SERVER_BIN          = ${SERVER_BIN_DIR}server_app
SERVER_SRC_C        = ${wildcard ${SERVER_SRC_C_DIR}*.c}
SERVER_SRC_CC       = ${wildcard ${SERVER_SRC_CC_DIR}*.cc}
SERVER_SRC_JAVA     = ${wildcard ${SERVER_SRC_JAVA_DIR}*.java}

# Flags
CFLAGS              = -g -W -Wall
IFLAGS              = -I${INC_DIR}

# Compilers
C                   = gcc
CC                  = g++

# Rules
all: ${CLIENT_BIN} ${SERVER_BIN}
${CLIENT_BIN}:
${SERVER_BIN}:
// NEED HELP HERE

现在我有了所有的源文件,我应该在每个文件的以下步骤中写什么:

  1. gcc -c -o file.o file.c
  2. g++ -c -o file.o file.cc -I/PATH_TO_C_HEADER
  3. g++ -o APP_NAME C_O_FILES CC_O_FILES

【问题讨论】:

  • 我建议在每个需要编译源码的目录下放置一个Makefile,它只负责编译该目录下的源码。然后,您的父 makefile(对于项目)需要做的就是调用其他 makefile 并担心它自己的目标(例如客户端和源)。
  • Make 让人头疼,递归 make 是一种错误模式。用tup (gittup.org/tup) 来做。它更简单、更先进、更快,而且您不必担心标头依赖性。
  • 如果你不需要使用 make 使用类似tup 的东西当然是一个合理的想法。如果你确实需要(或想要)让它工作得很好。
  • @Peter 您可能想阅读 dsi 的链接,了解为什么递归 make 是一个坏主意。

标签: c++ c gcc makefile


【解决方案1】:

make 有 built-in rules 用于将 C 和 C++ 文件编译为目标文件,您可以使用它们而不是通过使用他们期望的内置标志来编写自己的。同样,make 有一个从目标文件构建二进制文件的规则(只要二进制文件与源文件之一完全匹配)。

编译 C 程序

n.o is made automatically from n.c with a recipe of the form ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’.

编译 C++ 程序

n.o is made automatically from n.cc, n.cpp, or n.C with a recipe of the form ‘$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c’. We encourage you to use the suffix ‘.cc’ for C++ source files instead of ‘.C’.

链接单个对象文件

n 是通过 C 编译器运行链接器(通常称为 ld)从 n.o 自动生成的。使用的精确配方是“$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)”。

对于只有一个源文件的简单程序,这条规则是正确的。如果有多个目标文件(可能来自其他各种源文件),其中一个的名称与可执行文件的名称匹配,它也会做正确的事情。因此,

x: y.o z.o

当x.c、y.c、z.c都存在时会执行:

cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o

在更复杂的情况下,例如当没有名称源自可执行文件名的目标文件时,您必须为链接编写显式配方。

每一种自动制作成'.o'目标文件的文件都会通过使用编译器('$(CC)', '$(FC)' or '$(PC)'; the C compiler' $(CC)' 用于组装 '.s' 文件)而不使用 '-c' 选项。这可以通过使用“.o”目标文件作为中间文件来完成,但是一步完成编译和链接会更快,所以它就是这样完成的。

因此,只需设置正确的标志(如这些规则中所使用的)并将正确的 .o 文件先决条件添加到您的二进制目标,您就应该完成了。

【讨论】:

    【解决方案2】:

    如果您必须使用 make,这可以帮助您入门(仅适用于 C 文件):

    # CLIENT
    CLIENT_BIN_DIR      = client/bin/
    CLIENT_SRC_C_DIR    = client/src/c/
    
    # CLIENT
    CLIENT_BIN          = $(CLIENT_BIN_DIR)client_app
    CLIENT_SRC_C        = $(wildcard $(CLIENT_SRC_C_DIR)*.c)
    CLIENT_SRC_C_O      = $(CLIENT_SRC_C:.c=.o)
    CLIENT_SRC_C_D      = $(CLIENT_SRC_C:.c=.d)
    
    # Flags
    CFLAGS              = -g -W -Wall
    IFLAGS              = -I$(INC_DIR)
    
    # Compilers
    C                   = gcc
    
    #Create header dependency files (included headers are dependencies too)
    %.d: %.c
        $(C) $(CFLAGS) -MM -MF $@ -MT $*.o $<
    
    #include will remake the dependency files if they need to be
    -include $(CLIENT_SRC_C_D)
    
    #O files will be created via the implicit rule
    $(CLIENT_BIN): $(CLIENT_SRC_C_O)
        $(C) $(CFLAGS) $(LDFLAGS) $^ -o $(@)
    
    all: $(CLIENT_BIN)
    

    https://www.gnu.org/software/make/manual/make.html 真的很有帮助。

    否则,如果可以的话,我推荐tup,它比make更简单、更灵活、更快、更高级。

    【讨论】:

      【解决方案3】:
      # client binary depends on c and c++ .o's
      $(CLIENT_BIN): $(CLIENT_SRC_C:.c=.o) $(CLIENT_SRC_CC:.cc=.o)
      
      # server binary depends on c and c++ .o's
      $(SERVER_BIN): $(SERVER_SRC_C:.c=.o) $(SERVER_SRC_CC:.cc=.o)
      
      # example header dependency (file.cc depends on file.h and file1.h)
      $(CLIENT_SRC_CC_DIR)file.o: $(CLIENT_SRC_CC_DIR)file.h $(CLIENT_SRC_CC_DIR)file1.h
      

      GNU Make 具有编译 C 和 C++ 源文件和生成最终二进制文件的隐含规则,因此无需重写它们。

      另外,您可能想RTFM

      【讨论】:

      • 对由多个 *.o 文件组成的二进制文件没有隐含规则。
      • @PSkocik 只要二进制文件具有匹配的.o.c 文件,默认的隐式规则就会起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      相关资源
      最近更新 更多