【问题标题】:Make multiple targets in 'all'在“全部”中创建多个目标
【发布时间】:2012-06-11 08:08:54
【问题描述】:

我正在尝试使用 Makefile 构建库的调试和发布版本,并将这些库复制到相关的构建目录,例如

.PHONY: all clean distclean

all: $(program_NAME_DEBUG)
    $(CP) $(program_NAME_DEBUG) $(BUILD_DIR)/debug/$(program_NAME_DEBUG)
    $(RM) $(program_NAME_DEBUG)
    $(RM) $(program_OBJS)
    $(program_NAME_RELEASE)
    $(CP) $(program_NAME_RELEASE) $(BUILD_DIR)/release/$(program_NAME_RELEASE)
    $(RM) $(program_NAME_RELEASE)
    $(RM) $(program_OBJS)

$(program_NAME_DEBUG): $(program_OBJS)
    $(LINK_DEBUG.c) -shared -Wl,-soname,$(program_NAME_DEBUG) $(program_OBJS) -o $(program_NAME_DEBUG)

$(program_NAME_RELEASE): $(program_OBJS)
    $(LINK_RELEASE.c) -shared -Wl,-soname,$(program_NAME_RELEASE) $(program_OBJS) -o $(program_NAME_RELEASE)

第一个目标 (program_NAME_DEBUG) 编译正常,但第二个目标 (program_NAME_RELEASE) 产生以下错误:

libGlam_rel.so
make: libGlam_rel.so: Command not found
make: *** [all] Error 127

libGlam_rel.soprogram_NAME_RELEASE 的值

它似乎不像识别第一个目标那样识别第二个目标?

编辑

终于搞定了。

一个问题是多个目录中有 src 文件,使用 VPATH 对其进行排序,例如

# specify dirs other then current dir to search for src files
VPATH = ../../pulse_IO/src ../../../g2/src

在平台条件的东西中添加了额外的库目标,例如

# Platform specific conditional compilation
UNAME := $(shell uname)

TARGET := Glam

ifeq ($(UNAME), Linux)
# LINUX version
program_NAME := lib$(TARGET).so
program_DEBUG_NAME := lib$(TARGET)_dbg.so
program_RELEASE_NAME := lib$(TARGET)_rel.so
BUILD_DIR = ../build/linux
endif

ifeq ($(UNAME), MINGW32_NT-6.1)
# WINDOWS version
program_NAME := lib$(TARGET).dll
program_DEBUG_NAME := lib$(TARGET)_dbg.dll
program_RELEASE_NAME := lib$(TARGET)_rel.dll
BUILD_DIR = ../build/windows
endif

添加了新的调试和发布目标文件:

DEBUG_OBJS := $(addprefix $(BUILD_DIR)/debug/,${program_OBJS})
RELEASE_OBJS := $(addprefix $(BUILD_DIR)/release/,${program_OBJS})

设置我的调试并发布 CFLAGS:

DEBUG_CFLAGS := -fPIC -g -Wall -DDEBUG=1
RELEASE_CFLAGS := -fPIC -O2 -Wall -DDEBUG=0

整理了所有的调试和发布编译器选项:

DEBUG_LINK.c := $(CC) $(DEBUG_CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
RELEASE_LINK.c := $(CC) $(RELEASE_CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

将我的新规则添加到“全部”目标:

.PHONY: all clean

all: $(program_DEBUG_NAME) $(program_RELEASE_NAME)

规则集如下所示(包括替代隐式目标文件生成):

$(program_DEBUG_NAME): $(DEBUG_OBJS)
    $(DEBUG_LINK.c) -shared -Wl,-soname,$@ $^ -o $(BUILD_DIR)/debug/$@

$(program_RELEASE_NAME): $(RELEASE_OBJS)
    $(RELEASE_LINK.c) -shared -Wl,-soname,$@ $^ -o $(BUILD_DIR)/release/$@

# rule to build object files (replaces implicit rule)
$(BUILD_DIR)/debug/%.o: %.c
    $(DEBUG_LINK.c) $< -c -o $@

$(BUILD_DIR)/release/%.o: %.c
    $(RELEASE_LINK.c) $< -c -o $@

最后我修改了 clean 以处理所有新文件:

clean:
    @- $(RM) $(BUILD_DIR)/debug/$(program_DEBUG_NAME)
    @- $(RM) $(DEBUG_OBJS)
    @- $(RM) $(BUILD_DIR)/release/$(program_RELEASE_NAME)
    @- $(RM) $(RELEASE_OBJS)

这允许我使用单个 Makefile 跨 linux 和 windows 平台生成我的库的调试和发布版本,例如$ make -k

【问题讨论】:

  • 为什么?我想我在这里正确使用了 vpath,即搜索源 not 目标?
  • 对于一个简单的 makefile 来说很好,如果这就是你想要的,那就没问题了。如果您的项目开始具有相同名称但路径不同的文件,您将遇到问题
  • 啊是的 - 我知道这将是一个潜在的问题

标签: makefile


【解决方案1】:

首先要做的事情。您似乎想要两个库的不同版本的目标文件,但这个 makefile 中没有任何东西可以提供。

有几种方法可以做到这一点。最干净的可能是有两个目录:

debug_objs/
  ang.o
  naur.o
  gul.o

release_objs/
  ang.o
  naur.o
  gul.o

第二个最干净的是有不同的文件名:

  ang_debug.o
  naur_debug.o
  gul_debug.o

  ang_rel.o
  naur_rel.o
  gul_rel.o

无论哪种方式,您都可以为RELEASE_OBJSDEBUG_OBJS 编写规则(如果需要,我们可以提供帮助)。

现在看看库规则:

$(program_NAME_DEBUG): $(DEBUG_OBJS)
    $(LINK_DEBUG.c) -shared -Wl,-soname,$(program_NAME_DEBUG) $(DEBUG_OBJS) -o $(program_NAME_DEBUG)

$(program_NAME_RELEASE): $(RELEASE_OBJS)
    $(LINK_RELEASE.c) -shared -Wl,-soname,$(program_NAME_RELEASE) $(RELEASE_OBJS) -o $(program_NAME_RELEASE)

我们可以使用Automatic Variables 让它们更简洁:

$(program_NAME_DEBUG): $(DEBUG_OBJS)
    $(LINK_DEBUG.c) -shared -Wl,-soname,$@ $^ -o $@

$(program_NAME_RELEASE): $(RELEASE_OBJS)
    $(LINK_RELEASE.c) -shared -Wl,-soname,$@ $^ -o $@

(我们可以走得更远一点,但我们不要推它。)

现在为all。我们将$(program_NAME_RELEASE) 移动到它所属的先决条件列表中:

all: $(program_NAME_DEBUG) $(program_NAME_RELEASE)
    $(CP) $(program_NAME_DEBUG) $(BUILD_DIR)/debug/$(program_NAME_DEBUG)
    $(RM) $(program_NAME_DEBUG)
    $(RM) $(DEBUG_OBJS)
    $(CP) $(program_NAME_RELEASE) $(BUILD_DIR)/release/$(program_NAME_RELEASE)
    $(RM) $(program_NAME_RELEASE)
    $(RM) $(RELEASE_OBJS)

但是没有理由删除目标文件,因为它们不会发生冲突,如果它们是中间文件,Make 无论如何都会删除它们。当我们可以mv 时,不需要$(CP) 然后$(RM)。而且不需要mv;如果那是库所属的地方,我们可以首先在那里构建它们:

all: $(program_NAME_DEBUG) $(program_NAME_RELEASE)

$(program_NAME_DEBUG): $(DEBUG_OBJS)
    $(LINK_DEBUG.c) -shared -Wl,-soname,$@ $^ -o $(BUILD_DIR)/debug/$@

$(program_NAME_RELEASE): $(RELEASE_OBJS)
    $(LINK_RELEASE.c) -shared -Wl,-soname,$@ $^ -o $(BUILD_DIR)/release/$@

编辑:
构建目标文件(我猜你使用的是 C):

OBJS:= ang.o naur.o gul.o
DEBUG_OBJS := $(addprefix debug_objs/,$(OBJS))
RELEASE_OBJS := $(addprefix release_objs/,$(OBJS))

debug_objs/%.o: $(SOURCE_DIR)/%.c
    $(CC) $(DEBUG_FLAGS) $< -o $@

release_objs/%.o: $(SOURCE_DIR)/%.c
    $(CC) $(RELEASE_FLAGS) $< -o $@

(只要确保这些目录存在——您也可以自动执行该操作,但您一天所做的更改就足够了。)

【讨论】:

  • 这看起来很棒 - 我认为我的部分困惑是目标文件是自动生成的,这不是我想要的,正如你所说,我希望有两个目标来构建这些目标文件分成两个目录,一个调试目录和一个发布目录——你是怎么做的?
  • 我可能会忽略简洁性改进 - 对于初学者来说,它会使 Makefile 难以理解
  • @Hiett,我想过改进图书馆规则,但我不想一次打你太多...
  • @Hiett,让我们一步一步来。你能构建一个对象(例如make debug_objs/ang.o)吗?
  • @Hiett,你确定吗?我知道 makefile 有一个艰难的学习曲线,但这个并不是真的那么糟糕,我很乐意引导你完成它......
【解决方案2】:

这个改变应该可以做到:

all: $(program_NAME_DEBUG) $(program_NAME_RELEASE)

否则,make all 无法生成 $(program_NAME_RELEASE),这就是错误消息告诉您的内容。

【讨论】:

  • 谢谢会尝试,但目标文件的删除是否会以正确的顺序发生,例如在构建库的调试版本和发布版本之间?
  • 这在构建两个库时确实有效,但是因为在每种情况下都使用相同的目标文件(即它们在构建之间不会被删除),所以我有效地构建了同一个库两次这不是我想做的事
  • 我明白了。 Makefile 可能很棘手,所以让我们考虑一下。 $(program_NAME_DEBUG): 目标显然不是在构建 libGlamdring_rel.so。你希望它会吗?
  • hmmm,是在链接阶段 tehn 应用的编译标志,例如可以使用相同的目标文件来构建调试和发布版本吗?
  • 通过您的修改,看起来为调试和发布版本生成了相同的库,即它们的大小完全相同,以前当我使用不同的 Makefile 构建调试和发布版本时,它们已经不同尺寸,版本更小。这是一种尝试让一个 Makefile 构建两个版本,而无需我在两次编译之间进行任何修改
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多